summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/collision.cpp97
-rw-r--r--src/collision.h81
-rw-r--r--src/components/mappable.h4
-rw-r--r--src/components/ponderable.h16
-rw-r--r--src/systems/mapping.cpp2
-rw-r--r--src/systems/pondering.cpp882
-rw-r--r--src/systems/pondering.h23
-rw-r--r--src/systems/realizing.cpp4
8 files changed, 714 insertions, 395 deletions
diff --git a/src/collision.cpp b/src/collision.cpp deleted file mode 100644 index b747a90..0000000 --- a/src/collision.cpp +++ /dev/null
@@ -1,97 +0,0 @@
1#include "collision.h"
2
3bool Collision::operator<(const Collision& other) const
4{
5 // Most important is the type of collision
6 if (type_ != other.type_)
7 {
8 return (static_cast<int>(type_) > static_cast<int>(other.type_));
9 }
10
11 // Next, categorize the collisions arbitrarily based on direction
12 if (dir_ != other.dir_)
13 {
14 return (static_cast<int>(dir_) < static_cast<int>(other.dir_));
15 }
16
17 // We want to process closer collisions first
18 if (axis_ != other.axis_)
19 {
20 switch (dir_)
21 {
22 case Direction::left:
23 case Direction::up:
24 {
25 return (axis_ < other.axis_);
26 }
27
28 case Direction::right:
29 case Direction::down:
30 {
31 return (axis_ > other.axis_);
32 }
33 }
34 }
35
36 // Order the remaining attributes arbitrarily
37 return std::tie(collider_, lower_, upper_) <
38 std::tie(other.collider_, other.lower_, other.upper_);
39}
40
41bool Collision::isColliding(
42 double x,
43 double y,
44 int w,
45 int h) const
46{
47 int right = x + w;
48 int bottom = y + h;
49
50 switch (dir_)
51 {
52 case Direction::left:
53 case Direction::right:
54 {
55 if (!((bottom > lower_) && (y < upper_)))
56 {
57 return false;
58 }
59
60 break;
61 }
62
63 case Direction::up:
64 case Direction::down:
65 {
66 if (!((right > lower_) && (x < upper_)))
67 {
68 return false;
69 }
70
71 break;
72 }
73 }
74
75 switch (dir_)
76 {
77 case Direction::left:
78 {
79 return (axis_ >= x);
80 }
81
82 case Direction::right:
83 {
84 return (axis_ <= right);
85 }
86
87 case Direction::up:
88 {
89 return (axis_ >= y);
90 }
91
92 case Direction::down:
93 {
94 return (axis_ <= bottom);
95 }
96 }
97}
diff --git a/src/collision.h b/src/collision.h deleted file mode 100644 index e5371f8..0000000 --- a/src/collision.h +++ /dev/null
@@ -1,81 +0,0 @@
1#ifndef COLLISION_H_53D84877
2#define COLLISION_H_53D84877
3
4#include "entity_manager.h"
5#include "direction.h"
6
7class Collision {
8public:
9
10 using id_type = EntityManager::id_type;
11
12 // Types are defined in descending priority order
13 enum class Type {
14 wall,
15 platform,
16 adjacency,
17 warp,
18 danger
19 };
20
21 Collision(
22 id_type collider,
23 Direction dir,
24 Type type,
25 int axis,
26 double lower,
27 double upper) :
28 collider_(collider),
29 dir_(dir),
30 type_(type),
31 axis_(axis),
32 lower_(lower),
33 upper_(upper)
34 {
35 }
36
37 inline id_type getCollider() const
38 {
39 return collider_;
40 }
41
42 inline Direction getDirection() const
43 {
44 return dir_;
45 }
46
47 inline Type getType() const
48 {
49 return type_;
50 }
51
52 inline int getAxis() const
53 {
54 return axis_;
55 }
56
57 inline double getLower() const
58 {
59 return lower_;
60 }
61
62 inline double getUpper() const
63 {
64 return upper_;
65 }
66
67 bool operator<(const Collision& other) const;
68
69 bool isColliding(double x, double y, int w, int h) const;
70
71private:
72
73 id_type collider_;
74 Direction dir_;
75 Type type_;
76 int axis_;
77 double lower_;
78 double upper_;
79};
80
81#endif /* end of include guard: COLLISION_H_53D84877 */
diff --git a/src/components/mappable.h b/src/components/mappable.h index 6f3d38e..e92074e 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h
@@ -7,7 +7,7 @@
7#include <list> 7#include <list>
8#include "component.h" 8#include "component.h"
9#include "renderer/texture.h" 9#include "renderer/texture.h"
10#include "collision.h" 10#include "components/ponderable.h"
11#include "entity_manager.h" 11#include "entity_manager.h"
12 12
13class MappableComponent : public Component { 13class MappableComponent : public Component {
@@ -46,7 +46,7 @@ public:
46 class Boundary { 46 class Boundary {
47 public: 47 public:
48 48
49 using Type = Collision::Type; 49 using Type = PonderableComponent::Collision;
50 50
51 Boundary( 51 Boundary(
52 double axis, 52 double axis,
diff --git a/src/components/ponderable.h b/src/components/ponderable.h index fd7e775..5354f87 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h
@@ -21,6 +21,17 @@ public:
21 }; 21 };
22 22
23 /** 23 /**
24 * List of different types of collidable surfaces.
25 */
26 enum class Collision {
27 wall,
28 platform,
29 adjacency,
30 warp,
31 danger
32 };
33
34 /**
24 * Constructor for initializing the body type, which is a constant. 35 * Constructor for initializing the body type, which is a constant.
25 */ 36 */
26 PonderableComponent(Type type) : type(type) 37 PonderableComponent(Type type) : type(type)
@@ -67,6 +78,11 @@ public:
67 bool collidable = true; 78 bool collidable = true;
68 79
69 /** 80 /**
81 * The effect that colliding with this body has.
82 */
83 Collision colliderType = Collision::wall;
84
85 /**
70 * If this flag is disabled, the entity will be ignored by the pondering 86 * If this flag is disabled, the entity will be ignored by the pondering
71 * system. 87 * system.
72 * 88 *
diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index af67aed..d78c8fe 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp
@@ -175,7 +175,7 @@ void MappingSystem::generateBoundaries(id_type mapEntity)
175 175
176 addBoundary( 176 addBoundary(
177 mappable.downBoundaries, 177 mappable.downBoundaries,
178 y * TILE_HEIGHT, 178 y * TILE_HEIGHT + 1,
179 x * TILE_WIDTH, 179 x * TILE_WIDTH,
180 (x+1) * TILE_WIDTH, 180 (x+1) * TILE_WIDTH,
181 MappableComponent::Boundary::Type::danger); 181 MappableComponent::Boundary::Type::danger);
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 4ae6176..04b45a1 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp
@@ -11,7 +11,6 @@
11#include "systems/playing.h" 11#include "systems/playing.h"
12#include "systems/realizing.h" 12#include "systems/realizing.h"
13#include "consts.h" 13#include "consts.h"
14#include "collision.h"
15 14
16void PonderingSystem::tick(double dt) 15void PonderingSystem::tick(double dt)
17{ 16{
@@ -56,304 +55,566 @@ void PonderingSystem::tick(double dt)
56 const double oldRight = oldX + transformable.w; 55 const double oldRight = oldX + transformable.w;
57 const double oldBottom = oldY + transformable.h; 56 const double oldBottom = oldY + transformable.h;
58 57
59 double newX = oldX + ponderable.velX * dt; 58 CollisionResult result;
60 double newY = oldY + ponderable.velY * dt; 59 result.newX = oldX + ponderable.velX * dt;
60 result.newY = oldY + ponderable.velY * dt;
61 61
62 bool oldGrounded = ponderable.grounded; 62 bool oldGrounded = ponderable.grounded;
63 ponderable.grounded = false; 63 ponderable.grounded = false;
64 64
65 std::priority_queue<Collision> collisions; 65 // Find horizontal collisions.
66 66 if (result.newX < oldX)
67 // Find collisions
68 if (newX < oldX)
69 { 67 {
70 for (auto it = mappable.leftBoundaries.lower_bound(oldX); 68 bool boundaryCollision = false;
71 (it != std::end(mappable.leftBoundaries)) && (it->first >= newX); 69 auto it = mappable.leftBoundaries.lower_bound(oldX);
72 it++) 70
71 // Find the axis distance of the closest environmental boundary.
72 for (;
73 (it != std::end(mappable.leftBoundaries)) &&
74 (it->first >= result.newX);
75 it++)
73 { 76 {
74 if ((oldBottom > it->second.lower) 77 // Check that the boundary is in range for the other axis.
75 && (oldY < it->second.upper)) 78 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
76 { 79 {
77 // We have a collision! 80 // We have a collision!
78 collisions.emplace( 81 boundaryCollision = true;
79 mapEntity, 82
80 Direction::left, 83 break;
81 it->second.type,
82 it->first,
83 it->second.lower,
84 it->second.upper);
85 } 84 }
86 } 85 }
87 } else if (newX > oldX) 86
88 { 87 // Find a list of potential colliders, sorted so that the closest is
89 for (auto it = mappable.rightBoundaries.lower_bound(oldRight); 88 // first.
90 (it != std::end(mappable.rightBoundaries)) 89 std::vector<id_type> colliders;
91 && (it->first <= (newX + transformable.w)); 90
92 it++) 91 for (id_type collider : entities)
93 { 92 {
94 if ((oldBottom > it->second.lower) 93 // Can't collide with self.
95 && (oldY < it->second.upper)) 94 if (collider == entity)
96 { 95 {
97 // We have a collision! 96 continue;
98 collisions.emplace(
99 mapEntity,
100 Direction::right,
101 it->second.type,
102 it->first,
103 it->second.lower,
104 it->second.upper);
105 } 97 }
106 }
107 }
108 98
109 if (newY < oldY) 99 auto& colliderPonder = game_.getEntityManager().
110 { 100 getComponent<PonderableComponent>(collider);
111 for (auto it = mappable.upBoundaries.lower_bound(oldY); 101
112 (it != std::end(mappable.upBoundaries)) && (it->first >= newY); 102 // Only check objects that are active.
113 it++) 103 if (!colliderPonder.active)
114 {
115 if ((oldRight > it->second.lower)
116 && (oldX < it->second.upper))
117 { 104 {
118 // We have a collision! 105 continue;
119 collisions.emplace(
120 mapEntity,
121 Direction::up,
122 it->second.type,
123 it->first,
124 it->second.lower,
125 it->second.upper);
126 } 106 }
127 } 107
128 } else if (newY > oldY) 108 auto& colliderTrans = game_.getEntityManager().
129 { 109 getComponent<TransformableComponent>(collider);
130 for (auto it = mappable.downBoundaries.lower_bound(oldBottom); 110
131 (it != std::end(mappable.downBoundaries)) 111 // Check if the entity would move into the potential collider,
132 && (it->first <= (newY + transformable.h)); 112 if ((colliderTrans.x + colliderTrans.w > result.newX) &&
133 it++) 113 // that it wasn't already colliding,
134 { 114 (colliderTrans.x + colliderTrans.w <= oldX) &&
135 if ((oldRight > it->second.lower) 115 // that the position on the other axis is in range,
136 && (oldX < it->second.upper)) 116 (colliderTrans.y + colliderTrans.h > oldY) &&
117 (colliderTrans.y < oldBottom) &&
118 // and that the collider is not farther away than the environmental
119 // boundary.
120 (!boundaryCollision ||
121 (colliderTrans.x + colliderTrans.w >= it->first)))
137 { 122 {
138 // We have a collision! 123 colliders.push_back(collider);
139 collisions.emplace(
140 mapEntity,
141 Direction::down,
142 it->second.type,
143 it->first,
144 it->second.lower,
145 it->second.upper);
146 } 124 }
147 } 125 }
148 }
149 126
150 // Process collisions in order of priority 127 std::sort(
151 bool adjacentlyWarping = false; 128 std::begin(colliders),
152 Direction adjWarpDir; 129 std::end(colliders),
153 size_t adjWarpMapId; 130 [&] (id_type left, id_type right) {
131 auto& leftTrans = game_.getEntityManager().
132 getComponent<TransformableComponent>(left);
154 133
155 while (!collisions.empty()) 134 auto& rightTrans = game_.getEntityManager().
156 { 135 getComponent<TransformableComponent>(right);
157 Collision collision = collisions.top();
158 collisions.pop();
159
160 // Make sure that they are still colliding
161 if (!collision.isColliding(
162 newX,
163 newY,
164 transformable.w,
165 transformable.h))
166 {
167 continue;
168 }
169 136
170 bool touchedWall = false; 137 return (rightTrans.x < leftTrans.x);
171 bool stopProcessing = false; 138 });
172 139
173 switch (collision.getType()) 140 for (id_type collider : colliders)
174 { 141 {
175 case Collision::Type::wall: 142 auto& colliderTrans = game_.getEntityManager().
143 getComponent<TransformableComponent>(collider);
144
145 // Check if the entity would still move into the potential collider.
146 if (colliderTrans.x + colliderTrans.w <= result.newX)
176 { 147 {
177 touchedWall = true; 148 break;
149 }
178 150
151 auto& colliderPonder = game_.getEntityManager().
152 getComponent<PonderableComponent>(collider);
153
154 processCollision(
155 entity,
156 collider,
157 Direction::left,
158 colliderPonder.colliderType,
159 colliderTrans.x + colliderTrans.w,
160 colliderTrans.y,
161 colliderTrans.y + colliderTrans.h,
162 result);
163
164 if (result.stopProcessing)
165 {
179 break; 166 break;
180 } 167 }
168 }
181 169
182 case Collision::Type::platform: 170 // If movement hasn't been stopped by an intermediary object, and
171 // collision checking hasn't been stopped, process the environmental
172 // boundaries closest to the entity.
173 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
174 {
175 double boundaryAxis = it->first;
176
177 for (;
178 (it != std::end(mappable.leftBoundaries)) &&
179 (it->first == boundaryAxis);
180 it++)
183 { 181 {
184 if (game_.getEntityManager(). 182 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
185 hasComponent<OrientableComponent>(entity))
186 { 183 {
187 auto& orientable = game_.getEntityManager(). 184 processCollision(
188 getComponent<OrientableComponent>(entity); 185 entity,
189 186 mapEntity,
190 if (orientable.getDropState() != 187 Direction::left,
191 OrientableComponent::DropState::none) 188 it->second.type,
189 it->first,
190 it->second.lower,
191 it->second.upper,
192 result);
193
194 if (result.stopProcessing)
192 { 195 {
193 orientable.setDropState(OrientableComponent::DropState::active); 196 break;
194 } else {
195 touchedWall = true;
196 } 197 }
197 } else {
198 touchedWall = true;
199 } 198 }
199 }
200 }
201 } else if (result.newX > oldX)
202 {
203 bool boundaryCollision = false;
204 auto it = mappable.rightBoundaries.lower_bound(oldRight);
205
206 // Find the axis distance of the closest environmental boundary.
207 for (;
208 (it != std::end(mappable.rightBoundaries))
209 && (it->first <= (result.newX + transformable.w));
210 it++)
211 {
212 // Check that the boundary is in range for the other axis.
213 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
214 {
215 // We have a collision!
216 boundaryCollision = true;
200 217
201 break; 218 break;
202 } 219 }
220 }
221
222 // Find a list of potential colliders, sorted so that the closest is
223 // first.
224 std::vector<id_type> colliders;
203 225
204 case Collision::Type::adjacency: 226 for (id_type collider : entities)
227 {
228 // Can't collide with self.
229 if (collider == entity)
205 { 230 {
206 auto& mappable = game_.getEntityManager(). 231 continue;
207 getComponent<MappableComponent>(collision.getCollider()); 232 }
208 233
209 auto& adj = [&] () -> const MappableComponent::Adjacent& { 234 auto& colliderPonder = game_.getEntityManager().
210 switch (collision.getDirection()) 235 getComponent<PonderableComponent>(collider);
211 {
212 case Direction::left: return mappable.leftAdjacent;
213 case Direction::right: return mappable.rightAdjacent;
214 case Direction::up: return mappable.upAdjacent;
215 case Direction::down: return mappable.downAdjacent;
216 }
217 }();
218 236
219 switch (adj.type) 237 // Only check objects that are active.
220 { 238 if (!colliderPonder.active)
221 case MappableComponent::Adjacent::Type::wall: 239 {
222 { 240 continue;
223 touchedWall = true; 241 }
224 242
225 break; 243 auto& colliderTrans = game_.getEntityManager().
226 } 244 getComponent<TransformableComponent>(collider);
245
246 // Check if the entity would move into the potential collider,
247 if ((colliderTrans.x < result.newX + transformable.w) &&
248 // that it wasn't already colliding,
249 (colliderTrans.x >= oldRight) &&
250 // that the position on the other axis is in range,
251 (colliderTrans.y + colliderTrans.h > oldY) &&
252 (colliderTrans.y < oldBottom) &&
253 // and that the collider is not farther away than the environmental
254 // boundary.
255 (!boundaryCollision || (colliderTrans.x <= it->first)))
256 {
257 colliders.push_back(collider);
258 }
259 }
227 260
228 case MappableComponent::Adjacent::Type::wrap: 261 std::sort(
229 { 262 std::begin(colliders),
230 switch (collision.getDirection()) 263 std::end(colliders),
231 { 264 [&] (id_type left, id_type right) {
232 case Direction::left: 265 auto& leftTrans = game_.getEntityManager().
233 { 266 getComponent<TransformableComponent>(left);
234 newX = GAME_WIDTH + WALL_GAP - transformable.w;
235 267
236 break; 268 auto& rightTrans = game_.getEntityManager().
237 } 269 getComponent<TransformableComponent>(right);
238 270
239 case Direction::right: 271 return (leftTrans.x < rightTrans.x);
240 { 272 });
241 newX = -WALL_GAP;
242 273
243 break; 274 for (id_type collider : colliders)
244 } 275 {
276 auto& colliderTrans = game_.getEntityManager().
277 getComponent<TransformableComponent>(collider);
245 278
246 case Direction::up: 279 // Check if the entity would still move into the potential collider.
247 { 280 if (colliderTrans.x >= result.newX + transformable.w)
248 newY = MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.h; 281 {
282 break;
283 }
249 284
250 break; 285 auto& colliderPonder = game_.getEntityManager().
251 } 286 getComponent<PonderableComponent>(collider);
252 287
253 case Direction::down: 288 processCollision(
254 { 289 entity,
255 newY = -WALL_GAP; 290 collider,
291 Direction::right,
292 colliderPonder.colliderType,
293 colliderTrans.x,
294 colliderTrans.y,
295 colliderTrans.y + colliderTrans.h,
296 result);
297
298 if (result.stopProcessing)
299 {
300 break;
301 }
302 }
256 303
257 break; 304 // If movement hasn't been stopped by an intermediary object, and
258 } 305 // collision checking hasn't been stopped, process the environmental
259 } 306 // boundaries closest to the entity.
260 } 307 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
308 {
309 double boundaryAxis = it->first;
261 310
262 case MappableComponent::Adjacent::Type::warp: 311 for (;
312 (it != std::end(mappable.rightBoundaries)) &&
313 (it->first == boundaryAxis);
314 it++)
315 {
316 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
317 {
318 processCollision(
319 entity,
320 mapEntity,
321 Direction::right,
322 it->second.type,
323 it->first,
324 it->second.lower,
325 it->second.upper,
326 result);
327
328 if (result.stopProcessing)
263 { 329 {
264 if (game_.getEntityManager().
265 hasComponent<PlayableComponent>(entity))
266 {
267 adjacentlyWarping = true;
268 adjWarpDir = collision.getDirection();
269 adjWarpMapId = adj.mapId;
270 }
271
272 break; 330 break;
273 } 331 }
332 }
333 }
334 }
335 }
274 336
275 case MappableComponent::Adjacent::Type::reverse: 337 // Find vertical collisions
276 { 338 result.touchedWall = false;
277 // TODO: not yet implemented.
278 339
279 break; 340 if ((!result.stopProcessing) && (result.newY < oldY))
280 } 341 {
281 } 342 bool boundaryCollision = false;
343 auto it = mappable.upBoundaries.lower_bound(oldY);
344
345 // Find the axis distance of the closest environmental boundary.
346 for (;
347 (it != std::end(mappable.upBoundaries)) &&
348 (it->first >= result.newY);
349 it++)
350 {
351 // Check that the boundary is in range for the other axis.
352 if ((result.newX + transformable.h > it->second.lower) &&
353 (result.newX < it->second.upper))
354 {
355 // We have a collision!
356 boundaryCollision = true;
282 357
283 break; 358 break;
284 } 359 }
360 }
285 361
286 case Collision::Type::danger: 362 // Find a list of potential colliders, sorted so that the closest is
363 // first.
364 std::vector<id_type> colliders;
365
366 for (id_type collider : entities)
367 {
368 // Can't collide with self.
369 if (collider == entity)
287 { 370 {
288 if (game_.getEntityManager(). 371 continue;
289 hasComponent<PlayableComponent>(entity)) 372 }
290 {
291 game_.getSystemManager().getSystem<PlayingSystem>().die(entity);
292 373
293 adjacentlyWarping = false; 374 auto& colliderPonder = game_.getEntityManager().
294 } 375 getComponent<PonderableComponent>(collider);
295 376
296 stopProcessing = true; 377 // Only check objects that are active.
378 if (!colliderPonder.active)
379 {
380 continue;
381 }
297 382
298 break; 383 auto& colliderTrans = game_.getEntityManager().
384 getComponent<TransformableComponent>(collider);
385
386 // Check if the entity would move into the potential collider,
387 if ((colliderTrans.y + colliderTrans.h > result.newY) &&
388 // that it wasn't already colliding,
389 (colliderTrans.y + colliderTrans.h <= oldY) &&
390 // that the position on the other axis is in range,
391 (colliderTrans.x + colliderTrans.w > result.newX) &&
392 (colliderTrans.x < result.newX + transformable.w) &&
393 // and that the collider is not farther away than the environmental
394 // boundary.
395 (!boundaryCollision ||
396 (colliderTrans.y + colliderTrans.h >= it->first)))
397 {
398 colliders.push_back(collider);
299 } 399 }
400 }
401
402 std::sort(
403 std::begin(colliders),
404 std::end(colliders),
405 [&] (id_type left, id_type right) {
406 auto& leftTrans = game_.getEntityManager().
407 getComponent<TransformableComponent>(left);
408
409 auto& rightTrans = game_.getEntityManager().
410 getComponent<TransformableComponent>(right);
300 411
301 default: 412 return (rightTrans.y < leftTrans.y);
413 });
414
415 for (id_type collider : colliders)
416 {
417 auto& colliderTrans = game_.getEntityManager().
418 getComponent<TransformableComponent>(collider);
419
420 // Check if the entity would still move into the potential collider.
421 if (colliderTrans.y + colliderTrans.h <= result.newY)
302 { 422 {
303 // Not yet implemented. 423 break;
424 }
425
426 auto& colliderPonder = game_.getEntityManager().
427 getComponent<PonderableComponent>(collider);
304 428
429 processCollision(
430 entity,
431 collider,
432 Direction::up,
433 colliderPonder.colliderType,
434 colliderTrans.y + colliderTrans.h,
435 colliderTrans.x,
436 colliderTrans.x + colliderTrans.w,
437 result);
438
439 if (result.stopProcessing)
440 {
305 break; 441 break;
306 } 442 }
307 } 443 }
308 444
309 if (stopProcessing) 445 // If movement hasn't been stopped by an intermediary object, and
446 // collision checking hasn't been stopped, process the environmental
447 // boundaries closest to the entity.
448 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
310 { 449 {
311 break; 450 double boundaryAxis = it->first;
451
452 for (;
453 (it != std::end(mappable.upBoundaries)) &&
454 (it->first == boundaryAxis);
455 it++)
456 {
457 if ((result.newX + transformable.w > it->second.lower) &&
458 (result.newX < it->second.upper))
459 {
460 processCollision(
461 entity,
462 mapEntity,
463 Direction::up,
464 it->second.type,
465 it->first,
466 it->second.lower,
467 it->second.upper,
468 result);
469
470 if (result.stopProcessing)
471 {
472 break;
473 }
474 }
475 }
312 } 476 }
477 } else if ((!result.stopProcessing) && (result.newY > oldY))
478 {
479 bool boundaryCollision = false;
480 auto it = mappable.downBoundaries.lower_bound(oldBottom);
481
482 // Find the axis distance of the closest environmental boundary.
483 for (;
484 (it != std::end(mappable.downBoundaries))
485 && (it->first <= (result.newY + transformable.h));
486 it++)
487 {
488 // Check that the boundary is in range for the other axis.
489 if ((result.newX + transformable.w > it->second.lower) &&
490 (result.newX < it->second.upper))
491 {
492 // We have a collision!
493 boundaryCollision = true;
313 494
314 if (touchedWall) 495 break;
496 }
497 }
498
499 // Find a list of potential colliders, sorted so that the closest is
500 // first.
501 std::vector<id_type> colliders;
502
503 for (id_type collider : entities)
315 { 504 {
316 switch (collision.getDirection()) 505 // Can't collide with self.
506 if (collider == entity)
317 { 507 {
318 case Direction::left: 508 continue;
319 { 509 }
320 newX = collision.getAxis();
321 ponderable.velX = 0.0;
322 510
323 break; 511 auto& colliderPonder = game_.getEntityManager().
324 } 512 getComponent<PonderableComponent>(collider);
325 513
326 case Direction::right: 514 // Only check objects that are active.
327 { 515 if (!colliderPonder.active)
328 newX = collision.getAxis() - transformable.w; 516 {
329 ponderable.velX = 0.0; 517 continue;
518 }
330 519
331 break; 520 auto& colliderTrans = game_.getEntityManager().
332 } 521 getComponent<TransformableComponent>(collider);
522
523 // Check if the entity would move into the potential collider,
524 if ((colliderTrans.y < result.newY + transformable.h) &&
525 // that it wasn't already colliding,
526 (colliderTrans.y >= oldBottom) &&
527 // that the position on the other axis is in range,
528 (colliderTrans.x + colliderTrans.w > result.newX) &&
529 (colliderTrans.x < result.newX + transformable.w) &&
530 // and that the collider is not farther away than the environmental
531 // boundary.
532 (!boundaryCollision || (colliderTrans.y <= it->first)))
533 {
534 colliders.push_back(collider);
535 }
536 }
333 537
334 case Direction::up: 538 std::sort(
335 { 539 std::begin(colliders),
336 newY = collision.getAxis(); 540 std::end(colliders),
337 ponderable.velY = 0.0; 541 [&] (id_type left, id_type right) {
542 auto& leftTrans = game_.getEntityManager().
543 getComponent<TransformableComponent>(left);
338 544
339 break; 545 auto& rightTrans = game_.getEntityManager().
340 } 546 getComponent<TransformableComponent>(right);
341 547
342 case Direction::down: 548 return (leftTrans.y < rightTrans.y);
343 { 549 });
344 newY = collision.getAxis() - transformable.h; 550
345 ponderable.velY = 0.0; 551 for (id_type collider : colliders)
346 ponderable.grounded = true; 552 {
553 auto& colliderTrans = game_.getEntityManager().
554 getComponent<TransformableComponent>(collider);
555
556 // Check if the entity would still move into the potential collider.
557 if (colliderTrans.y >= result.newY + transformable.h)
558 {
559 break;
560 }
561
562 auto& colliderPonder = game_.getEntityManager().
563 getComponent<PonderableComponent>(collider);
564
565 processCollision(
566 entity,
567 collider,
568 Direction::down,
569 colliderPonder.colliderType,
570 colliderTrans.y,
571 colliderTrans.x,
572 colliderTrans.x + colliderTrans.w,
573 result);
574
575 if (result.stopProcessing)
576 {
577 break;
578 }
579 }
580
581 // If movement hasn't been stopped by an intermediary object, and
582 // collision checking hasn't been stopped, process the environmental
583 // boundaries closest to the entity.
584 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
585 {
586 double boundaryAxis = it->first;
347 587
348 break; 588 for (;
589 (it != std::end(mappable.downBoundaries)) &&
590 (it->first == boundaryAxis);
591 it++)
592 {
593 if ((result.newX + transformable.w > it->second.lower) &&
594 (result.newX < it->second.upper))
595 {
596 processCollision(
597 entity,
598 mapEntity,
599 Direction::down,
600 it->second.type,
601 it->first,
602 it->second.lower,
603 it->second.upper,
604 result);
605
606 if (result.stopProcessing)
607 {
608 break;
609 }
349 } 610 }
350 } 611 }
351 } 612 }
352 } 613 }
353 614
354 // Move 615 // Move
355 transformable.x = newX; 616 transformable.x = result.newX;
356 transformable.y = newY; 617 transformable.y = result.newY;
357 618
358 // Perform cleanup for orientable entites 619 // Perform cleanup for orientable entites
359 if (game_.getEntityManager().hasComponent<OrientableComponent>(entity)) 620 if (game_.getEntityManager().hasComponent<OrientableComponent>(entity))
@@ -381,12 +642,12 @@ void PonderingSystem::tick(double dt)
381 } 642 }
382 643
383 // Move to an adjacent map, if necessary 644 // Move to an adjacent map, if necessary
384 if (adjacentlyWarping) 645 if (result.adjacentlyWarping)
385 { 646 {
386 double warpX = newX; 647 double warpX = result.newX;
387 double warpY = newY; 648 double warpY = result.newY;
388 649
389 switch (adjWarpDir) 650 switch (result.adjWarpDir)
390 { 651 {
391 case Direction::left: 652 case Direction::left:
392 { 653 {
@@ -420,7 +681,7 @@ void PonderingSystem::tick(double dt)
420 game_.getSystemManager().getSystem<PlayingSystem>(). 681 game_.getSystemManager().getSystem<PlayingSystem>().
421 changeMap( 682 changeMap(
422 entity, 683 entity,
423 adjWarpMapId, 684 result.adjWarpMapId,
424 warpX, 685 warpX,
425 warpY); 686 warpY);
426 } 687 }
@@ -453,3 +714,196 @@ void PonderingSystem::initPrototype(id_type prototype)
453 ponderable.frozen = false; 714 ponderable.frozen = false;
454 ponderable.collidable = true; 715 ponderable.collidable = true;
455} 716}
717
718void PonderingSystem::processCollision(
719 id_type entity,
720 id_type collider,
721 Direction dir,
722 PonderableComponent::Collision type,
723 double axis,
724 double lower,
725 double upper,
726 CollisionResult& result)
727{
728 auto& ponderable = game_.getEntityManager().
729 getComponent<PonderableComponent>(entity);
730
731 auto& transformable = game_.getEntityManager().
732 getComponent<TransformableComponent>(entity);
733
734 switch (type)
735 {
736 case PonderableComponent::Collision::wall:
737 {
738 result.touchedWall = true;
739
740 break;
741 }
742
743 case PonderableComponent::Collision::platform:
744 {
745 if (game_.getEntityManager().
746 hasComponent<OrientableComponent>(entity))
747 {
748 auto& orientable = game_.getEntityManager().
749 getComponent<OrientableComponent>(entity);
750
751 if (orientable.getDropState() !=
752 OrientableComponent::DropState::none)
753 {
754 orientable.setDropState(OrientableComponent::DropState::active);
755 } else {
756 result.touchedWall = true;
757 }
758 } else {
759 result.touchedWall = true;
760 }
761
762 break;
763 }
764
765 case PonderableComponent::Collision::adjacency:
766 {
767 auto& mappable = game_.getEntityManager().
768 getComponent<MappableComponent>(collider);
769
770 auto& adj = [&] () -> const MappableComponent::Adjacent& {
771 switch (dir)
772 {
773 case Direction::left: return mappable.leftAdjacent;
774 case Direction::right: return mappable.rightAdjacent;
775 case Direction::up: return mappable.upAdjacent;
776 case Direction::down: return mappable.downAdjacent;
777 }
778 }();
779
780 switch (adj.type)
781 {
782 case MappableComponent::Adjacent::Type::wall:
783 {
784 result.touchedWall = true;
785
786 break;
787 }
788
789 case MappableComponent::Adjacent::Type::wrap:
790 {
791 switch (dir)
792 {
793 case Direction::left:
794 {
795 result.newX = GAME_WIDTH + WALL_GAP - transformable.w;
796
797 break;
798 }
799
800 case Direction::right:
801 {
802 result.newX = -WALL_GAP;
803
804 break;
805 }
806
807 case Direction::up:
808 {
809 result.newY =
810 MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.h;
811
812 break;
813 }
814
815 case Direction::down:
816 {
817 result.newY = -WALL_GAP;
818
819 break;
820 }
821 }
822 }
823
824 case MappableComponent::Adjacent::Type::warp:
825 {
826 if (game_.getEntityManager().
827 hasComponent<PlayableComponent>(entity))
828 {
829 result.adjacentlyWarping = true;
830 result.adjWarpDir = dir;
831 result.adjWarpMapId = adj.mapId;
832 }
833
834 break;
835 }
836
837 case MappableComponent::Adjacent::Type::reverse:
838 {
839 // TODO: not yet implemented.
840
841 break;
842 }
843 }
844
845 break;
846 }
847
848 case PonderableComponent::Collision::danger:
849 {
850 if (game_.getEntityManager().
851 hasComponent<PlayableComponent>(entity))
852 {
853 game_.getSystemManager().getSystem<PlayingSystem>().die(entity);
854
855 result.adjacentlyWarping = false;
856 }
857
858 result.stopProcessing = true;
859
860 break;
861 }
862
863 default:
864 {
865 // Not yet implemented.
866
867 break;
868 }
869 }
870
871 if (!result.stopProcessing && result.touchedWall)
872 {
873 switch (dir)
874 {
875 case Direction::left:
876 {
877 result.newX = axis;
878 ponderable.velX = 0.0;
879
880 break;
881 }
882
883 case Direction::right:
884 {
885 result.newX = axis - transformable.w;
886 ponderable.velX = 0.0;
887
888 break;
889 }
890
891 case Direction::up:
892 {
893 result.newY = axis;
894 ponderable.velY = 0.0;
895
896 break;
897 }
898
899 case Direction::down:
900 {
901 result.newY = axis - transformable.h;
902 ponderable.velY = 0.0;
903 ponderable.grounded = true;
904
905 break;
906 }
907 }
908 }
909}
diff --git a/src/systems/pondering.h b/src/systems/pondering.h index 58e6496..aa430db 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h
@@ -18,6 +18,29 @@ public:
18 18
19 void initPrototype(id_type prototype); 19 void initPrototype(id_type prototype);
20 20
21private:
22
23 struct CollisionResult
24 {
25 double newX;
26 double newY;
27 bool stopProcessing = false;
28 bool touchedWall = false;
29 bool adjacentlyWarping = false;
30 Direction adjWarpDir;
31 size_t adjWarpMapId;
32 };
33
34 void processCollision(
35 id_type entity,
36 id_type collider,
37 Direction dir,
38 PonderableComponent::Collision type,
39 double axis,
40 double lower,
41 double upper,
42 CollisionResult& result);
43
21}; 44};
22 45
23#endif /* end of include guard: PONDERING_H_F2530E0E */ 46#endif /* end of include guard: PONDERING_H_F2530E0E */
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index c86dd5e..3656acb 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp
@@ -207,6 +207,10 @@ EntityManager::id_type RealizingSystem::initSingleton(
207 207
208 animatable.origAnimation = "static"; 208 animatable.origAnimation = "static";
209 209
210 // Create a physics body.
211 game_.getSystemManager().getSystem<PonderingSystem>().
212 initializeBody(mapObject, PonderableComponent::Type::vacuumed);
213
210 mappable.objects.push_back(mapObject); 214 mappable.objects.push_back(mapObject);
211 } else if (!xmlStrcmp( 215 } else if (!xmlStrcmp(
212 mapNode->name, 216 mapNode->name,