Index: ps/trunk/binaries/data/mods/public/maps/scenarios/unit_pushing_test.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scenarios/unit_pushing_test.js +++ ps/trunk/binaries/data/mods/public/maps/scenarios/unit_pushing_test.js @@ -131,7 +131,7 @@ 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); Index: ps/trunk/binaries/data/mods/public/simulation/data/pathfinder.rng =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/pathfinder.rng +++ ps/trunk/binaries/data/mods/public/simulation/data/pathfinder.rng @@ -4,6 +4,9 @@ + + + Index: ps/trunk/binaries/data/mods/public/simulation/data/pathfinder.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/pathfinder.xml +++ ps/trunk/binaries/data/mods/public/simulation/data/pathfinder.xml @@ -4,6 +4,12 @@ 64 + + + + + 1.6 + Index: ps/trunk/source/simulation2/components/CCmpUnitMotion.h =================================================================== --- ps/trunk/source/simulation2/components/CCmpUnitMotion.h +++ ps/trunk/source/simulation2/components/CCmpUnitMotion.h @@ -159,7 +159,11 @@ 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 @@ "" "" "" - ""; + "" + "" + "" + "" + "" + ""; } virtual void Init(const CParamNode& paramNode) @@ -267,11 +276,12 @@ 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 @@ 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 @@ { CmpPtr cmpObstruction(GetEntityHandle()); if (cmpObstruction) - m_Pushing = cmpObstruction->GetBlockMovementFlag(false); + m_BlockMovement = cmpObstruction->GetBlockMovementFlag(false); break; } case MT_ValueModification: @@ -543,6 +557,12 @@ 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 @@ -731,18 +751,30 @@ 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::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); Index: ps/trunk/source/simulation2/components/CCmpUnitMotionManager.h =================================================================== --- ps/trunk/source/simulation2/components/CCmpUnitMotionManager.h +++ ps/trunk/source/simulation2/components/CCmpUnitMotionManager.h @@ -81,12 +81,18 @@ 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 @@ return ""; } - virtual void Init(const CParamNode& UNUSED(paramNode)) - { - } + virtual void Init(const CParamNode& UNUSED(paramNode)); virtual void Deinit() { @@ -155,6 +159,11 @@ return m_ComputingMotion; } + virtual bool IsPushingActivated() const + { + return m_PushingRadius != entity_pos_t::Zero(); + } + private: void ResetSubdivisions(); void OnTurnStart(); Index: ps/trunk/source/simulation2/components/CCmpUnitMotion_System.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpUnitMotion_System.cpp +++ ps/trunk/source/simulation2/components/CCmpUnitMotion_System.cpp @@ -51,12 +51,6 @@ 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. */ static const entity_pos_t PUSHING_MOVING_INFLUENCE_EXTENSION = entity_pos_t::FromInt(1); @@ -72,6 +66,29 @@ { } +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 @@ 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 @@ } } + if (m_PushingRadius != entity_pos_t::Zero()) { PROFILE2("MotionMgr_PushAdjust"); CmpPtr cmpPathfinder(GetSystemEntity()); @@ -242,7 +261,7 @@ 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; Index: ps/trunk/source/simulation2/components/ICmpUnitMotionManager.h =================================================================== --- ps/trunk/source/simulation2/components/ICmpUnitMotionManager.h +++ ps/trunk/source/simulation2/components/ICmpUnitMotionManager.h @@ -46,6 +46,11 @@ */ virtual bool ComputingMotion() const = 0; + /** + * @return whether pushing is currently enabled or not. + */ + virtual bool IsPushingActivated() const = 0; + }; #endif // INCLUDED_ICMPUNITMOTIONMANAGER