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"
20 #include "../menu/PartyMenu.h"
24 using app::Application;
26 using battle::BattleState;
27 using common::GameConfig;
28 using geometry::Vector;
29 using graphics::ColorFade;
30 using menu::PartyMenu;
34 MapState::MapState(GameConfig *g, Map *map)
51 void MapState::OnEnterState(SDL_Surface *screen) {
52 camera.Resize(screen->w, screen->h);
56 void MapState::OnExitState(SDL_Surface *screen) {
60 void MapState::OnResumeState(SDL_Surface *screen) {
61 camera.Resize(screen->w, screen->h);
64 void MapState::OnPauseState(SDL_Surface *screen) {
68 void MapState::OnResize(int width, int height) {
69 camera.Resize(width, height);
73 void MapState::HandleEvents(const Input &input) {
74 if (input.JustPressed(Input::ACTION_X)) {
75 Ctrl().PushState(new PartyMenu(game));
79 if (!controlled) return;
81 if (input.IsDown(Input::PAD_UP)) {
82 nextDirection = Entity::ORIENTATION_NORTH;
83 } else if (input.IsDown(Input::PAD_RIGHT)) {
84 nextDirection = Entity::ORIENTATION_EAST;
85 } else if (input.IsDown(Input::PAD_DOWN)) {
86 nextDirection = Entity::ORIENTATION_SOUTH;
87 } else if (input.IsDown(Input::PAD_LEFT)) {
88 nextDirection = Entity::ORIENTATION_WEST;
93 pushing = input.IsDown(Input::ACTION_A);
95 if (input.JustPressed(Input::DEBUG_1)) {
100 void MapState::UpdateWorld(float deltaT) {
101 if (controlled && controlled->TileLock(map->Tileset()->Size())) {
104 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
105 (*i)->Update(deltaT);
109 void MapState::OnTileLock() {
110 if (moveTimer.Running() && !moveTimer.JustHit()) return;
112 Vector<int> nowLock(controlled->Position());
114 if (nowLock != lastLock) {
115 event = OnGridLock();
118 } else if (moveTimer.JustHit()) {
119 event = OnGridLock();
127 if (nextDirection >= 0) {
129 bool blocked(CheckBlocking());
131 controlled->SetOrientation(Entity::Orientation(nextDirection));
134 controlled->SetSpeed(walkingSpeed);
137 pushed->SetOrientation(Entity::Orientation(nextDirection));
138 pushed->SetSpeed(walkingSpeed);
139 controlled->SetPushing();
141 controlled->SetHandsFree();
144 controlled->SetSpeed(0.0f);
145 StopFollowers(*controlled);
146 if (!moveTimer.Running()) {
147 int tileSize((controlled->GetOrientation() % 2) ? map->Tileset()->Width() : map->Tileset()->Height());
148 moveTimer = PhysicsTimers().StartInterval(tileSize/walkingSpeed);
152 if (!controlled->AnimationRunning()) {
153 controlled->StartAnimation(*this);
157 controlled->SetSpeed(0.0f);
158 StopFollowers(*controlled);
159 controlled->StopAnimation();
162 pushed->SetSpeed(0.0f);
170 bool MapState::CheckBlocking() {
172 pushed->SetSpeed(0.0f);
175 const Tile *tile(map->TileAt(controlled->Position()));
176 Vector<int> direction;
177 switch (nextDirection) {
178 case Entity::ORIENTATION_NORTH:
179 if (tile && tile->BlocksNorth()) {
182 direction = Vector<int>(0, -map->Tileset()->Height());
185 case Entity::ORIENTATION_EAST:
186 if (tile && tile->BlocksEast()) {
189 direction = Vector<int>(map->Tileset()->Width(), 0);
192 case Entity::ORIENTATION_SOUTH:
193 if (tile && tile->BlocksSouth()) {
196 direction = Vector<int>(0, map->Tileset()->Height());
199 case Entity::ORIENTATION_WEST:
200 if (tile && tile->BlocksWest()) {
203 direction = Vector<int>(-map->Tileset()->Width(), 0);
209 Vector<int> nextTilePosition(direction + controlled->Position());
210 Vector<int> nextTileCoords(map->TileCoordinates(nextTilePosition));
211 for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
212 const Entity &e(**i);
213 if (map->TileCoordinates(e.Position()) != nextTileCoords) continue;
214 if (!e.Blocking()) continue;
215 if (!pushing || !e.Pushable()) return true;
216 if (CheckBlocking(nextTilePosition, Entity::Orientation(nextDirection))) return true;
222 bool MapState::CheckBlocking(const Vector<int> &position, Entity::Orientation direction) const {
223 const Tile *tile(map->TileAt(position));
224 Vector<int> directionVector;
226 case Entity::ORIENTATION_NORTH:
227 if (tile && tile->BlocksNorth()) {
230 directionVector = Vector<int>(0, -map->Tileset()->Height());
233 case Entity::ORIENTATION_EAST:
234 if (tile && tile->BlocksEast()) {
237 directionVector = Vector<int>(map->Tileset()->Width(), 0);
240 case Entity::ORIENTATION_SOUTH:
241 if (tile && tile->BlocksSouth()) {
244 directionVector = Vector<int>(0, map->Tileset()->Height());
247 case Entity::ORIENTATION_WEST:
248 if (tile && tile->BlocksWest()) {
251 directionVector = Vector<int>(-map->Tileset()->Width(), 0);
257 Vector<int> nextTileCoords(map->TileCoordinates(directionVector + position));
258 for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
259 const Entity &e(**i);
260 if (map->TileCoordinates(e.Position()) == nextTileCoords && e.Blocking()) {
267 bool MapState::OnGridLock() {
273 return CheckMonster() || CheckLockTrigger();
277 void MapState::LockEntities() {
278 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
279 if (*i == controlled) {
283 (*i)->Position().Lock(map->Tileset()->Size());
287 bool MapState::CheckMonster() {
288 Vector<int> coords(map->TileCoordinates(controlled->Position()));
289 Vector<int> neighbor[4];
290 neighbor[0] = Vector<int>(coords.X(), coords.Y() - 1); // N
291 neighbor[1] = Vector<int>(coords.X() + 1, coords.Y()); // E
292 neighbor[2] = Vector<int>(coords.X(), coords.Y() + 1); // S
293 neighbor[3] = Vector<int>(coords.X() - 1, coords.Y()); // W
295 for (int i(0); i < 4; ++i) {
296 for (std::vector<Entity *>::iterator e(entities.begin()), end(entities.end()); e != end; ++e) {
297 if ((*e)->Hostile() && map->TileCoordinates((*e)->Position()) == neighbor[i]) {
298 // TODO: check for turn advantage, see #26
299 // TODO: other transition
300 BattleState *battleState(new BattleState(game, map->BattleBackgroundAt((*e)->Position()), (*e)->PartyLayout()));
301 for (int i(0); i < 4; ++i) {
302 if (game->state->party[i]) {
303 battleState->AddHero(*game->state->party[i]);
306 for (battle::Monster *monster((*e)->MonstersBegin()); monster != (*e)->MonstersEnd(); ++monster) {
307 battleState->AddMonster(*monster);
310 ColorFade *fadeIn(new ColorFade(this, 0, 500, true));
311 fadeIn->SetLeadInTime(500);
312 ColorFade *fadeOut(new ColorFade(this, 0, 500));
313 fadeOut->SetLeadOutTime(500);
315 Ctrl().PushState(fadeIn);
316 Ctrl().PushState(battleState);
317 Ctrl().PushState(fadeOut);
318 // TODO: move entity erase to happen after the transition or battle
321 // needed information here:
322 // - battle background (from tile/area/map)
323 // - monsters + layout (from entity)
330 bool MapState::CheckLockTrigger() {
331 Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
332 if (!trigger || trigger->GetType() != Trigger::TYPE_CONTACT) return false;
333 RunTrigger(*trigger);
337 void MapState::OnMove(bool realMove) {
338 if (CheckMoveTrigger()) {
341 // TODO: evaluate monster movements
343 UpdateFollower(*controlled);
345 StopFollowers(*controlled);
349 bool MapState::CheckMoveTrigger() {
350 Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
351 if (!trigger || int(trigger->GetType()) != nextDirection) return false;
352 RunTrigger(*trigger);
356 void MapState::RunTrigger(Trigger &trigger) {
357 if (!trigger.HasScript()) return;
358 runner.Run(*this, trigger.GetScript());
361 void MapState::UpdateFollower(Entity &e) {
362 if (!e.Follower()) return;
364 Entity &f(*e.Follower());
367 Vector<int> coords(map->TileCoordinates(e.Position()));
368 Vector<int> fCoords(map->TileCoordinates(f.Position()));
369 Vector<int> direction(coords - fCoords);
371 if (direction.Y() < 0) {
372 f.SetOrientation(Entity::ORIENTATION_NORTH);
373 f.SetSpeed(walkingSpeed);
374 f.StartAnimation(*this);
375 } else if (direction.X() > 0) {
376 f.SetOrientation(Entity::ORIENTATION_EAST);
377 f.SetSpeed(walkingSpeed);
378 f.StartAnimation(*this);
379 } else if (direction.Y() > 0) {
380 f.SetOrientation(Entity::ORIENTATION_SOUTH);
381 f.SetSpeed(walkingSpeed);
382 f.StartAnimation(*this);
383 } else if (direction.X() < 0) {
384 f.SetOrientation(Entity::ORIENTATION_WEST);
385 f.SetSpeed(walkingSpeed);
386 f.StartAnimation(*this);
393 void MapState::StopFollowers(Entity &e) {
394 for (Entity *f(e.Follower()); f; f = f->Follower()) {
401 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
403 Vector<int> position(coordinates * map->Tileset()->Size());
404 for (Entity *e(controlled); e; e = e->Follower()) {
405 e->Position() = position;
406 e->SetOrientation(controlled->GetOrientation());
412 void MapState::UnloadMap() {
416 void MapState::LoadMap(Map *m) {
418 for (Entity *e(m->EntitiesBegin()), *end(m->EntitiesEnd()); e != end; ++e) {
419 entities.push_back(e);
420 e->ResetPosition(map->Tileset()->Size());
422 for (Entity *e(controlled); e; e = e->Follower()) {
423 entities.push_back(e);
428 void MapState::Render(SDL_Surface *screen) {
429 SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
431 Vector<int> offset(camera.CalculateOffset());
432 map->Render(screen, offset);
435 map->RenderDebug(screen, offset);
438 std::sort(entities.begin(), entities.end(), ZCompare);
439 for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
440 (*i)->Render(screen, offset);
445 bool MapState::ZCompare(const Entity *lhs, const Entity *rhs) {
446 return lhs->Position().Y() < rhs->Position().Y();
450 void MapState::HandleSyscall(common::ScriptRunner &r) {
451 switch (r.IntegerRegister(0)) {
453 Ctrl().PushState(new ColorFade(this, 0, 500, true));
454 Ctrl().PushState(new TransitionState(this, reinterpret_cast<Map *>(r.AddressRegister(0)), r.VectorRegister(0)));
455 ColorFade *fadeOut(new ColorFade(this, 0, 500, false));
456 fadeOut->SetLeadOutTime(500);
457 Ctrl().PushState(fadeOut);