diff --git a/source/simulation2/components/CCmpUnitMotion.cpp b/source/simulation2/components/CCmpUnitMotion.h
similarity index 98%
rename from source/simulation2/components/CCmpUnitMotion.cpp
rename to source/simulation2/components/CCmpUnitMotion.h
index 8c5ce40663..70d7a9e8cb 100644
--- a/source/simulation2/components/CCmpUnitMotion.cpp
+++ b/source/simulation2/components/CCmpUnitMotion.h
@@ -15,11 +15,13 @@
* along with 0 A.D. If not, see .
*/
-#include "precompiled.h"
+#ifndef INCLUDED_CCMPUNITMOTION
+#define INCLUDED_CCMPUNITMOTION
#include "simulation2/system/Component.h"
#include "ICmpUnitMotion.h"
+#include "simulation2/components/CCmpUnitMotionManager.h"
#include "simulation2/components/ICmpObstruction.h"
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/components/ICmpOwnership.h"
@@ -41,6 +43,9 @@
#include "ps/Profile.h"
#include "renderer/Scene.h"
+// NB: this implementation of ICmpUnitMotion is very tightly coupled with UnitMotionManager.
+// As such, both are compiled in the same TU.
+
// For debugging; units will start going straight to the target
// instead of calling the pathfinder
#define DISABLE_PATHFINDER 0
@@ -113,8 +118,9 @@ static const u8 VERY_OBSTRUCTED_THRESHOLD = 10;
static const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1);
static const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1);
-class CCmpUnitMotion : public ICmpUnitMotion
+class CCmpUnitMotion final : public ICmpUnitMotion
{
+ friend class CCmpUnitMotionManager;
public:
static void ClassInit(CComponentManager& componentManager)
{
@@ -326,7 +332,7 @@ public:
case MT_Create:
{
if (!ENTITY_IS_LOCAL(GetEntityId()))
- CmpPtr(GetSystemEntity())->Register(GetEntityId(), m_FormationController);
+ CmpPtr(GetSystemEntity())->Register(this, GetEntityId(), m_FormationController);
break;
}
case MT_Destroy:
@@ -351,7 +357,7 @@ public:
{
OnValueModification();
if (!ENTITY_IS_LOCAL(GetEntityId()))
- CmpPtr(GetSystemEntity())->Register(GetEntityId(), m_FormationController);
+ CmpPtr(GetSystemEntity())->Register(this, GetEntityId(), m_FormationController);
break;
}
}
@@ -640,13 +646,11 @@ private:
* and ensure that distance comparisons are done while units are not being moved
* (otherwise they won't be commutative).
*/
- virtual void OnTurnStart();
+ void OnTurnStart();
- virtual void PreMove(ICmpUnitMotionManager::MotionState& state);
-
- virtual void Move(ICmpUnitMotionManager::MotionState& state, fixed dt);
-
- virtual void PostMove(ICmpUnitMotionManager::MotionState& state, fixed dt);
+ void PreMove(CCmpUnitMotionManager::MotionState& state);
+ void Move(CCmpUnitMotionManager::MotionState& state, fixed dt);
+ void PostMove(CCmpUnitMotionManager::MotionState& state, fixed dt);
/**
* Returns true if we are possibly at our destination.
@@ -911,14 +915,14 @@ void CCmpUnitMotion::OnTurnStart()
}
}
-void CCmpUnitMotion::PreMove(ICmpUnitMotionManager::MotionState& state)
+void CCmpUnitMotion::PreMove(CCmpUnitMotionManager::MotionState& state)
{
// 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);
}
-void CCmpUnitMotion::Move(ICmpUnitMotionManager::MotionState& state, fixed dt)
+void CCmpUnitMotion::Move(CCmpUnitMotionManager::MotionState& state, fixed dt)
{
PROFILE("Move");
@@ -930,7 +934,7 @@ void CCmpUnitMotion::Move(ICmpUnitMotionManager::MotionState& state, fixed dt)
state.wasObstructed = PerformMove(dt, state.cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, state.pos, state.angle);
}
-void CCmpUnitMotion::PostMove(ICmpUnitMotionManager::MotionState& state, fixed dt)
+void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::MotionState& state, fixed dt)
{
// Update our speed over this turn so that the visual actor shows the correct animation.
if (state.pos == state.initialPos)
@@ -1718,3 +1722,5 @@ void CCmpUnitMotion::RenderSubmit(SceneCollector& collector)
for (size_t i = 0; i < m_DebugOverlayShortPathLines.size(); ++i)
collector.Submit(&m_DebugOverlayShortPathLines[i]);
}
+
+#endif // INCLUDED_CCMPUNITMOTION
diff --git a/source/simulation2/components/CCmpUnitMotionManager.cpp b/source/simulation2/components/CCmpUnitMotionManager.h
similarity index 57%
rename from source/simulation2/components/CCmpUnitMotionManager.cpp
rename to source/simulation2/components/CCmpUnitMotionManager.h
index bf7fa96330..4078091981 100644
--- a/source/simulation2/components/CCmpUnitMotionManager.cpp
+++ b/source/simulation2/components/CCmpUnitMotionManager.h
@@ -15,17 +15,16 @@
* along with 0 A.D. If not, see .
*/
-#include "precompiled.h"
+#ifndef INCLUDED_CCMPUNITMOTIONMANAGER
+#define INCLUDED_CCMPUNITMOTIONMANAGER
#include "simulation2/system/Component.h"
#include "ICmpUnitMotionManager.h"
#include "simulation2/MessageTypes.h"
-#include "simulation2/components/ICmpUnitMotion.h"
#include "simulation2/system/EntityMap.h"
-#include "ps/CLogger.h"
-#include "ps/Profile.h"
+class CCmpUnitMotion;
class CCmpUnitMotionManager : public ICmpUnitMotionManager
{
@@ -40,6 +39,30 @@ public:
DEFAULT_COMPONENT_ALLOCATOR(UnitMotionManager)
+ // Persisted state for each unit.
+ struct MotionState
+ {
+ // 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.
+ CmpPtr cmpPosition;
+ CCmpUnitMotion* cmpUnitMotion;
+
+ // Position before units start moving
+ CFixedVector2D initialPos;
+ // Transient position during the movement.
+ CFixedVector2D pos;
+
+ fixed initialAngle;
+ fixed angle;
+
+ // If true, the entity needs to be handled during movement.
+ bool needUpdate;
+
+ // 'Leak' from UnitMotion.
+ bool wentStraight;
+ bool wasObstructed;
+ };
+
EntityMap m_Units;
EntityMap m_FormationControllers;
@@ -99,7 +122,7 @@ public:
}
}
- virtual void Register(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 bool ComputingMotion() const
@@ -114,77 +137,6 @@ public:
void Move(EntityMap& ents, fixed dt);
};
-void CCmpUnitMotionManager::Register(entity_id_t ent, bool formationController)
-{
- MotionState state = {
- CmpPtr(GetSimContext(), ent),
- CmpPtr(GetSimContext(), ent),
- CFixedVector2D(),
- CFixedVector2D(),
- fixed::Zero(),
- fixed::Zero(),
- false,
- false
- };
- if (!formationController)
- m_Units.insert(ent, state);
- else
- m_FormationControllers.insert(ent, state);
-}
-
-void CCmpUnitMotionManager::Unregister(entity_id_t ent)
-{
- EntityMap::iterator it = m_Units.find(ent);
- if (it != m_Units.end())
- {
- m_Units.erase(it);
- return;
- }
- it = m_FormationControllers.find(ent);
- if (it != m_FormationControllers.end())
- m_FormationControllers.erase(it);
-}
-
-void CCmpUnitMotionManager::OnTurnStart()
-{
- for (EntityMap::value_type& data : m_FormationControllers)
- data.second.cmpUnitMotion->OnTurnStart();
-
- for (EntityMap::value_type& data : m_Units)
- data.second.cmpUnitMotion->OnTurnStart();
-}
-
-void CCmpUnitMotionManager::MoveUnits(fixed dt)
-{
- Move(m_Units, dt);
-}
-
-void CCmpUnitMotionManager::MoveFormations(fixed dt)
-{
- Move(m_FormationControllers, dt);
-}
-
-void CCmpUnitMotionManager::Move(EntityMap& ents, fixed dt)
-{
- m_MovingUnits.clear();
- for (EntityMap::iterator it = ents.begin(); it != ents.end(); ++it)
- {
- it->second.cmpUnitMotion->PreMove(it->second);
- if (!it->second.needUpdate)
- continue;
- m_MovingUnits.push_back(it);
- it->second.initialPos = it->second.cmpPosition->GetPosition2D();
- it->second.initialAngle = it->second.cmpPosition->GetRotation().Y;
- it->second.pos = it->second.initialPos;
- it->second.angle = it->second.initialAngle;
- }
-
- for (EntityMap::iterator& it : m_MovingUnits)
- it->second.cmpUnitMotion->Move(it->second, dt);
-
- for (EntityMap::iterator& it : m_MovingUnits)
- it->second.cmpUnitMotion->PostMove(it->second, dt);
-}
-
-
REGISTER_COMPONENT_TYPE(UnitMotionManager)
+
+#endif // INCLUDED_CCMPUNITMOTIONMANAGER
diff --git a/source/simulation2/components/CCmpUnitMotion_System.cpp b/source/simulation2/components/CCmpUnitMotion_System.cpp
new file mode 100644
index 0000000000..19c564e518
--- /dev/null
+++ b/source/simulation2/components/CCmpUnitMotion_System.cpp
@@ -0,0 +1,101 @@
+/* Copyright (C) 2021 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 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
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "CCmpUnitMotion.h"
+#include "CCmpUnitMotionManager.h"
+
+#include "ps/CLogger.h"
+#include "ps/Profile.h"
+
+// NB: this TU contains the CCmpUnitMotion/CCmpUnitMotionManager couple.
+// In practice, UnitMotionManager functions need access to the full implementation of UnitMotion,
+// but UnitMotion needs access to MotionState (defined in UnitMotionManager).
+// To avoid inclusion issues, implementation of UnitMotionManager that uses UnitMotion is here.
+
+void CCmpUnitMotionManager::Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController)
+{
+ MotionState state = {
+ CmpPtr(GetSimContext(), ent),
+ component,
+ CFixedVector2D(),
+ CFixedVector2D(),
+ fixed::Zero(),
+ fixed::Zero(),
+ false,
+ false
+ };
+ if (!formationController)
+ m_Units.insert(ent, state);
+ else
+ m_FormationControllers.insert(ent, state);
+}
+
+void CCmpUnitMotionManager::Unregister(entity_id_t ent)
+{
+ EntityMap::iterator it = m_Units.find(ent);
+ if (it != m_Units.end())
+ {
+ m_Units.erase(it);
+ return;
+ }
+ it = m_FormationControllers.find(ent);
+ if (it != m_FormationControllers.end())
+ m_FormationControllers.erase(it);
+}
+
+void CCmpUnitMotionManager::OnTurnStart()
+{
+ for (EntityMap::value_type& data : m_FormationControllers)
+ data.second.cmpUnitMotion->OnTurnStart();
+
+ for (EntityMap::value_type& data : m_Units)
+ data.second.cmpUnitMotion->OnTurnStart();
+}
+
+void CCmpUnitMotionManager::MoveUnits(fixed dt)
+{
+ Move(m_Units, dt);
+}
+
+void CCmpUnitMotionManager::MoveFormations(fixed dt)
+{
+ Move(m_FormationControllers, dt);
+}
+
+void CCmpUnitMotionManager::Move(EntityMap& ents, fixed dt)
+{
+ m_MovingUnits.clear();
+ for (EntityMap::iterator it = ents.begin(); it != ents.end(); ++it)
+ {
+ it->second.cmpUnitMotion->PreMove(it->second);
+ if (!it->second.needUpdate)
+ continue;
+ m_MovingUnits.push_back(it);
+ it->second.initialPos = it->second.cmpPosition->GetPosition2D();
+ it->second.initialAngle = it->second.cmpPosition->GetRotation().Y;
+ it->second.pos = it->second.initialPos;
+ it->second.angle = it->second.initialAngle;
+ }
+
+ for (EntityMap::iterator& it : m_MovingUnits)
+ it->second.cmpUnitMotion->Move(it->second, dt);
+
+ for (EntityMap::iterator& it : m_MovingUnits)
+ it->second.cmpUnitMotion->PostMove(it->second, dt);
+}
diff --git a/source/simulation2/components/ICmpUnitMotion.cpp b/source/simulation2/components/ICmpUnitMotion.cpp
index e6ccb7d893..196a88d319 100644
--- a/source/simulation2/components/ICmpUnitMotion.cpp
+++ b/source/simulation2/components/ICmpUnitMotion.cpp
@@ -48,14 +48,6 @@ class CCmpUnitMotionScripted : public ICmpUnitMotion
public:
DEFAULT_SCRIPT_WRAPPER(UnitMotionScripted)
-private:
- virtual void OnTurnStart() {};
- virtual void PreMove(ICmpUnitMotionManager::MotionState&) {};
- virtual void Move(ICmpUnitMotionManager::MotionState&, fixed) {};
- virtual void PostMove(ICmpUnitMotionManager::MotionState&, fixed) {};
-
-public:
-
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange)
{
return m_Script.Call("MoveToPointRange", x, z, minRange, maxRange);
diff --git a/source/simulation2/components/ICmpUnitMotion.h b/source/simulation2/components/ICmpUnitMotion.h
index 49f442eb7a..caf8c5a78a 100644
--- a/source/simulation2/components/ICmpUnitMotion.h
+++ b/source/simulation2/components/ICmpUnitMotion.h
@@ -22,9 +22,6 @@
#include "simulation2/components/ICmpPathfinder.h" // for pass_class_t
#include "simulation2/components/ICmpPosition.h" // for entity_pos_t
-#include "simulation2/components/ICmpUnitMotionManager.h"
-
-class CCmpUnitMotionManager;
/**
* Motion interface for entities with complex movement capabilities.
@@ -36,18 +33,6 @@ class CCmpUnitMotionManager;
*/
class ICmpUnitMotion : public IComponent
{
-protected:
- friend class CCmpUnitMotionManager;
-
- /**
- * This external interface is used by the Unit Motion Manager.
- * Components that do not register there do not need to implement these.
- */
- virtual void OnTurnStart() = 0;
- virtual void PreMove(ICmpUnitMotionManager::MotionState& state) = 0;
- virtual void Move(ICmpUnitMotionManager::MotionState& state, fixed dt) = 0;
- virtual void PostMove(ICmpUnitMotionManager::MotionState& state, fixed dt) = 0;
-
public:
/**
diff --git a/source/simulation2/components/ICmpUnitMotionManager.h b/source/simulation2/components/ICmpUnitMotionManager.h
index 5f91f9c20c..26794abdad 100644
--- a/source/simulation2/components/ICmpUnitMotionManager.h
+++ b/source/simulation2/components/ICmpUnitMotionManager.h
@@ -20,40 +20,25 @@
#include "simulation2/system/Interface.h"
-#include "maths/Fixed.h"
-#include "maths/FixedVector2D.h"
-#include "simulation2/system/CmpPtr.h"
-
-class ICmpPosition;
-class ICmpUnitMotion;
+class CCmpUnitMotion;
+/**
+ * UnitMotionManager - handles motion for CCmpUnitMotion.
+ * This allows units to push past each other instead of requiring pathfinder computations,
+ * making movement much smoother overall.
+ */
class ICmpUnitMotionManager : public IComponent
{
public:
- // Persisted state for each unit.
- struct MotionState
- {
- // Component references - these must be kept alive for the duration of motion.
- CmpPtr cmpPosition;
- CmpPtr cmpUnitMotion;
+ DECLARE_INTERFACE_TYPE(UnitMotionManager)
- // Position before units start moving
- CFixedVector2D initialPos;
- // Transient position during the movement.
- CFixedVector2D pos;
+private:
+ /**
+ * This class makes no sense outside of CCmpUnitMotion. This enforces that tight coupling.
+ */
+ friend class CCmpUnitMotion;
- fixed initialAngle;
- fixed angle;
-
- // If true, the entity needs to be handled during movement.
- bool needUpdate;
-
- // 'Leak' from UnitMotion.
- bool wentStraight;
- bool wasObstructed;
- };
-
- virtual void Register(entity_id_t ent, bool formationController) = 0;
+ virtual void Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController) = 0;
virtual void Unregister(entity_id_t ent) = 0;
/**
@@ -61,7 +46,6 @@ public:
*/
virtual bool ComputingMotion() const = 0;
- DECLARE_INTERFACE_TYPE(UnitMotionManager)
};
#endif // INCLUDED_ICMPUNITMOTIONMANAGER