Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpUnitMotion.cpp
Show All 20 Lines | |||||
#include "ICmpUnitMotion.h" | #include "ICmpUnitMotion.h" | ||||
#include "simulation2/components/ICmpObstruction.h" | #include "simulation2/components/ICmpObstruction.h" | ||||
#include "simulation2/components/ICmpObstructionManager.h" | #include "simulation2/components/ICmpObstructionManager.h" | ||||
#include "simulation2/components/ICmpOwnership.h" | #include "simulation2/components/ICmpOwnership.h" | ||||
#include "simulation2/components/ICmpPosition.h" | #include "simulation2/components/ICmpPosition.h" | ||||
#include "simulation2/components/ICmpPathfinder.h" | #include "simulation2/components/ICmpPathfinder.h" | ||||
#include "simulation2/components/ICmpRangeManager.h" | #include "simulation2/components/ICmpRangeManager.h" | ||||
#include "simulation2/components/ICmpUnitMotionManager.h" | |||||
#include "simulation2/components/ICmpValueModificationManager.h" | #include "simulation2/components/ICmpValueModificationManager.h" | ||||
#include "simulation2/components/ICmpVisual.h" | #include "simulation2/components/ICmpVisual.h" | ||||
#include "simulation2/helpers/Geometry.h" | #include "simulation2/helpers/Geometry.h" | ||||
#include "simulation2/helpers/Render.h" | #include "simulation2/helpers/Render.h" | ||||
#include "simulation2/MessageTypes.h" | #include "simulation2/MessageTypes.h" | ||||
#include "simulation2/serialization/SerializeTemplates.h" | #include "simulation2/serialization/SerializeTemplates.h" | ||||
#include "graphics/Overlay.h" | #include "graphics/Overlay.h" | ||||
▲ Show 20 Lines • Show All 261 Lines • ▼ Show 20 Lines | virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) | ||||
if (cmpPathfinder) | if (cmpPathfinder) | ||||
m_PassClass = cmpPathfinder->GetPassabilityClass(m_PassClassName); | m_PassClass = cmpPathfinder->GetPassabilityClass(m_PassClassName); | ||||
} | } | ||||
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) | virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) | ||||
{ | { | ||||
switch (msg.GetType()) | switch (msg.GetType()) | ||||
{ | { | ||||
case MT_Update_MotionFormation: | |||||
{ | |||||
if (m_FormationController) | |||||
{ | |||||
fixed dt = static_cast<const CMessageUpdate_MotionFormation&> (msg).turnLength; | |||||
Move(dt); | |||||
} | |||||
break; | |||||
} | |||||
case MT_Update_MotionUnit: | |||||
{ | |||||
if (!m_FormationController) | |||||
{ | |||||
fixed dt = static_cast<const CMessageUpdate_MotionUnit&> (msg).turnLength; | |||||
Move(dt); | |||||
} | |||||
break; | |||||
} | |||||
case MT_RenderSubmit: | case MT_RenderSubmit: | ||||
{ | { | ||||
PROFILE("UnitMotion::RenderSubmit"); | PROFILE("UnitMotion::RenderSubmit"); | ||||
const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg); | const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg); | ||||
RenderSubmit(msgData.collector); | RenderSubmit(msgData.collector); | ||||
break; | break; | ||||
} | } | ||||
case MT_PathResult: | case MT_PathResult: | ||||
{ | { | ||||
const CMessagePathResult& msgData = static_cast<const CMessagePathResult&> (msg); | const CMessagePathResult& msgData = static_cast<const CMessagePathResult&> (msg); | ||||
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; | ||||
FALLTHROUGH; | |||||
CmpPtr<ICmpValueModificationManager> cmpValueModificationManager(GetSystemEntity()); | |||||
if (!cmpValueModificationManager) | |||||
break; | |||||
m_WalkSpeed = cmpValueModificationManager->ApplyModifications(L"UnitMotion/WalkSpeed", m_TemplateWalkSpeed, GetEntityId()); | |||||
m_RunMultiplier = cmpValueModificationManager->ApplyModifications(L"UnitMotion/RunMultiplier", m_TemplateRunMultiplier, GetEntityId()); | |||||
// For MT_Deserialize compute m_Speed from the serialized m_SpeedMultiplier. | |||||
// For MT_ValueModification and MT_OwnershipChanged, adjust m_SpeedMultiplier if needed | |||||
// (in case then new m_RunMultiplier value is lower than the old). | |||||
SetSpeedMultiplier(m_SpeedMultiplier); | |||||
break; | |||||
} | } | ||||
case MT_OwnershipChanged: | case MT_OwnershipChanged: | ||||
{ | |||||
const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg); | |||||
if (!ENTITY_IS_LOCAL(GetEntityId())) | |||||
{ | |||||
CmpPtr<ICmpUnitMotionManager> cmpUnitMotionManager(GetSystemEntity()); | |||||
if (msgData.from == INVALID_PLAYER && msgData.to != INVALID_PLAYER) | |||||
cmpUnitMotionManager->Register(GetEntityId(), m_FormationController); | |||||
else if (msgData.to == INVALID_PLAYER && msgData.from != INVALID_PLAYER) | |||||
cmpUnitMotionManager->Unregister(GetEntityId()); | |||||
} | |||||
FALLTHROUGH; | |||||
} | |||||
case MT_Deserialized: | case MT_Deserialized: | ||||
{ | { | ||||
CmpPtr<ICmpValueModificationManager> cmpValueModificationManager(GetSystemEntity()); | CmpPtr<ICmpValueModificationManager> cmpValueModificationManager(GetSystemEntity()); | ||||
if (!cmpValueModificationManager) | if (!cmpValueModificationManager) | ||||
break; | break; | ||||
m_WalkSpeed = cmpValueModificationManager->ApplyModifications(L"UnitMotion/WalkSpeed", m_TemplateWalkSpeed, GetEntityId()); | m_WalkSpeed = cmpValueModificationManager->ApplyModifications(L"UnitMotion/WalkSpeed", m_TemplateWalkSpeed, GetEntityId()); | ||||
m_RunMultiplier = cmpValueModificationManager->ApplyModifications(L"UnitMotion/RunMultiplier", m_TemplateRunMultiplier, GetEntityId()); | m_RunMultiplier = cmpValueModificationManager->ApplyModifications(L"UnitMotion/RunMultiplier", m_TemplateRunMultiplier, GetEntityId()); | ||||
▲ Show 20 Lines • Show All 244 Lines • ▼ Show 20 Lines | private: | ||||
/** | /** | ||||
* 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 PreMove(MotionState& state, fixed dt); | ||||
void Move(MotionState& state, fixed dt); | |||||
void PostMove(MotionState& state, fixed dt); | |||||
/** | /** | ||||
* Returns true if we are possibly at our destination. | * Returns true if we are possibly at our destination. | ||||
* Since the concept of being at destination is dependent on why the move was requested, | * Since the concept of being at destination is dependent on why the move was requested, | ||||
* UnitMotion can only ever hint about this, hence the conditional tone. | * UnitMotion can only ever hint about this, hence the conditional tone. | ||||
*/ | */ | ||||
bool PossiblyAtDestination() const; | bool PossiblyAtDestination() const; | ||||
▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | if (!m_LongPath.m_Waypoints.empty()) | ||||
RequestShortPath(pos, goal, true); | RequestShortPath(pos, goal, true); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
ComputePathToGoal(pos, goal); | ComputePathToGoal(pos, goal); | ||||
} | } | ||||
void CCmpUnitMotion::Move(fixed dt) | void CCmpUnitMotion::PreMove(MotionState& state, fixed UNUSED(dt)) | ||||
{ | { | ||||
PROFILE("Move"); | PROFILE("PreMove"); | ||||
// If we were idle and will still be, we can return. | |||||
// TODO: this will need to be removed if pushing is implemented. | |||||
if (m_CurSpeed == fixed::Zero() && m_MoveRequest.m_Type == MoveRequest::NONE) | |||||
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, | ||||
// moving will be resumed. | // moving will be resumed. | ||||
// Units may want to move to move to the target's last known position, | // Units may want to move to move to the target's last known position, | ||||
// but that should be decided by UnitAI (handling MoveFailed), not UnitMotion. | // but that should be decided by UnitAI (handling MoveFailed), not UnitMotion. | ||||
m_LongPath.m_Waypoints.clear(); | m_LongPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.clear(); | m_ShortPath.m_Waypoints.clear(); | ||||
MoveFailed(); | MoveFailed(); | ||||
} | } | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
state.cmpPosition = cmpPosition; | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return; | return; | ||||
CFixedVector2D initialPos = cmpPosition->GetPosition2D(); | |||||
// 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; | state.initialPos = cmpPosition->GetPosition2D(); | ||||
state.pos = state.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); | state.wentStraight = TryGoingStraightToTarget(state.initialPos); | ||||
} | |||||
bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos); | void CCmpUnitMotion::Move(MotionState& state, fixed dt) | ||||
{ | |||||
PROFILE("Move"); | |||||
state.wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, state.pos); | |||||
} | |||||
void CCmpUnitMotion::PostMove(MotionState& state, fixed dt) | |||||
{ | |||||
// 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 (state.pos == state.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 = state.pos - state.initialPos; | ||||
// Face towards the target | // Face towards the target | ||||
entity_angle_t angle = atan2_approx(offset.X, offset.Y); | entity_angle_t angle = atan2_approx(offset.X, offset.Y); | ||||
cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle); | |||||
state.cmpPosition->MoveAndTurnTo(state.pos.X, state.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 (state.wasObstructed && HandleObstructedMove()) | ||||
return; | return; | ||||
else if (!wasObstructed) | else if (!state.wasObstructed) | ||||
m_FailedPathComputations = 0; | 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 (!wentStraight && PathingUpdateNeeded(pos)) | if (!state.wentStraight && PathingUpdateNeeded(state.pos)) | ||||
{ | { | ||||
PathGoal goal; | PathGoal goal; | ||||
if (ComputeGoal(goal, m_MoveRequest)) | if (ComputeGoal(goal, m_MoveRequest)) | ||||
ComputePathToGoal(pos, goal); | ComputePathToGoal(state.pos, goal); | ||||
} | } | ||||
else if (m_FollowKnownImperfectPathCountdown > 0) | else if (m_FollowKnownImperfectPathCountdown > 0) | ||||
--m_FollowKnownImperfectPathCountdown; | --m_FollowKnownImperfectPathCountdown; | ||||
} | } | ||||
bool CCmpUnitMotion::PossiblyAtDestination() const | bool CCmpUnitMotion::PossiblyAtDestination() const | ||||
{ | { | ||||
if (m_MoveRequest.m_Type == MoveRequest::NONE) | if (m_MoveRequest.m_Type == MoveRequest::NONE) | ||||
▲ Show 20 Lines • Show All 657 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator