4 * Created on: Sep 29, 2012
12 #include "TransitionState.h"
14 #include "../app/Application.h"
15 #include "../app/Input.h"
16 #include "../battle/BattleState.h"
17 #include "../common/GameConfig.h"
18 #include "../common/GameState.h"
19 #include "../graphics/ColorFade.h"
23 using app::Application;
25 using battle::BattleState;
26 using common::GameConfig;
27 using geometry::Vector;
28 using graphics::ColorFade;
32 MapState::MapState(GameConfig *g, Map *map)
50 void MapState::EnterState(Application &c, SDL_Surface *screen) {
52 camera.Resize(screen->w, screen->h);
56 void MapState::ExitState(Application &ctrl, SDL_Surface *screen) {
60 void MapState::ResumeState(Application &ctrl, SDL_Surface *screen) {
61 camera.Resize(screen->w, screen->h);
64 void MapState::PauseState(Application &ctrl, SDL_Surface *screen) {
68 void MapState::Resize(int width, int height) {
69 camera.Resize(width, height);
73 void MapState::HandleEvents(const Input &input) {
74 if (!controlled) return;
76 if (input.IsDown(Input::PAD_UP)) {
77 nextDirection = Entity::ORIENTATION_NORTH;
78 } else if (input.IsDown(Input::PAD_RIGHT)) {
79 nextDirection = Entity::ORIENTATION_EAST;
80 } else if (input.IsDown(Input::PAD_DOWN)) {
81 nextDirection = Entity::ORIENTATION_SOUTH;
82 } else if (input.IsDown(Input::PAD_LEFT)) {
83 nextDirection = Entity::ORIENTATION_WEST;
88 pushing = input.IsDown(Input::ACTION_A);
90 if (input.JustPressed(Input::DEBUG_1)) {
95 void MapState::UpdateWorld(float deltaT) {
96 if (controlled && controlled->TileLock(map->Tileset()->Size())) {
99 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
100 (*i)->Update(deltaT);
104 void MapState::OnTileLock() {
105 if (moveTimer.Running() && !moveTimer.JustHit()) return;
107 Vector<int> nowLock(controlled->Position());
109 if (nowLock != lastLock) {
110 event = OnGridLock();
113 } else if (moveTimer.JustHit()) {
114 event = OnGridLock();
122 if (nextDirection >= 0) {
124 bool blocked(CheckBlocking());
126 controlled->SetOrientation(Entity::Orientation(nextDirection));
129 controlled->SetSpeed(walkingSpeed);
132 pushed->SetOrientation(Entity::Orientation(nextDirection));
133 pushed->SetSpeed(walkingSpeed);
134 controlled->SetPushing();
136 controlled->SetHandsFree();
139 controlled->SetSpeed(0.0f);
140 StopFollowers(*controlled);
141 if (!moveTimer.Running()) {
142 int tileSize((controlled->GetOrientation() % 2) ? map->Tileset()->Width() : map->Tileset()->Height());
143 moveTimer = PhysicsTimers().StartInterval(tileSize/walkingSpeed);
147 if (!controlled->AnimationRunning()) {
148 controlled->StartAnimation(*this);
152 controlled->SetSpeed(0.0f);
153 StopFollowers(*controlled);
154 controlled->StopAnimation();
157 pushed->SetSpeed(0.0f);
165 bool MapState::CheckBlocking() {
167 pushed->SetSpeed(0.0f);
170 const Tile *tile(map->TileAt(controlled->Position()));
171 Vector<int> direction;
172 switch (nextDirection) {
173 case Entity::ORIENTATION_NORTH:
174 if (tile && tile->BlocksNorth()) {
177 direction = Vector<int>(0, -map->Tileset()->Height());
180 case Entity::ORIENTATION_EAST:
181 if (tile && tile->BlocksEast()) {
184 direction = Vector<int>(map->Tileset()->Width(), 0);
187 case Entity::ORIENTATION_SOUTH:
188 if (tile && tile->BlocksSouth()) {
191 direction = Vector<int>(0, map->Tileset()->Height());
194 case Entity::ORIENTATION_WEST:
195 if (tile && tile->BlocksWest()) {
198 direction = Vector<int>(-map->Tileset()->Width(), 0);
204 Vector<int> nextTilePosition(direction + controlled->Position());
205 Vector<int> nextTileCoords(map->TileCoordinates(nextTilePosition));
206 for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
207 const Entity &e(**i);
208 if (map->TileCoordinates(e.Position()) != nextTileCoords) continue;
209 if (!e.Blocking()) continue;
210 if (!pushing || !e.Pushable()) return true;
211 if (CheckBlocking(nextTilePosition, Entity::Orientation(nextDirection))) return true;
217 bool MapState::CheckBlocking(const Vector<int> &position, Entity::Orientation direction) const {
218 const Tile *tile(map->TileAt(position));
219 Vector<int> directionVector;
221 case Entity::ORIENTATION_NORTH:
222 if (tile && tile->BlocksNorth()) {
225 directionVector = Vector<int>(0, -map->Tileset()->Height());
228 case Entity::ORIENTATION_EAST:
229 if (tile && tile->BlocksEast()) {
232 directionVector = Vector<int>(map->Tileset()->Width(), 0);
235 case Entity::ORIENTATION_SOUTH:
236 if (tile && tile->BlocksSouth()) {
239 directionVector = Vector<int>(0, map->Tileset()->Height());
242 case Entity::ORIENTATION_WEST:
243 if (tile && tile->BlocksWest()) {
246 directionVector = Vector<int>(-map->Tileset()->Width(), 0);
252 Vector<int> nextTileCoords(map->TileCoordinates(directionVector + position));
253 for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
254 const Entity &e(**i);
255 if (map->TileCoordinates(e.Position()) == nextTileCoords && e.Blocking()) {
262 bool MapState::OnGridLock() {
268 return CheckMonster() || CheckLockTrigger();
272 void MapState::LockEntities() {
273 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
274 if (*i == controlled) {
278 (*i)->Position().Lock(map->Tileset()->Size());
282 bool MapState::CheckMonster() {
283 Vector<int> coords(map->TileCoordinates(controlled->Position()));
284 Vector<int> neighbor[4];
285 neighbor[0] = Vector<int>(coords.X(), coords.Y() - 1); // N
286 neighbor[1] = Vector<int>(coords.X() + 1, coords.Y()); // E
287 neighbor[2] = Vector<int>(coords.X(), coords.Y() + 1); // S
288 neighbor[3] = Vector<int>(coords.X() - 1, coords.Y()); // W
290 for (int i(0); i < 4; ++i) {
291 for (std::vector<Entity *>::iterator e(entities.begin()), end(entities.end()); e != end; ++e) {
292 if ((*e)->Hostile() && map->TileCoordinates((*e)->Position()) == neighbor[i]) {
293 // TODO: check for turn advantage, see #26
294 // TODO: other transition
295 BattleState *battleState(new BattleState(game, map->BattleBackgroundAt((*e)->Position()), (*e)->PartyLayout()));
296 for (int i(0); i < 4; ++i) {
297 if (game->state->party[i]) {
298 battleState->AddHero(*game->state->party[i]);
301 for (battle::Monster *monster((*e)->MonstersBegin()); monster != (*e)->MonstersEnd(); ++monster) {
302 battleState->AddMonster(*monster);
305 ColorFade *fadeIn(new ColorFade(this, 0, 500, true));
306 fadeIn->SetLeadInTime(500);
307 ColorFade *fadeOut(new ColorFade(this, 0, 500));
308 fadeOut->SetLeadOutTime(500);
310 ctrl->PushState(fadeIn);
311 ctrl->PushState(battleState);
312 ctrl->PushState(fadeOut);
313 // TODO: move entity erase to happen after the transition or battle
316 // needed information here:
317 // - battle background (from tile/area/map)
318 // - monsters + layout (from entity)
325 bool MapState::CheckLockTrigger() {
326 Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
327 if (!trigger || trigger->GetType() != Trigger::TYPE_CONTACT) return false;
328 RunTrigger(*trigger);
332 void MapState::OnMove(bool realMove) {
333 if (CheckMoveTrigger()) {
336 // TODO: evaluate monster movements
338 UpdateFollower(*controlled);
340 StopFollowers(*controlled);
344 bool MapState::CheckMoveTrigger() {
345 Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
346 if (!trigger || int(trigger->GetType()) != nextDirection) return false;
347 RunTrigger(*trigger);
351 void MapState::RunTrigger(Trigger &trigger) {
352 if (!trigger.HasScript()) return;
353 runner.Run(*this, trigger.GetScript());
356 void MapState::UpdateFollower(Entity &e) {
357 if (!e.Follower()) return;
359 Entity &f(*e.Follower());
362 Vector<int> coords(map->TileCoordinates(e.Position()));
363 Vector<int> fCoords(map->TileCoordinates(f.Position()));
364 Vector<int> direction(coords - fCoords);
366 if (direction.Y() < 0) {
367 f.SetOrientation(Entity::ORIENTATION_NORTH);
368 f.SetSpeed(walkingSpeed);
369 f.StartAnimation(*this);
370 } else if (direction.X() > 0) {
371 f.SetOrientation(Entity::ORIENTATION_EAST);
372 f.SetSpeed(walkingSpeed);
373 f.StartAnimation(*this);
374 } else if (direction.Y() > 0) {
375 f.SetOrientation(Entity::ORIENTATION_SOUTH);
376 f.SetSpeed(walkingSpeed);
377 f.StartAnimation(*this);
378 } else if (direction.X() < 0) {
379 f.SetOrientation(Entity::ORIENTATION_WEST);
380 f.SetSpeed(walkingSpeed);
381 f.StartAnimation(*this);
388 void MapState::StopFollowers(Entity &e) {
389 for (Entity *f(e.Follower()); f; f = f->Follower()) {
396 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
398 Vector<int> position(coordinates * map->Tileset()->Size());
399 for (Entity *e(controlled); e; e = e->Follower()) {
400 e->Position() = position;
401 e->SetOrientation(controlled->GetOrientation());
407 void MapState::UnloadMap() {
411 void MapState::LoadMap(Map *m) {
413 for (Entity *e(m->EntitiesBegin()), *end(m->EntitiesEnd()); e != end; ++e) {
414 entities.push_back(e);
416 for (Entity *e(controlled); e; e = e->Follower()) {
417 entities.push_back(e);
422 void MapState::Render(SDL_Surface *screen) {
423 SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
425 Vector<int> offset(camera.CalculateOffset());
426 map->Render(screen, offset);
429 map->RenderDebug(screen, offset);
432 std::sort(entities.begin(), entities.end(), ZCompare);
433 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
434 (*i)->Render(screen, offset);
439 bool MapState::ZCompare(const Entity *lhs, const Entity *rhs) {
440 return lhs->Position().Y() < rhs->Position().Y();
444 void MapState::HandleSyscall(common::ScriptRunner &r) {
445 switch (r.Integer0()) {
447 ctrl->PushState(new ColorFade(this, 0, 500, true));
448 ctrl->PushState(new TransitionState(this, reinterpret_cast<Map *>(r.Address0()), r.Vector0()));
449 ColorFade *fadeOut(new ColorFade(this, 0, 500, false));
450 fadeOut->SetLeadOutTime(500);
451 ctrl->PushState(fadeOut);