Index: source/simulation2/components/CCmpObstructionManager.cpp =================================================================== --- source/simulation2/components/CCmpObstructionManager.cpp +++ source/simulation2/components/CCmpObstructionManager.cpp @@ -821,18 +821,26 @@ bool CCmpObstructionManager::IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const { fixed dist = DistanceToPoint(ent, px, pz); - return dist != fixed::FromInt(-1) && dist <= maxRange + fixed::FromFloat(0.0001) && (opposite ? MaxDistanceToPoint(ent, px, pz) : dist) >= minRange - fixed::FromFloat(0.0001); + // Treat -1 max range as infinite + return dist != fixed::FromInt(-1) && + (dist <= (maxRange + fixed::FromFloat(0.0001)) || maxRange < fixed::Zero()) && + (opposite ? MaxDistanceToPoint(ent, px, pz) : dist) >= minRange - fixed::FromFloat(0.0001); } bool CCmpObstructionManager::IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const { fixed dist = DistanceToTarget(ent, target); - return dist != fixed::FromInt(-1) && dist <= maxRange + fixed::FromFloat(0.0001) && (opposite ? MaxDistanceToTarget(ent, target) : dist) >= minRange- fixed::FromFloat(0.0001); + // Treat -1 max range as infinite + return dist != fixed::FromInt(-1) && + (dist <= (maxRange + fixed::FromFloat(0.0001)) || maxRange < fixed::Zero()) && + (opposite ? MaxDistanceToTarget(ent, target) : dist) >= minRange - fixed::FromFloat(0.0001); } bool CCmpObstructionManager::IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const { entity_pos_t distance = (CFixedVector2D(x, z) - CFixedVector2D(px, pz)).Length(); - return distance <= maxRange + fixed::FromFloat(0.0001) && distance >= minRange - fixed::FromFloat(0.0001); + // Treat -1 max range as infinite + return (distance <= (maxRange + fixed::FromFloat(0.0001)) || maxRange < fixed::Zero()) && + distance >= minRange - fixed::FromFloat(0.0001); } bool CCmpObstructionManager::TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits) const Index: source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- source/simulation2/components/CCmpUnitMotion.cpp +++ source/simulation2/components/CCmpUnitMotion.cpp @@ -1415,21 +1415,27 @@ { // Ranged movement: + CmpPtr cmpObstruction(GetEntityHandle()); + ENSURE(cmpObstruction); + // Distance checks are nearest edge to nearest edge, so we need to account for our clearance + // and we must make sure diagonals also fit so multiply by slightly more than sqrt(2) + entity_pos_t goalDelta = cmpObstruction->GetUnitRadius() * 3 /2; + entity_pos_t distance = (pos - CFixedVector2D(x, z)).Length(); if (distance < minRange) { // Too close to target - move outwards to a circle - // that's slightly larger than the min range + // that's slightly larger than the min range. goal.type = PathGoal::INVERTED_CIRCLE; - goal.hw = minRange + Pathfinding::GOAL_DELTA; + goal.hw = minRange + goalDelta; } else if (maxRange >= entity_pos_t::Zero() && distance > maxRange) { // Too far from target - move inwards to a circle // that's slightly smaller than the max range goal.type = PathGoal::CIRCLE; - goal.hw = maxRange - Pathfinding::GOAL_DELTA; + goal.hw = maxRange - goalDelta; // If maxRange was abnormally small, // collapse the circle into a point @@ -1501,7 +1507,7 @@ /* * If we're starting outside the maxRange, we need to move closer in. * If we're starting inside the minRange, we need to move further out. - * These ranges are measured from the center of this entity to the edge of the target; + * These ranges are measured from the edge of this entity to the edge of the target; * we add the goal range onto the size of the target shape to get the goal shape. * (Then we extend it outwards/inwards by a little bit to be sure we'll end up * within the right range, in case of minor numerical inaccuracies.) @@ -1535,6 +1541,12 @@ cmpObstruction->GetPreviousObstructionSquare(previousObstruction); entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize, true); + CmpPtr cmpThisObstruction(GetEntityHandle()); + ENSURE(cmpThisObstruction); + // Distance checks are nearest edge to nearest edge, so we need to account for our clearance + // and we must make sure diagonals also fit so multiply by slightly more than sqrt(2) + entity_pos_t goalDelta = cmpThisObstruction->GetUnitRadius() * 3 /2; + bool inside = distance.IsZero() && !Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize).IsZero(); if ((distance < minRange && previousDistance < minRange) || inside) { @@ -1543,7 +1555,7 @@ // Circumscribe the square entity_pos_t circleRadius = halfSize.Length(); - entity_pos_t goalDistance = minRange + Pathfinding::GOAL_DELTA; + entity_pos_t goalDistance = minRange + goalDelta; if (ShouldTreatTargetAsCircle(minRange, circleRadius)) { @@ -1591,7 +1603,7 @@ return true; } - entity_pos_t goalDistance = maxRange - Pathfinding::GOAL_DELTA; + entity_pos_t goalDistance = maxRange - goalDelta; goal.type = PathGoal::CIRCLE; goal.hw = circleRadius + goalDistance; @@ -1601,12 +1613,12 @@ // The target is large relative to our range, so treat it as a square and // get close enough that the diagonals come within range - entity_pos_t goalDistance = (maxRange - Pathfinding::GOAL_DELTA)*2 / 3; // multiply by slightly less than 1/sqrt(2) + entity_pos_t goalDistance = (maxRange - goalDelta) * 2 / 3; // multiply by slightly less than 1/sqrt(2) goal.type = PathGoal::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 + entity_pos_t delta = std::max(goalDistance, goalDelta + 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; } Index: source/simulation2/components/ICmpObstructionManager.h =================================================================== --- source/simulation2/components/ICmpObstructionManager.h +++ source/simulation2/components/ICmpObstructionManager.h @@ -201,16 +201,19 @@ /** * Check if the given entity is in range of the other point given those parameters. + * @param maxRange - if -1, treated as infinite. */ virtual bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const = 0; /** * Check if the given entity is in range of the target given those parameters. + * @param maxRange - if -1, treated as infinite. */ virtual bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const = 0; /** * Check if the given point is in range of the other point given those parameters. + * @param maxRange - if -1, treated as infinite. */ virtual bool IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const = 0; Index: source/simulation2/helpers/Pathfinding.h =================================================================== --- source/simulation2/helpers/Pathfinding.h +++ source/simulation2/helpers/Pathfinding.h @@ -148,15 +148,6 @@ const int NAVCELL_SIZE_INT = 1; const int NAVCELL_SIZE_LOG2 = 0; - /** - * For extending the goal outwards/inwards a little bit - * NOTE: keep next to the definition of NAVCELL_SIZE to avoid init order problems - * between translation units. - * TODO: figure out whether this is actually needed. It was added back in r8751 (in 2010) for unclear reasons - * and it does not seem to really improve behavior today - */ - const entity_pos_t GOAL_DELTA = NAVCELL_SIZE/8; - /** * To make sure the long-range pathfinder is more strict than the short-range one, * we need to slightly over-rasterize. So we extend the clearance radius by 1.