Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp
Show First 20 Lines • Show All 597 Lines • ▼ Show 20 Lines | private: | ||||
bool TryGoingStraightToTarget(const CFixedVector2D& from); | bool TryGoingStraightToTarget(const CFixedVector2D& from); | ||||
/** | /** | ||||
* Returns whether our we need to recompute a path to reach our target. | * Returns whether our we need to recompute a path to reach our target. | ||||
*/ | */ | ||||
bool PathingUpdateNeeded(const CFixedVector2D& from); | bool PathingUpdateNeeded(const CFixedVector2D& from); | ||||
/** | /** | ||||
* Update goal position if moving target | |||||
*/ | |||||
void UpdateFinalGoal(); | |||||
/** | |||||
* Returns whether we are close enough to the target to assume it's a good enough | * Returns whether we are close enough to the target to assume it's a good enough | ||||
* position to stop. | * position to stop. | ||||
*/ | */ | ||||
bool CloseEnoughFromDestinationToStop(const CFixedVector2D& from) const; | bool CloseEnoughFromDestinationToStop(const CFixedVector2D& from) const; | ||||
/** | /** | ||||
* Returns whether the length of the given path, plus the distance from | * Returns whether the length of the given path, plus the distance from | ||||
* 'from' to the first waypoints, it shorter than minDistance. | * 'from' to the first waypoints, it shorter than minDistance. | ||||
*/ | */ | ||||
bool PathIsShort(const WaypointPath& path, const CFixedVector2D& from, entity_pos_t minDistance) const; | bool PathIsShort(const WaypointPath& path, const CFixedVector2D& from, entity_pos_t minDistance) const; | ||||
/** | /** | ||||
* 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, | * noTarget is true only when used inside TryGoingStraightToTarget, | ||||
* in which case we do not want the target obstruction otherwise it would always fail | * in which case we do not want the target obstruction otherwise it would always fail | ||||
*/ | */ | ||||
ControlGroupMovementObstructionFilter GetObstructionFilter(bool noTarget = false) const; | ControlGroupMovementObstructionFilter GetObstructionFilter(bool noTarget = false) const; | ||||
/** | /** | ||||
* Create a PathGoal from a move request. | |||||
* @returns true if the goal was successfully created. | |||||
*/ | |||||
bool ComputeGoal(PathGoal& out, const MoveRequest& moveRequest) const; | |||||
/** | |||||
* Start moving to the given goal, from our current position 'from'. | * Start moving to the given goal, from our current position 'from'. | ||||
* Might go in a straight line immediately, or might start an asynchronous | * Might go in a straight line immediately, or might start an asynchronous | ||||
* path request. | * path request. | ||||
*/ | */ | ||||
void BeginPathing(const CFixedVector2D& from, const PathGoal& goal); | void BeginPathing(const CFixedVector2D& from, const PathGoal& goal); | ||||
/** | /** | ||||
* Start an asynchronous long path query. | * Start an asynchronous long path query. | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | if (m_ShortPath.m_Waypoints.empty()) | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
if (CloseEnoughFromDestinationToStop(pos)) | if (CloseEnoughFromDestinationToStop(pos)) | ||||
{ | { | ||||
MoveSucceeded(); | MoveSucceeded(); | ||||
return; | return; | ||||
} | } | ||||
UpdateFinalGoal(); | ComputeGoal(m_FinalGoal, m_MoveRequest); | ||||
RequestLongPath(pos, m_FinalGoal); | RequestLongPath(pos, m_FinalGoal); | ||||
m_PathState = PATHSTATE_WAITING_REQUESTING_LONG; | m_PathState = PATHSTATE_WAITING_REQUESTING_LONG; | ||||
return; | return; | ||||
} | } | ||||
// else we could, so reset our number of tries. | // else we could, so reset our number of tries. | ||||
m_Tries = 0; | m_Tries = 0; | ||||
▲ Show 20 Lines • Show All 213 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::HandleObstructedMove() | ||||
if (!m_LongPath.m_Waypoints.empty()) | if (!m_LongPath.m_Waypoints.empty()) | ||||
{ | { | ||||
PathGoal goal = { PathGoal::POINT, m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z }; | PathGoal goal = { PathGoal::POINT, m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z }; | ||||
RequestShortPath(pos, goal, true); | RequestShortPath(pos, goal, true); | ||||
m_PathState = PATHSTATE_WAITING_REQUESTING_SHORT; | m_PathState = PATHSTATE_WAITING_REQUESTING_SHORT; | ||||
return true; | return true; | ||||
} | } | ||||
// Else, just entirely recompute | // Else, just entirely recompute | ||||
UpdateFinalGoal(); | ComputeGoal(m_FinalGoal, m_MoveRequest); | ||||
BeginPathing(pos, m_FinalGoal); | BeginPathing(pos, m_FinalGoal); | ||||
// potential TODO: We could switch the short-range pathfinder for something else entirely. | // potential TODO: We could switch the short-range pathfinder for something else entirely. | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::TargetHasValidPosition() const | bool CCmpUnitMotion::TargetHasValidPosition() const | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::PathingUpdateNeeded(const CFixedVector2D& from) | ||||
// We won't be in-range when we reach our final waypoint : recompute our path. | // We won't be in-range when we reach our final waypoint : recompute our path. | ||||
m_FinalGoal.x = estimatedTargetShape.x; | m_FinalGoal.x = estimatedTargetShape.x; | ||||
m_FinalGoal.z = estimatedTargetShape.z; | m_FinalGoal.z = estimatedTargetShape.z; | ||||
BeginPathing(from, m_FinalGoal); | BeginPathing(from, m_FinalGoal); | ||||
m_PathState = PATHSTATE_FOLLOWING_REQUESTING_LONG; | m_PathState = PATHSTATE_FOLLOWING_REQUESTING_LONG; | ||||
return true; | return true; | ||||
} | } | ||||
void CCmpUnitMotion::UpdateFinalGoal() | |||||
{ | |||||
if (m_MoveRequest.m_Type != MoveRequest::ENTITY || m_MoveRequest.m_Type != MoveRequest::OFFSET) | |||||
return; | |||||
CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), m_MoveRequest.m_Entity); | |||||
if (!cmpUnitMotion) | |||||
return; | |||||
if (IsFormationMember()) | |||||
return; | |||||
CFixedVector2D targetPos; | |||||
if (!ComputeTargetPosition(targetPos)) | |||||
return; | |||||
m_FinalGoal.x = targetPos.X; | |||||
m_FinalGoal.z = targetPos.Y; | |||||
} | |||||
bool CCmpUnitMotion::CloseEnoughFromDestinationToStop(const CFixedVector2D& from) const | bool CCmpUnitMotion::CloseEnoughFromDestinationToStop(const CFixedVector2D& from) const | ||||
{ | { | ||||
if (m_MoveRequest.m_Type != MoveRequest::POINT || m_FinalGoal.DistanceToPoint(from) > SHORT_PATH_GOAL_RADIUS) | if (m_MoveRequest.m_Type != MoveRequest::POINT || m_FinalGoal.DistanceToPoint(from) > SHORT_PATH_GOAL_RADIUS) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::PathIsShort(const WaypointPath& path, const CFixedVector2D& from, entity_pos_t minDistance) const | bool CCmpUnitMotion::PathIsShort(const WaypointPath& path, const CFixedVector2D& from, entity_pos_t minDistance) const | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
ControlGroupMovementObstructionFilter CCmpUnitMotion::GetObstructionFilter(bool noTarget) const | ControlGroupMovementObstructionFilter CCmpUnitMotion::GetObstructionFilter(bool noTarget) const | ||||
{ | { | ||||
entity_id_t group = noTarget ? m_MoveRequest.m_Entity : GetGroup(); | entity_id_t group = noTarget ? m_MoveRequest.m_Entity : GetGroup(); | ||||
return ControlGroupMovementObstructionFilter(ShouldAvoidMovingUnits(), group); | return ControlGroupMovementObstructionFilter(ShouldAvoidMovingUnits(), group); | ||||
} | } | ||||
// 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. | |||||
// 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 | |||||
{ | |||||
// 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. | |||||
// 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::ComputeGoal(PathGoal& out, const MoveRequest& moveRequest) const | |||||
{ | |||||
if (moveRequest.m_Type == MoveRequest::NONE) | |||||
return false; | |||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return false; | |||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | |||||
CFixedVector2D targetPosition; | |||||
if (!ComputeTargetPosition(targetPosition)) | |||||
return false; | |||||
ICmpObstructionManager::ObstructionSquare targetObstruction; | |||||
if (moveRequest.m_Type == MoveRequest::ENTITY) | |||||
{ | |||||
CmpPtr<ICmpObstruction> cmpTargetObstruction(GetSimContext(), moveRequest.m_Entity); | |||||
if (cmpTargetObstruction) | |||||
cmpTargetObstruction->GetObstructionSquare(targetObstruction); | |||||
} | |||||
targetObstruction.x = targetPosition.X; | |||||
targetObstruction.z = targetPosition.Y; | |||||
ICmpObstructionManager::ObstructionSquare obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle()); | |||||
if (cmpObstruction) | |||||
cmpObstruction->GetObstructionSquare(obstruction); | |||||
else | |||||
{ | |||||
obstruction.x = pos.X; | |||||
obstruction.z = pos.Y; | |||||
} | |||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); | |||||
ENSURE(cmpObstructionManager); | |||||
entity_pos_t distance = cmpObstructionManager->DistanceBetweenShapes(obstruction, targetObstruction); | |||||
out.x = targetObstruction.x; | |||||
out.z = targetObstruction.z; | |||||
out.hw = targetObstruction.hw; | |||||
out.hh = targetObstruction.hh; | |||||
out.u = targetObstruction.u; | |||||
out.v = targetObstruction.v; | |||||
if (moveRequest.m_MinRange > fixed::Zero() || moveRequest.m_MaxRange > fixed::Zero() || | |||||
targetObstruction.hw > fixed::Zero()) | |||||
out.type = PathGoal::SQUARE; | |||||
else | |||||
{ | |||||
out.type = PathGoal::POINT; | |||||
return true; | |||||
} | |||||
CFixedVector2D halfSize(targetObstruction.hw, targetObstruction.hh); | |||||
if (distance < moveRequest.m_MinRange) | |||||
{ | |||||
entity_pos_t circleRadius = halfSize.Length(); | |||||
// 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 goalDistance = moveRequest.m_MinRange + m_Clearance * 3 / 2; | |||||
if (ShouldTreatTargetAsCircle(moveRequest.m_MinRange, circleRadius)) | |||||
{ | |||||
out.type = PathGoal::INVERTED_CIRCLE; | |||||
out.hw = circleRadius + goalDistance; | |||||
} | |||||
else | |||||
{ | |||||
out.type = PathGoal::INVERTED_SQUARE; | |||||
out.hw = targetObstruction.hw + goalDistance; | |||||
out.hh = targetObstruction.hh + goalDistance; | |||||
} | |||||
} | |||||
else if (moveRequest.m_MaxRange >= fixed::Zero() && distance > moveRequest.m_MaxRange) | |||||
{ | |||||
entity_pos_t circleRadius = halfSize.Length(); | |||||
if (ShouldTreatTargetAsCircle(moveRequest.m_MaxRange, circleRadius)) | |||||
{ | |||||
entity_pos_t goalDistance = moveRequest.m_MaxRange; | |||||
out.type = PathGoal::CIRCLE; | |||||
out.hw = circleRadius + goalDistance; | |||||
} | |||||
else | |||||
{ | |||||
// 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 = moveRequest.m_MaxRange * 2 / 3; // multiply by slightly less than 1/sqrt(2) | |||||
out.type = PathGoal::SQUARE; | |||||
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 | |||||
out.hw = targetObstruction.hw + delta; | |||||
out.hh = targetObstruction.hh + delta; | |||||
} | |||||
} | |||||
// Do nothing in particular in case we are already in range. | |||||
return true; | |||||
} | |||||
void CCmpUnitMotion::BeginPathing(const CFixedVector2D& from, const PathGoal& goal) | void CCmpUnitMotion::BeginPathing(const CFixedVector2D& from, const PathGoal& goal) | ||||
{ | { | ||||
// reset our state for sanity. | // reset our state for sanity. | ||||
m_ExpectedPathTicket = 0; | m_ExpectedPathTicket = 0; | ||||
m_PathState = PATHSTATE_NONE; | m_PathState = PATHSTATE_NONE; | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | |||||
bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange, entity_id_t target) | bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange, entity_id_t target) | ||||
{ | { | ||||
PROFILE("MoveToPointRange"); | PROFILE("MoveToPointRange"); | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return false; | return false; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | |||||
PathGoal goal; | |||||
goal.x = x; | |||||
goal.z = z; | |||||
if (minRange.IsZero() && maxRange.IsZero()) | |||||
{ | |||||
// Non-ranged movement: | |||||
// Head directly for the goal | |||||
goal.type = PathGoal::POINT; | |||||
} | |||||
else | |||||
{ | |||||
// Ranged movement: | |||||
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. | |||||
goal.type = PathGoal::INVERTED_CIRCLE; | |||||
// 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) | |||||
goal.hw = minRange + m_Clearance * 3 /2; | |||||
} | |||||
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; | |||||
// If maxRange was abnormally small, | |||||
// collapse the circle into a point | |||||
if (goal.hw <= entity_pos_t::Zero()) | |||||
goal.type = PathGoal::POINT; | |||||
} | |||||
} | |||||
m_MoveRequest = MoveRequest(CFixedVector2D(x, z), minRange, maxRange); | m_MoveRequest = MoveRequest(CFixedVector2D(x, z), minRange, maxRange); | ||||
m_FinalGoal = goal; | ComputeGoal(m_FinalGoal, m_MoveRequest); | ||||
m_Tries = 0; | m_Tries = 0; | ||||
BeginPathing(pos, goal); | BeginPathing(cmpPosition->GetPosition2D(), m_FinalGoal); | ||||
return true; | return true; | ||||
} | } | ||||
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 | |||||
// is a round-cornered square which we can approximate as either a circle or as a square. | |||||
// 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) | bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) | ||||
{ | { | ||||
PROFILE("MoveToTargetRange"); | PROFILE("MoveToTargetRange"); | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return false; | return false; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | |||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); | |||||
if (!cmpObstructionManager) | |||||
return false; | |||||
bool hasObstruction = false; | |||||
ICmpObstructionManager::ObstructionSquare obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), target); | |||||
if (cmpObstruction) | |||||
hasObstruction = cmpObstruction->GetObstructionSquare(obstruction); | |||||
if (!hasObstruction) | |||||
{ | |||||
// The target didn't have an obstruction or obstruction shape, so treat it as a point instead | |||||
CmpPtr<ICmpPosition> cmpTargetPosition(GetSimContext(), target); | |||||
if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld()) | |||||
return false; | |||||
CFixedVector2D targetPos = cmpTargetPosition->GetPosition2D(); | |||||
return MoveToPointRange(targetPos.X, targetPos.Y, minRange, maxRange); | |||||
} | |||||
/* | |||||
* 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 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.) | |||||
* | |||||
* There's a bit of a problem with large square targets: | |||||
* the pathfinder only lets us move to goals that are squares, but the points an equal | |||||
* distance from the target make a rounded square shape instead. | |||||
* | |||||
* When moving closer, we could shrink the goal radius to 1/sqrt(2) so the goal shape fits entirely | |||||
* within the desired rounded square, but that gives an unfair advantage to attackers who approach | |||||
* the target diagonally. | |||||
* | |||||
* If the target is small relative to the range (e.g. archers attacking anything), | |||||
* then we cheat and pretend the target is actually a circle. | |||||
* (TODO: that probably looks rubbish for things like walls?) | |||||
* | |||||
* If the target is large relative to the range (e.g. melee units attacking buildings), | |||||
* then we multiply maxRange by approx 1/sqrt(2) to guarantee they'll always aim close enough. | |||||
* (Those units should set minRange to 0 so they'll never be considered *too* close.) | |||||
*/ | |||||
CFixedVector2D halfSize(obstruction.hw, obstruction.hh); | |||||
PathGoal goal; | |||||
goal.x = obstruction.x; | |||||
goal.z = obstruction.z; | |||||
entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize, true); | |||||
// 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, true); | |||||
bool inside = distance.IsZero() && !Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize).IsZero(); | |||||
if ((distance < minRange && previousDistance < minRange) || inside) | |||||
{ | |||||
// Too close to the square - need to move away | |||||
// Circumscribe the square | |||||
entity_pos_t circleRadius = halfSize.Length(); | |||||
// 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 goalDistance = minRange + m_Clearance * 3 /2; | |||||
if (ShouldTreatTargetAsCircle(minRange, circleRadius)) | |||||
{ | |||||
// The target is small relative to our range, so pretend it's a circle | |||||
goal.type = PathGoal::INVERTED_CIRCLE; | |||||
goal.hw = circleRadius + goalDistance; | |||||
} | |||||
else | |||||
{ | |||||
goal.type = PathGoal::INVERTED_SQUARE; | |||||
goal.u = obstruction.u; | |||||
goal.v = obstruction.v; | |||||
goal.hw = obstruction.hw + goalDistance; | |||||
goal.hh = obstruction.hh + goalDistance; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
// We might need to move closer: | |||||
// Circumscribe the square | |||||
entity_pos_t circleRadius = halfSize.Length(); | |||||
if (ShouldTreatTargetAsCircle(maxRange, circleRadius)) | |||||
{ | |||||
// The target is small relative to our range, so pretend it's a circle | |||||
// Note that the distance to the circle will always be less than | |||||
// 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; | |||||
entity_pos_t goalDistance = maxRange; | |||||
goal.type = PathGoal::CIRCLE; | |||||
goal.hw = circleRadius + goalDistance; | |||||
} | |||||
else | |||||
{ | |||||
// 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 * 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 | |||||
goal.hw = obstruction.hw + delta; | |||||
goal.hh = obstruction.hh + delta; | |||||
} | |||||
} | |||||
m_MoveRequest = MoveRequest(target, minRange, maxRange); | m_MoveRequest = MoveRequest(target, minRange, maxRange); | ||||
m_FinalGoal = goal; | ComputeGoal(m_FinalGoal, m_MoveRequest); | ||||
m_Tries = 0; | m_Tries = 0; | ||||
BeginPathing(pos, goal); | BeginPathing(cmpPosition->GetPosition2D(), m_FinalGoal); | ||||
return true; | return true; | ||||
} | } | ||||
void CCmpUnitMotion::MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z) | void CCmpUnitMotion::MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z) | ||||
{ | { | ||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target); | CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return; | return; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | |||||
PathGoal goal; | |||||
goal.type = PathGoal::POINT; | |||||
goal.x = pos.X; | |||||
goal.z = pos.Y; | |||||
m_MoveRequest = MoveRequest(target, CFixedVector2D(x, z)); | m_MoveRequest = MoveRequest(target, CFixedVector2D(x, z)); | ||||
m_FinalGoal = goal; | ComputeGoal(m_FinalGoal, m_MoveRequest); | ||||
m_Tries = 0; | m_Tries = 0; | ||||
BeginPathing(pos, goal); | BeginPathing(cmpPosition->GetPosition2D(), m_FinalGoal); | ||||
} | } | ||||
void CCmpUnitMotion::RenderPath(const WaypointPath& path, std::vector<SOverlayLine>& lines, CColor color) | void CCmpUnitMotion::RenderPath(const WaypointPath& path, std::vector<SOverlayLine>& lines, CColor color) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 41 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator