]> git.localhorst.tv Git - l2e.git/blob - src/map/MapState.cpp
7e6d7328d2756bb533527cf41efc25a9a2b6f703
[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         Vector<int> nextPosition;
138         switch (nextDirection) {
139                 case Entity::ORIENTATION_NORTH:
140                         if (tile.BlocksNorth()) {
141                                 return true;
142                         } else {
143                                 nextPosition = Vector<int>(
144                                                 controlled->Position().X(),
145                                                 controlled->Position().Y() - map->Tileset()->Height());
146                         }
147                         break;
148                 case Entity::ORIENTATION_EAST:
149                         if (tile.BlocksEast()) {
150                                 return true;
151                         } else {
152                                 nextPosition = Vector<int>(
153                                                 controlled->Position().X() + map->Tileset()->Width(),
154                                                 controlled->Position().Y());
155                         }
156                         break;
157                 case Entity::ORIENTATION_SOUTH:
158                         if (tile.BlocksSouth()) {
159                                 return true;
160                         } else {
161                                 nextPosition = Vector<int>(
162                                                 controlled->Position().X(),
163                                                 controlled->Position().Y() + map->Tileset()->Height());
164                         }
165                         break;
166                 case Entity::ORIENTATION_WEST:
167                         if (tile.BlocksWest()) {
168                                 return true;
169                         } else {
170                                 nextPosition = Vector<int>(
171                                                 controlled->Position().X() - map->Tileset()->Width(),
172                                                 controlled->Position().Y());
173                         }
174                         break;
175                 default:
176                         return false;
177         }
178         Vector<int> nextTileCoords(map->TileCoordinates(nextPosition));
179         for (std::vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
180                 const Entity &e(**i);
181                 if (map->TileCoordinates(e.Position()) == nextTileCoords && e.Blocking()) {
182                         return true;
183                 }
184         }
185         return false;
186 }
187
188 void MapState::OnGridLock() {
189         if (skipLock) {
190                 skipLock = false;
191         } else {
192                 LockEntities();
193                 CheckMonster();
194                 CheckTrigger();
195         }
196 }
197
198 void MapState::LockEntities() {
199         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
200                 if (*i == controlled) {
201                         // don't lock player
202                         continue;
203                 }
204                 (*i)->Position().Lock(map->Tileset()->Size());
205         }
206 }
207
208 void MapState::CheckMonster() {
209         Vector<int> coords(map->TileCoordinates(controlled->Position()));
210         Vector<int> neighbor[4];
211         neighbor[0] = Vector<int>(coords.X(), coords.Y() - 1); // N
212         neighbor[1] = Vector<int>(coords.X() + 1, coords.Y()); // E
213         neighbor[2] = Vector<int>(coords.X(), coords.Y() + 1); // S
214         neighbor[3] = Vector<int>(coords.X() - 1, coords.Y()); // W
215
216         for (int i(0); i < 4; ++i) {
217                 for (std::vector<Entity *>::iterator e(entities.begin()), end(entities.end()); e != end; ++e) {
218                         if ((*e)->Hostile() && map->TileCoordinates((*e)->Position()) == neighbor[i]) {
219                                 // TODO: check for turn advantage, see #26
220                                 // TODO: remove entity, push battle state and transition and halt all other activity
221                                 // needed information here:
222                                 //  - battle background (from tile?)
223                                 //  - monsters + layout (from entity)
224                                 //  - battle resources (from global resources)
225                         }
226                 }
227         }
228 }
229
230 void MapState::CheckTrigger() {
231         Trigger *trigger(map->TriggerAt(Vector<int>(controlled->Position())));
232         if (trigger) {
233                 // TODO: run trigger script
234                 if (trigger->map) {
235                         Transition(trigger->map, trigger->target);
236                 }
237         }
238
239 }
240
241 void MapState::OnMove(bool realMove) {
242         // TODO: evaluate monster movements
243         if (realMove) {
244                 UpdateFollower(*controlled);
245         } else {
246                 StopFollowers(*controlled);
247         }
248 }
249
250 void MapState::UpdateFollower(Entity &e) {
251         if (!e.Follower()) return;
252
253         Entity &f(*e.Follower());
254         UpdateFollower(f);
255
256         Vector<int> coords(map->TileCoordinates(e.Position()));
257         Vector<int> fCoords(map->TileCoordinates(f.Position()));
258         Vector<int> direction(coords - fCoords);
259
260         if (direction.Y() < 0) {
261                 f.SetOrientation(Entity::ORIENTATION_NORTH);
262                 f.SetSpeed(walkingSpeed);
263                 f.StartAnimation(*this);
264         } else if (direction.X() > 0) {
265                 f.SetOrientation(Entity::ORIENTATION_EAST);
266                 f.SetSpeed(walkingSpeed);
267                 f.StartAnimation(*this);
268         } else if (direction.Y() > 0) {
269                 f.SetOrientation(Entity::ORIENTATION_SOUTH);
270                 f.SetSpeed(walkingSpeed);
271                 f.StartAnimation(*this);
272         } else if (direction.X() < 0) {
273                 f.SetOrientation(Entity::ORIENTATION_WEST);
274                 f.SetSpeed(walkingSpeed);
275                 f.StartAnimation(*this);
276         } else {
277                 f.SetSpeed(0.0f);
278                 f.StopAnimation();
279         }
280 }
281
282 void MapState::StopFollowers(Entity &e) {
283         for (Entity *f(e.Follower()); f; f = f->Follower()) {
284                 f->SetSpeed(0.0f);
285                 f->StopAnimation();
286         }
287 }
288
289
290 void MapState::Transition(Map *newMap, const Vector<int> &coordinates) {
291         UnloadMap();
292         Vector<int> position(coordinates * map->Tileset()->Size());
293         for (Entity *e(controlled); e; e = e->Follower()) {
294                 e->Position() = position;
295                 e->SetOrientation(controlled->GetOrientation());
296         }
297         LoadMap(newMap);
298         skipLock = true;
299 }
300
301 void MapState::UnloadMap() {
302         entities.clear();
303 }
304
305 void MapState::LoadMap(Map *m) {
306         map = m;
307         entities.insert(entities.end(), m->EntitiesBegin(), m->EntitiesEnd());
308         for (Entity *e(controlled); e; e = e->Follower()) {
309                 entities.push_back(e);
310         }
311 }
312
313
314 void MapState::Render(SDL_Surface *screen) {
315         SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
316
317         Vector<int> offset(camera.CalculateOffset());
318         map->Render(screen, offset);
319
320         if (debug) {
321                 map->RenderDebug(screen, offset);
322         }
323
324         std::sort(entities.begin(), entities.end(), ZCompare);
325         for (std::vector<Entity *>::iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
326                 (*i)->Render(screen, offset);
327         }
328 }
329
330
331 bool MapState::ZCompare(const Entity *lhs, const Entity *rhs) {
332         return lhs->Position().Y() < rhs->Position().Y();
333 }
334
335 }