Changeset View
Standalone View
source/simulation2/components/CCmpUnitMotion.cpp
Show First 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | public: | ||||||||
bool m_DebugOverlayEnabled; | bool m_DebugOverlayEnabled; | ||||||||
std::vector<SOverlayLine> m_DebugOverlayLongPathLines; | std::vector<SOverlayLine> m_DebugOverlayLongPathLines; | ||||||||
std::vector<SOverlayLine> m_DebugOverlayShortPathLines; | std::vector<SOverlayLine> m_DebugOverlayShortPathLines; | ||||||||
// Template state: | // Template state: | ||||||||
bool m_FormationController; | bool m_FormationController; | ||||||||
fixed m_TemplateWalkSpeed, m_TemplateRunMultiplier; | fixed m_TemplateWalkSpeed, m_TemplateRunMultiplier, m_TemplateAcceleration; | ||||||||
pass_class_t m_PassClass; | pass_class_t m_PassClass; | ||||||||
std::string m_PassClassName; | std::string m_PassClassName; | ||||||||
// Dynamic state: | // Dynamic state: | ||||||||
entity_pos_t m_Clearance; | entity_pos_t m_Clearance; | ||||||||
// cached for efficiency | // cached for efficiency | ||||||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | public: | ||||||||
// If the entity moves, it will do so at m_WalkSpeed * m_SpeedMultiplier. | // If the entity moves, it will do so at m_WalkSpeed * m_SpeedMultiplier. | ||||||||
fixed m_SpeedMultiplier; | fixed m_SpeedMultiplier; | ||||||||
// This caches the resulting speed from m_WalkSpeed * m_SpeedMultiplier for convenience. | // This caches the resulting speed from m_WalkSpeed * m_SpeedMultiplier for convenience. | ||||||||
fixed m_Speed; | fixed m_Speed; | ||||||||
// Current mean speed (over the last turn). | // Current mean speed (over the last turn). | ||||||||
fixed m_CurSpeed; | fixed m_CurSpeed; | ||||||||
// The real current speed (at the end of last turn). Only zero if don't move last and this turn. | |||||||||
FreagarachUnsubmitted Not Done Inline Actions
Freagarach: | |||||||||
fixed m_RealCurSpeed; | |||||||||
Not Done Inline ActionsHaving CurSpeed and RealCurSpeed seems misleading to me. Because they both are real speed, but maybe in different time points. I think they should have different names and a more detailed comment. vladislavbelov: Having `CurSpeed` and `RealCurSpeed` seems misleading to me. Because they both are real speed… | |||||||||
fixed m_Acceleration; | |||||||||
Not Done Inline ActionsThe acceleration breaks path finder more, since the acceleration isn't accounted. vladislavbelov: The acceleration breaks path finder more, since the acceleration isn't accounted. | |||||||||
Done Inline ActionsOnly short range pathfinder collision tests will be less accurate. Else doesn't seem affected. bb: Only short range pathfinder collision tests will be less accurate. Else doesn't seem affected. | |||||||||
// 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; | ||||||||
static std::string GetSchema() | static std::string GetSchema() | ||||||||
{ | { | ||||||||
Show All 9 Lines | return | ||||||||
"<element name='WalkSpeed' a:help='Basic movement speed (in metres per second)'>" | "<element name='WalkSpeed' a:help='Basic movement speed (in metres per second)'>" | ||||||||
"<ref name='positiveDecimal'/>" | "<ref name='positiveDecimal'/>" | ||||||||
"</element>" | "</element>" | ||||||||
"<optional>" | "<optional>" | ||||||||
"<element name='RunMultiplier' a:help='How much faster the unit goes when running (as a multiple of walk speed)'>" | "<element name='RunMultiplier' a:help='How much faster the unit goes when running (as a multiple of walk speed)'>" | ||||||||
"<ref name='positiveDecimal'/>" | "<ref name='positiveDecimal'/>" | ||||||||
"</element>" | "</element>" | ||||||||
"</optional>" | "</optional>" | ||||||||
"<element name='Acceleration' a:help='Acceleration (in metres per second^2)'>" | |||||||||
"<ref name='positiveDecimal'/>" | |||||||||
"</element>" | |||||||||
"<element name='PassabilityClass' a:help='Identifies the terrain passability class (values are defined in special/pathfinder.xml)'>" | "<element name='PassabilityClass' a:help='Identifies the terrain passability class (values are defined in special/pathfinder.xml)'>" | ||||||||
"<text/>" | "<text/>" | ||||||||
"</element>"; | "</element>"; | ||||||||
} | } | ||||||||
virtual void Init(const CParamNode& paramNode) | virtual void Init(const CParamNode& paramNode) | ||||||||
{ | { | ||||||||
m_FormationController = paramNode.GetChild("FormationController").ToBool(); | m_FormationController = paramNode.GetChild("FormationController").ToBool(); | ||||||||
m_FacePointAfterMove = true; | m_FacePointAfterMove = true; | ||||||||
m_WalkSpeed = m_TemplateWalkSpeed = m_Speed = paramNode.GetChild("WalkSpeed").ToFixed(); | m_WalkSpeed = m_TemplateWalkSpeed = m_Speed = paramNode.GetChild("WalkSpeed").ToFixed(); | ||||||||
m_SpeedMultiplier = fixed::FromInt(1); | m_SpeedMultiplier = fixed::FromInt(1); | ||||||||
m_CurSpeed = fixed::Zero(); | m_CurSpeed = fixed::Zero(); | ||||||||
m_RealCurSpeed = fixed::Zero(); | |||||||||
m_RunMultiplier = m_TemplateRunMultiplier = fixed::FromInt(1); | m_RunMultiplier = m_TemplateRunMultiplier = fixed::FromInt(1); | ||||||||
if (paramNode.GetChild("RunMultiplier").IsOk()) | if (paramNode.GetChild("RunMultiplier").IsOk()) | ||||||||
m_RunMultiplier = m_TemplateRunMultiplier = paramNode.GetChild("RunMultiplier").ToFixed(); | m_RunMultiplier = m_TemplateRunMultiplier = paramNode.GetChild("RunMultiplier").ToFixed(); | ||||||||
m_Acceleration = m_TemplateAcceleration = paramNode.GetChild("Acceleration").ToFixed(); | |||||||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||||||
if (cmpPathfinder) | if (cmpPathfinder) | ||||||||
{ | { | ||||||||
m_PassClassName = paramNode.GetChild("PassabilityClass").ToUTF8(); | m_PassClassName = paramNode.GetChild("PassabilityClass").ToUTF8(); | ||||||||
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()); | ||||||||
Show All 24 Lines | void SerializeCommon(S& serialize) | ||||||||
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.NumberFixed_Unbounded("current speed", m_RealCurSpeed); | |||||||||
FreagarachUnsubmitted Not Done Inline ActionsIs this (the duplication) on purpose? Freagarach: Is this (the duplication) on purpose? | |||||||||
bbAuthorUnsubmitted Done Inline ActionsDon't see the duplication m_CurSpeed and m_RealCurSpeed are different values and since the both change the simstate, they both need to be serialized. bb: Don't see the duplication m_CurSpeed and m_RealCurSpeed are different values and since the both… | |||||||||
serialize.NumberFixed_Unbounded("acceleration", m_Acceleration); | |||||||||
serialize.Bool("facePointAfterMove", m_FacePointAfterMove); | serialize.Bool("facePointAfterMove", m_FacePointAfterMove); | ||||||||
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) | ||||||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | case MT_PathResult: | ||||||||
PathResult(msgData.ticket, msgData.path); | PathResult(msgData.ticket, msgData.path); | ||||||||
break; | break; | ||||||||
} | } | ||||||||
case MT_ValueModification: | case MT_ValueModification: | ||||||||
{ | { | ||||||||
const CMessageValueModification& msgData = static_cast<const CMessageValueModification&> (msg); | const CMessageValueModification& msgData = static_cast<const CMessageValueModification&> (msg); | ||||||||
if (msgData.component != L"UnitMotion") | if (msgData.component != L"UnitMotion") | ||||||||
break; | break; | ||||||||
bbAuthorUnsubmitted Done Inline Actionsnuke bb: nuke | |||||||||
FALLTHROUGH; | FALLTHROUGH; | ||||||||
} | } | ||||||||
case MT_OwnershipChanged: | case MT_OwnershipChanged: | ||||||||
case MT_Deserialized: | case MT_Deserialized: | ||||||||
{ | { | ||||||||
CmpPtr<ICmpValueModificationManager> cmpValueModificationManager(GetSystemEntity()); | CmpPtr<ICmpValueModificationManager> cmpValueModificationManager(GetSystemEntity()); | ||||||||
if (!cmpValueModificationManager) | if (!cmpValueModificationManager) | ||||||||
break; | break; | ||||||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | virtual fixed GetWalkSpeed() const | ||||||||
return m_WalkSpeed; | return m_WalkSpeed; | ||||||||
} | } | ||||||||
virtual fixed GetRunMultiplier() const | virtual fixed GetRunMultiplier() const | ||||||||
{ | { | ||||||||
return m_RunMultiplier; | return m_RunMultiplier; | ||||||||
} | } | ||||||||
virtual fixed GetAcceleration() const | |||||||||
{ | |||||||||
return m_Acceleration; | |||||||||
} | |||||||||
virtual void SetAcceleration(fixed acceleration) | |||||||||
{ | |||||||||
m_Acceleration = acceleration; | |||||||||
} | |||||||||
bbAuthorUnsubmitted Done Inline ActionsunitMotionFlying needs same update bb: unitMotionFlying needs same update | |||||||||
virtual pass_class_t GetPassabilityClass() const | virtual pass_class_t GetPassabilityClass() const | ||||||||
{ | { | ||||||||
return m_PassClass; | return m_PassClass; | ||||||||
} | } | ||||||||
virtual std::string GetPassabilityClassName() const | virtual std::string GetPassabilityClassName() const | ||||||||
{ | { | ||||||||
return m_PassClassName; | return m_PassClassName; | ||||||||
▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | private: | ||||||||
*/ | */ | ||||||||
bool PossiblyAtDestination() const; | bool PossiblyAtDestination() const; | ||||||||
/** | /** | ||||||||
* Process the move the unit will do this turn. | * Process the move the unit will do this turn. | ||||||||
* This does not send actually change the position. | * This does not send actually change the position. | ||||||||
* @returns true if the move was obstructed. | * @returns true if the move was obstructed. | ||||||||
*/ | */ | ||||||||
bool PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const; | bool PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed,entity_angle_t angle) const; | ||||||||
/** | /** | ||||||||
* Update other components on our speed. | * Update other components on our speed. | ||||||||
* (For performance, this should try to avoid sending messages). | * (For performance, this should try to avoid sending messages). | ||||||||
*/ | */ | ||||||||
void UpdateMovementState(entity_pos_t speed); | void UpdateMovementState(entity_pos_t speed); | ||||||||
/** | /** | ||||||||
▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | |||||||||
} | } | ||||||||
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. | ||||||||
if (m_CurSpeed == fixed::Zero() && m_MoveRequest.m_Type == MoveRequest::NONE) | if (m_RealCurSpeed == fixed::Zero() && m_CurSpeed == fixed::Zero() && m_MoveRequest.m_Type == MoveRequest::NONE) | ||||||||
return; | return; | ||||||||
if (PossiblyAtDestination()) | if (PossiblyAtDestination()) | ||||||||
MoveSucceeded(); | MoveSucceeded(); | ||||||||
else if (!TargetHasValidPosition()) | else if (!TargetHasValidPosition()) | ||||||||
{ | { | ||||||||
// Scrap waypoints - we don't know where to go. | // Scrap waypoints - we don't know where to go. | ||||||||
// If the move request remains unchanged and the target again has a valid position later on, | // If the move request remains unchanged and the target again has a valid position later on, | ||||||||
Show All 15 Lines | void CCmpUnitMotion::Move(fixed dt) | ||||||||
// Keep track of the current unit's position during the update | // Keep track of the current unit's position during the update | ||||||||
CFixedVector2D pos = initialPos; | CFixedVector2D pos = initialPos; | ||||||||
// If we're chasing a potentially-moving unit and are currently close | // If we're chasing a potentially-moving unit and are currently close | ||||||||
// enough to its current position, and we can head in a straight line | // enough to its current position, and we can head in a straight line | ||||||||
// to it, then throw away our current path and go straight to it | // to it, then throw away our current path and go straight to it | ||||||||
bool wentStraight = TryGoingStraightToTarget(initialPos); | bool wentStraight = TryGoingStraightToTarget(initialPos); | ||||||||
bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos); | bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos, m_RealCurSpeed, cmpPosition->GetRotation().Y); | ||||||||
// Update our speed over this turn so that the visual actor shows the correct animation. | // Update our speed over this turn so that the visual actor shows the correct animation. | ||||||||
if (pos == initialPos) | if (pos == initialPos) | ||||||||
UpdateMovementState(fixed::Zero()); | UpdateMovementState(fixed::Zero()); | ||||||||
else | else | ||||||||
{ | { | ||||||||
// Update the Position component after our movement (if we actually moved anywhere) | // Update the Position component after our movement (if we actually moved anywhere) | ||||||||
CFixedVector2D offset = pos - initialPos; | CFixedVector2D offset = pos - initialPos; | ||||||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | if (m_MoveRequest.m_Type == MoveRequest::OFFSET) | ||||||||
CFixedVector2D targetPos; | CFixedVector2D targetPos; | ||||||||
ComputeTargetPosition(targetPos); | ComputeTargetPosition(targetPos); | ||||||||
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, fixed& speed, entity_angle_t angle) const | ||||||||
{ | { | ||||||||
// If there are no waypoint, behave as though we were obstructed and let HandleObstructedMove handle it. | // 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 true; | 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 | ||||||||
Show All 10 Lines | bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed, entity_angle_t angle) const | ||||||||
// 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 | ||||||||
// partially onto a much slower/faster tile) | // partially onto a much slower/faster tile) | ||||||||
// TODO: Terrain-dependent speeds are not currently supported | // TODO: Terrain-dependent speeds are not currently supported | ||||||||
fixed terrainSpeed = fixed::FromInt(1); | fixed terrainSpeed = fixed::FromInt(1); | ||||||||
fixed maxSpeed = basicSpeed.Multiply(terrainSpeed); | fixed maxSpeed = basicSpeed.Multiply(terrainSpeed); | ||||||||
// We want to move (at most) maxSpeed*dt units from pos towards the next waypoint | // We want to move (at most) std::min(maxSpeed * dt, nowSpeed * dt + 1/2 * acceleration * dt^2) units from pos towards the next waypoint | ||||||||
fixed timeLeft = dt; | fixed timeLeft = dt; | ||||||||
fixed zero = fixed::Zero(); | fixed zero = fixed::Zero(); | ||||||||
// Take the current rotation as the previous direction, slight TODO, might need to be the previous angle. | |||||||||
fixed sin, cos; | |||||||||
sincos_approx(angle, sin, cos); | |||||||||
CFixedVector2D prevOffset(sin, cos); | |||||||||
while (timeLeft > zero) | while (timeLeft > zero) | ||||||||
{ | { | ||||||||
// If we ran out of path, we have to stop | // If we ran out of path, we have to stop | ||||||||
if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | ||||||||
break; | break; | ||||||||
CFixedVector2D target; | CFixedVector2D target; | ||||||||
if (shortPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty()) | ||||||||
target = CFixedVector2D(longPath.m_Waypoints.back().x, longPath.m_Waypoints.back().z); | target = CFixedVector2D(longPath.m_Waypoints.back().x, longPath.m_Waypoints.back().z); | ||||||||
else | else | ||||||||
target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z); | target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z); | ||||||||
CFixedVector2D offset = target - pos; | CFixedVector2D offset = target - pos; | ||||||||
// Work out how far we can travel in timeLeft | // Modify the speed depending on the angle difference between previous and next offset. | ||||||||
fixed maxdist = maxSpeed.Multiply(timeLeft); | sincos_approx(atan2_approx(offset.X, offset.Y) / 2 - atan2_approx(prevOffset.X, prevOffset.Y) / 2, sin, cos); | ||||||||
speed = speed.Multiply(cos); | |||||||||
// Work out how far we can travel in timeLeft. | |||||||||
fixed maxdist = std::min(maxSpeed, speed + m_Acceleration.Multiply(dt) / 2).Multiply(timeLeft); | |||||||||
Not Done Inline ActionsDividing by 2 seems incorrect to me, since accelerations means how the speed is growing (m/s per second = m/s^2). vladislavbelov: Dividing by 2 seems incorrect to me, since accelerations means how the speed is growing (m/s… | |||||||||
Done Inline ActionsAhhaa, can explain some classical mechanics here: I hope you argree that a=d^2/dt^2x(t) (acceleration being the second derivative of the position of the unit). The general solution to this differential equation is x(t)=1/2at^2+v_0t+x_0 (where v_0 and x_0 are the initial position and velocity). Assuming x_0=0 (since we are interested in a difference between two points, not the global position), we see dist=1/2at^2+v_0t. Which is the formula used. bb: Ahhaa, can explain some classical mechanics here: I hope you argree that a=d^2/dt^2x(t)… | |||||||||
Not Done Inline ActionsI hold a B.Sc. in physics so I may be helpful here... All of the formulas you have stated here seem to be correct. If we want to calculate the distance x that is covered, we must use x(t) = 1/2*at^2 (+v_0*t (+x_0)). (A) accelerating to v_max with a>0 (B) moving constantly with v_max and a=0 (C) combine In order to reduce the steps, you can also calculate (1), (3) and then (5) where you insert (2) and (4) - however that will make readability much worse... Palaxin: I hold a B.Sc. in physics so I may be helpful here... All of the formulas you have stated here… | |||||||||
Not Done Inline ActionsSorry, on a quick look I interpreted your first sentence as a question :/ Palaxin: Sorry, on a quick look I interpreted your first sentence as a question :/
So actually you… | |||||||||
Not Done Inline ActionsAn interesting read nevertheless :) Freagarach: An interesting read nevertheless :) | |||||||||
Not Done Inline Actionsthx! Palaxin: thx!
For the sake of correctness, in (1) it should read a = dv/dt_1 --> ... --> dt_1 = (v_max… | |||||||||
Done Inline ActionsYou indeed found a mistake: using dt is inherently wrong (since it has no meaning inside the loop) and I wasn't capping the speed correctly at maxSpeed. FYI my physics should be enough (master student), maybe not at 3AM when I wrote the patch though... bb: You indeed found a mistake: using dt is inherently wrong (since it has no meaning inside the… | |||||||||
// If the target is close, we can move there directly | // If the target is close, we can move there directly. | ||||||||
fixed offsetLength = offset.Length(); | fixed offsetLength = offset.Length(); | ||||||||
if (offsetLength <= maxdist) | if (offsetLength <= maxdist) | ||||||||
{ | { | ||||||||
if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | ||||||||
{ | { | ||||||||
prevOffset = offset; | |||||||||
pos = target; | pos = target; | ||||||||
// Spend the rest of the time heading towards the next waypoint | // Spend the rest of the time heading towards the next waypoint | ||||||||
timeLeft = (maxdist - offsetLength) / maxSpeed; | timeLeft = (maxdist - offsetLength) / maxSpeed; | ||||||||
speed = std::min(maxSpeed, speed + m_Acceleration.Multiply(dt - timeLeft)); | |||||||||
if (shortPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty()) | ||||||||
longPath.m_Waypoints.pop_back(); | longPath.m_Waypoints.pop_back(); | ||||||||
else | else | ||||||||
shortPath.m_Waypoints.pop_back(); | shortPath.m_Waypoints.pop_back(); | ||||||||
continue; | continue; | ||||||||
} | } | ||||||||
else | else | ||||||||
{ | { | ||||||||
// Error - path was obstructed | // Error - path was obstructed | ||||||||
return true; | return true; | ||||||||
} | } | ||||||||
} | } | ||||||||
else | else | ||||||||
{ | { | ||||||||
// Not close enough, so just move in the right direction | // Not close enough, so just move in the right direction | ||||||||
offset.Normalize(maxdist); | offset.Normalize(maxdist); | ||||||||
target = pos + offset; | target = pos + offset; | ||||||||
speed = std::min(maxSpeed, speed + m_Acceleration.Multiply(dt)); | |||||||||
if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | ||||||||
pos = target; | pos = target; | ||||||||
else | else | ||||||||
return true; | return true; | ||||||||
break; | break; | ||||||||
} | } | ||||||||
} | } | ||||||||
return false; | return false; | ||||||||
} | } | ||||||||
void CCmpUnitMotion::UpdateMovementState(entity_pos_t speed) | void CCmpUnitMotion::UpdateMovementState(entity_pos_t speed) | ||||||||
{ | { | ||||||||
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle()); | CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle()); | ||||||||
CmpPtr<ICmpVisual> cmpVisual(GetEntityHandle()); | CmpPtr<ICmpVisual> cmpVisual(GetEntityHandle()); | ||||||||
// Not moving this an last turn. | |||||||||
Not Done Inline Actions
Freagarach: | |||||||||
if (speed == fixed::Zero() && m_CurSpeed == fixed::Zero()) | |||||||||
m_RealCurSpeed = fixed::Zero(); | |||||||||
// Moved last turn, didn't this turn. | // Moved last turn, didn't this turn. | ||||||||
if (speed == fixed::Zero() && m_CurSpeed > fixed::Zero()) | else if (speed == fixed::Zero() && m_CurSpeed > fixed::Zero()) | ||||||||
{ | { | ||||||||
if (cmpObstruction) | if (cmpObstruction) | ||||||||
cmpObstruction->SetMovingFlag(false); | cmpObstruction->SetMovingFlag(false); | ||||||||
if (cmpVisual) | if (cmpVisual) | ||||||||
cmpVisual->SelectMovementAnimation("idle", fixed::FromInt(1)); | cmpVisual->SelectMovementAnimation("idle", fixed::FromInt(1)); | ||||||||
} | } | ||||||||
// Moved this turn, didn't last turn | // Moved this turn, didn't last turn | ||||||||
else if (speed > fixed::Zero() && m_CurSpeed == fixed::Zero()) | else if (speed > fixed::Zero() && m_CurSpeed == fixed::Zero()) | ||||||||
{ | { | ||||||||
if (cmpObstruction) | if (cmpObstruction) | ||||||||
cmpObstruction->SetMovingFlag(true); | cmpObstruction->SetMovingFlag(true); | ||||||||
if (cmpVisual) | if (cmpVisual) | ||||||||
cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", m_Speed); | cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", 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", speed); | ||||||||
bbAuthorUnsubmitted Done Inline ActionsThese changes need to happen anyhow, the bug only gets showcased by this patch bb: These changes need to happen anyhow, the bug only gets showcased by this patch | |||||||||
m_CurSpeed = speed; | m_CurSpeed = speed; | ||||||||
} | } | ||||||||
bool CCmpUnitMotion::HandleObstructedMove() | bool CCmpUnitMotion::HandleObstructedMove() | ||||||||
{ | { | ||||||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||||||
▲ Show 20 Lines • Show All 513 Lines • Show Last 20 Lines |