]> git.localhorst.tv Git - l2e.git/blob - src/map/MapState.cpp
8f5a1277e610b1d86e787eb8c2c8fef009278ce8
[l2e.git] / src / map / MapState.cpp
1 /*
2  * MapState.cpp
3  *
4  *  Created on: Sep 29, 2012
5  *      Author: holy
6  */
7
8 #include "MapState.h"
9
10 #include "Map.h"
11 #include "Tile.h"
12 #include "TransitionState.h"
13 #include "Trigger.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
21 #include <algorithm>
22
23 using app::Application;
24 using app::Input;
25 using battle::BattleState;
26 using common::GameConfig;
27 using geometry::Vector;
28 using graphics::ColorFade;
29
30 namespace map {
31
32 MapState::MapState(GameConfig *g, Map *map)
33 : game(g)
34 , ctrl(0)
35 , map(map)
36 , controlled(0)
37 , tempTarget(20, 20)
38 , camera(100, 100, &tempTarget)
39 , walkingSpeed(64)
40 , nextDirection(-1)
41 , afterLock(false)
42 , skipLock(false)
43 , debug(false) {
44
45 }
46
47
48 void MapState::EnterState(Application &c, SDL_Surface *screen) {
49         ctrl = &c;
50         camera.Resize(screen->w, screen->h);
51         LoadMap(map);
52 }
53
54 void MapState::ExitState(Application &ctrl, SDL_Surface *screen) {
55
56 }
57
58 void MapState::ResumeState(Application &ctrl, SDL_Surface *screen) {
59         camera.Resize(screen->w, screen->h);
60 }
61
62 void MapState::PauseState(Application &ctrl, SDL_Surface *screen) {
63
64 }
65
66 void MapState::Resize(int width, int height) {
67         camera.Resize(width, height);
68 }
69
70
71 void MapState::HandleEvents(const Input &input) {
72         if (!controlled) return;
73
74         if (input.IsDown(Input::PAD_UP)) {
75                 nextDirection = Entity::ORIENTATION_NORTH;
76         } else if (input.IsDown(Input::PAD_RIGHT)) {
77                 nextDirection = Entity::ORIENTATION_EAST;
78         } else if (input.IsDown(Input::PAD_DOWN)) {
79                 nextDirection = Entity::ORIENTATION_SOUTH;
80         } else if (input.IsDown(Input::PAD_LEFT)) {
81                 nextDirection = Entity::ORIENTATION_WEST;
82         } else {
83                 nextDirection = -1;
84         }
85
86         if (input.JustPressed(Input::DEBUG_1)) {
87                 debug = !debug;
88         }
89 }
90
91 void MapState::UpdateWorld(float deltaT) {
92         if (controlled && controlled->TileLock(map->Tileset()->Size())) {
93                 OnTileLock();
94         }
95         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
96                 (*i)->Update(deltaT);
97         }
98 }
99
100 void MapState::OnTileLock() {
101         if (moveTimer.Running() && !moveTimer.JustHit()) return;
102
103         Vector<int> nowLock(controlled->Position());
104         bool event(false);
105         if (nowLock != lastLock) {
106                 event = OnGridLock();
107                 afterLock = true;
108                 moveTimer.Clear();
109         } else if (moveTimer.JustHit()) {
110                 event = OnGridLock();
111                 afterLock = true;
112         }
113
114         if (event) {
115                 return;
116         }
117
118         if (nextDirection >= 0) {
119                 bool blocked(CheckBlocking());
120                 if (afterLock) {
121                         OnMove(!blocked);
122                         afterLock = false;
123                 }
124                 controlled->SetOrientation(Entity::Orientation(nextDirection));
125                 if (!blocked) {
126                         controlled->SetSpeed(walkingSpeed);
127                         moveTimer.Clear();
128                 } else {
129                         controlled->SetSpeed(0.0f);
130                         StopFollowers(*controlled);
131                         if (!moveTimer.Running()) {
132                                 int tileSize((controlled->GetOrientation() % 2) ? map->Tileset()->Width() : map->Tileset()->Height());
133                                 moveTimer = PhysicsTimers().StartInterval(tileSize/walkingSpeed);
134                         }
135                 }
136                 if (!controlled->AnimationRunning()) {
137                         controlled->StartAnimation(*this);
138                 }
139         } else {
140                 controlled->SetSpeed(0.0f);
141                 StopFollowers(*controlled);
142                 controlled->StopAnimation();
143                 moveTimer.Clear();
144         }
145
146         lastLock = nowLock;
147 }
148
149 bool MapState::CheckBlocking() const {
150         const Tile *tile(map->TileAt(controlled->Position()));
151         if (!tile) {
152                 return false;
153         }
154         Vector<int> nextPosition;
155         switch (nextDirection) {
156                 case Entity::ORIENTATION_NORTH:
157                         if (tile->BlocksNorth()) {
158                                 return true;
159                         } else {
160                                 nextPosition = Vector<int>(
161                                                 controlled->Position().X(),
162                                                 controlled->Position().Y() - map->Tileset()->Height());
163                         }
164                         break;
165                 case Entity::ORIENTATION_EAST:
166                         if (tile->BlocksEast()) {
167                                 return true;
168                         } else {
169                                 nextPosition = Vector<int>(
170                                                 controlled->Position().X() + map->Tileset()->Width(),
171                                                 controlled->Position().Y());
172                         }
173                         break;
174                 case Entity::ORIENTATION_SOUTH:
175                         if (tile->BlocksSouth()) {
176                                 return true;
177                         } else {
178                                 nextPosition = Vector<int>(
179                                                 controlled->Position().X(),
180                                                 controlled->Position().Y() + map->Tileset()->Height());
181                         }
182                         break;
183                 case Entity::ORIENTATION_WEST:
184                         if (tile->BlocksWest()) {
185                                 return true;
186                         } else {
187                                 nextPosition = Vector<int>(
188                                                 controlled->Position().X() - map->Tileset()->Width(),
189                                                 controlled->Position().Y());
190                         }
191                         break;
192                 default:
193                         return false;
194         }
195         Vector<int> nextTileCoords(map->TileCoordinates(nextPosition));
196         for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
197                 const Entity &e(**i);
198                 if (map->TileCoordinates(e.Position()) == nextTileCoords && e.Blocking()) {
199                         return true;
200                 }
201         }
202         return false;
203 }
204
205 bool MapState::OnGridLock() {
206         if (skipLock) {
207                 skipLock = false;
208                 return false;
209         } else {
210                 LockEntities();
211                 return CheckMonster() || CheckTrigger();
212         }
213 }
214
215 void MapState::LockEntities() {
216         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
217                 if (*i == controlled) {
218                         // don't lock player
219                         continue;
220                 }
221                 (*i)->Position().Lock(map->Tileset()->Size());
222         }
223 }
224
225 bool MapState::CheckMonster() {
226         Vector<int> coords(map->TileCoordinates(controlled->Position()));
227         Vector<int> neighbor[4];
228         neighbor[0] = Vector<int>(coords.X(), coords.Y() - 1); // N
229         neighbor[1] = Vector<int>(coords.X() + 1, coords.Y()); // E
230         neighbor[2] = Vector<int>(coords.X(), coords.Y() + 1); // S
231         neighbor[3] = Vector<int>(coords.X() - 1, coords.Y()); // W
232
233         for (int i(0); i < 4; ++i) {
234                 for (std::vector<Entity *>::iterator e(entities.begin()), end(entities.end()); e != end; ++e) {
235                         if ((*e)->Hostile() && map->TileCoordinates((*e)->Position()) == neighbor[i]) {
236                                 // TODO: check for turn advantage, see #26
237                                 // TODO: other transition
238                                 BattleState *battleState(new BattleState(game, map->BattleBackgroundAt((*e)->Position()), (*e)->PartyLayout()));
239                                 for (int i(0); i < 4; ++i) {
240                                         if (game->state->party[i]) {
241                                                 battleState->AddHero(*game->state->party[i]);
242                                         }
243                                 }
244                                 for (battle::Monster *monster((*e)->MonstersBegin()); monster != (*e)->MonstersEnd(); ++monster) {
245                                         battleState->AddMonster(*monster);
246                                 }
247
248                                 ColorFade *fadeIn(new ColorFade(this, 0, 500, true));
249                                 fadeIn->SetLeadInTime(500);
250                                 ColorFade *fadeOut(new ColorFade(this, 0, 500));
251                                 fadeOut->SetLeadOutTime(500);
252
253                                 ctrl->PushState(fadeIn);
254                                 ctrl->PushState(battleState);
255                                 ctrl->PushState(fadeOut);
256                                 // TODO: move entity erase to happen after the transition or battle
257                                 entities.erase(e);
258                                 return true;
259                                 // needed information here:
260                                 //  - battle background (from tile/area/map)
261                                 //  - monsters + layout (from entity)
262                         }
263                 }
264         }
265         return false;
266 }
267
268 bool MapState::CheckTrigger() {
269         Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
270         if (trigger) {
271                 // TODO: run trigger script
272                 if (trigger->map) {
273                         ctrl->PushState(new ColorFade(this, 0, 500, true));
274                         ctrl->PushState(new TransitionState(this, trigger->map, trigger->target));
275                         ColorFade *fadeOut(new ColorFade(this, 0, 500, false));
276                         fadeOut->SetLeadOutTime(500);
277                         ctrl->PushState(fadeOut);
278                         return true;
279                 }
280         }
281         return false;
282 }
283
284 void MapState::OnMove(bool realMove) {
285         // TODO: evaluate monster movements
286         if (realMove) {
287                 UpdateFollower(*controlled);
288         } else {
289                 StopFollowers(*controlled);
290         }
291 }
292
293 void MapState::UpdateFollower(Entity &e) {
294         if (!e.Follower()) return;
295
296         Entity &f(*e.Follower());
297         UpdateFollower(f);
298
299         Vector<int> coords(map->TileCoordinates(e.Position()));
300         Vector<int> fCoords(map->TileCoordinates(f.Position()));
301         Vector<int> direction(coords - fCoords);
302
303         if (direction.Y() < 0) {
304                 f.SetOrientation(Entity::ORIENTATION_NORTH);
305                 f.SetSpeed(walkingSpeed);
306                 f.StartAnimation(*this);
307         } else if (direction.X() > 0) {
308                 f.SetOrientation(Entity::ORIENTATION_EAST);
309                 f.SetSpeed(walkingSpeed);
310                 f.StartAnimation(*this);
311         } else if (direction.Y() > 0) {
312                 f.SetOrientation(Entity::ORIENTATION_SOUTH);
313                 f.SetSpeed(walkingSpeed);
314                 f.StartAnimation(*this);
315         } else if (direction.X() < 0) {
316                 f.SetOrientation(Entity::ORIENTATION_WEST);
317                 f.SetSpeed(walkingSpeed);
318                 f.StartAnimation(*this);
319         } else {
320                 f.SetSpeed(0.0f);
321                 f.StopAnimation();
322         }
323 }
324
325 void MapState::StopFollowers(Entity &e) {
326         for (Entity *f(e.Follower()); f; f = f->Follower()) {
327                 f->SetSpeed(0.0f);
328                 f->StopAnimation();
329         }
330 }
331
332
333 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
334         UnloadMap();
335         Vector<int> position(coordinates * map->Tileset()->Size());
336         for (Entity *e(controlled); e; e = e->Follower()) {
337                 e->Position() = position;
338                 e->SetOrientation(controlled->GetOrientation());
339         }
340         LoadMap(newMap);
341         skipLock = true;
342 }
343
344 void MapState::UnloadMap() {
345         entities.clear();
346 }
347
348 void MapState::LoadMap(Map *m) {
349         map = m;
350         for (Entity *e(m->EntitiesBegin()), *end(m->EntitiesEnd()); e != end; ++e) {
351                 entities.push_back(e);
352         }
353         for (Entity *e(controlled); e; e = e->Follower()) {
354                 entities.push_back(e);
355         }
356 }
357
358
359 void MapState::Render(SDL_Surface *screen) {
360         SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
361
362         Vector<int> offset(camera.CalculateOffset());
363         map->Render(screen, offset);
364
365         if (debug) {
366                 map->RenderDebug(screen, offset);
367         }
368
369         std::sort(entities.begin(), entities.end(), ZCompare);
370         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
371                 (*i)->Render(screen, offset);
372         }
373 }
374
375
376 bool MapState::ZCompare(const Entity *lhs, const Entity *rhs) {
377         return lhs->Position().Y() < rhs->Position().Y();
378 }
379
380 }