Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
static const entity_pos_t SHORT_PATH_MAX_SEARCH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*9); | static const entity_pos_t SHORT_PATH_MAX_SEARCH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*9); | ||||
/** | /** | ||||
* Minimum distance to goal for a long path request | * Minimum distance to goal for a long path request | ||||
*/ | */ | ||||
static const entity_pos_t LONG_PATH_MIN_DIST = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4); | static const entity_pos_t LONG_PATH_MIN_DIST = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4); | ||||
/** | /** | ||||
* When short-pathing, and the short-range pathfinder failed to return a path, | |||||
* Assume we are at destination if we are closer than this distance to the target | |||||
* And we have no target entity. | |||||
* This is somewhat arbitrary, but setting a too big distance means units might lose sight of their end goal too much; | |||||
*/ | |||||
static const entity_pos_t SHORT_PATH_GOAL_RADIUS = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*2); | |||||
/** | |||||
* If we are this close to our target entity/point, then think about heading | * If we are this close to our target entity/point, then think about heading | ||||
* for it in a straight line instead of pathfinding. | * for it in a straight line instead of pathfinding. | ||||
*/ | */ | ||||
static const entity_pos_t DIRECT_PATH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4); | static const entity_pos_t DIRECT_PATH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4); | ||||
/** | |||||
* When we fail more than this many path computations in a row, inform other components that the move will fail. | |||||
* 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. | |||||
* TODO: if UnitMotion could send differentiated "unreachable" and "currently stuck" failing messages, | |||||
* this could 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 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 All 23 Lines | public: | ||||
entity_pos_t m_Clearance; | entity_pos_t m_Clearance; | ||||
// cached for efficiency | // cached for efficiency | ||||
fixed m_WalkSpeed, m_RunMultiplier; | fixed m_WalkSpeed, m_RunMultiplier; | ||||
bool m_FacePointAfterMove; | bool m_FacePointAfterMove; | ||||
// Number of path computations that failed (in a row). | |||||
// When this gets above MAX_FAILED_PATH_COMPUTATIONS, inform other components | |||||
// that the move will likely fail. | |||||
u8 m_FailedPathComputations = 0; | |||||
struct Ticket { | struct Ticket { | ||||
u32 m_Ticket = 0; // asynchronous request ID we're waiting for, or 0 if none | u32 m_Ticket = 0; // asynchronous request ID we're waiting for, or 0 if none | ||||
enum Type { | enum Type { | ||||
SHORT_PATH, | SHORT_PATH, | ||||
LONG_PATH | LONG_PATH | ||||
} m_Type; | } m_Type; | ||||
void clear() { m_Ticket = 0; } | void clear() { m_Ticket = 0; } | ||||
Show All 27 Lines | public: | ||||
// Current mean speed (over the last turn). | // Current mean speed (over the last turn). | ||||
fixed m_CurSpeed; | fixed m_CurSpeed; | ||||
// Currently active paths (storing waypoints in reverse order). | // Currently active paths (storing waypoints in reverse order). | ||||
// The last item in each path is the point we're currently heading towards. | // The last item in each path is the point we're currently heading towards. | ||||
WaypointPath m_LongPath; | WaypointPath m_LongPath; | ||||
WaypointPath m_ShortPath; | WaypointPath m_ShortPath; | ||||
// Motion planning | |||||
u8 m_Tries; // how many tries we've done to get to our current Final Goal. | |||||
static std::string GetSchema() | static std::string GetSchema() | ||||
{ | { | ||||
return | return | ||||
"<a:help>Provides the unit with the ability to move around the world by itself.</a:help>" | "<a:help>Provides the unit with the ability to move around the world by itself.</a:help>" | ||||
"<a:example>" | "<a:example>" | ||||
"<WalkSpeed>7.0</WalkSpeed>" | "<WalkSpeed>7.0</WalkSpeed>" | ||||
"<PassabilityClass>default</PassabilityClass>" | "<PassabilityClass>default</PassabilityClass>" | ||||
"</a:example>" | "</a:example>" | ||||
Show All 34 Lines | if (cmpPathfinder) | ||||
m_PassClass = cmpPathfinder->GetPassabilityClass(m_PassClassName); | m_PassClass = cmpPathfinder->GetPassabilityClass(m_PassClassName); | ||||
m_Clearance = cmpPathfinder->GetClearance(m_PassClass); | m_Clearance = cmpPathfinder->GetClearance(m_PassClass); | ||||
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle()); | CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle()); | ||||
if (cmpObstruction) | if (cmpObstruction) | ||||
cmpObstruction->SetUnitClearance(m_Clearance); | cmpObstruction->SetUnitClearance(m_Clearance); | ||||
} | } | ||||
m_Tries = 0; | |||||
m_DebugOverlayEnabled = false; | m_DebugOverlayEnabled = false; | ||||
} | } | ||||
virtual void Deinit() | virtual void Deinit() | ||||
{ | { | ||||
} | } | ||||
template<typename S> | template<typename S> | ||||
void SerializeCommon(S& serialize) | void SerializeCommon(S& serialize) | ||||
{ | { | ||||
serialize.StringASCII("pass class", m_PassClassName, 0, 64); | serialize.StringASCII("pass class", m_PassClassName, 0, 64); | ||||
serialize.NumberU32_Unbounded("ticket", m_ExpectedPathTicket.m_Ticket); | serialize.NumberU32_Unbounded("ticket", m_ExpectedPathTicket.m_Ticket); | ||||
SerializeU8_Enum<Ticket::Type, Ticket::Type::LONG_PATH>()(serialize, "ticket type", m_ExpectedPathTicket.m_Type); | SerializeU8_Enum<Ticket::Type, Ticket::Type::LONG_PATH>()(serialize, "ticket type", m_ExpectedPathTicket.m_Type); | ||||
serialize.NumberU8("failed path computations", m_FailedPathComputations, 0, 255); | |||||
Silier: vs2013 serialisation out of bounds
pyrogenesis.exe!ISerializer::NumberU8(const char * name… | |||||
wraitiiAuthorUnsubmitted Done Inline ActionsAs said on IRC, it was actually L232, fixed by D2074. wraitii: As said on IRC, it was actually L232, fixed by D2074. | |||||
SerializeU8_Enum<MoveRequest::Type, MoveRequest::Type::OFFSET>()(serialize, "target type", m_MoveRequest.m_Type); | SerializeU8_Enum<MoveRequest::Type, MoveRequest::Type::OFFSET>()(serialize, "target type", m_MoveRequest.m_Type); | ||||
serialize.NumberU32_Unbounded("target entity", m_MoveRequest.m_Entity); | serialize.NumberU32_Unbounded("target entity", m_MoveRequest.m_Entity); | ||||
serialize.NumberFixed_Unbounded("target pos x", m_MoveRequest.m_Position.X); | serialize.NumberFixed_Unbounded("target pos x", m_MoveRequest.m_Position.X); | ||||
serialize.NumberFixed_Unbounded("target pos y", m_MoveRequest.m_Position.Y); | serialize.NumberFixed_Unbounded("target pos y", m_MoveRequest.m_Position.Y); | ||||
serialize.NumberFixed_Unbounded("target min range", m_MoveRequest.m_MinRange); | serialize.NumberFixed_Unbounded("target min range", m_MoveRequest.m_MinRange); | ||||
serialize.NumberFixed_Unbounded("target max range", m_MoveRequest.m_MaxRange); | serialize.NumberFixed_Unbounded("target max range", m_MoveRequest.m_MaxRange); | ||||
serialize.NumberFixed_Unbounded("speed multiplier", m_SpeedMultiplier); | serialize.NumberFixed_Unbounded("speed multiplier", m_SpeedMultiplier); | ||||
serialize.NumberFixed_Unbounded("current speed", m_CurSpeed); | serialize.NumberFixed_Unbounded("current speed", m_CurSpeed); | ||||
serialize.Bool("facePointAfterMove", m_FacePointAfterMove); | serialize.Bool("facePointAfterMove", m_FacePointAfterMove); | ||||
serialize.NumberU8("tries", m_Tries, 0, 255); | |||||
SerializeVector<SerializeWaypoint>()(serialize, "long path", m_LongPath.m_Waypoints); | SerializeVector<SerializeWaypoint>()(serialize, "long path", m_LongPath.m_Waypoints); | ||||
SerializeVector<SerializeWaypoint>()(serialize, "short path", m_ShortPath.m_Waypoints); | SerializeVector<SerializeWaypoint>()(serialize, "short path", m_ShortPath.m_Waypoints); | ||||
} | } | ||||
virtual void Serialize(ISerializer& serialize) | virtual void Serialize(ISerializer& serialize) | ||||
{ | { | ||||
SerializeCommon(serialize); | SerializeCommon(serialize); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | void UpdateMovementState(entity_pos_t speed) | ||||
// Speed change, update the visual actor if necessary. | // Speed change, update the visual actor if necessary. | ||||
else if (speed != m_CurSpeed && cmpVisual) | else if (speed != m_CurSpeed && cmpVisual) | ||||
cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", m_Speed); | cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", m_Speed); | ||||
m_CurSpeed = speed; | m_CurSpeed = speed; | ||||
} | } | ||||
/** | /** | ||||
* Increment the number of failed path and notify other components if required. | |||||
*/ | |||||
void IncrementFailedPathComputationAndMaybeNotify() | |||||
{ | |||||
m_FailedPathComputations++; | |||||
if (m_FailedPathComputations >= MAX_FAILED_PATH_COMPUTATIONS) | |||||
{ | |||||
MoveFailed(); | |||||
m_FailedPathComputations = 0; | |||||
} | |||||
} | |||||
/** | |||||
* Handle the result of an asynchronous path query. | * Handle the result of an asynchronous path query. | ||||
*/ | */ | ||||
void PathResult(u32 ticket, const WaypointPath& path); | void PathResult(u32 ticket, const WaypointPath& path); | ||||
/** | /** | ||||
* Do the per-turn movement and other updates. | * Do the per-turn movement and other updates. | ||||
*/ | */ | ||||
void Move(fixed dt); | void Move(fixed dt); | ||||
▲ Show 20 Lines • Show All 50 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) const; | bool PathingUpdateNeeded(const CFixedVector2D& from) const; | ||||
/** | /** | ||||
* Returns whether we are close enough to the target to assume it's a good enough | |||||
* position to stop. | |||||
*/ | |||||
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 | ||||
*/ | */ | ||||
Show All 37 Lines | private: | ||||
void RenderSubmit(SceneCollector& collector); | void RenderSubmit(SceneCollector& collector); | ||||
}; | }; | ||||
REGISTER_COMPONENT_TYPE(UnitMotion) | REGISTER_COMPONENT_TYPE(UnitMotion) | ||||
void CCmpUnitMotion::PathResult(u32 ticket, const WaypointPath& path) | void CCmpUnitMotion::PathResult(u32 ticket, const WaypointPath& path) | ||||
{ | { | ||||
// Ignore obsolete path requests | // Ignore obsolete path requests | ||||
if (ticket != m_ExpectedPathTicket.m_Ticket) | if (ticket != m_ExpectedPathTicket.m_Ticket || m_MoveRequest.m_Type == MoveRequest::NONE) | ||||
return; | return; | ||||
Ticket::Type ticketType = m_ExpectedPathTicket.m_Type; | Ticket::Type ticketType = m_ExpectedPathTicket.m_Type; | ||||
m_ExpectedPathTicket.clear(); | m_ExpectedPathTicket.clear(); | ||||
// Check that we are still able to do something with that path | // Check that we are still able to do something with that path | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
{ | { | ||||
// We will probably fail to move so inform components but keep on trying anyways. | // We will probably fail to move so inform components but keep on trying anyways. | ||||
MoveFailed(); | MoveFailed(); | ||||
return; | return; | ||||
} | } | ||||
if (ticketType == Ticket::LONG_PATH) | if (ticketType == Ticket::LONG_PATH) | ||||
{ | { | ||||
m_LongPath = path; | m_LongPath = path; | ||||
// If there's no waypoints then we couldn't get near the target. | // If there's no waypoints then we couldn't get near the target. | ||||
// Sort of hack: Just try going directly to the goal point instead | // Sort of hack: Just try going directly to the goal point instead | ||||
// (via the short pathfinder), so if we're stuck and the user clicks | // (via the short pathfinder over the next turns), so if we're stuck and the user clicks | ||||
// close enough to the unit then we can probably get unstuck | // close enough to the unit then we can probably get unstuck | ||||
// NB: this relies on HandleObstructedMove requesting short paths if we still have long waypoints. | |||||
if (m_LongPath.m_Waypoints.empty()) | if (m_LongPath.m_Waypoints.empty()) | ||||
{ | { | ||||
IncrementFailedPathComputationAndMaybeNotify(); | |||||
CFixedVector2D targetPos; | CFixedVector2D targetPos; | ||||
if (ComputeTargetPosition(targetPos)) | if (ComputeTargetPosition(targetPos)) | ||||
m_LongPath.m_Waypoints.emplace_back(Waypoint{ targetPos.X, targetPos.Y }); | m_LongPath.m_Waypoints.emplace_back(Waypoint{ targetPos.X, targetPos.Y }); | ||||
} | } | ||||
return; | |||||
} | } | ||||
else | |||||
{ | |||||
m_ShortPath = path; | |||||
// If there's no waypoints then we couldn't get near the target | m_ShortPath = 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 (e.g. by idle entities) | |||||
// If not, and we are not in a formation, retry | |||||
// unless we are close to our target and we don't have a target entity. | |||||
// This makes sure that units don't clump too much when they are not in a formation and tasked to move. | |||||
if (m_LongPath.m_Waypoints.size() > 1) | |||||
m_LongPath.m_Waypoints.pop_back(); | |||||
CMessageMotionChanged msg(false); | |||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg); | |||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | if (!m_ShortPath.m_Waypoints.empty()) | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return; | return; | ||||
// Don't notify if we are a formation member - we can occasionally be stuck for a long time | |||||
// if our current offset is unreachable. | |||||
if (!IsFormationMember()) | |||||
IncrementFailedPathComputationAndMaybeNotify(); | |||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
if (CloseEnoughFromDestinationToStop(pos)) | // If there's no waypoints then we couldn't get near the target | ||||
// If we're globally following a long path, try to remove the next waypoint, | |||||
// it might be obstructed (e.g. by idle entities which the long-range pathfinder doesn't see). | |||||
if (!m_LongPath.m_Waypoints.empty()) | |||||
{ | { | ||||
MoveSucceeded(); | m_LongPath.m_Waypoints.pop_back(); | ||||
if (!m_LongPath.m_Waypoints.empty()) | |||||
{ | |||||
PathGoal goal = { PathGoal::POINT, m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z }; | |||||
RequestShortPath(pos, goal, false); | |||||
return; | return; | ||||
} | } | ||||
} | |||||
PathGoal goal; | PathGoal goal; | ||||
if (ComputeGoal(goal, m_MoveRequest)) | // If we can't compute a goal, we'll fail in the next Move() call so do nothing special. | ||||
RequestLongPath(pos, goal); | if (!ComputeGoal(goal, m_MoveRequest)) | ||||
return; | return; | ||||
} | BeginPathing(pos, goal); | ||||
// else we could, so reset our number of tries. | |||||
m_Tries = 0; | |||||
} | |||||
} | } | ||||
void CCmpUnitMotion::Move(fixed dt) | void CCmpUnitMotion::Move(fixed dt) | ||||
{ | { | ||||
PROFILE("Move"); | PROFILE("Move"); | ||||
// If we were idle and will still be, we can return. | // If we were idle and will still be, we can return. | ||||
// TODO: this will need to be removed if pushing is implemented. | // TODO: this will need to be removed if pushing is implemented. | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | else | ||||
cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle); | cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle); | ||||
// Calculate the mean speed over this past turn. | // Calculate the mean speed over this past turn. | ||||
UpdateMovementState(offset.Length() / dt); | UpdateMovementState(offset.Length() / dt); | ||||
} | } | ||||
if (wasObstructed && HandleObstructedMove()) | if (wasObstructed && HandleObstructedMove()) | ||||
return; | return; | ||||
else if (!wasObstructed) | |||||
m_FailedPathComputations = 0; | |||||
// We may need to recompute our path sometimes (e.g. if our target moves). | // We may need to recompute our path sometimes (e.g. if our target moves). | ||||
// Since we request paths asynchronously anyways, this does not need to be done before moving. | // Since we request paths asynchronously anyways, this does not need to be done before moving. | ||||
if (PathingUpdateNeeded(pos)) | if (PathingUpdateNeeded(pos)) | ||||
{ | { | ||||
PathGoal goal; | PathGoal goal; | ||||
if (ComputeGoal(goal, m_MoveRequest)) | if (ComputeGoal(goal, m_MoveRequest)) | ||||
BeginPathing(pos, goal); | BeginPathing(pos, goal); | ||||
Show All 23 Lines | if (m_MoveRequest.m_Type == MoveRequest::OFFSET) | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
return cmpObstructionManager->IsInPointRange(GetEntityId(), targetPos.X, targetPos.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false); | return cmpObstructionManager->IsInPointRange(GetEntityId(), targetPos.X, targetPos.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const | bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const | ||||
{ | { | ||||
// If there are no waypoint, behave as though we were obstructed and let HandleObstructedMove handle it. | |||||
if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | ||||
return false; | return true; | ||||
// TODO: there's some asymmetry here when units look at other | // TODO: there's some asymmetry here when units look at other | ||||
// units' positions - the result will depend on the order of execution. | // units' positions - the result will depend on the order of execution. | ||||
// Maybe we should split the updates into multiple phases to minimise | // Maybe we should split the updates into multiple phases to minimise | ||||
// that problem. | // that problem. | ||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||
if (!cmpPathfinder) | ENSURE(cmpPathfinder); | ||||
return false; | |||||
fixed basicSpeed = m_Speed; | fixed basicSpeed = m_Speed; | ||||
// If in formation, run to keep up; otherwise just walk | // If in formation, run to keep up; otherwise just walk | ||||
if (IsFormationMember()) | if (IsFormationMember()) | ||||
basicSpeed = m_Speed.Multiply(m_RunMultiplier); | basicSpeed = m_Speed.Multiply(m_RunMultiplier); | ||||
// Find the speed factor of the underlying terrain | // Find the speed factor of the underlying terrain | ||||
// (We only care about the tile we start on - it doesn't matter if we're moving | // (We only care about the tile we start on - it doesn't matter if we're moving | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::HandleObstructedMove() | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return false; | return false; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
// Oops, we hit something (very likely another unit). | // Oops, we hit something (very likely another unit). | ||||
if (CloseEnoughFromDestinationToStop(pos)) | |||||
{ | |||||
// Pretend we're arrived in case other components agree and we end up stopping moving. | |||||
MoveSucceeded(); | |||||
return true; | |||||
} | |||||
// If we still have long waypoints, try and compute a short path | // If we still have long waypoints, try and compute a short path | ||||
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, false); | ||||
return true; | return true; | ||||
} | } | ||||
// Else, just entirely recompute | // Else, just entirely recompute | ||||
PathGoal goal; | PathGoal goal; | ||||
if (!ComputeGoal(goal, m_MoveRequest)) | if (!ComputeGoal(goal, m_MoveRequest)) | ||||
return false; | return false; | ||||
BeginPathing(pos, goal); | BeginPathing(pos, goal); | ||||
// 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. | ||||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::PathingUpdateNeeded(const CFixedVector2D& from) const | ||||
if (cmpObstructionManager->AreShapesInRange(shape, estimatedTargetShape, | if (cmpObstructionManager->AreShapesInRange(shape, estimatedTargetShape, | ||||
m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false)) | m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false)) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::CloseEnoughFromDestinationToStop(const CFixedVector2D& from) const | |||||
{ | |||||
if (m_MoveRequest.m_Type != MoveRequest::POINT) | |||||
return false; | |||||
CFixedVector2D targetPos; | |||||
if (!ComputeTargetPosition(targetPos)) | |||||
return true; // We failed to compute a position so we'll stop anyways. | |||||
return (from - targetPos).CompareLength(SHORT_PATH_GOAL_RADIUS) <= 0; | |||||
} | |||||
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 | ||||
{ | { | ||||
CFixedVector2D prev = from; | CFixedVector2D prev = from; | ||||
entity_pos_t distLeft = minDistance; | entity_pos_t distLeft = minDistance; | ||||
for (ssize_t i = (ssize_t)path.m_Waypoints.size()-1; i >= 0; --i) | for (ssize_t i = (ssize_t)path.m_Waypoints.size()-1; i >= 0; --i) | ||||
{ | { | ||||
// Check if the next path segment is longer than the requested minimum | // Check if the next path segment is longer than the requested minimum | ||||
▲ Show 20 Lines • Show All 219 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
void CCmpUnitMotion::RequestShortPath(const CFixedVector2D &from, const PathGoal& goal, bool avoidMovingUnits) | void CCmpUnitMotion::RequestShortPath(const CFixedVector2D &from, const PathGoal& goal, bool avoidMovingUnits) | ||||
{ | { | ||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||
if (!cmpPathfinder) | if (!cmpPathfinder) | ||||
return; | return; | ||||
// wrapping around on m_Tries isn't really a problem so don't check for overflow. | fixed searchRange = std::max(SHORT_PATH_MIN_SEARCH_RANGE * (m_FailedPathComputations + 1), goal.DistanceToPoint(from)); | ||||
fixed searchRange = std::max(SHORT_PATH_MIN_SEARCH_RANGE * ++m_Tries, goal.DistanceToPoint(from)); | |||||
if (goal.type != PathGoal::POINT && searchRange < goal.hw && searchRange < SHORT_PATH_MIN_SEARCH_RANGE * 2) | if (goal.type != PathGoal::POINT && searchRange < goal.hw && searchRange < SHORT_PATH_MIN_SEARCH_RANGE * 2) | ||||
searchRange = std::min(goal.hw, SHORT_PATH_MIN_SEARCH_RANGE * 2); | searchRange = std::min(goal.hw, SHORT_PATH_MIN_SEARCH_RANGE * 2); | ||||
if (searchRange > SHORT_PATH_MAX_SEARCH_RANGE) | if (searchRange > SHORT_PATH_MAX_SEARCH_RANGE) | ||||
searchRange = SHORT_PATH_MAX_SEARCH_RANGE; | searchRange = SHORT_PATH_MAX_SEARCH_RANGE; | ||||
m_ExpectedPathTicket.m_Type = Ticket::SHORT_PATH; | m_ExpectedPathTicket.m_Type = Ticket::SHORT_PATH; | ||||
m_ExpectedPathTicket.m_Ticket = cmpPathfinder->ComputeShortPathAsync(from.X, from.Y, m_Clearance, searchRange, goal, m_PassClass, avoidMovingUnits, GetGroup(), GetEntityId()); | m_ExpectedPathTicket.m_Ticket = cmpPathfinder->ComputeShortPathAsync(from.X, from.Y, m_Clearance, searchRange, goal, m_PassClass, avoidMovingUnits, GetGroup(), GetEntityId()); | ||||
} | } | ||||
bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) | bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) | ||||
{ | { | ||||
PROFILE("MoveToPointRange"); | PROFILE("MoveToPointRange"); | ||||
MoveRequest moveRequest(CFixedVector2D(x, z), minRange, maxRange); | MoveRequest moveRequest(CFixedVector2D(x, z), minRange, maxRange); | ||||
PathGoal goal; | PathGoal goal; | ||||
if (!ComputeGoal(goal, moveRequest)) | if (!ComputeGoal(goal, moveRequest)) | ||||
return false; | return false; | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return false; | return false; | ||||
m_MoveRequest = moveRequest; | m_MoveRequest = moveRequest; | ||||
m_Tries = 0; | m_FailedPathComputations = 0; | ||||
BeginPathing(cmpPosition->GetPosition2D(), goal); | BeginPathing(cmpPosition->GetPosition2D(), goal); | ||||
return true; | return true; | ||||
} | } | ||||
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"); | ||||
MoveRequest moveRequest(target, minRange, maxRange); | MoveRequest moveRequest(target, minRange, maxRange); | ||||
PathGoal goal; | PathGoal goal; | ||||
if (!ComputeGoal(goal, moveRequest)) | if (!ComputeGoal(goal, moveRequest)) | ||||
return false; | return false; | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return false; | return false; | ||||
m_MoveRequest = moveRequest; | m_MoveRequest = moveRequest; | ||||
m_Tries = 0; | m_FailedPathComputations = 0; | ||||
BeginPathing(cmpPosition->GetPosition2D(), goal); | BeginPathing(cmpPosition->GetPosition2D(), goal); | ||||
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) | ||||
{ | { | ||||
MoveRequest moveRequest(target, CFixedVector2D(x, z)); | MoveRequest moveRequest(target, CFixedVector2D(x, z)); | ||||
PathGoal goal; | PathGoal goal; | ||||
if (!ComputeGoal(goal, moveRequest)) | if (!ComputeGoal(goal, moveRequest)) | ||||
return; | return; | ||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target); | CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return; | return; | ||||
m_MoveRequest = moveRequest; | m_MoveRequest = moveRequest; | ||||
m_Tries = 0; | m_FailedPathComputations = 0; | ||||
BeginPathing(cmpPosition->GetPosition2D(), goal); | BeginPathing(cmpPosition->GetPosition2D(), goal); | ||||
} | } | ||||
void CCmpUnitMotion::RenderPath(const WaypointPath& path, std::vector<SOverlayLine>& lines, CColor color) | void CCmpUnitMotion::RenderPath(const WaypointPath& path, std::vector<SOverlayLine>& lines, CColor color) | ||||
{ | { | ||||
bool floating = false; | bool floating = false; | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (cmpPosition) | if (cmpPosition) | ||||
floating = cmpPosition->CanFloat(); | floating = cmpPosition->CanFloat(); | ||||
lines.clear(); | lines.clear(); | ||||
Show All 35 Lines |
Wildfire Games · Phabricator
vs2013 serialisation out of bounds
pyrogenesis.exe!ISerializer::NumberU8(const char * name, unsigned char value, unsigned char lower, unsigned char upper) Line 31 at path\source\simulation2\serialization\iserializer.cpp(31)