1
0
forked from 0ad/0ad

Moddability for pushing: radius in XML, allow deactivating globally/some templates.

Differential Revision: https://code.wildfiregames.com/D4040
This was SVN commit r25635.
This commit is contained in:
wraitii 2021-06-02 17:36:32 +00:00
parent 07e44a75a1
commit 48ea6ee7d2
7 changed files with 98 additions and 24 deletions

View File

@ -131,7 +131,7 @@ experiments.collecting_tree = {
let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager);
// Make that tree essentially infinite.
cmpModifiersManager.AddModifiers("inf_tree", {
"ResourceSupply/Max": [{ "affects": ["Tree"], "replace": 50000 }],
"ResourceSupply/Max": [{ "replace": 50000 }],
}, target);
let cmpSupply = Engine.QueryInterface(target, IID_ResourceSupply);
cmpSupply.SetAmount(50000);

View File

@ -4,6 +4,9 @@
<element name="MaxSameTurnMoves">
<data type="nonNegativeInteger"/>
</element>
<element name="PushingRadius">
<data type="decimal"/>
</element>
<element name="PassabilityClasses">
<oneOrMore>
<element>

View File

@ -4,6 +4,12 @@
<!-- Setting the value to 0 disable this functionality -->
<MaxSameTurnMoves>64</MaxSameTurnMoves>
<!-- Multiplier for the distance at which units push each other. -->
<!-- Setting the value to 0 disables unit pushing entirely. -->
<!-- Note that values above 2-3 are likely to start behaving weirdly -->
<!-- (in particular, formations offsets may need adjusting). -->
<PushingRadius>1.6</PushingRadius>
<PassabilityClasses>
<!-- Unit pathfinding classes: -->

View File

@ -159,7 +159,11 @@ public:
bool m_FacePointAfterMove;
// Whether the unit participates in pushing.
bool m_Pushing = true;
bool m_Pushing = false;
// Whether the unit blocks movement (& is blocked by movement blockers)
// Cached from ICmpObstruction.
bool m_BlockMovement = false;
// Internal counter used when recovering from obstructed movement.
// Most notably, increases the search range of the vertex pathfinder.
@ -239,7 +243,12 @@ public:
"</optional>"
"<element name='PassabilityClass' a:help='Identifies the terrain passability class (values are defined in special/pathfinder.xml)'>"
"<text/>"
"</element>";
"</element>"
"<optional>"
"<element name='DisablePushing'>"
"<data type='boolean'/>"
"</element>"
"</optional>";
}
virtual void Init(const CParamNode& paramNode)
@ -267,11 +276,12 @@ public:
if (cmpObstruction)
{
cmpObstruction->SetUnitClearance(m_Clearance);
if (!cmpObstruction->GetBlockMovementFlag(true))
m_Pushing = false;
m_BlockMovement = cmpObstruction->GetBlockMovementFlag(true);
}
}
SetParticipateInPushing(!paramNode.GetChild("DisablePushing").IsOk() || !paramNode.GetChild("DisablePushing").ToBool());
m_DebugOverlayEnabled = false;
}
@ -322,6 +332,10 @@ public:
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
if (cmpPathfinder)
m_PassClass = cmpPathfinder->GetPassabilityClass(m_PassClassName);
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle());
if (cmpObstruction)
m_BlockMovement = cmpObstruction->GetBlockMovementFlag(false);
}
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
@ -357,7 +371,7 @@ public:
{
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle());
if (cmpObstruction)
m_Pushing = cmpObstruction->GetBlockMovementFlag(false);
m_BlockMovement = cmpObstruction->GetBlockMovementFlag(false);
break;
}
case MT_ValueModification:
@ -543,6 +557,12 @@ private:
return IsFormationMember() ? m_MoveRequest.m_Entity : GetEntityId();
}
void SetParticipateInPushing(bool pushing)
{
CmpPtr<ICmpUnitMotionManager> cmpUnitMotionManager(GetSystemEntity());
m_Pushing = pushing && cmpUnitMotionManager->IsPushingActivated();
}
/**
* Warns other components that our current movement will likely fail (e.g. we won't be able to reach our target)
* This should only be called before the actual movement in a given turn, or units might both move and try to do things
@ -730,19 +750,31 @@ private:
*/
void FaceTowardsPointFromPos(const CFixedVector2D& pos, entity_pos_t x, entity_pos_t z);
/**
* Units in 'pushing' mode are marked as 'moving' in the obstruction manager.
* Units in 'pushing' mode should skip them in checkMovement (to enable pushing).
* However, units for which pushing is deactivated should collide against everyone.
* Units that don't block movement never participate in pushing, but they also
* shouldn't collide with pushing units.
*/
bool ShouldCollideWithMovingUnits() const
{
return !m_Pushing && m_BlockMovement;
}
/**
* Returns an appropriate obstruction filter for use with path requests.
*/
ControlGroupMovementObstructionFilter GetObstructionFilter() const
{
return ControlGroupMovementObstructionFilter(false, GetGroup());
return ControlGroupMovementObstructionFilter(ShouldCollideWithMovingUnits(), GetGroup());
}
/**
* Filter a specific tag on top of the existing control groups.
*/
SkipTagAndControlGroupObstructionFilter GetObstructionFilter(const ICmpObstructionManager::tag_t& tag) const
{
return SkipTagAndControlGroupObstructionFilter(tag, false, GetGroup());
return SkipTagAndControlGroupObstructionFilter(tag, ShouldCollideWithMovingUnits(), GetGroup());
}
/**
@ -931,20 +963,20 @@ void CCmpUnitMotion::OnTurnStart()
void CCmpUnitMotion::PreMove(CCmpUnitMotionManager::MotionState& state)
{
state.ignore = !m_Pushing;
state.ignore = !m_Pushing || !m_BlockMovement;
// If we were idle and will still be, no need for an update.
state.needUpdate = state.cmpPosition->IsInWorld() &&
(m_CurSpeed != fixed::Zero() || m_MoveRequest.m_Type != MoveRequest::NONE);
if (state.ignore)
if (!m_BlockMovement)
return;
state.controlGroup = IsFormationMember() ? m_MoveRequest.m_Entity : INVALID_ENTITY;
// Update moving flag, this is an internal construct used for pushing,
// so it does not really reflect whether the unit is actually moving or not.
state.isMoving = m_MoveRequest.m_Type != MoveRequest::NONE;
state.isMoving = m_Pushing && m_MoveRequest.m_Type != MoveRequest::NONE;
CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle());
if (cmpObstruction)
cmpObstruction->SetMovingFlag(state.isMoving);

View File

@ -81,12 +81,18 @@ public:
bool isMoving = false;
};
// Multiplier for the pushing radius. Pre-multiplied by the circle-square correction factor.
// "Template" state, not serialized (cannot be changed mid-game).
entity_pos_t m_PushingRadius;
// These vectors are reconstructed on deserialization.
EntityMap<MotionState> m_Units;
EntityMap<MotionState> m_FormationControllers;
// The vectors are cleared each frame.
Grid<std::vector<EntityMap<MotionState>::iterator>> m_MovingUnits;
// Turn-local state below, not serialised.
Grid<std::vector<EntityMap<MotionState>::iterator>> m_MovingUnits;
bool m_ComputingMotion;
static std::string GetSchema()
@ -94,9 +100,7 @@ public:
return "<a:component type='system'/><empty/>";
}
virtual void Init(const CParamNode& UNUSED(paramNode))
{
}
virtual void Init(const CParamNode& UNUSED(paramNode));
virtual void Deinit()
{
@ -155,6 +159,11 @@ public:
return m_ComputingMotion;
}
virtual bool IsPushingActivated() const
{
return m_PushingRadius != entity_pos_t::Zero();
}
private:
void ResetSubdivisions();
void OnTurnStart();

View File

@ -50,12 +50,6 @@ namespace {
*/
static const entity_pos_t PUSHING_CORRECTION = entity_pos_t::FromInt(5) / 7;
/**
* The maximum distance at which units exert a pushing influence on each other, as a multiple of clearance.
* (This is multiplied by PUSHING_CORRECTION for convenience).
*/
static const entity_pos_t PUSHING_RADIUS = (entity_pos_t::FromInt(8) / 5).Multiply(PUSHING_CORRECTION);
/**
* When moving, units exert a pushing influence at a greater distance.
*/
@ -72,6 +66,29 @@ CCmpUnitMotionManager::MotionState::MotionState(CmpPtr<ICmpPosition> cmpPos, CCm
{
}
void CCmpUnitMotionManager::Init(const CParamNode&)
{
// Load some data - see CCmpPathfinder.xml.
// This assumes the pathfinder component is initialised first and registers the validator.
// TODO: there seems to be no real reason why we could not register a 'system' entity somewhere instead.
CParamNode externalParamNode;
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
const CParamNode radius = externalParamNode.GetChild("Pathfinder").GetChild("PushingRadius");
if (radius.IsOk())
{
m_PushingRadius = radius.ToFixed();
if (m_PushingRadius < entity_pos_t::Zero())
{
LOGWARNING("Pushing radius cannot be below 0. De-activating pushing but 'pathfinder.xml' should be updated.");
m_PushingRadius = entity_pos_t::Zero();
}
// No upper value, but things won't behave sanely if values are too high.
}
else
m_PushingRadius = entity_pos_t::FromInt(8) / 5;
m_PushingRadius = m_PushingRadius.Multiply(PUSHING_CORRECTION);
}
void CCmpUnitMotionManager::Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController)
{
MotionState state(CmpPtr<ICmpPosition>(GetSimContext(), ent), component);
@ -145,7 +162,8 @@ void CCmpUnitMotionManager::Move(EntityMap<MotionState>& ents, fixed dt)
if (it->second.needUpdate)
it->second.cmpUnitMotion->Move(it->second, dt);
if (&ents == &m_Units)
// Skip pushing entirely if the radius is 0
if (&ents == &m_Units && m_PushingRadius != entity_pos_t::Zero())
{
PROFILE2("MotionMgr_Pushing");
for (std::vector<EntityMap<MotionState>::iterator>* vec : assigned)
@ -166,6 +184,7 @@ void CCmpUnitMotionManager::Move(EntityMap<MotionState>& ents, fixed dt)
}
}
if (m_PushingRadius != entity_pos_t::Zero())
{
PROFILE2("MotionMgr_PushAdjust");
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
@ -242,7 +261,7 @@ void CCmpUnitMotionManager::Push(EntityMap<MotionState>::value_type& a, EntityMa
if (movingPush == 1)
return;
entity_pos_t combinedClearance = (a.second.cmpUnitMotion->m_Clearance + b.second.cmpUnitMotion->m_Clearance).Multiply(PUSHING_RADIUS);
entity_pos_t combinedClearance = (a.second.cmpUnitMotion->m_Clearance + b.second.cmpUnitMotion->m_Clearance).Multiply(m_PushingRadius);
entity_pos_t maxDist = combinedClearance;
if (movingPush)
maxDist += PUSHING_MOVING_INFLUENCE_EXTENSION;

View File

@ -46,6 +46,11 @@ private:
*/
virtual bool ComputingMotion() const = 0;
/**
* @return whether pushing is currently enabled or not.
*/
virtual bool IsPushingActivated() const = 0;
};
#endif // INCLUDED_ICMPUNITMOTIONMANAGER