]> git.localhorst.tv Git - l2e.git/blob - src/map/MapState.cpp
af156304b9412c6523e7aec39f7b0547e6fadd3f
[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 #include "../menu/PartyMenu.h"
21
22 #include <algorithm>
23
24 using app::Application;
25 using app::Input;
26 using battle::BattleState;
27 using common::GameConfig;
28 using geometry::Vector;
29 using graphics::ColorFade;
30 using menu::PartyMenu;
31
32 namespace map {
33
34 MapState::MapState(GameConfig *g, Map *map)
35 : game(g)
36 , map(map)
37 , controlled(0)
38 , pushed(0)
39 , lastLock(-1, -1)
40 , camera(100, 100, 0)
41 , walkingSpeed(64)
42 , nextDirection(-1)
43 , afterLock(false)
44 , skipLock(false)
45 , pushing(false)
46 , debug(false) {
47
48 }
49
50
51 void MapState::OnEnterState(SDL_Surface *screen) {
52         camera.Resize(screen->w, screen->h);
53         LoadMap(map);
54 }
55
56 void MapState::OnExitState(SDL_Surface *screen) {
57
58 }
59
60 void MapState::OnResumeState(SDL_Surface *screen) {
61         camera.Resize(screen->w, screen->h);
62 }
63
64 void MapState::OnPauseState(SDL_Surface *screen) {
65
66 }
67
68 void MapState::OnResize(int width, int height) {
69         camera.Resize(width, height);
70 }
71
72
73 void MapState::HandleEvents(const Input &input) {
74         if (input.JustPressed(Input::ACTION_X)) {
75                 Ctrl().PushState(new PartyMenu(game));
76                 return;
77         }
78
79         if (!controlled) return;
80
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;
89         } else {
90                 nextDirection = -1;
91         }
92
93         pushing = input.IsDown(Input::ACTION_A);
94
95         if (input.JustPressed(Input::DEBUG_1)) {
96                 debug = !debug;
97         }
98 }
99
100 void MapState::UpdateWorld(float deltaT) {
101         if (controlled && controlled->TileLock(map->Tileset()->Size())) {
102                 OnTileLock();
103         }
104         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
105                 (*i)->Update(deltaT);
106         }
107 }
108
109 void MapState::OnTileLock() {
110         if (moveTimer.Running() && !moveTimer.JustHit()) return;
111
112         Vector<int> nowLock(controlled->Position());
113         bool event(false);
114         if (nowLock != lastLock) {
115                 event = OnGridLock();
116                 afterLock = true;
117                 moveTimer.Clear();
118         } else if (moveTimer.JustHit()) {
119                 event = OnGridLock();
120                 afterLock = true;
121         }
122
123         if (event) {
124                 return;
125         }
126
127         if (nextDirection >= 0) {
128                 if (afterLock) {
129                         bool blocked(CheckBlocking());
130                         OnMove(!blocked);
131                         controlled->SetOrientation(Entity::Orientation(nextDirection));
132                         if (!blocked) {
133                                 afterLock = false;
134                                 controlled->SetSpeed(walkingSpeed);
135                                 moveTimer.Clear();
136                                 if (pushed) {
137                                         pushed->SetOrientation(Entity::Orientation(nextDirection));
138                                         pushed->SetSpeed(walkingSpeed);
139                                         controlled->SetPushing();
140                                 } else {
141                                         controlled->SetHandsFree();
142                                 }
143                         } else {
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);
149                                 }
150                                 pushed = 0;
151                         }
152                         if (!controlled->AnimationRunning()) {
153                                 controlled->StartAnimation(*this);
154                         }
155                 }
156         } else {
157                 controlled->SetSpeed(0.0f);
158                 StopFollowers(*controlled);
159                 controlled->StopAnimation();
160                 moveTimer.Clear();
161                 if (pushed) {
162                         pushed->SetSpeed(0.0f);
163                         pushed = 0;
164                 }
165         }
166
167         lastLock = nowLock;
168 }
169
170 bool MapState::CheckBlocking() {
171         if (pushed) {
172                 pushed->SetSpeed(0.0f);
173                 pushed = 0;
174         }
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()) {
180                                 return true;
181                         } else {
182                                 direction = Vector<int>(0, -map->Tileset()->Height());
183                         }
184                         break;
185                 case Entity::ORIENTATION_EAST:
186                         if (tile && tile->BlocksEast()) {
187                                 return true;
188                         } else {
189                                 direction = Vector<int>(map->Tileset()->Width(), 0);
190                         }
191                         break;
192                 case Entity::ORIENTATION_SOUTH:
193                         if (tile && tile->BlocksSouth()) {
194                                 return true;
195                         } else {
196                                 direction = Vector<int>(0, map->Tileset()->Height());
197                         }
198                         break;
199                 case Entity::ORIENTATION_WEST:
200                         if (tile && tile->BlocksWest()) {
201                                 return true;
202                         } else {
203                                 direction = Vector<int>(-map->Tileset()->Width(), 0);
204                         }
205                         break;
206                 default:
207                         return false;
208         }
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;
217                 pushed = *i;
218         }
219         return false;
220 }
221
222 bool MapState::CheckBlocking(const Vector<int> &position, Entity::Orientation direction) const {
223         const Tile *tile(map->TileAt(position));
224         Vector<int> directionVector;
225         switch (direction) {
226                 case Entity::ORIENTATION_NORTH:
227                         if (tile && tile->BlocksNorth()) {
228                                 return true;
229                         } else {
230                                 directionVector = Vector<int>(0, -map->Tileset()->Height());
231                         }
232                         break;
233                 case Entity::ORIENTATION_EAST:
234                         if (tile && tile->BlocksEast()) {
235                                 return true;
236                         } else {
237                                 directionVector = Vector<int>(map->Tileset()->Width(), 0);
238                         }
239                         break;
240                 case Entity::ORIENTATION_SOUTH:
241                         if (tile && tile->BlocksSouth()) {
242                                 return true;
243                         } else {
244                                 directionVector = Vector<int>(0, map->Tileset()->Height());
245                         }
246                         break;
247                 case Entity::ORIENTATION_WEST:
248                         if (tile && tile->BlocksWest()) {
249                                 return true;
250                         } else {
251                                 directionVector = Vector<int>(-map->Tileset()->Width(), 0);
252                         }
253                         break;
254                 default:
255                         return false;
256         }
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()) {
261                         return true;
262                 }
263         }
264         return false;
265 }
266
267 bool MapState::OnGridLock() {
268         if (skipLock) {
269                 skipLock = false;
270                 return false;
271         } else {
272                 LockEntities();
273                 return CheckMonster() || CheckLockTrigger();
274         }
275 }
276
277 void MapState::LockEntities() {
278         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
279                 if (*i == controlled) {
280                         // don't lock player
281                         continue;
282                 }
283                 (*i)->Position().Lock(map->Tileset()->Size());
284         }
285 }
286
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
294
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]);
304                                         }
305                                 }
306                                 for (battle::Monster *monster((*e)->MonstersBegin()); monster != (*e)->MonstersEnd(); ++monster) {
307                                         battleState->AddMonster(*monster);
308                                 }
309
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);
314
315                                 Ctrl().PushState(fadeIn);
316                                 Ctrl().PushState(battleState);
317                                 Ctrl().PushState(fadeOut);
318                                 // TODO: move entity erase to happen after the transition or battle
319                                 entities.erase(e);
320                                 return true;
321                                 // needed information here:
322                                 //  - battle background (from tile/area/map)
323                                 //  - monsters + layout (from entity)
324                         }
325                 }
326         }
327         return false;
328 }
329
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);
334         return true;
335 }
336
337 void MapState::OnMove(bool realMove) {
338         if (CheckMoveTrigger()) {
339                 return;
340         }
341         // TODO: evaluate monster movements
342         if (realMove) {
343                 UpdateFollower(*controlled);
344         } else {
345                 StopFollowers(*controlled);
346         }
347 }
348
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);
353         return true;
354 }
355
356 void MapState::RunTrigger(Trigger &trigger) {
357         if (!trigger.HasScript()) return;
358         runner.Run(*this, trigger.GetScript());
359 }
360
361 void MapState::UpdateFollower(Entity &e) {
362         if (!e.Follower()) return;
363
364         Entity &f(*e.Follower());
365         UpdateFollower(f);
366
367         Vector<int> coords(map->TileCoordinates(e.Position()));
368         Vector<int> fCoords(map->TileCoordinates(f.Position()));
369         Vector<int> direction(coords - fCoords);
370
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);
387         } else {
388                 f.SetSpeed(0.0f);
389                 f.StopAnimation();
390         }
391 }
392
393 void MapState::StopFollowers(Entity &e) {
394         for (Entity *f(e.Follower()); f; f = f->Follower()) {
395                 f->SetSpeed(0.0f);
396                 f->StopAnimation();
397         }
398 }
399
400
401 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
402         UnloadMap();
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());
407         }
408         LoadMap(newMap);
409         skipLock = true;
410 }
411
412 void MapState::UnloadMap() {
413         entities.clear();
414 }
415
416 void MapState::LoadMap(Map *m) {
417         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());
421         }
422         for (Entity *e(controlled); e; e = e->Follower()) {
423                 entities.push_back(e);
424         }
425 }
426
427
428 void MapState::Render(SDL_Surface *screen) {
429         SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
430
431         Vector<int> offset(camera.CalculateOffset());
432         map->Render(screen, offset);
433
434         if (debug) {
435                 map->RenderDebug(screen, offset);
436         }
437
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);
441         }
442 }
443
444
445 bool MapState::ZCompare(const Entity *lhs, const Entity *rhs) {
446         return lhs->Position().Y() < rhs->Position().Y();
447 }
448
449
450 void MapState::HandleSyscall(common::ScriptRunner &r) {
451         switch (r.IntegerRegister(0)) {
452                 case TRANSITION: {
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);
458                         break;
459                 }
460         }
461 }
462
463 }