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