Line data Source code
1 : /* Copyright (C) 2022 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #ifndef INCLUDED_CCMPUNITMOTIONMANAGER
19 : #define INCLUDED_CCMPUNITMOTIONMANAGER
20 :
21 : #include "simulation2/system/Component.h"
22 : #include "ICmpUnitMotionManager.h"
23 :
24 : #include "simulation2/MessageTypes.h"
25 : #include "simulation2/components/ICmpTerrain.h"
26 : #include "simulation2/helpers/Grid.h"
27 : #include "simulation2/system/EntityMap.h"
28 :
29 : class CCmpUnitMotion;
30 :
31 9 : class CCmpUnitMotionManager final : public ICmpUnitMotionManager
32 : {
33 : public:
34 : static void ClassInit(CComponentManager& componentManager);
35 :
36 6 : DEFAULT_COMPONENT_ALLOCATOR(UnitMotionManager)
37 :
38 : /**
39 : * Maximum value for pushing pressure.
40 : */
41 : static constexpr int MAX_PRESSURE = 255;
42 :
43 : // Persisted state for each unit.
44 : struct MotionState
45 : {
46 : MotionState(ICmpPosition* cmpPos, CCmpUnitMotion* cmpMotion);
47 :
48 : // Component references - these must be kept alive for the duration of motion.
49 : // NB: this is generally a super dangerous thing to do,
50 : // but the tight coupling with CCmpUnitMotion makes it workable.
51 : // NB: this assumes that components do _not_ move in memory,
52 : // which is currently a fair assumption but might change in the future.
53 : ICmpPosition* cmpPosition;
54 : CCmpUnitMotion* cmpUnitMotion;
55 :
56 : // Position before units start moving
57 : CFixedVector2D initialPos;
58 : // Transient position during the movement.
59 : CFixedVector2D pos;
60 :
61 : // Accumulated "pushing" from nearby units.
62 : CFixedVector2D push;
63 :
64 : fixed speed;
65 :
66 : fixed initialAngle;
67 : fixed angle;
68 :
69 : // Used for formations - units with the same control group won't push at a distance.
70 : // (this is required because formations may be tight and large units may end up never settling.
71 : entity_id_t controlGroup = INVALID_ENTITY;
72 :
73 : // This is a ad-hoc counter to store under how much pushing 'pressure' an entity is.
74 : // More pressure will slow the unit down and make it harder to push,
75 : // which effectively bogs down groups of colliding units.
76 : uint8_t pushingPressure = 0;
77 :
78 : // Meta-flag -> this entity won't push nor be pushed.
79 : // (used for entities that have their obstruction disabled).
80 : bool ignore = false;
81 :
82 : // If true, the entity needs to be handled during movement.
83 : bool needUpdate = false;
84 :
85 : bool wentStraight = false;
86 : bool wasObstructed = false;
87 :
88 : // Clone of the obstruction manager flag for efficiency
89 : bool isMoving = false;
90 : };
91 :
92 : // "Template" state, not serialized (cannot be changed mid-game).
93 :
94 : // The maximal distance at which units push each other is the combined unit clearances, multipled by this factor,
95 : // itself pre-multiplied by the circle-square correction factor.
96 : entity_pos_t m_PushingRadiusMultiplier;
97 : // Additive modifiers to the maximum pushing distance for moving units and idle units respectively.
98 : entity_pos_t m_MovingPushExtension;
99 : entity_pos_t m_StaticPushExtension;
100 : // Multiplier for the pushing 'spread'.
101 : // This should be understand as the % of the maximum distance where pushing will be "in full force".
102 : entity_pos_t m_MovingPushingSpread;
103 : entity_pos_t m_StaticPushingSpread;
104 :
105 : // Pushing forces below this value are ignored - this prevents units moving forever by very small increments.
106 : entity_pos_t m_MinimalPushing;
107 :
108 : // Multiplier for pushing pressure strength.
109 : entity_pos_t m_PushingPressureStrength;
110 : // Per-turn reduction in pushing pressure.
111 : entity_pos_t m_PushingPressureDecay;
112 :
113 : // These vectors are reconstructed on deserialization.
114 :
115 : EntityMap<MotionState> m_Units;
116 : EntityMap<MotionState> m_FormationControllers;
117 :
118 : // Turn-local state below, not serialised.
119 :
120 : Grid<std::vector<EntityMap<MotionState>::iterator>> m_MovingUnits;
121 : bool m_ComputingMotion;
122 :
123 116 : static std::string GetSchema()
124 : {
125 116 : return "<a:component type='system'/><empty/>";
126 : }
127 :
128 : void Init(const CParamNode& UNUSED(paramNode)) override;
129 :
130 3 : void Deinit() override
131 : {
132 3 : }
133 :
134 : void Serialize(ISerializer& serialize) override;
135 : void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override;
136 :
137 : void HandleMessage(const CMessage& msg, bool global) override;
138 :
139 : void Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController) override;
140 : void Unregister(entity_id_t ent) override;
141 :
142 0 : bool ComputingMotion() const override
143 : {
144 0 : return m_ComputingMotion;
145 : }
146 :
147 0 : bool IsPushingActivated() const override
148 : {
149 0 : return m_PushingRadiusMultiplier != entity_pos_t::Zero();
150 : }
151 :
152 : private:
153 : void OnDeserialized();
154 : void ResetSubdivisions();
155 : void OnTurnStart();
156 :
157 : void MoveUnits(fixed dt);
158 : void MoveFormations(fixed dt);
159 : void Move(EntityMap<MotionState>& ents, fixed dt);
160 :
161 : void Push(EntityMap<MotionState>::value_type& a, EntityMap<MotionState>::value_type& b, fixed dt);
162 : };
163 :
164 116 : REGISTER_COMPONENT_TYPE(UnitMotionManager)
165 :
166 : #endif // INCLUDED_CCMPUNITMOTIONMANAGER
|