summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-05-04 10:50:15 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-05-09 17:59:13 -0400
commit893dbf8a235db2b4f1fafacf90290b0821f0048c (patch)
tree4e224187fbaae891c4ddfa5f38c7f8eae11f06e3
parentff99cefc647f9215ad1cdc9f408c9fc655b45706 (diff)
downloadtherapy-893dbf8a235db2b4f1fafacf90290b0821f0048c.tar.gz
therapy-893dbf8a235db2b4f1fafacf90290b0821f0048c.tar.bz2
therapy-893dbf8a235db2b4f1fafacf90290b0821f0048c.zip
Recursively handle ferried bodies
PonderingSystem now recursively ticks bodies, starting with unferried bodies at the top level, and recursively ticking their passengers. This fixes the second issue described in 8f1c4f1 -- that passengers may be ticked before their ferries, causing it to use out-of-date information about the ferry's location.
-rw-r--r--src/systems/pondering.cpp1232
-rw-r--r--src/systems/pondering.h5
2 files changed, 637 insertions, 600 deletions
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 4aa47f2..e6417eb 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp
@@ -15,15 +15,6 @@
15 15
16void PonderingSystem::tick(double dt) 16void PonderingSystem::tick(double dt)
17{ 17{
18 auto& realizable = game_.getEntityManager().
19 getComponent<RealizableComponent>(
20 game_.getSystemManager().getSystem<RealizingSystem>().getSingleton());
21
22 id_type mapEntity = realizable.activeMap;
23
24 auto& mappable = game_.getEntityManager().
25 getComponent<MappableComponent>(mapEntity);
26
27 auto entities = game_.getEntityManager().getEntitiesWithComponents< 18 auto entities = game_.getEntityManager().getEntitiesWithComponents<
28 PonderableComponent, 19 PonderableComponent,
29 TransformableComponent>(); 20 TransformableComponent>();
@@ -33,750 +24,791 @@ void PonderingSystem::tick(double dt)
33 auto& ponderable = game_.getEntityManager(). 24 auto& ponderable = game_.getEntityManager().
34 getComponent<PonderableComponent>(entity); 25 getComponent<PonderableComponent>(entity);
35 26
36 if (!ponderable.active || ponderable.frozen) 27 // We will recursively process ferried bodies after their ferries have been
28 // processed, so hold off on processing ferried bodies at the top level.
29 if (ponderable.ferried)
37 { 30 {
38 continue; 31 continue;
39 } 32 }
40 33
41 auto& transformable = game_.getEntityManager(). 34 tickBody(
42 getComponent<TransformableComponent>(entity); 35 entity,
36 dt,
37 entities);
38 }
39}
43 40
44 // Accelerate 41void PonderingSystem::initializeBody(
45 ponderable.velX += ponderable.accelX * dt; 42 id_type entity,
46 ponderable.velY += ponderable.accelY * dt; 43 PonderableComponent::Type type)
44{
45 auto& ponderable = game_.getEntityManager().
46 emplaceComponent<PonderableComponent>(entity, type);
47 47
48 if ((ponderable.type == PonderableComponent::Type::freefalling) 48 if (type == PonderableComponent::Type::freefalling)
49 && (ponderable.velY > TERMINAL_VELOCITY)) 49 {
50 { 50 ponderable.accelY = NORMAL_GRAVITY;
51 ponderable.velY = TERMINAL_VELOCITY; 51 }
52 } 52}
53 53
54 const double oldX = transformable.x; 54void PonderingSystem::initPrototype(id_type prototype)
55 const double oldY = transformable.y; 55{
56 const double oldRight = oldX + transformable.w; 56 auto& ponderable = game_.getEntityManager().
57 const double oldBottom = oldY + transformable.h; 57 getComponent<PonderableComponent>(prototype);
58 58
59 CollisionResult result; 59 ponderable.velX = 0.0;
60 ponderable.velY = 0.0;
61 ponderable.accelX = 0.0;
62 ponderable.accelY = 0.0;
63 ponderable.grounded = false;
64 ponderable.frozen = false;
65 ponderable.collidable = true;
66 ponderable.ferried = false;
67 ponderable.passengers.clear();
68}
60 69
61 if (ponderable.ferried) 70void PonderingSystem::unferry(id_type entity)
62 { 71{
63 auto& ferryTrans = game_.getEntityManager(). 72 auto& ponderable = game_.getEntityManager().
64 getComponent<TransformableComponent>(ponderable.ferry); 73 getComponent<PonderableComponent>(entity);
65
66 result.newX = ferryTrans.x + ponderable.relX;
67 result.newY = ferryTrans.y + ponderable.relY;
68 } else {
69 result.newX = transformable.x;
70 result.newY = transformable.y;
71 }
72 74
73 result.newX += ponderable.velX * dt; 75 if (ponderable.ferried)
74 result.newY += ponderable.velY * dt; 76 {
77 ponderable.ferried = false;
75 78
76 bool oldGrounded = ponderable.grounded; 79 auto& ferryPonder = game_.getEntityManager().
77 ponderable.grounded = false; 80 getComponent<PonderableComponent>(ponderable.ferry);
78 81
79 // Find horizontal collisions. 82 ferryPonder.passengers.erase(entity);
80 if (result.newX < oldX) 83 }
81 { 84}
82 bool boundaryCollision = false;
83 auto it = mappable.leftBoundaries.lower_bound(oldX);
84 85
85 // Find the axis distance of the closest environmental boundary. 86void PonderingSystem::tickBody(
86 for (; 87 id_type entity,
87 (it != std::end(mappable.leftBoundaries)) && 88 double dt,
88 (it->first >= result.newX); 89 const std::set<id_type>& entities)
89 it++) 90{
90 { 91 auto& ponderable = game_.getEntityManager().
91 // Check that the boundary is in range for the other axis. 92 getComponent<PonderableComponent>(entity);
92 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
93 {
94 // We have a collision!
95 boundaryCollision = true;
96 93
97 break; 94 if (!ponderable.active || ponderable.frozen)
98 } 95 {
99 } 96 continue;
97 }
100 98
101 // Find a list of potential colliders, sorted so that the closest is 99 auto& realizable = game_.getEntityManager().
102 // first. 100 getComponent<RealizableComponent>(
103 std::vector<id_type> colliders; 101 game_.getSystemManager().getSystem<RealizingSystem>().getSingleton());
104 102
105 for (id_type collider : entities) 103 id_type mapEntity = realizable.activeMap;
106 {
107 // Can't collide with self.
108 if (collider == entity)
109 {
110 continue;
111 }
112 104
113 auto& colliderPonder = game_.getEntityManager(). 105 auto& mappable = game_.getEntityManager().
114 getComponent<PonderableComponent>(collider); 106 getComponent<MappableComponent>(mapEntity);
115 107
116 // Only check objects that are active. 108 auto& transformable = game_.getEntityManager().
117 if (!colliderPonder.active) 109 getComponent<TransformableComponent>(entity);
118 {
119 continue;
120 }
121 110
122 auto& colliderTrans = game_.getEntityManager(). 111 // Accelerate
123 getComponent<TransformableComponent>(collider); 112 ponderable.velX += ponderable.accelX * dt;
124 113 ponderable.velY += ponderable.accelY * dt;
125 // Check if the entity would move into the potential collider,
126 if ((colliderTrans.x + colliderTrans.w > result.newX) &&
127 // that it wasn't already colliding,
128 (colliderTrans.x + colliderTrans.w <= oldX) &&
129 // that the position on the other axis is in range,
130 (colliderTrans.y + colliderTrans.h > oldY) &&
131 (colliderTrans.y < oldBottom) &&
132 // and that the collider is not farther away than the environmental
133 // boundary.
134 (!boundaryCollision ||
135 (colliderTrans.x + colliderTrans.w >= it->first)))
136 {
137 colliders.push_back(collider);
138 }
139 }
140 114
141 std::sort( 115 if ((ponderable.type == PonderableComponent::Type::freefalling)
142 std::begin(colliders), 116 && (ponderable.velY > TERMINAL_VELOCITY))
143 std::end(colliders), 117 {
144 [&] (id_type left, id_type right) { 118 ponderable.velY = TERMINAL_VELOCITY;
145 auto& leftTrans = game_.getEntityManager(). 119 }
146 getComponent<TransformableComponent>(left);
147 120
148 auto& rightTrans = game_.getEntityManager(). 121 const double oldX = transformable.x;
149 getComponent<TransformableComponent>(right); 122 const double oldY = transformable.y;
123 const double oldRight = oldX + transformable.w;
124 const double oldBottom = oldY + transformable.h;
150 125
151 return (rightTrans.x < leftTrans.x); 126 CollisionResult result;
152 });
153 127
154 for (id_type collider : colliders) 128 if (ponderable.ferried)
155 { 129 {
156 auto& colliderTrans = game_.getEntityManager(). 130 auto& ferryTrans = game_.getEntityManager().
157 getComponent<TransformableComponent>(collider); 131 getComponent<TransformableComponent>(ponderable.ferry);
132
133 result.newX = ferryTrans.x + ponderable.relX;
134 result.newY = ferryTrans.y + ponderable.relY;
135 } else {
136 result.newX = transformable.x;
137 result.newY = transformable.y;
138 }
158 139
159 // Check if the entity would still move into the potential collider. 140 result.newX += ponderable.velX * dt;
160 if (colliderTrans.x + colliderTrans.w <= result.newX) 141 result.newY += ponderable.velY * dt;
161 {
162 break;
163 }
164 142
165 auto& colliderPonder = game_.getEntityManager(). 143 bool oldGrounded = ponderable.grounded;
166 getComponent<PonderableComponent>(collider); 144 ponderable.grounded = false;
167 145
168 processCollision( 146 // Find horizontal collisions.
169 entity, 147 if (result.newX < oldX)
170 collider, 148 {
171 Direction::left, 149 bool boundaryCollision = false;
172 colliderPonder.colliderType, 150 auto it = mappable.leftBoundaries.lower_bound(oldX);
173 colliderTrans.x + colliderTrans.w, 151
174 colliderTrans.y, 152 // Find the axis distance of the closest environmental boundary.
175 colliderTrans.y + colliderTrans.h, 153 for (;
176 result); 154 (it != std::end(mappable.leftBoundaries)) &&
155 (it->first >= result.newX);
156 it++)
157 {
158 // Check that the boundary is in range for the other axis.
159 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
160 {
161 // We have a collision!
162 boundaryCollision = true;
177 163
178 if (result.stopProcessing) 164 break;
179 {
180 break;
181 }
182 } 165 }
166 }
183 167
184 // If movement hasn't been stopped by an intermediary object, and 168 // Find a list of potential colliders, sorted so that the closest is
185 // collision checking hasn't been stopped, process the environmental 169 // first.
186 // boundaries closest to the entity. 170 std::vector<id_type> colliders;
187 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
188 {
189 double boundaryAxis = it->first;
190 171
191 for (; 172 for (id_type collider : entities)
192 (it != std::end(mappable.leftBoundaries)) &&
193 (it->first == boundaryAxis);
194 it++)
195 {
196 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
197 {
198 processCollision(
199 entity,
200 mapEntity,
201 Direction::left,
202 it->second.type,
203 it->first,
204 it->second.lower,
205 it->second.upper,
206 result);
207
208 if (result.stopProcessing)
209 {
210 break;
211 }
212 }
213 }
214 }
215 } else if (result.newX > oldX)
216 { 173 {
217 bool boundaryCollision = false; 174 // Can't collide with self.
218 auto it = mappable.rightBoundaries.lower_bound(oldRight); 175 if (collider == entity)
219
220 // Find the axis distance of the closest environmental boundary.
221 for (;
222 (it != std::end(mappable.rightBoundaries))
223 && (it->first <= (result.newX + transformable.w));
224 it++)
225 { 176 {
226 // Check that the boundary is in range for the other axis. 177 continue;
227 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
228 {
229 // We have a collision!
230 boundaryCollision = true;
231
232 break;
233 }
234 } 178 }
235 179
236 // Find a list of potential colliders, sorted so that the closest is 180 auto& colliderPonder = game_.getEntityManager().
237 // first. 181 getComponent<PonderableComponent>(collider);
238 std::vector<id_type> colliders;
239 182
240 for (id_type collider : entities) 183 // Only check objects that are active.
184 if (!colliderPonder.active)
241 { 185 {
242 // Can't collide with self. 186 continue;
243 if (collider == entity) 187 }
244 {
245 continue;
246 }
247
248 auto& colliderPonder = game_.getEntityManager().
249 getComponent<PonderableComponent>(collider);
250
251 // Only check objects that are active.
252 if (!colliderPonder.active)
253 {
254 continue;
255 }
256 188
257 auto& colliderTrans = game_.getEntityManager(). 189 auto& colliderTrans = game_.getEntityManager().
258 getComponent<TransformableComponent>(collider); 190 getComponent<TransformableComponent>(collider);
259 191
260 // Check if the entity would move into the potential collider, 192 // Check if the entity would move into the potential collider,
261 if ((colliderTrans.x < result.newX + transformable.w) && 193 if ((colliderTrans.x + colliderTrans.w > result.newX) &&
262 // that it wasn't already colliding, 194 // that it wasn't already colliding,
263 (colliderTrans.x >= oldRight) && 195 (colliderTrans.x + colliderTrans.w <= oldX) &&
264 // that the position on the other axis is in range, 196 // that the position on the other axis is in range,
265 (colliderTrans.y + colliderTrans.h > oldY) && 197 (colliderTrans.y + colliderTrans.h > oldY) &&
266 (colliderTrans.y < oldBottom) && 198 (colliderTrans.y < oldBottom) &&
267 // and that the collider is not farther away than the environmental 199 // and that the collider is not farther away than the environmental
268 // boundary. 200 // boundary.
269 (!boundaryCollision || (colliderTrans.x <= it->first))) 201 (!boundaryCollision ||
270 { 202 (colliderTrans.x + colliderTrans.w >= it->first)))
271 colliders.push_back(collider); 203 {
272 } 204 colliders.push_back(collider);
273 } 205 }
206 }
274 207
275 std::sort( 208 std::sort(
276 std::begin(colliders), 209 std::begin(colliders),
277 std::end(colliders), 210 std::end(colliders),
278 [&] (id_type left, id_type right) { 211 [&] (id_type left, id_type right) {
279 auto& leftTrans = game_.getEntityManager(). 212 auto& leftTrans = game_.getEntityManager().
280 getComponent<TransformableComponent>(left); 213 getComponent<TransformableComponent>(left);
281 214
282 auto& rightTrans = game_.getEntityManager(). 215 auto& rightTrans = game_.getEntityManager().
283 getComponent<TransformableComponent>(right); 216 getComponent<TransformableComponent>(right);
284 217
285 return (leftTrans.x < rightTrans.x); 218 return (rightTrans.x < leftTrans.x);
286 }); 219 });
287 220
288 for (id_type collider : colliders) 221 for (id_type collider : colliders)
289 { 222 {
290 auto& colliderTrans = game_.getEntityManager(). 223 auto& colliderTrans = game_.getEntityManager().
291 getComponent<TransformableComponent>(collider); 224 getComponent<TransformableComponent>(collider);
292 225
293 // Check if the entity would still move into the potential collider. 226 // Check if the entity would still move into the potential collider.
294 if (colliderTrans.x >= result.newX + transformable.w) 227 if (colliderTrans.x + colliderTrans.w <= result.newX)
295 { 228 {
296 break; 229 break;
297 } 230 }
298 231
299 auto& colliderPonder = game_.getEntityManager(). 232 auto& colliderPonder = game_.getEntityManager().
300 getComponent<PonderableComponent>(collider); 233 getComponent<PonderableComponent>(collider);
301 234
302 processCollision( 235 processCollision(
303 entity, 236 entity,
304 collider, 237 collider,
305 Direction::right, 238 Direction::left,
306 colliderPonder.colliderType, 239 colliderPonder.colliderType,
307 colliderTrans.x, 240 colliderTrans.x + colliderTrans.w,
308 colliderTrans.y, 241 colliderTrans.y,
309 colliderTrans.y + colliderTrans.h, 242 colliderTrans.y + colliderTrans.h,
310 result); 243 result);
311 244
312 if (result.stopProcessing) 245 if (result.stopProcessing)
313 { 246 {
314 break; 247 break;
315 }
316 } 248 }
249 }
317 250
318 // If movement hasn't been stopped by an intermediary object, and 251 // If movement hasn't been stopped by an intermediary object, and
319 // collision checking hasn't been stopped, process the environmental 252 // collision checking hasn't been stopped, process the environmental
320 // boundaries closest to the entity. 253 // boundaries closest to the entity.
321 if (!result.stopProcessing && !result.touchedWall && boundaryCollision) 254 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
322 { 255 {
323 double boundaryAxis = it->first; 256 double boundaryAxis = it->first;
324 257
325 for (; 258 for (;
326 (it != std::end(mappable.rightBoundaries)) && 259 (it != std::end(mappable.leftBoundaries)) &&
327 (it->first == boundaryAxis); 260 (it->first == boundaryAxis);
328 it++) 261 it++)
262 {
263 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
329 { 264 {
330 if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) 265 processCollision(
266 entity,
267 mapEntity,
268 Direction::left,
269 it->second.type,
270 it->first,
271 it->second.lower,
272 it->second.upper,
273 result);
274
275 if (result.stopProcessing)
331 { 276 {
332 processCollision( 277 break;
333 entity,
334 mapEntity,
335 Direction::right,
336 it->second.type,
337 it->first,
338 it->second.lower,
339 it->second.upper,
340 result);
341
342 if (result.stopProcessing)
343 {
344 break;
345 }
346 } 278 }
347 } 279 }
348 } 280 }
349 } 281 }
350 282 } else if (result.newX > oldX)
351 // Find vertical collisions 283 {
352 result.touchedWall = false; 284 bool boundaryCollision = false;
353 285 auto it = mappable.rightBoundaries.lower_bound(oldRight);
354 if ((!result.stopProcessing) && (result.newY < oldY)) 286
287 // Find the axis distance of the closest environmental boundary.
288 for (;
289 (it != std::end(mappable.rightBoundaries))
290 && (it->first <= (result.newX + transformable.w));
291 it++)
355 { 292 {
356 bool boundaryCollision = false; 293 // Check that the boundary is in range for the other axis.
357 auto it = mappable.upBoundaries.lower_bound(oldY); 294 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
358
359 // Find the axis distance of the closest environmental boundary.
360 for (;
361 (it != std::end(mappable.upBoundaries)) &&
362 (it->first >= result.newY);
363 it++)
364 { 295 {
365 // Check that the boundary is in range for the other axis. 296 // We have a collision!
366 if ((result.newX + transformable.h > it->second.lower) && 297 boundaryCollision = true;
367 (result.newX < it->second.upper))
368 {
369 // We have a collision!
370 boundaryCollision = true;
371 298
372 break; 299 break;
373 }
374 } 300 }
301 }
375 302
376 // Find a list of potential colliders, sorted so that the closest is 303 // Find a list of potential colliders, sorted so that the closest is
377 // first. 304 // first.
378 std::vector<id_type> colliders; 305 std::vector<id_type> colliders;
379 306
380 for (id_type collider : entities) 307 for (id_type collider : entities)
308 {
309 // Can't collide with self.
310 if (collider == entity)
381 { 311 {
382 // Can't collide with self. 312 continue;
383 if (collider == entity) 313 }
384 {
385 continue;
386 }
387 314
388 auto& colliderPonder = game_.getEntityManager(). 315 auto& colliderPonder = game_.getEntityManager().
389 getComponent<PonderableComponent>(collider); 316 getComponent<PonderableComponent>(collider);
390 317
391 // Only check objects that are active. 318 // Only check objects that are active.
392 if (!colliderPonder.active) 319 if (!colliderPonder.active)
393 { 320 {
394 continue; 321 continue;
395 } 322 }
396 323
397 auto& colliderTrans = game_.getEntityManager(). 324 auto& colliderTrans = game_.getEntityManager().
398 getComponent<TransformableComponent>(collider); 325 getComponent<TransformableComponent>(collider);
399 326
400 // Check if the entity would move into the potential collider, 327 // Check if the entity would move into the potential collider,
401 if ((colliderTrans.y + colliderTrans.h > result.newY) && 328 if ((colliderTrans.x < result.newX + transformable.w) &&
402 // that it wasn't already colliding, 329 // that it wasn't already colliding,
403 (colliderTrans.y + colliderTrans.h <= oldY) && 330 (colliderTrans.x >= oldRight) &&
404 // that the position on the other axis is in range, 331 // that the position on the other axis is in range,
405 (colliderTrans.x + colliderTrans.w > result.newX) && 332 (colliderTrans.y + colliderTrans.h > oldY) &&
406 (colliderTrans.x < result.newX + transformable.w) && 333 (colliderTrans.y < oldBottom) &&
407 // and that the collider is not farther away than the environmental 334 // and that the collider is not farther away than the environmental
408 // boundary. 335 // boundary.
409 (!boundaryCollision || 336 (!boundaryCollision || (colliderTrans.x <= it->first)))
410 (colliderTrans.y + colliderTrans.h >= it->first))) 337 {
411 { 338 colliders.push_back(collider);
412 colliders.push_back(collider);
413 }
414 } 339 }
340 }
415 341
416 std::sort( 342 std::sort(
417 std::begin(colliders), 343 std::begin(colliders),
418 std::end(colliders), 344 std::end(colliders),
419 [&] (id_type left, id_type right) { 345 [&] (id_type left, id_type right) {
420 auto& leftTrans = game_.getEntityManager(). 346 auto& leftTrans = game_.getEntityManager().
421 getComponent<TransformableComponent>(left); 347 getComponent<TransformableComponent>(left);
422 348
423 auto& rightTrans = game_.getEntityManager(). 349 auto& rightTrans = game_.getEntityManager().
424 getComponent<TransformableComponent>(right); 350 getComponent<TransformableComponent>(right);
425 351
426 return (rightTrans.y < leftTrans.y); 352 return (leftTrans.x < rightTrans.x);
427 }); 353 });
428 354
429 for (id_type collider : colliders) 355 for (id_type collider : colliders)
430 { 356 {
431 auto& colliderTrans = game_.getEntityManager(). 357 auto& colliderTrans = game_.getEntityManager().
432 getComponent<TransformableComponent>(collider); 358 getComponent<TransformableComponent>(collider);
433 359
434 // Check if the entity would still move into the potential collider. 360 // Check if the entity would still move into the potential collider.
435 if (colliderTrans.y + colliderTrans.h <= result.newY) 361 if (colliderTrans.x >= result.newX + transformable.w)
436 { 362 {
437 break; 363 break;
438 } 364 }
439 365
440 auto& colliderPonder = game_.getEntityManager(). 366 auto& colliderPonder = game_.getEntityManager().
441 getComponent<PonderableComponent>(collider); 367 getComponent<PonderableComponent>(collider);
442 368
443 processCollision( 369 processCollision(
444 entity, 370 entity,
445 collider, 371 collider,
446 Direction::up, 372 Direction::right,
447 colliderPonder.colliderType, 373 colliderPonder.colliderType,
448 colliderTrans.y + colliderTrans.h, 374 colliderTrans.x,
449 colliderTrans.x, 375 colliderTrans.y,
450 colliderTrans.x + colliderTrans.w, 376 colliderTrans.y + colliderTrans.h,
451 result); 377 result);
452 378
453 if (result.stopProcessing) 379 if (result.stopProcessing)
454 { 380 {
455 break; 381 break;
456 }
457 } 382 }
383 }
458 384
459 // If movement hasn't been stopped by an intermediary object, and 385 // If movement hasn't been stopped by an intermediary object, and
460 // collision checking hasn't been stopped, process the environmental 386 // collision checking hasn't been stopped, process the environmental
461 // boundaries closest to the entity. 387 // boundaries closest to the entity.
462 if (!result.stopProcessing && !result.touchedWall && boundaryCollision) 388 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
463 { 389 {
464 double boundaryAxis = it->first; 390 double boundaryAxis = it->first;
465 391
466 for (; 392 for (;
467 (it != std::end(mappable.upBoundaries)) && 393 (it != std::end(mappable.rightBoundaries)) &&
468 (it->first == boundaryAxis); 394 (it->first == boundaryAxis);
469 it++) 395 it++)
396 {
397 if ((oldBottom > it->second.lower) && (oldY < it->second.upper))
470 { 398 {
471 if ((result.newX + transformable.w > it->second.lower) && 399 processCollision(
472 (result.newX < it->second.upper)) 400 entity,
401 mapEntity,
402 Direction::right,
403 it->second.type,
404 it->first,
405 it->second.lower,
406 it->second.upper,
407 result);
408
409 if (result.stopProcessing)
473 { 410 {
474 processCollision( 411 break;
475 entity,
476 mapEntity,
477 Direction::up,
478 it->second.type,
479 it->first,
480 it->second.lower,
481 it->second.upper,
482 result);
483
484 if (result.stopProcessing)
485 {
486 break;
487 }
488 } 412 }
489 } 413 }
490 } 414 }
491 } else if ((!result.stopProcessing) && (result.newY > oldY)) 415 }
492 { 416 }
493 bool boundaryCollision = false;
494 auto it = mappable.downBoundaries.lower_bound(oldBottom);
495 417
496 // Find the axis distance of the closest environmental boundary. 418 // Find vertical collisions
497 for (; 419 result.touchedWall = false;
498 (it != std::end(mappable.downBoundaries)) 420
499 && (it->first <= (result.newY + transformable.h)); 421 if ((!result.stopProcessing) && (result.newY < oldY))
500 it++) 422 {
423 bool boundaryCollision = false;
424 auto it = mappable.upBoundaries.lower_bound(oldY);
425
426 // Find the axis distance of the closest environmental boundary.
427 for (;
428 (it != std::end(mappable.upBoundaries)) &&
429 (it->first >= result.newY);
430 it++)
431 {
432 // Check that the boundary is in range for the other axis.
433 if ((result.newX + transformable.h > it->second.lower) &&
434 (result.newX < it->second.upper))
501 { 435 {
502 // Check that the boundary is in range for the other axis. 436 // We have a collision!
503 if ((result.newX + transformable.w > it->second.lower) && 437 boundaryCollision = true;
504 (result.newX < it->second.upper))
505 {
506 // We have a collision!
507 boundaryCollision = true;
508 438
509 break; 439 break;
510 }
511 } 440 }
441 }
512 442
513 // Find a list of potential colliders, sorted so that the closest is 443 // Find a list of potential colliders, sorted so that the closest is
514 // first. 444 // first.
515 std::vector<id_type> colliders; 445 std::vector<id_type> colliders;
516 446
517 for (id_type collider : entities) 447 for (id_type collider : entities)
448 {
449 // Can't collide with self.
450 if (collider == entity)
518 { 451 {
519 // Can't collide with self. 452 continue;
520 if (collider == entity) 453 }
521 {
522 continue;
523 }
524 454
525 auto& colliderPonder = game_.getEntityManager(). 455 auto& colliderPonder = game_.getEntityManager().
526 getComponent<PonderableComponent>(collider); 456 getComponent<PonderableComponent>(collider);
527 457
528 // Only check objects that are active. 458 // Only check objects that are active.
529 if (!colliderPonder.active) 459 if (!colliderPonder.active)
530 { 460 {
531 continue; 461 continue;
532 } 462 }
533 463
534 auto& colliderTrans = game_.getEntityManager(). 464 auto& colliderTrans = game_.getEntityManager().
535 getComponent<TransformableComponent>(collider); 465 getComponent<TransformableComponent>(collider);
536 466
537 // Check if the entity would move into the potential collider, 467 // Check if the entity would move into the potential collider,
538 if ((colliderTrans.y < result.newY + transformable.h) && 468 if ((colliderTrans.y + colliderTrans.h > result.newY) &&
539 // that it wasn't already colliding, 469 // that it wasn't already colliding,
540 (colliderTrans.y >= oldBottom) && 470 (colliderTrans.y + colliderTrans.h <= oldY) &&
541 // that the position on the other axis is in range, 471 // that the position on the other axis is in range,
542 (colliderTrans.x + colliderTrans.w > result.newX) && 472 (colliderTrans.x + colliderTrans.w > result.newX) &&
543 (colliderTrans.x < result.newX + transformable.w) && 473 (colliderTrans.x < result.newX + transformable.w) &&
544 // and that the collider is not farther away than the environmental 474 // and that the collider is not farther away than the environmental
545 // boundary. 475 // boundary.
546 (!boundaryCollision || (colliderTrans.y <= it->first))) 476 (!boundaryCollision ||
547 { 477 (colliderTrans.y + colliderTrans.h >= it->first)))
548 colliders.push_back(collider); 478 {
549 } 479 colliders.push_back(collider);
550 } 480 }
481 }
551 482
552 std::sort( 483 std::sort(
553 std::begin(colliders), 484 std::begin(colliders),
554 std::end(colliders), 485 std::end(colliders),
555 [&] (id_type left, id_type right) { 486 [&] (id_type left, id_type right) {
556 auto& leftTrans = game_.getEntityManager(). 487 auto& leftTrans = game_.getEntityManager().
557 getComponent<TransformableComponent>(left); 488 getComponent<TransformableComponent>(left);
558 489
559 auto& rightTrans = game_.getEntityManager(). 490 auto& rightTrans = game_.getEntityManager().
560 getComponent<TransformableComponent>(right); 491 getComponent<TransformableComponent>(right);
561 492
562 return (leftTrans.y < rightTrans.y); 493 return (rightTrans.y < leftTrans.y);
563 }); 494 });
564 495
565 for (id_type collider : colliders) 496 for (id_type collider : colliders)
566 { 497 {
567 auto& colliderTrans = game_.getEntityManager(). 498 auto& colliderTrans = game_.getEntityManager().
568 getComponent<TransformableComponent>(collider); 499 getComponent<TransformableComponent>(collider);
569 500
570 // Check if the entity would still move into the potential collider. 501 // Check if the entity would still move into the potential collider.
571 if (colliderTrans.y >= result.newY + transformable.h) 502 if (colliderTrans.y + colliderTrans.h <= result.newY)
572 { 503 {
573 break; 504 break;
574 } 505 }
575 506
576 auto& colliderPonder = game_.getEntityManager(). 507 auto& colliderPonder = game_.getEntityManager().
577 getComponent<PonderableComponent>(collider); 508 getComponent<PonderableComponent>(collider);
578 509
579 processCollision( 510 processCollision(
580 entity, 511 entity,
581 collider, 512 collider,
582 Direction::down, 513 Direction::up,
583 colliderPonder.colliderType, 514 colliderPonder.colliderType,
584 colliderTrans.y, 515 colliderTrans.y + colliderTrans.h,
585 colliderTrans.x, 516 colliderTrans.x,
586 colliderTrans.x + colliderTrans.w, 517 colliderTrans.x + colliderTrans.w,
587 result); 518 result);
588 519
589 if (result.stopProcessing) 520 if (result.stopProcessing)
590 { 521 {
591 break; 522 break;
592 }
593 } 523 }
524 }
594 525
595 // If movement hasn't been stopped by an intermediary object, and 526 // If movement hasn't been stopped by an intermediary object, and
596 // collision checking hasn't been stopped, process the environmental 527 // collision checking hasn't been stopped, process the environmental
597 // boundaries closest to the entity. 528 // boundaries closest to the entity.
598 if (!result.stopProcessing && !result.touchedWall && boundaryCollision) 529 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
599 { 530 {
600 double boundaryAxis = it->first; 531 double boundaryAxis = it->first;
601 532
602 for (; 533 for (;
603 (it != std::end(mappable.downBoundaries)) && 534 (it != std::end(mappable.upBoundaries)) &&
604 (it->first == boundaryAxis); 535 (it->first == boundaryAxis);
605 it++) 536 it++)
537 {
538 if ((result.newX + transformable.w > it->second.lower) &&
539 (result.newX < it->second.upper))
606 { 540 {
607 if ((result.newX + transformable.w > it->second.lower) && 541 processCollision(
608 (result.newX < it->second.upper)) 542 entity,
543 mapEntity,
544 Direction::up,
545 it->second.type,
546 it->first,
547 it->second.lower,
548 it->second.upper,
549 result);
550
551 if (result.stopProcessing)
609 { 552 {
610 processCollision( 553 break;
611 entity,
612 mapEntity,
613 Direction::down,
614 it->second.type,
615 it->first,
616 it->second.lower,
617 it->second.upper,
618 result);
619
620 if (result.stopProcessing)
621 {
622 break;
623 }
624 } 554 }
625 } 555 }
626 } 556 }
627 } 557 }
558 } else if ((!result.stopProcessing) && (result.newY > oldY))
559 {
560 bool boundaryCollision = false;
561 auto it = mappable.downBoundaries.lower_bound(oldBottom);
562
563 // Find the axis distance of the closest environmental boundary.
564 for (;
565 (it != std::end(mappable.downBoundaries))
566 && (it->first <= (result.newY + transformable.h));
567 it++)
568 {
569 // Check that the boundary is in range for the other axis.
570 if ((result.newX + transformable.w > it->second.lower) &&
571 (result.newX < it->second.upper))
572 {
573 // We have a collision!
574 boundaryCollision = true;
628 575
629 // Move 576 break;
630 transformable.x = result.newX; 577 }
631 transformable.y = result.newY; 578 }
579
580 // Find a list of potential colliders, sorted so that the closest is
581 // first.
582 std::vector<id_type> colliders;
632 583
633 // Perform cleanup for orientable entites 584 for (id_type collider : entities)
634 if (game_.getEntityManager().hasComponent<OrientableComponent>(entity))
635 { 585 {
636 auto& orientable = game_.getEntityManager(). 586 // Can't collide with self.
637 getComponent<OrientableComponent>(entity); 587 if (collider == entity)
588 {
589 continue;
590 }
591
592 auto& colliderPonder = game_.getEntityManager().
593 getComponent<PonderableComponent>(collider);
638 594
639 // Handle changes in groundedness 595 // Only check objects that are active.
640 if (ponderable.grounded != oldGrounded) 596 if (!colliderPonder.active)
641 { 597 {
642 if (ponderable.grounded) 598 continue;
643 {
644 game_.getSystemManager().getSystem<OrientingSystem>().land(entity);
645 } else {
646 game_.getSystemManager().
647 getSystem<OrientingSystem>().startFalling(entity);
648 }
649 } 599 }
650 600
651 // Complete dropping, if necessary 601 auto& colliderTrans = game_.getEntityManager().
652 if (orientable.getDropState() == OrientableComponent::DropState::active) 602 getComponent<TransformableComponent>(collider);
603
604 // Check if the entity would move into the potential collider,
605 if ((colliderTrans.y < result.newY + transformable.h) &&
606 // that it wasn't already colliding,
607 (colliderTrans.y >= oldBottom) &&
608 // that the position on the other axis is in range,
609 (colliderTrans.x + colliderTrans.w > result.newX) &&
610 (colliderTrans.x < result.newX + transformable.w) &&
611 // and that the collider is not farther away than the environmental
612 // boundary.
613 (!boundaryCollision || (colliderTrans.y <= it->first)))
653 { 614 {
654 orientable.setDropState(OrientableComponent::DropState::none); 615 colliders.push_back(collider);
655 } 616 }
656 } 617 }
657 618
658 // Ferry or unferry as necessary 619 std::sort(
659 if ((ponderable.type == PonderableComponent::Type::freefalling) && 620 std::begin(colliders),
660 (ponderable.grounded != oldGrounded)) 621 std::end(colliders),
661 { 622 [&] (id_type left, id_type right) {
662 if (ponderable.grounded && 623 auto& leftTrans = game_.getEntityManager().
663 game_.getEntityManager(). 624 getComponent<TransformableComponent>(left);
664 hasComponent<PonderableComponent>(result.groundEntity)) 625
665 { 626 auto& rightTrans = game_.getEntityManager().
666 // The body is now being ferried 627 getComponent<TransformableComponent>(right);
667 auto& ferryPonder = game_.getEntityManager().
668 getComponent<PonderableComponent>(result.groundEntity);
669 628
670 ponderable.ferried = true; 629 return (leftTrans.y < rightTrans.y);
671 ponderable.ferry = result.groundEntity; 630 });
672 631
673 ferryPonder.passengers.insert(entity); 632 for (id_type collider : colliders)
674 } else if (ponderable.ferried) 633 {
634 auto& colliderTrans = game_.getEntityManager().
635 getComponent<TransformableComponent>(collider);
636
637 // Check if the entity would still move into the potential collider.
638 if (colliderTrans.y >= result.newY + transformable.h)
675 { 639 {
676 // The body is no longer being ferried 640 break;
677 unferry(entity);
678 } 641 }
679 }
680 642
681 // Update a ferry passenger's relative position 643 auto& colliderPonder = game_.getEntityManager().
682 if (ponderable.ferried) 644 getComponent<PonderableComponent>(collider);
683 {
684 auto& ferryTrans = game_.getEntityManager().
685 getComponent<TransformableComponent>(ponderable.ferry);
686 645
687 ponderable.relX = transformable.x - ferryTrans.x; 646 processCollision(
688 ponderable.relY = transformable.y - ferryTrans.y; 647 entity,
648 collider,
649 Direction::down,
650 colliderPonder.colliderType,
651 colliderTrans.y,
652 colliderTrans.x,
653 colliderTrans.x + colliderTrans.w,
654 result);
655
656 if (result.stopProcessing)
657 {
658 break;
659 }
689 } 660 }
690 661
691 // Move to an adjacent map, if necessary 662 // If movement hasn't been stopped by an intermediary object, and
692 if (result.adjacentlyWarping) 663 // collision checking hasn't been stopped, process the environmental
664 // boundaries closest to the entity.
665 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
693 { 666 {
694 double warpX = result.newX; 667 double boundaryAxis = it->first;
695 double warpY = result.newY;
696 668
697 switch (result.adjWarpDir) 669 for (;
670 (it != std::end(mappable.downBoundaries)) &&
671 (it->first == boundaryAxis);
672 it++)
698 { 673 {
699 case Direction::left: 674 if ((result.newX + transformable.w > it->second.lower) &&
675 (result.newX < it->second.upper))
700 { 676 {
701 warpX = GAME_WIDTH + WALL_GAP - transformable.w; 677 processCollision(
702 678 entity,
703 break; 679 mapEntity,
680 Direction::down,
681 it->second.type,
682 it->first,
683 it->second.lower,
684 it->second.upper,
685 result);
686
687 if (result.stopProcessing)
688 {
689 break;
690 }
704 } 691 }
692 }
693 }
694 }
705 695
706 case Direction::right: 696 // Move
707 { 697 transformable.x = result.newX;
708 warpX = -WALL_GAP; 698 transformable.y = result.newY;
709 699
710 break; 700 // Perform cleanup for orientable entites
711 } 701 if (game_.getEntityManager().hasComponent<OrientableComponent>(entity))
702 {
703 auto& orientable = game_.getEntityManager().
704 getComponent<OrientableComponent>(entity);
712 705
713 case Direction::up: 706 // Handle changes in groundedness
714 { 707 if (ponderable.grounded != oldGrounded)
715 warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h; 708 {
709 if (ponderable.grounded)
710 {
711 game_.getSystemManager().getSystem<OrientingSystem>().land(entity);
712 } else {
713 game_.getSystemManager().
714 getSystem<OrientingSystem>().startFalling(entity);
715 }
716 }
716 717
717 break; 718 // Complete dropping, if necessary
718 } 719 if (orientable.getDropState() == OrientableComponent::DropState::active)
720 {
721 orientable.setDropState(OrientableComponent::DropState::none);
722 }
723 }
719 724
720 case Direction::down: 725 // Ferry or unferry as necessary
721 { 726 if ((ponderable.type == PonderableComponent::Type::freefalling) &&
722 warpY = -WALL_GAP; 727 (ponderable.grounded != oldGrounded))
728 {
729 if (ponderable.grounded &&
730 game_.getEntityManager().
731 hasComponent<PonderableComponent>(result.groundEntity))
732 {
733 // The body is now being ferried
734 auto& ferryPonder = game_.getEntityManager().
735 getComponent<PonderableComponent>(result.groundEntity);
723 736
724 break; 737 ponderable.ferried = true;
725 } 738 ponderable.ferry = result.groundEntity;
726 }
727 739
728 game_.getSystemManager().getSystem<PlayingSystem>(). 740 ferryPonder.passengers.insert(entity);
729 changeMap( 741 } else if (ponderable.ferried)
730 entity, 742 {
731 result.adjWarpMapId, 743 // The body is no longer being ferried
732 warpX, 744 unferry(entity);
733 warpY);
734 } 745 }
735 } 746 }
736}
737 747
738void PonderingSystem::initializeBody( 748 // Update a ferry passenger's relative position
739 id_type entity, 749 if (ponderable.ferried)
740 PonderableComponent::Type type) 750 {
741{ 751 auto& ferryTrans = game_.getEntityManager().
742 auto& ponderable = game_.getEntityManager(). 752 getComponent<TransformableComponent>(ponderable.ferry);
743 emplaceComponent<PonderableComponent>(entity, type);
744 753
745 if (type == PonderableComponent::Type::freefalling) 754 ponderable.relX = transformable.x - ferryTrans.x;
755 ponderable.relY = transformable.y - ferryTrans.y;
756 }
757
758 // Handle ferry passengers
759 std::set<id_type> passengers = ponderable.passengers;
760
761 for (id_type passenger : passengers)
746 { 762 {
747 ponderable.accelY = NORMAL_GRAVITY; 763 tickBody(
764 passenger,
765 dt,
766 entities);
748 } 767 }
749}
750 768
751void PonderingSystem::initPrototype(id_type prototype) 769 // Move to an adjacent map, if necessary
752{ 770 if (result.adjacentlyWarping)
753 auto& ponderable = game_.getEntityManager(). 771 {
754 getComponent<PonderableComponent>(prototype); 772 double warpX = result.newX;
773 double warpY = result.newY;
755 774
756 ponderable.velX = 0.0; 775 switch (result.adjWarpDir)
757 ponderable.velY = 0.0; 776 {
758 ponderable.accelX = 0.0; 777 case Direction::left:
759 ponderable.accelY = 0.0; 778 {
760 ponderable.grounded = false; 779 warpX = GAME_WIDTH + WALL_GAP - transformable.w;
761 ponderable.frozen = false;
762 ponderable.collidable = true;
763 ponderable.ferried = false;
764 ponderable.passengers.clear();
765}
766 780
767void PonderingSystem::unferry(id_type entity) 781 break;
768{ 782 }
769 auto& ponderable = game_.getEntityManager().
770 getComponent<PonderableComponent>(entity);
771 783
772 if (ponderable.ferried) 784 case Direction::right:
773 { 785 {
774 ponderable.ferried = false; 786 warpX = -WALL_GAP;
775 787
776 auto& ferryPonder = game_.getEntityManager(). 788 break;
777 getComponent<PonderableComponent>(ponderable.ferry); 789 }
778 790
779 ferryPonder.passengers.erase(entity); 791 case Direction::up:
792 {
793 warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h;
794
795 break;
796 }
797
798 case Direction::down:
799 {
800 warpY = -WALL_GAP;
801
802 break;
803 }
804 }
805
806 game_.getSystemManager().getSystem<PlayingSystem>().
807 changeMap(
808 entity,
809 result.adjWarpMapId,
810 warpX,
811 warpY);
780 } 812 }
781} 813}
782 814
diff --git a/src/systems/pondering.h b/src/systems/pondering.h index adc0cda..eed0d32 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h
@@ -46,6 +46,11 @@ private:
46 id_type groundEntity; 46 id_type groundEntity;
47 }; 47 };
48 48
49 void tickBody(
50 id_type entity,
51 double dt,
52 const std::set<id_type>& entities);
53
49 void processCollision( 54 void processCollision(
50 id_type entity, 55 id_type entity,
51 id_type collider, 56 id_type collider,