Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpUnitMotionManager.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 All 16 Lines | |||||
#include "simulation2/helpers/Grid.h" | #include "simulation2/helpers/Grid.h" | ||||
#include "simulation2/system/EntityMap.h" | #include "simulation2/system/EntityMap.h" | ||||
class CCmpUnitMotion; | class CCmpUnitMotion; | ||||
class CCmpUnitMotionManager : public ICmpUnitMotionManager | class CCmpUnitMotionManager : public ICmpUnitMotionManager | ||||
{ | { | ||||
public: | public: | ||||
static void ClassInit(CComponentManager& componentManager) | static void ClassInit(CComponentManager& componentManager); | ||||
{ | |||||
componentManager.SubscribeToMessageType(MT_TerrainChanged); | |||||
componentManager.SubscribeToMessageType(MT_TurnStart); | |||||
componentManager.SubscribeToMessageType(MT_Update_Final); | |||||
componentManager.SubscribeToMessageType(MT_Update_MotionUnit); | |||||
componentManager.SubscribeToMessageType(MT_Update_MotionFormation); | |||||
} | |||||
DEFAULT_COMPONENT_ALLOCATOR(UnitMotionManager) | DEFAULT_COMPONENT_ALLOCATOR(UnitMotionManager) | ||||
/** | |||||
* Maximum value for pushing pressure. | |||||
*/ | |||||
static constexpr int MAX_PRESSURE = 255; | |||||
// Persisted state for each unit. | // Persisted state for each unit. | ||||
struct MotionState | struct MotionState | ||||
{ | { | ||||
MotionState(CmpPtr<ICmpPosition> cmpPos, CCmpUnitMotion* cmpMotion); | MotionState(ICmpPosition* cmpPos, CCmpUnitMotion* cmpMotion); | ||||
// Component references - these must be kept alive for the duration of motion. | // Component references - these must be kept alive for the duration of motion. | ||||
// NB: this is generally not something one should do, but because of the tight coupling here it's doable. | // NB: this is generally a super dangerous thing to do, | ||||
CmpPtr<ICmpPosition> cmpPosition; | // but the tight coupling with CCmpUnitMotion makes it workable. | ||||
// NB: this assumes that components do _not_ move in memory, | |||||
// which is currently a fair assumption but might change in the future. | |||||
ICmpPosition* cmpPosition; | |||||
CCmpUnitMotion* cmpUnitMotion; | CCmpUnitMotion* cmpUnitMotion; | ||||
// Position before units start moving | // Position before units start moving | ||||
CFixedVector2D initialPos; | CFixedVector2D initialPos; | ||||
// Transient position during the movement. | // Transient position during the movement. | ||||
CFixedVector2D pos; | CFixedVector2D pos; | ||||
// Accumulated "pushing" from nearby units. | // Accumulated "pushing" from nearby units. | ||||
CFixedVector2D push; | CFixedVector2D push; | ||||
fixed speed; | fixed speed; | ||||
fixed initialAngle; | fixed initialAngle; | ||||
fixed angle; | fixed angle; | ||||
// Used for formations - units with the same control group won't push at a distance. | // Used for formations - units with the same control group won't push at a distance. | ||||
// (this is required because formations may be tight and large units may end up never settling. | // (this is required because formations may be tight and large units may end up never settling. | ||||
entity_id_t controlGroup = INVALID_ENTITY; | entity_id_t controlGroup = INVALID_ENTITY; | ||||
// This is a ad-hoc counter to store under how much pushing 'pressure' an entity is. | |||||
// More pressure will slow the unit down and make it harder to push, | |||||
// which effectively bogs down groups of colliding units. | |||||
uint8_t pushingPressure = 0; | |||||
// Meta-flag -> this entity won't push nor be pushed. | // Meta-flag -> this entity won't push nor be pushed. | ||||
// (used for entities that have their obstruction disabled). | // (used for entities that have their obstruction disabled). | ||||
bool ignore = false; | bool ignore = false; | ||||
// If true, the entity needs to be handled during movement. | // If true, the entity needs to be handled during movement. | ||||
bool needUpdate = false; | bool needUpdate = false; | ||||
bool wentStraight = false; | bool wentStraight = false; | ||||
bool wasObstructed = false; | bool wasObstructed = false; | ||||
// Clone of the obstruction manager flag for efficiency | // Clone of the obstruction manager flag for efficiency | ||||
bool isMoving = false; | bool isMoving = false; | ||||
}; | }; | ||||
// "Template" state, not serialized (cannot be changed mid-game). | // "Template" state, not serialized (cannot be changed mid-game). | ||||
// Multiplier for the pushing radius. Pre-multiplied by the circle-square correction factor. | // The maximal distance at which units push each other is the combined unit clearances, multipled by this factor, | ||||
entity_pos_t m_PushingRadius; | // itself pre-multiplied by the circle-square correction factor. | ||||
// Additive modifiers to the pushing radius for moving units and idle units respectively. | entity_pos_t m_PushingRadiusMultiplier; | ||||
// Additive modifiers to the maximum pushing distance for moving units and idle units respectively. | |||||
entity_pos_t m_MovingPushExtension; | entity_pos_t m_MovingPushExtension; | ||||
entity_pos_t m_StaticPushExtension; | entity_pos_t m_StaticPushExtension; | ||||
// Multiplier for the pushing 'spread'. | |||||
// This should be understand as the % of the maximum distance where pushing will be "in full force". | |||||
entity_pos_t m_MovingPushingSpread; | |||||
entity_pos_t m_StaticPushingSpread; | |||||
// Pushing forces below this value are ignored - this prevents units moving forever by very small increments. | // Pushing forces below this value are ignored - this prevents units moving forever by very small increments. | ||||
entity_pos_t m_MinimalPushing; | entity_pos_t m_MinimalPushing; | ||||
// Multiplier for pushing pressure strength. | |||||
entity_pos_t m_PushingPressureStrength; | |||||
// Per-turn reduction in pushing pressure. | |||||
entity_pos_t m_PushingPressureDecay; | |||||
// These vectors are reconstructed on deserialization. | // These vectors are reconstructed on deserialization. | ||||
EntityMap<MotionState> m_Units; | EntityMap<MotionState> m_Units; | ||||
EntityMap<MotionState> m_FormationControllers; | EntityMap<MotionState> m_FormationControllers; | ||||
// Turn-local state below, not serialised. | // Turn-local state below, not serialised. | ||||
Grid<std::vector<EntityMap<MotionState>::iterator>> m_MovingUnits; | Grid<std::vector<EntityMap<MotionState>::iterator>> m_MovingUnits; | ||||
bool m_ComputingMotion; | bool m_ComputingMotion; | ||||
static std::string GetSchema() | static std::string GetSchema() | ||||
{ | { | ||||
return "<a:component type='system'/><empty/>"; | return "<a:component type='system'/><empty/>"; | ||||
} | } | ||||
virtual void Init(const CParamNode& UNUSED(paramNode)); | virtual void Init(const CParamNode& UNUSED(paramNode)); | ||||
virtual void Deinit() | virtual void Deinit() | ||||
{ | { | ||||
} | } | ||||
virtual void Serialize(ISerializer& UNUSED(serialize)) | virtual void Serialize(ISerializer& serialize); | ||||
{ | virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize); | ||||
} | |||||
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) | virtual void HandleMessage(const CMessage& msg, bool global); | ||||
{ | |||||
Init(paramNode); | |||||
ResetSubdivisions(); | |||||
} | |||||
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) | |||||
{ | |||||
switch (msg.GetType()) | |||||
{ | |||||
case MT_TerrainChanged: | |||||
{ | |||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity()); | |||||
if (cmpTerrain->GetVerticesPerSide() != m_MovingUnits.width()) | |||||
ResetSubdivisions(); | |||||
break; | |||||
} | |||||
case MT_TurnStart: | |||||
{ | |||||
OnTurnStart(); | |||||
break; | |||||
} | |||||
case MT_Update_MotionFormation: | |||||
{ | |||||
fixed dt = static_cast<const CMessageUpdate_MotionFormation&>(msg).turnLength; | |||||
m_ComputingMotion = true; | |||||
MoveFormations(dt); | |||||
m_ComputingMotion = false; | |||||
break; | |||||
} | |||||
case MT_Update_MotionUnit: | |||||
{ | |||||
fixed dt = static_cast<const CMessageUpdate_MotionUnit&>(msg).turnLength; | |||||
m_ComputingMotion = true; | |||||
MoveUnits(dt); | |||||
m_ComputingMotion = false; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
virtual void Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController); | virtual void Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController); | ||||
virtual void Unregister(entity_id_t ent); | virtual void Unregister(entity_id_t ent); | ||||
virtual bool ComputingMotion() const | virtual bool ComputingMotion() const | ||||
{ | { | ||||
return m_ComputingMotion; | return m_ComputingMotion; | ||||
} | } | ||||
virtual bool IsPushingActivated() const | virtual bool IsPushingActivated() const | ||||
{ | { | ||||
return m_PushingRadius != entity_pos_t::Zero(); | return m_PushingRadiusMultiplier != entity_pos_t::Zero(); | ||||
} | } | ||||
private: | private: | ||||
void OnDeserialized(); | |||||
void ResetSubdivisions(); | void ResetSubdivisions(); | ||||
void OnTurnStart(); | void OnTurnStart(); | ||||
void MoveUnits(fixed dt); | void MoveUnits(fixed dt); | ||||
void MoveFormations(fixed dt); | void MoveFormations(fixed dt); | ||||
void Move(EntityMap<MotionState>& ents, fixed dt); | void Move(EntityMap<MotionState>& ents, fixed dt); | ||||
void Push(EntityMap<MotionState>::value_type& a, EntityMap<MotionState>::value_type& b, fixed dt); | void Push(EntityMap<MotionState>::value_type& a, EntityMap<MotionState>::value_type& b, fixed dt); | ||||
}; | }; | ||||
void CCmpUnitMotionManager::ResetSubdivisions() | |||||
{ | |||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity()); | |||||
if (!cmpTerrain) | |||||
return; | |||||
size_t size = cmpTerrain->GetMapSize(); | |||||
u16 gridSquareSize = static_cast<u16>(size / 20 + 1); | |||||
m_MovingUnits.resize(gridSquareSize, gridSquareSize); | |||||
} | |||||
REGISTER_COMPONENT_TYPE(UnitMotionManager) | REGISTER_COMPONENT_TYPE(UnitMotionManager) | ||||
#endif // INCLUDED_CCMPUNITMOTIONMANAGER | #endif // INCLUDED_CCMPUNITMOTIONMANAGER |
Wildfire Games · Phabricator