forked from 0ad/0ad
Changes to unitMotion.
Make sure we do not treat as circles entities that we really should treat as squares (such as trees). This fixes an issue reported by Stan. Make sure we never forget about our destination if we are blocked by unit obstructions. This makes sure that units in a group but not in formation will not be blocked by the other units, and probably makes the general behavior more sane. Helps following [17166] Refs #3505, #3471, #3376 This was SVN commit r17191.
This commit is contained in:
parent
9efd79e240
commit
e26b59c917
@ -695,6 +695,13 @@ REGISTER_COMPONENT_TYPE(UnitMotion)
|
||||
|
||||
void CCmpUnitMotion::PathResult(u32 ticket, const WaypointPath& path)
|
||||
{
|
||||
// reset our state for sanity.
|
||||
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle());
|
||||
if (cmpObstruction)
|
||||
cmpObstruction->SetMovingFlag(false);
|
||||
|
||||
m_Moving = false;
|
||||
|
||||
if (ticket == m_Planning.expectedPathTicket)
|
||||
{
|
||||
// If no path was found, better cancel the planning
|
||||
@ -742,8 +749,13 @@ void CCmpUnitMotion::PathResult(u32 ticket, const WaypointPath& path)
|
||||
StartSucceeded();
|
||||
|
||||
m_PathState = PATHSTATE_FOLLOWING;
|
||||
|
||||
if (cmpObstruction)
|
||||
cmpObstruction->SetMovingFlag(true);
|
||||
|
||||
m_Moving = true;
|
||||
}
|
||||
else if (m_PathState == PATHSTATE_WAITING_REQUESTING_SHORT)
|
||||
else if (m_PathState == PATHSTATE_WAITING_REQUESTING_SHORT || m_PathState == PATHSTATE_FOLLOWING_REQUESTING_SHORT)
|
||||
{
|
||||
m_ShortPath = path;
|
||||
|
||||
@ -751,49 +763,42 @@ void CCmpUnitMotion::PathResult(u32 ticket, const WaypointPath& path)
|
||||
if (m_ShortPath.m_Waypoints.empty())
|
||||
{
|
||||
// If we're globally following a long path, try to remove the next waypoint, it might be obstructed
|
||||
// If not, and we are not in a formation, retry with a long-range one.
|
||||
if (m_LongPath.m_Waypoints.size() > 1)
|
||||
m_LongPath.m_Waypoints.pop_back();
|
||||
else if (!IsFormationMember())
|
||||
{
|
||||
StartFailed();
|
||||
return;
|
||||
}
|
||||
else
|
||||
else if (IsFormationMember())
|
||||
{
|
||||
m_Moving = false;
|
||||
CMessageMotionChanged msg(true, true);
|
||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
||||
return;
|
||||
}
|
||||
|
||||
CMessageMotionChanged msg(false, false);
|
||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
||||
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
||||
if (!cmpPosition || !cmpPosition->IsInWorld())
|
||||
return;
|
||||
|
||||
CFixedVector2D pos = cmpPosition->GetPosition2D();
|
||||
|
||||
m_LongPath.m_Waypoints.clear();
|
||||
RequestLongPath(pos, m_FinalGoal);
|
||||
m_PathState = PATHSTATE_WAITING_REQUESTING_LONG;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we've got a short path that we can follow
|
||||
StartSucceeded();
|
||||
m_PathState = PATHSTATE_FOLLOWING;
|
||||
}
|
||||
else if (m_PathState == PATHSTATE_FOLLOWING_REQUESTING_SHORT)
|
||||
{
|
||||
// Replace the current path with the new one
|
||||
m_ShortPath = path;
|
||||
|
||||
// If there's no waypoints then we couldn't get near the target
|
||||
if (m_ShortPath.m_Waypoints.empty())
|
||||
{
|
||||
// We should stop moving (unless we're in a formation, in which
|
||||
// case we should continue following it)
|
||||
if (!IsFormationMember())
|
||||
{
|
||||
MoveFailed();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Moving = false;
|
||||
CMessageMotionChanged msg(false, true);
|
||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
||||
}
|
||||
}
|
||||
if (!HasValidPath())
|
||||
StartSucceeded();
|
||||
|
||||
m_PathState = PATHSTATE_FOLLOWING;
|
||||
|
||||
if (cmpObstruction)
|
||||
cmpObstruction->SetMovingFlag(true);
|
||||
|
||||
m_Moving = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1008,7 +1013,6 @@ void CCmpUnitMotion::Move(fixed dt)
|
||||
if (m_PathState == PATHSTATE_FOLLOWING)
|
||||
{
|
||||
// If we're not currently computing any new paths:
|
||||
|
||||
if (m_LongPath.m_Waypoints.empty() && m_ShortPath.m_Waypoints.empty())
|
||||
{
|
||||
if (IsFormationMember())
|
||||
@ -1293,14 +1297,6 @@ void CCmpUnitMotion::BeginPathing(const CFixedVector2D& from, const PathGoal& go
|
||||
// Cancel any pending path requests
|
||||
m_ExpectedPathTicket = 0;
|
||||
|
||||
// Update the unit's movement status.
|
||||
m_Moving = true;
|
||||
|
||||
// Set our 'moving' flag, so other units pathfinding now will ignore us
|
||||
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle());
|
||||
if (cmpObstruction)
|
||||
cmpObstruction->SetMovingFlag(true);
|
||||
|
||||
#if DISABLE_PATHFINDER
|
||||
{
|
||||
CmpPtr<ICmpPathfinder> cmpPathfinder (GetSimContext(), SYSTEM_ENTITY);
|
||||
@ -1340,7 +1336,12 @@ void CCmpUnitMotion::BeginPathing(const CFixedVector2D& from, const PathGoal& go
|
||||
// need a long path, so we shouldn't simply check linear distance
|
||||
if (goal.DistanceToPoint(from) < SHORT_PATH_SEARCH_RANGE)
|
||||
{
|
||||
// add our final goal as a long range waypoint so we don't forget
|
||||
// where we are going if the short-range pathfinder returns
|
||||
// an aborted path.
|
||||
m_LongPath.m_Waypoints.clear();
|
||||
CFixedVector2D target = m_FinalGoal.NearestPointOnGoal(from);
|
||||
m_LongPath.m_Waypoints.emplace_back(Waypoint{ target.X, target.Y });
|
||||
m_PathState = PATHSTATE_WAITING_REQUESTING_SHORT;
|
||||
RequestShortPath(from, goal, true);
|
||||
}
|
||||
@ -1493,15 +1494,10 @@ bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t
|
||||
{
|
||||
// Given a square, plus a target range we should reach, the shape at that distance
|
||||
// is a round-cornered square which we can approximate as either a circle or as a square.
|
||||
// Choose the shape that will minimise the worst-case error:
|
||||
|
||||
// For a square, error is (sqrt(2)-1) * (range-rSquare) at the corners
|
||||
entity_pos_t errSquare = (entity_pos_t::FromInt(4142)/10000).Multiply(range);
|
||||
|
||||
// For a circle, error is radius-hw at the sides and radius-hh at the top/bottom
|
||||
entity_pos_t errCircle = circleRadius - std::min(hw, hh);
|
||||
|
||||
return (errCircle < errSquare);
|
||||
// Previously, we used the shape that minimized the worst-case error.
|
||||
// However that is unsage in some situations. So let's be less clever and
|
||||
// just check if our range is at least three times bigger than the circleradius
|
||||
return (range > circleRadius*3);
|
||||
}
|
||||
|
||||
bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
|
||||
|
@ -127,8 +127,10 @@ namespace Pathfinding
|
||||
* 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;
|
||||
const entity_pos_t GOAL_DELTA = NAVCELL_SIZE/8;
|
||||
|
||||
/**
|
||||
* Compute the navcell indexes on the grid nearest to a given point
|
||||
|
Loading…
Reference in New Issue
Block a user