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