From 893dbf8a235db2b4f1fafacf90290b0821f0048c Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Fri, 4 May 2018 10:50:15 -0400 Subject: 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. --- src/systems/pondering.cpp | 1232 +++++++++++++++++++++++---------------------- src/systems/pondering.h | 5 + 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 @@ void PonderingSystem::tick(double dt) { - auto& realizable = game_.getEntityManager(). - getComponent( - game_.getSystemManager().getSystem().getSingleton()); - - id_type mapEntity = realizable.activeMap; - - auto& mappable = game_.getEntityManager(). - getComponent(mapEntity); - auto entities = game_.getEntityManager().getEntitiesWithComponents< PonderableComponent, TransformableComponent>(); @@ -33,750 +24,791 @@ void PonderingSystem::tick(double dt) auto& ponderable = game_.getEntityManager(). getComponent(entity); - if (!ponderable.active || ponderable.frozen) + // We will recursively process ferried bodies after their ferries have been + // processed, so hold off on processing ferried bodies at the top level. + if (ponderable.ferried) { continue; } - auto& transformable = game_.getEntityManager(). - getComponent(entity); + tickBody( + entity, + dt, + entities); + } +} - // Accelerate - ponderable.velX += ponderable.accelX * dt; - ponderable.velY += ponderable.accelY * dt; +void PonderingSystem::initializeBody( + id_type entity, + PonderableComponent::Type type) +{ + auto& ponderable = game_.getEntityManager(). + emplaceComponent(entity, type); - if ((ponderable.type == PonderableComponent::Type::freefalling) - && (ponderable.velY > TERMINAL_VELOCITY)) - { - ponderable.velY = TERMINAL_VELOCITY; - } + if (type == PonderableComponent::Type::freefalling) + { + ponderable.accelY = NORMAL_GRAVITY; + } +} - const double oldX = transformable.x; - const double oldY = transformable.y; - const double oldRight = oldX + transformable.w; - const double oldBottom = oldY + transformable.h; +void PonderingSystem::initPrototype(id_type prototype) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(prototype); - CollisionResult result; + ponderable.velX = 0.0; + ponderable.velY = 0.0; + ponderable.accelX = 0.0; + ponderable.accelY = 0.0; + ponderable.grounded = false; + ponderable.frozen = false; + ponderable.collidable = true; + ponderable.ferried = false; + ponderable.passengers.clear(); +} - if (ponderable.ferried) - { - auto& ferryTrans = game_.getEntityManager(). - getComponent(ponderable.ferry); - - result.newX = ferryTrans.x + ponderable.relX; - result.newY = ferryTrans.y + ponderable.relY; - } else { - result.newX = transformable.x; - result.newY = transformable.y; - } +void PonderingSystem::unferry(id_type entity) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); - result.newX += ponderable.velX * dt; - result.newY += ponderable.velY * dt; + if (ponderable.ferried) + { + ponderable.ferried = false; - bool oldGrounded = ponderable.grounded; - ponderable.grounded = false; + auto& ferryPonder = game_.getEntityManager(). + getComponent(ponderable.ferry); - // Find horizontal collisions. - if (result.newX < oldX) - { - bool boundaryCollision = false; - auto it = mappable.leftBoundaries.lower_bound(oldX); + ferryPonder.passengers.erase(entity); + } +} - // Find the axis distance of the closest environmental boundary. - for (; - (it != std::end(mappable.leftBoundaries)) && - (it->first >= result.newX); - it++) - { - // Check that the boundary is in range for the other axis. - if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) - { - // We have a collision! - boundaryCollision = true; +void PonderingSystem::tickBody( + id_type entity, + double dt, + const std::set& entities) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); - break; - } - } + if (!ponderable.active || ponderable.frozen) + { + continue; + } - // Find a list of potential colliders, sorted so that the closest is - // first. - std::vector colliders; + auto& realizable = game_.getEntityManager(). + getComponent( + game_.getSystemManager().getSystem().getSingleton()); - for (id_type collider : entities) - { - // Can't collide with self. - if (collider == entity) - { - continue; - } + id_type mapEntity = realizable.activeMap; - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); + auto& mappable = game_.getEntityManager(). + getComponent(mapEntity); - // Only check objects that are active. - if (!colliderPonder.active) - { - continue; - } + auto& transformable = game_.getEntityManager(). + getComponent(entity); - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); - - // Check if the entity would move into the potential collider, - if ((colliderTrans.x + colliderTrans.w > result.newX) && - // that it wasn't already colliding, - (colliderTrans.x + colliderTrans.w <= oldX) && - // that the position on the other axis is in range, - (colliderTrans.y + colliderTrans.h > oldY) && - (colliderTrans.y < oldBottom) && - // and that the collider is not farther away than the environmental - // boundary. - (!boundaryCollision || - (colliderTrans.x + colliderTrans.w >= it->first))) - { - colliders.push_back(collider); - } - } + // Accelerate + ponderable.velX += ponderable.accelX * dt; + ponderable.velY += ponderable.accelY * dt; - std::sort( - std::begin(colliders), - std::end(colliders), - [&] (id_type left, id_type right) { - auto& leftTrans = game_.getEntityManager(). - getComponent(left); + if ((ponderable.type == PonderableComponent::Type::freefalling) + && (ponderable.velY > TERMINAL_VELOCITY)) + { + ponderable.velY = TERMINAL_VELOCITY; + } - auto& rightTrans = game_.getEntityManager(). - getComponent(right); + const double oldX = transformable.x; + const double oldY = transformable.y; + const double oldRight = oldX + transformable.w; + const double oldBottom = oldY + transformable.h; - return (rightTrans.x < leftTrans.x); - }); + CollisionResult result; - for (id_type collider : colliders) - { - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); + if (ponderable.ferried) + { + auto& ferryTrans = game_.getEntityManager(). + getComponent(ponderable.ferry); + + result.newX = ferryTrans.x + ponderable.relX; + result.newY = ferryTrans.y + ponderable.relY; + } else { + result.newX = transformable.x; + result.newY = transformable.y; + } - // Check if the entity would still move into the potential collider. - if (colliderTrans.x + colliderTrans.w <= result.newX) - { - break; - } + result.newX += ponderable.velX * dt; + result.newY += ponderable.velY * dt; - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); + bool oldGrounded = ponderable.grounded; + ponderable.grounded = false; - processCollision( - entity, - collider, - Direction::left, - colliderPonder.colliderType, - colliderTrans.x + colliderTrans.w, - colliderTrans.y, - colliderTrans.y + colliderTrans.h, - result); + // Find horizontal collisions. + if (result.newX < oldX) + { + bool boundaryCollision = false; + auto it = mappable.leftBoundaries.lower_bound(oldX); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.leftBoundaries)) && + (it->first >= result.newX); + it++) + { + // Check that the boundary is in range for the other axis. + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) + { + // We have a collision! + boundaryCollision = true; - if (result.stopProcessing) - { - break; - } + break; } + } - // If movement hasn't been stopped by an intermediary object, and - // collision checking hasn't been stopped, process the environmental - // boundaries closest to the entity. - if (!result.stopProcessing && !result.touchedWall && boundaryCollision) - { - double boundaryAxis = it->first; + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; - for (; - (it != std::end(mappable.leftBoundaries)) && - (it->first == boundaryAxis); - it++) - { - if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) - { - processCollision( - entity, - mapEntity, - Direction::left, - it->second.type, - it->first, - it->second.lower, - it->second.upper, - result); - - if (result.stopProcessing) - { - break; - } - } - } - } - } else if (result.newX > oldX) + for (id_type collider : entities) { - bool boundaryCollision = false; - auto it = mappable.rightBoundaries.lower_bound(oldRight); - - // Find the axis distance of the closest environmental boundary. - for (; - (it != std::end(mappable.rightBoundaries)) - && (it->first <= (result.newX + transformable.w)); - it++) + // Can't collide with self. + if (collider == entity) { - // Check that the boundary is in range for the other axis. - if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) - { - // We have a collision! - boundaryCollision = true; - - break; - } + continue; } - // Find a list of potential colliders, sorted so that the closest is - // first. - std::vector colliders; + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - for (id_type collider : entities) + // Only check objects that are active. + if (!colliderPonder.active) { - // Can't collide with self. - if (collider == entity) - { - continue; - } - - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); - - // Only check objects that are active. - if (!colliderPonder.active) - { - continue; - } + continue; + } - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); - - // Check if the entity would move into the potential collider, - if ((colliderTrans.x < result.newX + transformable.w) && - // that it wasn't already colliding, - (colliderTrans.x >= oldRight) && - // that the position on the other axis is in range, - (colliderTrans.y + colliderTrans.h > oldY) && - (colliderTrans.y < oldBottom) && - // and that the collider is not farther away than the environmental - // boundary. - (!boundaryCollision || (colliderTrans.x <= it->first))) - { - colliders.push_back(collider); - } + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.x + colliderTrans.w > result.newX) && + // that it wasn't already colliding, + (colliderTrans.x + colliderTrans.w <= oldX) && + // that the position on the other axis is in range, + (colliderTrans.y + colliderTrans.h > oldY) && + (colliderTrans.y < oldBottom) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || + (colliderTrans.x + colliderTrans.w >= it->first))) + { + colliders.push_back(collider); } + } - std::sort( - std::begin(colliders), - std::end(colliders), - [&] (id_type left, id_type right) { - auto& leftTrans = game_.getEntityManager(). - getComponent(left); + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); - auto& rightTrans = game_.getEntityManager(). - getComponent(right); + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - return (leftTrans.x < rightTrans.x); - }); + return (rightTrans.x < leftTrans.x); + }); - for (id_type collider : colliders) - { - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); + for (id_type collider : colliders) + { + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); - // Check if the entity would still move into the potential collider. - if (colliderTrans.x >= result.newX + transformable.w) - { - break; - } + // Check if the entity would still move into the potential collider. + if (colliderTrans.x + colliderTrans.w <= result.newX) + { + break; + } - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - processCollision( - entity, - collider, - Direction::right, - colliderPonder.colliderType, - colliderTrans.x, - colliderTrans.y, - colliderTrans.y + colliderTrans.h, - result); + processCollision( + entity, + collider, + Direction::left, + colliderPonder.colliderType, + colliderTrans.x + colliderTrans.w, + colliderTrans.y, + colliderTrans.y + colliderTrans.h, + result); - if (result.stopProcessing) - { - break; - } + if (result.stopProcessing) + { + break; } + } - // If movement hasn't been stopped by an intermediary object, and - // collision checking hasn't been stopped, process the environmental - // boundaries closest to the entity. - if (!result.stopProcessing && !result.touchedWall && boundaryCollision) - { - double boundaryAxis = it->first; + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) + { + double boundaryAxis = it->first; - for (; - (it != std::end(mappable.rightBoundaries)) && - (it->first == boundaryAxis); - it++) + for (; + (it != std::end(mappable.leftBoundaries)) && + (it->first == boundaryAxis); + it++) + { + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) { - if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) + processCollision( + entity, + mapEntity, + Direction::left, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) { - processCollision( - entity, - mapEntity, - Direction::right, - it->second.type, - it->first, - it->second.lower, - it->second.upper, - result); - - if (result.stopProcessing) - { - break; - } + break; } } } } - - // Find vertical collisions - result.touchedWall = false; - - if ((!result.stopProcessing) && (result.newY < oldY)) + } else if (result.newX > oldX) + { + bool boundaryCollision = false; + auto it = mappable.rightBoundaries.lower_bound(oldRight); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.rightBoundaries)) + && (it->first <= (result.newX + transformable.w)); + it++) { - bool boundaryCollision = false; - auto it = mappable.upBoundaries.lower_bound(oldY); - - // Find the axis distance of the closest environmental boundary. - for (; - (it != std::end(mappable.upBoundaries)) && - (it->first >= result.newY); - it++) + // Check that the boundary is in range for the other axis. + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) { - // Check that the boundary is in range for the other axis. - if ((result.newX + transformable.h > it->second.lower) && - (result.newX < it->second.upper)) - { - // We have a collision! - boundaryCollision = true; + // We have a collision! + boundaryCollision = true; - break; - } + break; } + } - // Find a list of potential colliders, sorted so that the closest is - // first. - std::vector colliders; + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; - for (id_type collider : entities) + for (id_type collider : entities) + { + // Can't collide with self. + if (collider == entity) { - // Can't collide with self. - if (collider == entity) - { - continue; - } + continue; + } - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - // Only check objects that are active. - if (!colliderPonder.active) - { - continue; - } + // Only check objects that are active. + if (!colliderPonder.active) + { + continue; + } - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); - - // Check if the entity would move into the potential collider, - if ((colliderTrans.y + colliderTrans.h > result.newY) && - // that it wasn't already colliding, - (colliderTrans.y + colliderTrans.h <= oldY) && - // that the position on the other axis is in range, - (colliderTrans.x + colliderTrans.w > result.newX) && - (colliderTrans.x < result.newX + transformable.w) && - // and that the collider is not farther away than the environmental - // boundary. - (!boundaryCollision || - (colliderTrans.y + colliderTrans.h >= it->first))) - { - colliders.push_back(collider); - } + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.x < result.newX + transformable.w) && + // that it wasn't already colliding, + (colliderTrans.x >= oldRight) && + // that the position on the other axis is in range, + (colliderTrans.y + colliderTrans.h > oldY) && + (colliderTrans.y < oldBottom) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || (colliderTrans.x <= it->first))) + { + colliders.push_back(collider); } + } - std::sort( - std::begin(colliders), - std::end(colliders), - [&] (id_type left, id_type right) { - auto& leftTrans = game_.getEntityManager(). - getComponent(left); + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); - auto& rightTrans = game_.getEntityManager(). - getComponent(right); + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - return (rightTrans.y < leftTrans.y); - }); + return (leftTrans.x < rightTrans.x); + }); - for (id_type collider : colliders) - { - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); + for (id_type collider : colliders) + { + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); - // Check if the entity would still move into the potential collider. - if (colliderTrans.y + colliderTrans.h <= result.newY) - { - break; - } + // Check if the entity would still move into the potential collider. + if (colliderTrans.x >= result.newX + transformable.w) + { + break; + } - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - processCollision( - entity, - collider, - Direction::up, - colliderPonder.colliderType, - colliderTrans.y + colliderTrans.h, - colliderTrans.x, - colliderTrans.x + colliderTrans.w, - result); + processCollision( + entity, + collider, + Direction::right, + colliderPonder.colliderType, + colliderTrans.x, + colliderTrans.y, + colliderTrans.y + colliderTrans.h, + result); - if (result.stopProcessing) - { - break; - } + if (result.stopProcessing) + { + break; } + } - // If movement hasn't been stopped by an intermediary object, and - // collision checking hasn't been stopped, process the environmental - // boundaries closest to the entity. - if (!result.stopProcessing && !result.touchedWall && boundaryCollision) - { - double boundaryAxis = it->first; + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) + { + double boundaryAxis = it->first; - for (; - (it != std::end(mappable.upBoundaries)) && - (it->first == boundaryAxis); - it++) + for (; + (it != std::end(mappable.rightBoundaries)) && + (it->first == boundaryAxis); + it++) + { + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) { - if ((result.newX + transformable.w > it->second.lower) && - (result.newX < it->second.upper)) + processCollision( + entity, + mapEntity, + Direction::right, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) { - processCollision( - entity, - mapEntity, - Direction::up, - it->second.type, - it->first, - it->second.lower, - it->second.upper, - result); - - if (result.stopProcessing) - { - break; - } + break; } } } - } else if ((!result.stopProcessing) && (result.newY > oldY)) - { - bool boundaryCollision = false; - auto it = mappable.downBoundaries.lower_bound(oldBottom); + } + } - // Find the axis distance of the closest environmental boundary. - for (; - (it != std::end(mappable.downBoundaries)) - && (it->first <= (result.newY + transformable.h)); - it++) + // Find vertical collisions + result.touchedWall = false; + + if ((!result.stopProcessing) && (result.newY < oldY)) + { + bool boundaryCollision = false; + auto it = mappable.upBoundaries.lower_bound(oldY); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.upBoundaries)) && + (it->first >= result.newY); + it++) + { + // Check that the boundary is in range for the other axis. + if ((result.newX + transformable.h > it->second.lower) && + (result.newX < it->second.upper)) { - // Check that the boundary is in range for the other axis. - if ((result.newX + transformable.w > it->second.lower) && - (result.newX < it->second.upper)) - { - // We have a collision! - boundaryCollision = true; + // We have a collision! + boundaryCollision = true; - break; - } + break; } + } - // Find a list of potential colliders, sorted so that the closest is - // first. - std::vector colliders; + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; - for (id_type collider : entities) + for (id_type collider : entities) + { + // Can't collide with self. + if (collider == entity) { - // Can't collide with self. - if (collider == entity) - { - continue; - } + continue; + } - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - // Only check objects that are active. - if (!colliderPonder.active) - { - continue; - } + // Only check objects that are active. + if (!colliderPonder.active) + { + continue; + } - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); - - // Check if the entity would move into the potential collider, - if ((colliderTrans.y < result.newY + transformable.h) && - // that it wasn't already colliding, - (colliderTrans.y >= oldBottom) && - // that the position on the other axis is in range, - (colliderTrans.x + colliderTrans.w > result.newX) && - (colliderTrans.x < result.newX + transformable.w) && - // and that the collider is not farther away than the environmental - // boundary. - (!boundaryCollision || (colliderTrans.y <= it->first))) - { - colliders.push_back(collider); - } + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.y + colliderTrans.h > result.newY) && + // that it wasn't already colliding, + (colliderTrans.y + colliderTrans.h <= oldY) && + // that the position on the other axis is in range, + (colliderTrans.x + colliderTrans.w > result.newX) && + (colliderTrans.x < result.newX + transformable.w) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || + (colliderTrans.y + colliderTrans.h >= it->first))) + { + colliders.push_back(collider); } + } - std::sort( - std::begin(colliders), - std::end(colliders), - [&] (id_type left, id_type right) { - auto& leftTrans = game_.getEntityManager(). - getComponent(left); + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); - auto& rightTrans = game_.getEntityManager(). - getComponent(right); + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - return (leftTrans.y < rightTrans.y); - }); + return (rightTrans.y < leftTrans.y); + }); - for (id_type collider : colliders) - { - auto& colliderTrans = game_.getEntityManager(). - getComponent(collider); + for (id_type collider : colliders) + { + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); - // Check if the entity would still move into the potential collider. - if (colliderTrans.y >= result.newY + transformable.h) - { - break; - } + // Check if the entity would still move into the potential collider. + if (colliderTrans.y + colliderTrans.h <= result.newY) + { + break; + } - auto& colliderPonder = game_.getEntityManager(). - getComponent(collider); + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - processCollision( - entity, - collider, - Direction::down, - colliderPonder.colliderType, - colliderTrans.y, - colliderTrans.x, - colliderTrans.x + colliderTrans.w, - result); + processCollision( + entity, + collider, + Direction::up, + colliderPonder.colliderType, + colliderTrans.y + colliderTrans.h, + colliderTrans.x, + colliderTrans.x + colliderTrans.w, + result); - if (result.stopProcessing) - { - break; - } + if (result.stopProcessing) + { + break; } + } - // If movement hasn't been stopped by an intermediary object, and - // collision checking hasn't been stopped, process the environmental - // boundaries closest to the entity. - if (!result.stopProcessing && !result.touchedWall && boundaryCollision) - { - double boundaryAxis = it->first; + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) + { + double boundaryAxis = it->first; - for (; - (it != std::end(mappable.downBoundaries)) && - (it->first == boundaryAxis); - it++) + for (; + (it != std::end(mappable.upBoundaries)) && + (it->first == boundaryAxis); + it++) + { + if ((result.newX + transformable.w > it->second.lower) && + (result.newX < it->second.upper)) { - if ((result.newX + transformable.w > it->second.lower) && - (result.newX < it->second.upper)) + processCollision( + entity, + mapEntity, + Direction::up, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) { - processCollision( - entity, - mapEntity, - Direction::down, - it->second.type, - it->first, - it->second.lower, - it->second.upper, - result); - - if (result.stopProcessing) - { - break; - } + break; } } } } + } else if ((!result.stopProcessing) && (result.newY > oldY)) + { + bool boundaryCollision = false; + auto it = mappable.downBoundaries.lower_bound(oldBottom); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.downBoundaries)) + && (it->first <= (result.newY + transformable.h)); + it++) + { + // Check that the boundary is in range for the other axis. + if ((result.newX + transformable.w > it->second.lower) && + (result.newX < it->second.upper)) + { + // We have a collision! + boundaryCollision = true; - // Move - transformable.x = result.newX; - transformable.y = result.newY; + break; + } + } + + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; - // Perform cleanup for orientable entites - if (game_.getEntityManager().hasComponent(entity)) + for (id_type collider : entities) { - auto& orientable = game_.getEntityManager(). - getComponent(entity); + // Can't collide with self. + if (collider == entity) + { + continue; + } + + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - // Handle changes in groundedness - if (ponderable.grounded != oldGrounded) + // Only check objects that are active. + if (!colliderPonder.active) { - if (ponderable.grounded) - { - game_.getSystemManager().getSystem().land(entity); - } else { - game_.getSystemManager(). - getSystem().startFalling(entity); - } + continue; } - // Complete dropping, if necessary - if (orientable.getDropState() == OrientableComponent::DropState::active) + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.y < result.newY + transformable.h) && + // that it wasn't already colliding, + (colliderTrans.y >= oldBottom) && + // that the position on the other axis is in range, + (colliderTrans.x + colliderTrans.w > result.newX) && + (colliderTrans.x < result.newX + transformable.w) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || (colliderTrans.y <= it->first))) { - orientable.setDropState(OrientableComponent::DropState::none); + colliders.push_back(collider); } } - // Ferry or unferry as necessary - if ((ponderable.type == PonderableComponent::Type::freefalling) && - (ponderable.grounded != oldGrounded)) - { - if (ponderable.grounded && - game_.getEntityManager(). - hasComponent(result.groundEntity)) - { - // The body is now being ferried - auto& ferryPonder = game_.getEntityManager(). - getComponent(result.groundEntity); + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); + + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - ponderable.ferried = true; - ponderable.ferry = result.groundEntity; + return (leftTrans.y < rightTrans.y); + }); - ferryPonder.passengers.insert(entity); - } else if (ponderable.ferried) + for (id_type collider : colliders) + { + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would still move into the potential collider. + if (colliderTrans.y >= result.newY + transformable.h) { - // The body is no longer being ferried - unferry(entity); + break; } - } - // Update a ferry passenger's relative position - if (ponderable.ferried) - { - auto& ferryTrans = game_.getEntityManager(). - getComponent(ponderable.ferry); + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - ponderable.relX = transformable.x - ferryTrans.x; - ponderable.relY = transformable.y - ferryTrans.y; + processCollision( + entity, + collider, + Direction::down, + colliderPonder.colliderType, + colliderTrans.y, + colliderTrans.x, + colliderTrans.x + colliderTrans.w, + result); + + if (result.stopProcessing) + { + break; + } } - // Move to an adjacent map, if necessary - if (result.adjacentlyWarping) + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) { - double warpX = result.newX; - double warpY = result.newY; + double boundaryAxis = it->first; - switch (result.adjWarpDir) + for (; + (it != std::end(mappable.downBoundaries)) && + (it->first == boundaryAxis); + it++) { - case Direction::left: + if ((result.newX + transformable.w > it->second.lower) && + (result.newX < it->second.upper)) { - warpX = GAME_WIDTH + WALL_GAP - transformable.w; - - break; + processCollision( + entity, + mapEntity, + Direction::down, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) + { + break; + } } + } + } + } - case Direction::right: - { - warpX = -WALL_GAP; + // Move + transformable.x = result.newX; + transformable.y = result.newY; - break; - } + // Perform cleanup for orientable entites + if (game_.getEntityManager().hasComponent(entity)) + { + auto& orientable = game_.getEntityManager(). + getComponent(entity); - case Direction::up: - { - warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h; + // Handle changes in groundedness + if (ponderable.grounded != oldGrounded) + { + if (ponderable.grounded) + { + game_.getSystemManager().getSystem().land(entity); + } else { + game_.getSystemManager(). + getSystem().startFalling(entity); + } + } - break; - } + // Complete dropping, if necessary + if (orientable.getDropState() == OrientableComponent::DropState::active) + { + orientable.setDropState(OrientableComponent::DropState::none); + } + } - case Direction::down: - { - warpY = -WALL_GAP; + // Ferry or unferry as necessary + if ((ponderable.type == PonderableComponent::Type::freefalling) && + (ponderable.grounded != oldGrounded)) + { + if (ponderable.grounded && + game_.getEntityManager(). + hasComponent(result.groundEntity)) + { + // The body is now being ferried + auto& ferryPonder = game_.getEntityManager(). + getComponent(result.groundEntity); - break; - } - } + ponderable.ferried = true; + ponderable.ferry = result.groundEntity; - game_.getSystemManager().getSystem(). - changeMap( - entity, - result.adjWarpMapId, - warpX, - warpY); + ferryPonder.passengers.insert(entity); + } else if (ponderable.ferried) + { + // The body is no longer being ferried + unferry(entity); } } -} -void PonderingSystem::initializeBody( - id_type entity, - PonderableComponent::Type type) -{ - auto& ponderable = game_.getEntityManager(). - emplaceComponent(entity, type); + // Update a ferry passenger's relative position + if (ponderable.ferried) + { + auto& ferryTrans = game_.getEntityManager(). + getComponent(ponderable.ferry); - if (type == PonderableComponent::Type::freefalling) + ponderable.relX = transformable.x - ferryTrans.x; + ponderable.relY = transformable.y - ferryTrans.y; + } + + // Handle ferry passengers + std::set passengers = ponderable.passengers; + + for (id_type passenger : passengers) { - ponderable.accelY = NORMAL_GRAVITY; + tickBody( + passenger, + dt, + entities); } -} -void PonderingSystem::initPrototype(id_type prototype) -{ - auto& ponderable = game_.getEntityManager(). - getComponent(prototype); + // Move to an adjacent map, if necessary + if (result.adjacentlyWarping) + { + double warpX = result.newX; + double warpY = result.newY; - ponderable.velX = 0.0; - ponderable.velY = 0.0; - ponderable.accelX = 0.0; - ponderable.accelY = 0.0; - ponderable.grounded = false; - ponderable.frozen = false; - ponderable.collidable = true; - ponderable.ferried = false; - ponderable.passengers.clear(); -} + switch (result.adjWarpDir) + { + case Direction::left: + { + warpX = GAME_WIDTH + WALL_GAP - transformable.w; -void PonderingSystem::unferry(id_type entity) -{ - auto& ponderable = game_.getEntityManager(). - getComponent(entity); + break; + } - if (ponderable.ferried) - { - ponderable.ferried = false; + case Direction::right: + { + warpX = -WALL_GAP; - auto& ferryPonder = game_.getEntityManager(). - getComponent(ponderable.ferry); + break; + } - ferryPonder.passengers.erase(entity); + case Direction::up: + { + warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h; + + break; + } + + case Direction::down: + { + warpY = -WALL_GAP; + + break; + } + } + + game_.getSystemManager().getSystem(). + changeMap( + entity, + result.adjWarpMapId, + warpX, + warpY); } } 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: id_type groundEntity; }; + void tickBody( + id_type entity, + double dt, + const std::set& entities); + void processCollision( id_type entity, id_type collider, -- cgit 1.4.1