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