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:
parent
07e44a75a1
commit
48ea6ee7d2
@ -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);
|
||||
|
@ -4,6 +4,9 @@
|
||||
<element name="MaxSameTurnMoves">
|
||||
<data type="nonNegativeInteger"/>
|
||||
</element>
|
||||
<element name="PushingRadius">
|
||||
<data type="decimal"/>
|
||||
</element>
|
||||
<element name="PassabilityClasses">
|
||||
<oneOrMore>
|
||||
<element>
|
||||
|
@ -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: -->
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user