From 48ea6ee7d24c5b450fdabae43923603ca2a96887 Mon Sep 17 00:00:00 2001 From: wraitii Date: Wed, 2 Jun 2021 17:36:32 +0000 Subject: [PATCH] Moddability for pushing: radius in XML, allow deactivating globally/some templates. Differential Revision: https://code.wildfiregames.com/D4040 This was SVN commit r25635. --- .../maps/scenarios/unit_pushing_test.js | 2 +- .../public/simulation/data/pathfinder.rng | 3 ++ .../public/simulation/data/pathfinder.xml | 6 +++ .../simulation2/components/CCmpUnitMotion.h | 52 +++++++++++++++---- .../components/CCmpUnitMotionManager.h | 19 +++++-- .../components/CCmpUnitMotion_System.cpp | 35 ++++++++++--- .../components/ICmpUnitMotionManager.h | 5 ++ 7 files changed, 98 insertions(+), 24 deletions(-) diff --git a/binaries/data/mods/public/maps/scenarios/unit_pushing_test.js b/binaries/data/mods/public/maps/scenarios/unit_pushing_test.js index c8303aba9d..88a3b39d88 100644 --- a/binaries/data/mods/public/maps/scenarios/unit_pushing_test.js +++ b/binaries/data/mods/public/maps/scenarios/unit_pushing_test.js @@ -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); diff --git a/binaries/data/mods/public/simulation/data/pathfinder.rng b/binaries/data/mods/public/simulation/data/pathfinder.rng index c1e45d2f0f..e3bdd2d457 100644 --- a/binaries/data/mods/public/simulation/data/pathfinder.rng +++ b/binaries/data/mods/public/simulation/data/pathfinder.rng @@ -4,6 +4,9 @@ + + + diff --git a/binaries/data/mods/public/simulation/data/pathfinder.xml b/binaries/data/mods/public/simulation/data/pathfinder.xml index 96c656c9ce..8c85c77ad2 100644 --- a/binaries/data/mods/public/simulation/data/pathfinder.xml +++ b/binaries/data/mods/public/simulation/data/pathfinder.xml @@ -4,6 +4,12 @@ 64 + + + + + 1.6 + diff --git a/source/simulation2/components/CCmpUnitMotion.h b/source/simulation2/components/CCmpUnitMotion.h index d6407631d5..389c4b2129 100644 --- a/source/simulation2/components/CCmpUnitMotion.h +++ b/source/simulation2/components/CCmpUnitMotion.h @@ -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: "" "" "" - ""; + "" + "" + "" + "" + "" + ""; } 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 cmpPathfinder(GetSystemEntity()); if (cmpPathfinder) m_PassClass = cmpPathfinder->GetPassabilityClass(m_PassClassName); + + CmpPtr cmpObstruction(GetEntityHandle()); + if (cmpObstruction) + m_BlockMovement = cmpObstruction->GetBlockMovementFlag(false); } virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) @@ -357,7 +371,7 @@ public: { CmpPtr 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 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 cmpObstruction(GetEntityHandle()); if (cmpObstruction) cmpObstruction->SetMovingFlag(state.isMoving); diff --git a/source/simulation2/components/CCmpUnitMotionManager.h b/source/simulation2/components/CCmpUnitMotionManager.h index 6860e20f76..98fb51f65a 100644 --- a/source/simulation2/components/CCmpUnitMotionManager.h +++ b/source/simulation2/components/CCmpUnitMotionManager.h @@ -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 m_Units; EntityMap m_FormationControllers; - // The vectors are cleared each frame. - Grid::iterator>> m_MovingUnits; + // Turn-local state below, not serialised. + Grid::iterator>> m_MovingUnits; bool m_ComputingMotion; static std::string GetSchema() @@ -94,9 +100,7 @@ public: return ""; } - 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(); diff --git a/source/simulation2/components/CCmpUnitMotion_System.cpp b/source/simulation2/components/CCmpUnitMotion_System.cpp index 434f8d2727..f0fe0fac8a 100644 --- a/source/simulation2/components/CCmpUnitMotion_System.cpp +++ b/source/simulation2/components/CCmpUnitMotion_System.cpp @@ -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 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(GetSimContext(), ent), component); @@ -145,7 +162,8 @@ void CCmpUnitMotionManager::Move(EntityMap& 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::iterator>* vec : assigned) @@ -166,6 +184,7 @@ void CCmpUnitMotionManager::Move(EntityMap& ents, fixed dt) } } + if (m_PushingRadius != entity_pos_t::Zero()) { PROFILE2("MotionMgr_PushAdjust"); CmpPtr cmpPathfinder(GetSystemEntity()); @@ -242,7 +261,7 @@ void CCmpUnitMotionManager::Push(EntityMap::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; diff --git a/source/simulation2/components/ICmpUnitMotionManager.h b/source/simulation2/components/ICmpUnitMotionManager.h index 26794abdad..845180c5fa 100644 --- a/source/simulation2/components/ICmpUnitMotionManager.h +++ b/source/simulation2/components/ICmpUnitMotionManager.h @@ -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