diff --git a/source/simulation2/components/CCmpUnitMotion.cpp b/source/simulation2/components/CCmpUnitMotion.cpp index 952aa2e714..c803336c63 100644 --- a/source/simulation2/components/CCmpUnitMotion.cpp +++ b/source/simulation2/components/CCmpUnitMotion.cpp @@ -1036,13 +1036,10 @@ void CCmpUnitMotion::Move(fixed dt) // nearest point on the square, not towards its center } } - } - // If we have a target entity, and we're not miles away from the end of - // our current path, and the target moved enough, then recompute our - // whole path - if (m_PathState == PATHSTATE_FOLLOWING) - { + // If we have a target entity, and we're not miles away from the end of + // our current path, and the target moved enough, then recompute our + // whole path if (IsFormationMember()) CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA_FORMATION); else @@ -1425,7 +1422,7 @@ bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos { // Too close to target - move outwards to a circle // that's slightly larger than the min range - goal.type = PathGoal::CIRCLE;// TODO: INVERTED_CIRCLE; + goal.type = PathGoal::INVERTED_CIRCLE; goal.hw = minRange + g_GoalDelta; } else if (maxRange >= entity_pos_t::Zero() && distance > maxRange) @@ -1584,7 +1581,12 @@ bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); - if (distance < minRange) + // Compare with previous obstruction + ICmpObstructionManager::ObstructionSquare previousObstruction; + cmpObstruction->GetPreviousObstructionSquare(previousObstruction); + entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize); + + if (distance < minRange && previousDistance < minRange) { // Too close to the square - need to move away @@ -1592,14 +1594,14 @@ bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange entity_pos_t goalDistance = minRange + g_GoalDelta; - goal.type = PathGoal::SQUARE; + goal.type = PathGoal::INVERTED_SQUARE; goal.u = obstruction.u; goal.v = obstruction.v; entity_pos_t delta = std::max(goalDistance, m_Clearance + entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/16); // ensure it's far enough to not intersect the building itself goal.hw = obstruction.hw + delta; goal.hh = obstruction.hh + delta; } - else if (maxRange < entity_pos_t::Zero() || distance < maxRange) + else if (maxRange < entity_pos_t::Zero() || distance < maxRange || previousDistance < maxRange) { // We're already in range - no need to move anywhere FaceTowardsPointFromPos(pos, goal.x, goal.z); @@ -1620,8 +1622,9 @@ bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange // the distance to the square, so the previous "distance < maxRange" // check is still valid (though not sufficient) entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius; + entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius; - if (circleDistance < maxRange) + if (circleDistance < maxRange || previousCircleDistance < maxRange) { // We're already in range - no need to move anywhere if (m_FacePointAfterMove) @@ -1688,7 +1691,7 @@ bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, CFixedVector2D halfSize(obstruction.hw, obstruction.hh); entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); - // compare with previous obstruction + // Compare with previous obstruction ICmpObstructionManager::ObstructionSquare previousObstruction; cmpObstruction->GetPreviousObstructionSquare(previousObstruction); entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize); @@ -1706,16 +1709,12 @@ bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, if (ShouldTreatTargetAsCircle(maxRange, obstruction.hw, obstruction.hh, circleRadius)) { // The target is small relative to our range, so pretend it's a circle - // and see if we're close enough to that - + // and see if we're close enough to that. + // Also check circle around previous position. entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius; + entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius; - if (circleDistance <= maxRange) - return true; - // also check circle around previous position - circleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius; - - if (circleDistance <= maxRange) + if (circleDistance <= maxRange || previousCircleDistance <= maxRange) return true; } @@ -1728,11 +1727,9 @@ bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, return false; CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D(); - entity_pos_t distance = (pos - targetPos).Length(); - return minRange <= distance && - (maxRange < entity_pos_t::Zero() || distance <= maxRange); + return minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange); } } diff --git a/source/simulation2/helpers/Geometry.cpp b/source/simulation2/helpers/Geometry.cpp index 392c34bda7..7ac4cb450d 100644 --- a/source/simulation2/helpers/Geometry.cpp +++ b/source/simulation2/helpers/Geometry.cpp @@ -85,8 +85,6 @@ fixed Geometry::DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedV fixed hw = halfSize.X; fixed hh = halfSize.Y; - // TODO: I haven't actually tested this - if (-hw < du && du < hw) // regions B, I, G { fixed closest = (dv.Absolute() - hh).Absolute(); // horizontal edges diff --git a/source/simulation2/helpers/PathGoal.cpp b/source/simulation2/helpers/PathGoal.cpp index 8b4e84d75a..506b91c86a 100644 --- a/source/simulation2/helpers/PathGoal.cpp +++ b/source/simulation2/helpers/PathGoal.cpp @@ -82,10 +82,10 @@ static bool NavcellContainsSquare(int i, int j, // Otherwise, since the square is convex, there cannot be any other point // in the navcell that is outside the square. return ( - Geometry::PointIsInSquare(CFixedVector2D(x0 - x, z0 - z), u, v, CFixedVector2D(hw, hh)) - || Geometry::PointIsInSquare(CFixedVector2D(x1 - x, z0 - z), u, v, CFixedVector2D(hw, hh)) - || Geometry::PointIsInSquare(CFixedVector2D(x0 - x, z1 - z), u, v, CFixedVector2D(hw, hh)) - || Geometry::PointIsInSquare(CFixedVector2D(x1 - x, z1 - z), u, v, CFixedVector2D(hw, hh)) + !Geometry::PointIsInSquare(CFixedVector2D(x0 - x, z0 - z), u, v, CFixedVector2D(hw, hh)) + || !Geometry::PointIsInSquare(CFixedVector2D(x1 - x, z0 - z), u, v, CFixedVector2D(hw, hh)) + || !Geometry::PointIsInSquare(CFixedVector2D(x0 - x, z1 - z), u, v, CFixedVector2D(hw, hh)) + || !Geometry::PointIsInSquare(CFixedVector2D(x1 - x, z1 - z), u, v, CFixedVector2D(hw, hh)) ); } } @@ -121,7 +121,7 @@ bool PathGoal::NavcellRectContainsGoal(int i0, int j0, int i1, int j1, int* gi, int jmin = std::min(j0, j1); int jmax = std::max(j0, j1); - // Direction to iterate from ij0 towards ij1 + // Direction to iterate from (i0,j0) towards (i1,j1) int di = i1 < i0 ? -1 : +1; int dj = j1 < j0 ? -1 : +1; @@ -174,6 +174,36 @@ bool PathGoal::NavcellRectContainsGoal(int i0, int j0, int i1, int j1, int* gi, return false; } + case INVERTED_CIRCLE: + { + // Loop over all navcells in the given range (starting at (i0,j0) since + // this function is meant to find the goal navcell nearest to there + // assuming jmin==jmax || imin==imax), + // and check whether any point in each navcell is outside the goal circle. + // (TODO: this is pretty inefficient.) + for (int j = j0; jmin <= j && j <= jmax; j += dj) + { + for (int i = i0; imin <= i && i <= imax; i += di) + { + entity_pos_t x0 = entity_pos_t::FromInt(i).Multiply(Pathfinding::NAVCELL_SIZE); + entity_pos_t z0 = entity_pos_t::FromInt(j).Multiply(Pathfinding::NAVCELL_SIZE); + entity_pos_t x1 = x0 + Pathfinding::NAVCELL_SIZE; + entity_pos_t z1 = z0 + Pathfinding::NAVCELL_SIZE; + entity_pos_t nx = Clamp(x, x0, x1); + entity_pos_t nz = Clamp(z, z0, z1); + if ((CFixedVector2D(nx, nz) - CFixedVector2D(x, z)).CompareLength(hw) > 0) + { + if (gi) + *gi = i; + if (gj) + *gj = j; + return true; + } + } + } + return false; + } + case SQUARE: { // Loop over all navcells in the given range (starting at (i0,j0) since @@ -204,12 +234,35 @@ bool PathGoal::NavcellRectContainsGoal(int i0, int j0, int i1, int j1, int* gi, return false; } - case INVERTED_CIRCLE: case INVERTED_SQUARE: - // Haven't bothered implementing these, since they're not needed by the - // current pathfinder design - debug_warn(L"PathGoal::NavcellRectContainsGoal doesn't support inverted shapes"); + { + // Loop over all navcells in the given range (starting at (i0,j0) since + // this function is meant to find the goal navcell nearest to there + // assuming jmin==jmax || imin==imax), + // and check whether any point in each navcell is outside the goal square. + // (TODO: this is pretty inefficient.) + for (int j = j0; jmin <= j && j <= jmax; j += dj) + { + for (int i = i0; imin <= i && i <= imax; i += di) + { + entity_pos_t x0 = entity_pos_t::FromInt(i).Multiply(Pathfinding::NAVCELL_SIZE); + entity_pos_t z0 = entity_pos_t::FromInt(j).Multiply(Pathfinding::NAVCELL_SIZE); + entity_pos_t x1 = x0 + Pathfinding::NAVCELL_SIZE; + entity_pos_t z1 = z0 + Pathfinding::NAVCELL_SIZE; + entity_pos_t nx = Clamp(x, x0, x1); + entity_pos_t nz = Clamp(z, z0, z1); + if (!Geometry::PointIsInSquare(CFixedVector2D(nx - x, nz - z), u, v, CFixedVector2D(hw, hh))) + { + if (gi) + *gi = i; + if (gj) + *gj = j; + return true; + } + } + } return false; + } NODEFAULT; } @@ -229,6 +282,13 @@ bool PathGoal::RectContainsGoal(entity_pos_t x0, entity_pos_t z0, entity_pos_t x return (CFixedVector2D(nx, nz) - CFixedVector2D(x, z)).CompareLength(hw) <= 0; } + case INVERTED_CIRCLE: + { + entity_pos_t nx = Clamp(x, x0, x1); + entity_pos_t nz = Clamp(z, z0, z1); + return (CFixedVector2D(nx, nz) - CFixedVector2D(x, z)).CompareLength(hw) > 0; + } + case SQUARE: { entity_pos_t nx = Clamp(x, x0, x1); @@ -236,12 +296,12 @@ bool PathGoal::RectContainsGoal(entity_pos_t x0, entity_pos_t z0, entity_pos_t x return Geometry::PointIsInSquare(CFixedVector2D(nx - x, nz - z), u, v, CFixedVector2D(hw, hh)); } - case INVERTED_CIRCLE: case INVERTED_SQUARE: - // Haven't bothered implementing these, since they're not needed by the - // current pathfinder design - debug_warn(L"PathGoal::RectContainsGoal doesn't support inverted shapes"); - return false; + { + entity_pos_t nx = Clamp(x, x0, x1); + entity_pos_t nz = Clamp(z, z0, z1); + return !Geometry::PointIsInSquare(CFixedVector2D(nx - x, nz - z), u, v, CFixedVector2D(hw, hh)); + } NODEFAULT; }