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