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