]> git.localhorst.tv Git - l2e.git/blob - src/map/MapState.cpp
implemented pushable entities and pushing
[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 , pushed(0)
38 , tempTarget(20, 20)
39 , camera(100, 100, &tempTarget)
40 , walkingSpeed(64)
41 , nextDirection(-1)
42 , afterLock(false)
43 , skipLock(false)
44 , pushing(false)
45 , debug(false) {
46
47 }
48
49
50 void MapState::EnterState(Application &c, SDL_Surface *screen) {
51         ctrl = &c;
52         camera.Resize(screen->w, screen->h);
53         LoadMap(map);
54 }
55
56 void MapState::ExitState(Application &ctrl, SDL_Surface *screen) {
57
58 }
59
60 void MapState::ResumeState(Application &ctrl, SDL_Surface *screen) {
61         camera.Resize(screen->w, screen->h);
62 }
63
64 void MapState::PauseState(Application &ctrl, SDL_Surface *screen) {
65
66 }
67
68 void MapState::Resize(int width, int height) {
69         camera.Resize(width, height);
70 }
71
72
73 void MapState::HandleEvents(const Input &input) {
74         if (!controlled) return;
75
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;
84         } else {
85                 nextDirection = -1;
86         }
87
88         pushing = input.IsDown(Input::ACTION_A);
89
90         if (input.JustPressed(Input::DEBUG_1)) {
91                 debug = !debug;
92         }
93 }
94
95 void MapState::UpdateWorld(float deltaT) {
96         if (controlled && controlled->TileLock(map->Tileset()->Size())) {
97                 OnTileLock();
98         }
99         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
100                 (*i)->Update(deltaT);
101         }
102 }
103
104 void MapState::OnTileLock() {
105         if (moveTimer.Running() && !moveTimer.JustHit()) return;
106
107         Vector<int> nowLock(controlled->Position());
108         bool event(false);
109         if (nowLock != lastLock) {
110                 event = OnGridLock();
111                 afterLock = true;
112                 moveTimer.Clear();
113         } else if (moveTimer.JustHit()) {
114                 event = OnGridLock();
115                 afterLock = true;
116         }
117
118         if (event) {
119                 return;
120         }
121
122         if (nextDirection >= 0) {
123                 if (afterLock) {
124                         bool blocked(CheckBlocking());
125                         OnMove(!blocked);
126                         afterLock = false;
127                         controlled->SetOrientation(Entity::Orientation(nextDirection));
128                         if (!blocked) {
129                                 controlled->SetSpeed(walkingSpeed);
130                                 moveTimer.Clear();
131                                 if (pushed) {
132                                         pushed->SetOrientation(Entity::Orientation(nextDirection));
133                                         pushed->SetSpeed(walkingSpeed);
134                                         controlled->SetPushing();
135                                 } else {
136                                         controlled->SetHandsFree();
137                                 }
138                         } else {
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);
144                                 }
145                                 pushed = 0;
146                         }
147                         if (!controlled->AnimationRunning()) {
148                                 controlled->StartAnimation(*this);
149                         }
150                 }
151         } else {
152                 controlled->SetSpeed(0.0f);
153                 StopFollowers(*controlled);
154                 controlled->StopAnimation();
155                 moveTimer.Clear();
156                 if (pushed) {
157                         pushed->SetSpeed(0.0f);
158                         pushed = 0;
159                 }
160         }
161
162         lastLock = nowLock;
163 }
164
165 bool MapState::CheckBlocking() {
166         if (pushed) {
167                 pushed->SetSpeed(0.0f);
168                 pushed = 0;
169         }
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()) {
175                                 return true;
176                         } else {
177                                 direction = Vector<int>(0, -map->Tileset()->Height());
178                         }
179                         break;
180                 case Entity::ORIENTATION_EAST:
181                         if (tile && tile->BlocksEast()) {
182                                 return true;
183                         } else {
184                                 direction = Vector<int>(map->Tileset()->Width(), 0);
185                         }
186                         break;
187                 case Entity::ORIENTATION_SOUTH:
188                         if (tile && tile->BlocksSouth()) {
189                                 return true;
190                         } else {
191                                 direction = Vector<int>(0, map->Tileset()->Height());
192                         }
193                         break;
194                 case Entity::ORIENTATION_WEST:
195                         if (tile && tile->BlocksWest()) {
196                                 return true;
197                         } else {
198                                 direction = Vector<int>(-map->Tileset()->Width(), 0);
199                         }
200                         break;
201                 default:
202                         return false;
203         }
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;
212                 pushed = *i;
213         }
214         return false;
215 }
216
217 bool MapState::CheckBlocking(const Vector<int> &position, Entity::Orientation direction) const {
218         const Tile *tile(map->TileAt(position));
219         Vector<int> directionVector;
220         switch (direction) {
221                 case Entity::ORIENTATION_NORTH:
222                         if (tile && tile->BlocksNorth()) {
223                                 return true;
224                         } else {
225                                 directionVector = Vector<int>(0, -map->Tileset()->Height());
226                         }
227                         break;
228                 case Entity::ORIENTATION_EAST:
229                         if (tile && tile->BlocksEast()) {
230                                 return true;
231                         } else {
232                                 directionVector = Vector<int>(map->Tileset()->Width(), 0);
233                         }
234                         break;
235                 case Entity::ORIENTATION_SOUTH:
236                         if (tile && tile->BlocksSouth()) {
237                                 return true;
238                         } else {
239                                 directionVector = Vector<int>(0, map->Tileset()->Height());
240                         }
241                         break;
242                 case Entity::ORIENTATION_WEST:
243                         if (tile && tile->BlocksWest()) {
244                                 return true;
245                         } else {
246                                 directionVector = Vector<int>(-map->Tileset()->Width(), 0);
247                         }
248                         break;
249                 default:
250                         return false;
251         }
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()) {
256                         return true;
257                 }
258         }
259         return false;
260 }
261
262 bool MapState::OnGridLock() {
263         if (skipLock) {
264                 skipLock = false;
265                 return false;
266         } else {
267                 LockEntities();
268                 return CheckMonster() || CheckTrigger();
269         }
270 }
271
272 void MapState::LockEntities() {
273         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
274                 if (*i == controlled) {
275                         // don't lock player
276                         continue;
277                 }
278                 (*i)->Position().Lock(map->Tileset()->Size());
279         }
280 }
281
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
289
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]);
299                                         }
300                                 }
301                                 for (battle::Monster *monster((*e)->MonstersBegin()); monster != (*e)->MonstersEnd(); ++monster) {
302                                         battleState->AddMonster(*monster);
303                                 }
304
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);
309
310                                 ctrl->PushState(fadeIn);
311                                 ctrl->PushState(battleState);
312                                 ctrl->PushState(fadeOut);
313                                 // TODO: move entity erase to happen after the transition or battle
314                                 entities.erase(e);
315                                 return true;
316                                 // needed information here:
317                                 //  - battle background (from tile/area/map)
318                                 //  - monsters + layout (from entity)
319                         }
320                 }
321         }
322         return false;
323 }
324
325 bool MapState::CheckTrigger() {
326         Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
327         if (trigger) {
328                 // TODO: run trigger script
329                 if (trigger->map) {
330                         ctrl->PushState(new ColorFade(this, 0, 500, true));
331                         ctrl->PushState(new TransitionState(this, trigger->map, trigger->target));
332                         ColorFade *fadeOut(new ColorFade(this, 0, 500, false));
333                         fadeOut->SetLeadOutTime(500);
334                         ctrl->PushState(fadeOut);
335                         return true;
336                 }
337         }
338         return false;
339 }
340
341 void MapState::OnMove(bool realMove) {
342         // TODO: evaluate monster movements
343         if (realMove) {
344                 UpdateFollower(*controlled);
345         } else {
346                 StopFollowers(*controlled);
347         }
348 }
349
350 void MapState::UpdateFollower(Entity &e) {
351         if (!e.Follower()) return;
352
353         Entity &f(*e.Follower());
354         UpdateFollower(f);
355
356         Vector<int> coords(map->TileCoordinates(e.Position()));
357         Vector<int> fCoords(map->TileCoordinates(f.Position()));
358         Vector<int> direction(coords - fCoords);
359
360         if (direction.Y() < 0) {
361                 f.SetOrientation(Entity::ORIENTATION_NORTH);
362                 f.SetSpeed(walkingSpeed);
363                 f.StartAnimation(*this);
364         } else if (direction.X() > 0) {
365                 f.SetOrientation(Entity::ORIENTATION_EAST);
366                 f.SetSpeed(walkingSpeed);
367                 f.StartAnimation(*this);
368         } else if (direction.Y() > 0) {
369                 f.SetOrientation(Entity::ORIENTATION_SOUTH);
370                 f.SetSpeed(walkingSpeed);
371                 f.StartAnimation(*this);
372         } else if (direction.X() < 0) {
373                 f.SetOrientation(Entity::ORIENTATION_WEST);
374                 f.SetSpeed(walkingSpeed);
375                 f.StartAnimation(*this);
376         } else {
377                 f.SetSpeed(0.0f);
378                 f.StopAnimation();
379         }
380 }
381
382 void MapState::StopFollowers(Entity &e) {
383         for (Entity *f(e.Follower()); f; f = f->Follower()) {
384                 f->SetSpeed(0.0f);
385                 f->StopAnimation();
386         }
387 }
388
389
390 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
391         UnloadMap();
392         Vector<int> position(coordinates * map->Tileset()->Size());
393         for (Entity *e(controlled); e; e = e->Follower()) {
394                 e->Position() = position;
395                 e->SetOrientation(controlled->GetOrientation());
396         }
397         LoadMap(newMap);
398         skipLock = true;
399 }
400
401 void MapState::UnloadMap() {
402         entities.clear();
403 }
404
405 void MapState::LoadMap(Map *m) {
406         map = m;
407         for (Entity *e(m->EntitiesBegin()), *end(m->EntitiesEnd()); e != end; ++e) {
408                 entities.push_back(e);
409         }
410         for (Entity *e(controlled); e; e = e->Follower()) {
411                 entities.push_back(e);
412         }
413 }
414
415
416 void MapState::Render(SDL_Surface *screen) {
417         SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
418
419         Vector<int> offset(camera.CalculateOffset());
420         map->Render(screen, offset);
421
422         if (debug) {
423                 map->RenderDebug(screen, offset);
424         }
425
426         std::sort(entities.begin(), entities.end(), ZCompare);
427         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
428                 (*i)->Render(screen, offset);
429         }
430 }
431
432
433 bool MapState::ZCompare(const Entity *lhs, const Entity *rhs) {
434         return lhs->Position().Y() < rhs->Position().Y();
435 }
436
437 }