Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp
Show First 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | |||||
* Experimentally, this number needs to be somewhat high or moving groups of units will lead to stuck units. | * Experimentally, this number needs to be somewhat high or moving groups of units will lead to stuck units. | ||||
* However, too high means units will look idle for a long time when they are failing to move. | * However, too high means units will look idle for a long time when they are failing to move. | ||||
* TODO: if UnitMotion could send differentiated "unreachable" and "currently stuck" failing messages, | * TODO: if UnitMotion could send differentiated "unreachable" and "currently stuck" failing messages, | ||||
* this could probably be lowered. | * this could probably be lowered. | ||||
* TODO: when unit pushing is implemented, this number can probably be lowered. | * TODO: when unit pushing is implemented, this number can probably be lowered. | ||||
*/ | */ | ||||
static const u8 MAX_FAILED_PATH_COMPUTATIONS = 15; | static const u8 MAX_FAILED_PATH_COMPUTATIONS = 15; | ||||
/** | |||||
* If we have failed path computations this many times and ComputePathToGoal is called, | |||||
* always run a long-path, to avoid getting stuck sometimes (see D1424). | |||||
*/ | |||||
static const u8 MAX_FAILED_PATH_COMPUTATIONS_BEFORE_LONG_PATH = 3; | |||||
static const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1); | static const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1); | ||||
static const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1); | static const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1); | ||||
class CCmpUnitMotion : public ICmpUnitMotion | class CCmpUnitMotion : public ICmpUnitMotion | ||||
{ | { | ||||
public: | public: | ||||
static void ClassInit(CComponentManager& componentManager) | static void ClassInit(CComponentManager& componentManager) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 493 Lines • ▼ Show 20 Lines | private: | ||||
/** | /** | ||||
* Rotate to face towards the target point, given the current pos | * Rotate to face towards the target point, given the current pos | ||||
*/ | */ | ||||
void FaceTowardsPointFromPos(const CFixedVector2D& pos, entity_pos_t x, entity_pos_t z); | void FaceTowardsPointFromPos(const CFixedVector2D& pos, entity_pos_t x, entity_pos_t z); | ||||
/** | /** | ||||
* Returns an appropriate obstruction filter for use with path requests. | * Returns an appropriate obstruction filter for use with path requests. | ||||
* noTarget is true only when used inside TryGoingStraightToTarget, | |||||
* in which case we do not want the target obstruction otherwise it would always fail | |||||
*/ | */ | ||||
ControlGroupMovementObstructionFilter GetObstructionFilter(bool noTarget = false) const; | ControlGroupMovementObstructionFilter GetObstructionFilter() const; | ||||
/** | /** | ||||
* Create a PathGoal from a move request. | * Create a PathGoal from a move request. | ||||
* @returns true if the goal was successfully created. | * @returns true if the goal was successfully created. | ||||
*/ | */ | ||||
bool ComputeGoal(PathGoal& out, const MoveRequest& moveRequest) const; | bool ComputeGoal(PathGoal& out, const MoveRequest& moveRequest) const; | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 413 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::TryGoingStraightToTarget(const CFixedVector2D& from) | ||||
goal.x = targetPos.X; | goal.x = targetPos.X; | ||||
goal.z = targetPos.Y; | goal.z = targetPos.Y; | ||||
// (we ignore changes to the target's rotation, since only buildings are | // (we ignore changes to the target's rotation, since only buildings are | ||||
// square and buildings don't move) | // square and buildings don't move) | ||||
// Find the point on the goal shape that we should head towards | // Find the point on the goal shape that we should head towards | ||||
CFixedVector2D goalPos = goal.NearestPointOnGoal(from); | CFixedVector2D goalPos = goal.NearestPointOnGoal(from); | ||||
// Check if there's any collisions on that route | // Check if there's any collisions on that route. | ||||
if (!cmpPathfinder->CheckMovement(GetObstructionFilter(true), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass)) | // For entity goals, skip only the specific obstruction tag or with e.g. walls we might ignore too many entities. | ||||
ICmpObstructionManager::tag_t specificIgnore; | |||||
if (m_MoveRequest.m_Type == MoveRequest::ENTITY) | |||||
{ | |||||
CmpPtr<ICmpObstruction> cmpTargetObstruction(GetSimContext(), m_MoveRequest.m_Entity); | |||||
if (cmpTargetObstruction) | |||||
specificIgnore = cmpTargetObstruction->GetObstruction(); | |||||
} | |||||
if (specificIgnore.valid()) | |||||
{ | |||||
if (!cmpPathfinder->CheckMovement(SkipTagObstructionFilter(specificIgnore), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass)) | |||||
return false; | return false; | ||||
} | |||||
else if (!cmpPathfinder->CheckMovement(GetObstructionFilter(), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass)) | |||||
return false; | |||||
// That route is okay, so update our path | // That route is okay, so update our path | ||||
m_LongPath.m_Waypoints.clear(); | m_LongPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.clear(); | m_ShortPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::PathingUpdateNeeded(const CFixedVector2D& from) const | bool CCmpUnitMotion::PathingUpdateNeeded(const CFixedVector2D& from) const | ||||
{ | { | ||||
if (m_MoveRequest.m_Type == MoveRequest::NONE) | if (m_MoveRequest.m_Type == MoveRequest::NONE) | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | if (!offset.IsZero()) | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition) | if (!cmpPosition) | ||||
return; | return; | ||||
cmpPosition->TurnTo(angle); | cmpPosition->TurnTo(angle); | ||||
} | } | ||||
} | } | ||||
ControlGroupMovementObstructionFilter CCmpUnitMotion::GetObstructionFilter(bool noTarget) const | ControlGroupMovementObstructionFilter CCmpUnitMotion::GetObstructionFilter() const | ||||
{ | { | ||||
entity_id_t group = noTarget ? m_MoveRequest.m_Entity : GetGroup(); | return ControlGroupMovementObstructionFilter(ShouldAvoidMovingUnits(), GetGroup()); | ||||
return ControlGroupMovementObstructionFilter(ShouldAvoidMovingUnits(), group); | |||||
} | } | ||||
// The pathfinder cannot go to "rounded rectangles" goals, which are what happens with square targets and a non-null range. | // The pathfinder cannot go to "rounded rectangles" goals, which are what happens with square targets and a non-null range. | ||||
// Depending on what the best approximation is, we either pretend the target is a circle or a square. | // Depending on what the best approximation is, we either pretend the target is a circle or a square. | ||||
// One needs to be careful that the approximated geometry will be in the range. | // One needs to be careful that the approximated geometry will be in the range. | ||||
bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const | bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const | ||||
{ | { | ||||
// Given a square, plus a target range we should reach, the shape at that distance | // Given a square, plus a target range we should reach, the shape at that distance | ||||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | #if DISABLE_PATHFINDER | ||||
m_ShortPath.m_Waypoints.clear(); | m_ShortPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | ||||
return; | return; | ||||
} | } | ||||
#endif | #endif | ||||
// If the target is close and we can reach it in a straight line, | // If the target is close and we can reach it in a straight line, | ||||
// then we'll just go along the straight line instead of computing a path. | // then we'll just go along the straight line instead of computing a path. | ||||
if (TryGoingStraightToTarget(from)) | if (m_FailedPathComputations != MAX_FAILED_PATH_COMPUTATIONS_BEFORE_LONG_PATH && TryGoingStraightToTarget(from)) | ||||
return; | return; | ||||
// Otherwise we need to compute a path. | // Otherwise we need to compute a path. | ||||
// If it's close then just do a short path, not a long path | // If it's close then just do a short path, not a long path | ||||
// TODO: If it's close on the opposite side of a river then we really | // TODO: If it's close on the opposite side of a river then we really | ||||
// need a long path, so we shouldn't simply check linear distance | // need a long path, so we shouldn't simply check linear distance | ||||
// the check is arbitrary but should be a reasonably small distance. | // the check is arbitrary but should be a reasonably small distance. | ||||
if (goal.DistanceToPoint(from) < LONG_PATH_MIN_DIST) | // To avoid getting stuck because the short-range pathfinder is bounded, occasionally compute a long path instead. | ||||
if (m_FailedPathComputations != MAX_FAILED_PATH_COMPUTATIONS_BEFORE_LONG_PATH && goal.DistanceToPoint(from) < LONG_PATH_MIN_DIST) | |||||
{ | { | ||||
m_LongPath.m_Waypoints.clear(); | m_LongPath.m_Waypoints.clear(); | ||||
RequestShortPath(from, goal, true); | RequestShortPath(from, goal, true); | ||||
} | } | ||||
else | else | ||||
RequestLongPath(from, goal); | RequestLongPath(from, goal); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 140 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator