5 #include "TransitionState.h"
7 #include "../app/Application.h"
8 #include "../app/Input.h"
9 #include "../battle/BattleState.h"
10 #include "../common/GameConfig.h"
11 #include "../common/GameState.h"
12 #include "../graphics/ColorFade.h"
13 #include "../menu/PartyMenu.h"
17 using app::Application;
19 using battle::BattleState;
20 using common::GameConfig;
23 using graphics::ColorFade;
24 using menu::PartyMenu;
28 MapState::MapState(GameConfig *g, Map *map)
45 void MapState::OnEnterState(SDL_Surface *screen) {
46 camera.Resize(screen->w, screen->h);
50 void MapState::OnExitState(SDL_Surface *screen) {
54 void MapState::OnResumeState(SDL_Surface *screen) {
55 camera.Resize(screen->w, screen->h);
58 void MapState::OnPauseState(SDL_Surface *screen) {
62 void MapState::OnResize(int width, int height) {
63 camera.Resize(width, height);
67 void MapState::HandleEvents(const Input &input) {
68 if (input.JustPressed(Input::ACTION_X)) {
69 Ctrl().PushState(new PartyMenu(game));
73 if (!controlled) return;
75 if (input.IsDown(Input::PAD_UP)) {
76 nextDirection = Entity::ORIENTATION_NORTH;
77 } else if (input.IsDown(Input::PAD_RIGHT)) {
78 nextDirection = Entity::ORIENTATION_EAST;
79 } else if (input.IsDown(Input::PAD_DOWN)) {
80 nextDirection = Entity::ORIENTATION_SOUTH;
81 } else if (input.IsDown(Input::PAD_LEFT)) {
82 nextDirection = Entity::ORIENTATION_WEST;
87 pushing = input.IsDown(Input::ACTION_A);
89 if (input.JustPressed(Input::DEBUG_1)) {
94 void MapState::UpdateWorld(Uint32 deltaT) {
95 if (controlled && controlled->TileLock(map->Tileset()->Size())) {
98 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
103 void MapState::OnTileLock() {
104 if (moveTimer.Running() && !moveTimer.JustHit()) return;
106 Vector<int> nowLock(ToInt(controlled->Position()));
108 if (nowLock != lastLock) {
109 event = OnGridLock();
112 } else if (moveTimer.JustHit()) {
113 event = OnGridLock();
121 if (nextDirection >= 0) {
123 bool blocked(CheckBlocking());
125 controlled->SetOrientation(Entity::Orientation(nextDirection));
128 controlled->SetSpeed(walkingSpeed);
131 pushed->SetOrientation(Entity::Orientation(nextDirection));
132 pushed->SetSpeed(walkingSpeed);
133 controlled->SetPushing();
135 controlled->SetHandsFree();
138 controlled->SetSpeed(0);
139 StopFollowers(*controlled);
140 if (!moveTimer.Running()) {
141 int tileSize((controlled->GetOrientation() % 2) ? map->Tileset()->Width() : map->Tileset()->Height());
142 moveTimer = PhysicsTimers().StartInterval(tileSize/walkingSpeed.Int());
146 if (!controlled->AnimationRunning()) {
147 controlled->StartAnimation(*this);
151 controlled->SetSpeed(0);
152 StopFollowers(*controlled);
153 controlled->StopAnimation();
164 bool MapState::CheckBlocking() {
169 const Tile *tile(map->TileAt(ToInt(controlled->Position())));
170 Vector<int> direction;
171 switch (nextDirection) {
172 case Entity::ORIENTATION_NORTH:
173 if (tile && tile->BlocksNorth()) {
176 direction = Vector<int>(0, -map->Tileset()->Height());
179 case Entity::ORIENTATION_EAST:
180 if (tile && tile->BlocksEast()) {
183 direction = Vector<int>(map->Tileset()->Width(), 0);
186 case Entity::ORIENTATION_SOUTH:
187 if (tile && tile->BlocksSouth()) {
190 direction = Vector<int>(0, map->Tileset()->Height());
193 case Entity::ORIENTATION_WEST:
194 if (tile && tile->BlocksWest()) {
197 direction = Vector<int>(-map->Tileset()->Width(), 0);
203 Vector<int> nextTilePosition(direction + ToInt(controlled->Position()));
204 Vector<int> nextTileCoords(map->TileCoordinates(nextTilePosition));
205 for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
206 const Entity &e(**i);
207 if (map->TileCoordinates(ToInt(e.Position())) != nextTileCoords) continue;
208 if (!e.Blocking()) continue;
209 if (!pushing || !e.Pushable()) return true;
210 if (CheckBlocking(nextTilePosition, Entity::Orientation(nextDirection))) return true;
216 bool MapState::CheckBlocking(const Vector<int> &position, Entity::Orientation direction) const {
217 const Tile *tile(map->TileAt(position));
218 Vector<int> directionVector;
220 case Entity::ORIENTATION_NORTH:
221 if (tile && tile->BlocksNorth()) {
224 directionVector = Vector<int>(0, -map->Tileset()->Height());
227 case Entity::ORIENTATION_EAST:
228 if (tile && tile->BlocksEast()) {
231 directionVector = Vector<int>(map->Tileset()->Width(), 0);
234 case Entity::ORIENTATION_SOUTH:
235 if (tile && tile->BlocksSouth()) {
238 directionVector = Vector<int>(0, map->Tileset()->Height());
241 case Entity::ORIENTATION_WEST:
242 if (tile && tile->BlocksWest()) {
245 directionVector = Vector<int>(-map->Tileset()->Width(), 0);
251 Vector<int> nextTileCoords(map->TileCoordinates(directionVector + position));
252 for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
253 const Entity &e(**i);
254 if (map->TileCoordinates(ToInt(e.Position())) == nextTileCoords && e.Blocking()) {
261 bool MapState::OnGridLock() {
267 return CheckMonster() || CheckLockTrigger();
271 void MapState::LockEntities() {
272 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
273 if (*i == controlled) {
277 (*i)->Position().Lock(map->Tileset()->Size());
281 bool MapState::CheckMonster() {
282 Vector<int> coords(map->TileCoordinates(ToInt(controlled->Position())));
283 Vector<int> neighbor[4];
284 neighbor[0] = Vector<int>(coords.X(), coords.Y() - 1); // N
285 neighbor[1] = Vector<int>(coords.X() + 1, coords.Y()); // E
286 neighbor[2] = Vector<int>(coords.X(), coords.Y() + 1); // S
287 neighbor[3] = Vector<int>(coords.X() - 1, coords.Y()); // W
289 for (int i(0); i < 4; ++i) {
290 for (std::vector<Entity *>::iterator e(entities.begin()), end(entities.end()); e != end; ++e) {
291 if ((*e)->Hostile() && map->TileCoordinates(ToInt((*e)->Position())) == neighbor[i]) {
292 // TODO: check for turn advantage, see #26
293 // TODO: other transition
294 BattleState *battleState(new BattleState(game, map->BattleBackgroundAt(ToInt((*e)->Position())), (*e)->PartyLayout()));
295 for (int i(0); i < 4; ++i) {
296 if (game->state->party[i]) {
297 battleState->AddHero(*game->state->party[i]);
300 if (game->state->capsule) {
301 battleState->SetCapsule(&game->state->GetCapsule());
303 for (battle::Monster **monster((*e)->MonstersBegin()); monster != (*e)->MonstersEnd(); ++monster) {
304 battleState->AddMonster(**monster);
307 ColorFade *fadeIn(new ColorFade(this, 0, 500, true));
308 fadeIn->SetLeadInTime(500);
309 ColorFade *fadeOut(new ColorFade(this, 0, 500));
310 fadeOut->SetLeadOutTime(500);
312 Ctrl().PushState(fadeIn);
313 Ctrl().PushState(battleState);
314 Ctrl().PushState(fadeOut);
315 // TODO: move entity erase to happen after the transition or battle
324 bool MapState::CheckLockTrigger() {
325 Trigger *trigger(map->TriggerAt(ToInt(controlled->Position())));
326 if (!trigger || trigger->GetType() != Trigger::TYPE_CONTACT) return false;
327 RunTrigger(*trigger);
331 void MapState::OnMove(bool realMove) {
332 if (CheckMoveTrigger()) {
335 // TODO: evaluate monster movements
337 UpdateFollower(*controlled);
339 StopFollowers(*controlled);
343 bool MapState::CheckMoveTrigger() {
344 Trigger *trigger(map->TriggerAt(ToInt(controlled->Position())));
345 if (!trigger || int(trigger->GetType()) != nextDirection) return false;
346 RunTrigger(*trigger);
350 void MapState::RunTrigger(Trigger &trigger) {
351 if (!trigger.HasScript()) return;
352 runner.Run(*this, trigger.GetScript());
355 void MapState::UpdateFollower(Entity &e) {
356 if (!e.Follower()) return;
358 Entity &f(*e.Follower());
361 Vector<int> coords(map->TileCoordinates(ToInt(e.Position())));
362 Vector<int> fCoords(map->TileCoordinates(ToInt(f.Position())));
363 Vector<int> direction(coords - fCoords);
365 if (direction.Y() < 0) {
366 f.SetOrientation(Entity::ORIENTATION_NORTH);
367 f.SetSpeed(walkingSpeed);
368 f.StartAnimation(*this);
369 } else if (direction.X() > 0) {
370 f.SetOrientation(Entity::ORIENTATION_EAST);
371 f.SetSpeed(walkingSpeed);
372 f.StartAnimation(*this);
373 } else if (direction.Y() > 0) {
374 f.SetOrientation(Entity::ORIENTATION_SOUTH);
375 f.SetSpeed(walkingSpeed);
376 f.StartAnimation(*this);
377 } else if (direction.X() < 0) {
378 f.SetOrientation(Entity::ORIENTATION_WEST);
379 f.SetSpeed(walkingSpeed);
380 f.StartAnimation(*this);
387 void MapState::StopFollowers(Entity &e) {
388 for (Entity *f(e.Follower()); f; f = f->Follower()) {
395 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
397 Vector<int> position(coordinates * map->Tileset()->Size());
398 for (Entity *e(controlled); e; e = e->Follower()) {
399 e->Position() = position;
400 e->SetOrientation(controlled->GetOrientation());
406 void MapState::UnloadMap() {
410 void MapState::LoadMap(Map *m) {
412 for (Entity *e(m->EntitiesBegin()), *end(m->EntitiesEnd()); e != end; ++e) {
413 entities.push_back(e);
414 e->ResetPosition(map->Tileset()->Size());
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.IntegerRegister(0)) {
447 Ctrl().PushState(new ColorFade(this, 0, 500, true));
448 Ctrl().PushState(new TransitionState(this, reinterpret_cast<Map *>(r.AddressRegister(0)), r.VectorRegister(0)));
449 ColorFade *fadeOut(new ColorFade(this, 0, 500, false));
450 fadeOut->SetLeadOutTime(500);
451 Ctrl().PushState(fadeOut);