]> git.localhorst.tv Git - l2e.git/blob - src/map/MapState.cpp
implemented modulo operation for scripts
[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 , lastLock(-1, -1)
39 , camera(100, 100, 0)
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                         controlled->SetOrientation(Entity::Orientation(nextDirection));
127                         if (!blocked) {
128                                 afterLock = false;
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() || CheckLockTrigger();
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::CheckLockTrigger() {
326         Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
327         if (!trigger || trigger->GetType() != Trigger::TYPE_CONTACT) return false;
328         RunTrigger(*trigger);
329         return true;
330 }
331
332 void MapState::OnMove(bool realMove) {
333         if (CheckMoveTrigger()) {
334                 return;
335         }
336         // TODO: evaluate monster movements
337         if (realMove) {
338                 UpdateFollower(*controlled);
339         } else {
340                 StopFollowers(*controlled);
341         }
342 }
343
344 bool MapState::CheckMoveTrigger() {
345         Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
346         if (!trigger || int(trigger->GetType()) != nextDirection) return false;
347         RunTrigger(*trigger);
348         return true;
349 }
350
351 void MapState::RunTrigger(Trigger &trigger) {
352         if (!trigger.HasScript()) return;
353         runner.Run(*this, trigger.GetScript());
354 }
355
356 void MapState::UpdateFollower(Entity &e) {
357         if (!e.Follower()) return;
358
359         Entity &f(*e.Follower());
360         UpdateFollower(f);
361
362         Vector<int> coords(map->TileCoordinates(e.Position()));
363         Vector<int> fCoords(map->TileCoordinates(f.Position()));
364         Vector<int> direction(coords - fCoords);
365
366         if (direction.Y() < 0) {
367                 f.SetOrientation(Entity::ORIENTATION_NORTH);
368                 f.SetSpeed(walkingSpeed);
369                 f.StartAnimation(*this);
370         } else if (direction.X() > 0) {
371                 f.SetOrientation(Entity::ORIENTATION_EAST);
372                 f.SetSpeed(walkingSpeed);
373                 f.StartAnimation(*this);
374         } else if (direction.Y() > 0) {
375                 f.SetOrientation(Entity::ORIENTATION_SOUTH);
376                 f.SetSpeed(walkingSpeed);
377                 f.StartAnimation(*this);
378         } else if (direction.X() < 0) {
379                 f.SetOrientation(Entity::ORIENTATION_WEST);
380                 f.SetSpeed(walkingSpeed);
381                 f.StartAnimation(*this);
382         } else {
383                 f.SetSpeed(0.0f);
384                 f.StopAnimation();
385         }
386 }
387
388 void MapState::StopFollowers(Entity &e) {
389         for (Entity *f(e.Follower()); f; f = f->Follower()) {
390                 f->SetSpeed(0.0f);
391                 f->StopAnimation();
392         }
393 }
394
395
396 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
397         UnloadMap();
398         Vector<int> position(coordinates * map->Tileset()->Size());
399         for (Entity *e(controlled); e; e = e->Follower()) {
400                 e->Position() = position;
401                 e->SetOrientation(controlled->GetOrientation());
402         }
403         LoadMap(newMap);
404         skipLock = true;
405 }
406
407 void MapState::UnloadMap() {
408         entities.clear();
409 }
410
411 void MapState::LoadMap(Map *m) {
412         map = m;
413         for (Entity *e(m->EntitiesBegin()), *end(m->EntitiesEnd()); e != end; ++e) {
414                 entities.push_back(e);
415         }
416         for (Entity *e(controlled); e; e = e->Follower()) {
417                 entities.push_back(e);
418         }
419 }
420
421
422 void MapState::Render(SDL_Surface *screen) {
423         SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
424
425         Vector<int> offset(camera.CalculateOffset());
426         map->Render(screen, offset);
427
428         if (debug) {
429                 map->RenderDebug(screen, offset);
430         }
431
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);
435         }
436 }
437
438
439 bool MapState::ZCompare(const Entity *lhs, const Entity *rhs) {
440         return lhs->Position().Y() < rhs->Position().Y();
441 }
442
443
444 void MapState::HandleSyscall(common::ScriptRunner &r) {
445         switch (r.Integer0()) {
446                 case TRANSITION: {
447                         ctrl->PushState(new ColorFade(this, 0, 500, true));
448                         ctrl->PushState(new TransitionState(this, reinterpret_cast<Map *>(r.Address0()), r.Vector0()));
449                         ColorFade *fadeOut(new ColorFade(this, 0, 500, false));
450                         fadeOut->SetLeadOutTime(500);
451                         ctrl->PushState(fadeOut);
452                         break;
453                 }
454         }
455 }
456
457 }