Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpUnitMotion.h
/* Copyright (C) 2021 Wildfire Games. | /* Copyright (C) 2022 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
▲ Show 20 Lines • Show All 409 Lines • ▼ Show 20 Lines | virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) | ||||
case MT_OwnershipChanged: | case MT_OwnershipChanged: | ||||
{ | { | ||||
OnValueModification(); | OnValueModification(); | ||||
break; | break; | ||||
} | } | ||||
case MT_Deserialized: | case MT_Deserialized: | ||||
{ | { | ||||
OnValueModification(); | OnValueModification(); | ||||
if (!ENTITY_IS_LOCAL(GetEntityId())) | |||||
CmpPtr<ICmpUnitMotionManager>(GetSystemEntity())->Register(this, GetEntityId(), m_IsFormationController); | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void UpdateMessageSubscriptions() | void UpdateMessageSubscriptions() | ||||
{ | { | ||||
bool needRender = m_DebugOverlayEnabled; | bool needRender = m_DebugOverlayEnabled; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | virtual CFixedVector2D EstimateFuturePosition(const fixed dt) const | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
entity_angle_t angle = cmpPosition->GetRotation().Y; | entity_angle_t angle = cmpPosition->GetRotation().Y; | ||||
fixed speed = m_CurrentSpeed; | fixed speed = m_CurrentSpeed; | ||||
// Copy the path so we don't change it. | // Copy the path so we don't change it. | ||||
WaypointPath shortPath = m_ShortPath; | WaypointPath shortPath = m_ShortPath; | ||||
WaypointPath longPath = m_LongPath; | WaypointPath longPath = m_LongPath; | ||||
PerformMove(dt, cmpPosition->GetTurnRate(), shortPath, longPath, pos, speed, angle); | PerformMove(dt, cmpPosition->GetTurnRate(), shortPath, longPath, pos, speed, angle, 0); | ||||
return pos; | return pos; | ||||
} | } | ||||
virtual fixed GetAcceleration() const | virtual fixed GetAcceleration() const | ||||
{ | { | ||||
return m_Acceleration; | return m_Acceleration; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 253 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, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed, entity_angle_t& angle) const; | bool PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed, entity_angle_t& angle, uint8_t pushingPressure) 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, entity_pos_t meanSpeed); | void UpdateMovementState(entity_pos_t speed, entity_pos_t meanSpeed); | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 279 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
PROFILE("Move"); | PROFILE("Move"); | ||||
// 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. | ||||
state.wentStraight = TryGoingStraightToTarget(state.initialPos, true); | state.wentStraight = TryGoingStraightToTarget(state.initialPos, true); | ||||
state.wasObstructed = PerformMove(dt, state.cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, state.pos, state.speed, state.angle); | state.wasObstructed = PerformMove(dt, state.cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, state.pos, state.speed, state.angle, state.pushingPressure); | ||||
} | } | ||||
void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::MotionState& state, fixed dt) | void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::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 (state.pos == state.initialPos) | if (state.pos == state.initialPos) | ||||
{ | { | ||||
if (state.angle != state.initialAngle) | if (state.angle != state.initialAngle) | ||||
▲ Show 20 Lines • Show All 60 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 (targetPos-cmpPosition->GetPosition2D()).CompareLength(fixed::Zero()) <= 0; | return (targetPos-cmpPosition->GetPosition2D()).CompareLength(fixed::Zero()) <= 0; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed, entity_angle_t& angle) const | bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed, entity_angle_t& angle, uint8_t pushingPressure) 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; | ||||
// Wrap the angle to (-Pi, Pi]. | // Wrap the angle to (-Pi, Pi]. | ||||
while (angle > entity_angle_t::Pi()) | while (angle > entity_angle_t::Pi()) | ||||
angle -= entity_angle_t::Pi() * 2; | angle -= entity_angle_t::Pi() * 2; | ||||
while (angle < -entity_angle_t::Pi()) | while (angle < -entity_angle_t::Pi()) | ||||
angle += entity_angle_t::Pi() * 2; | angle += entity_angle_t::Pi() * 2; | ||||
// TODO: there's some asymmetry here when units look at other | |||||
// units' positions - the result will depend on the order of execution. | |||||
// Maybe we should split the updates into multiple phases to minimise | |||||
// that problem. | |||||
wraitii: This is no longer accurate | |||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||
ENSURE(cmpPathfinder); | ENSURE(cmpPathfinder); | ||||
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 (IsMovingAsFormation()) | if (IsMovingAsFormation()) | ||||
basicSpeed = m_Speed.Multiply(m_RunMultiplier); | basicSpeed = m_Speed.Multiply(m_RunMultiplier); | ||||
// If pushing pressure is applied, slow the unit down. | |||||
if (pushingPressure) | |||||
{ | |||||
// Values below this pressure don't slow the unit down. | |||||
constexpr int pressureMinThreshold = 10; | |||||
Not Done Inline ActionsConfig value? Why not uint8_t Stan: Config value? Why not uint8_t | |||||
Done Inline Actionsthis is an int because the calculations below are all in int. Could be hoisted to a config value though yeah wraitii: this is an int because the calculations below are all in int. Could be hoisted to a config… | |||||
Not Done Inline ActionsAll the lines until the basic speed computation are between 255 and 1, aren't they? Else assertion. Stan: All the lines until the basic speed computation are between 255 and 1, aren't they? Else… | |||||
// Leave some leeway to avoid slowing down too much over slight pushes, | |||||
// while also making sure that units don't completely stop, | |||||
// which would make movement un-necessarily awkward. | |||||
constexpr int maxPressure = std::numeric_limits<uint8_t>::max() - pressureMinThreshold; | |||||
Not Done Inline Actions#include <limits> Stan: #include <limits> | |||||
// Acts as the floor speed: (maxPressure - this) / maxPressure | |||||
constexpr int pressureMaxThreshold = maxPressure - 20; | |||||
static_assert(pressureMaxThreshold > 0); | |||||
uint8_t slowdown = maxPressure - std::min(pressureMaxThreshold, std::max(0, pushingPressure - pressureMinThreshold)); | |||||
Not Done Inline Actions#include <algorithm> Stan: #include <algorithm> | |||||
Not Done Inline Actionsu8? Stan: u8? | |||||
Not Done Inline ActionsNo way to write it as a constexpr too? Stan: No way to write it as a constexpr too? | |||||
basicSpeed = basicSpeed.Multiply(fixed::FromInt(slowdown) / maxPressure); | |||||
} | |||||
// 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); | ||||
▲ Show 20 Lines • Show All 721 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
This is no longer accurate