This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

source: ps/trunk/source/simulation2/components/tests/test_RangeManager.h

Last change on this file was 27965, checked in by Vladislav Belov, 13 months ago

Revert non-ASCII characters from source and configuration files introduced in rP27786.

Fixes #6846

Differential Revision: https://code.wildfiregames.com/D5185

  • Property svn:eol-style set to native
File size: 14.4 KB
Line 
1/* Copyright (C) 2023 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#include "maths/Matrix3D.h"
19#include "simulation2/system/ComponentTest.h"
20#include "simulation2/components/ICmpRangeManager.h"
21#include "simulation2/components/ICmpObstruction.h"
22#include "simulation2/components/ICmpPosition.h"
23#include "simulation2/components/ICmpVision.h"
24
25#include <boost/random/mersenne_twister.hpp>
26#include <boost/random/uniform_real_distribution.hpp>
27
28class MockVisionRgm : public ICmpVision
29{
30public:
31 DEFAULT_MOCK_COMPONENT()
32
33 entity_pos_t GetRange() const override { return entity_pos_t::FromInt(66); }
34 bool GetRevealShore() const override { return false; }
35};
36
37class MockPositionRgm : public ICmpPosition
38{
39public:
40 DEFAULT_MOCK_COMPONENT()
41
42 void SetTurretParent(entity_id_t UNUSED(id), const CFixedVector3D& UNUSED(pos)) override {}
43 entity_id_t GetTurretParent() const override {return INVALID_ENTITY;}
44 void UpdateTurretPosition() override {}
45 std::set<entity_id_t>* GetTurrets() override { return nullptr; }
46 bool IsInWorld() const override { return true; }
47 void MoveOutOfWorld() override { }
48 void MoveTo(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) override { }
49 void MoveAndTurnTo(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z), entity_angle_t UNUSED(a)) override { }
50 void JumpTo(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) override { }
51 void SetHeightOffset(entity_pos_t UNUSED(dy)) override { }
52 entity_pos_t GetHeightOffset() const override { return entity_pos_t::Zero(); }
53 void SetHeightFixed(entity_pos_t UNUSED(y)) override { }
54 entity_pos_t GetHeightFixed() const override { return entity_pos_t::Zero(); }
55 entity_pos_t GetHeightAtFixed(entity_pos_t, entity_pos_t) const override { return entity_pos_t::Zero(); }
56 bool IsHeightRelative() const override { return true; }
57 void SetHeightRelative(bool UNUSED(relative)) override { }
58 bool CanFloat() const override { return false; }
59 void SetFloating(bool UNUSED(flag)) override { }
60 void SetActorFloating(bool UNUSED(flag)) override { }
61 void SetConstructionProgress(fixed UNUSED(progress)) override { }
62 CFixedVector3D GetPosition() const override { return m_Pos; }
63 CFixedVector2D GetPosition2D() const override { return CFixedVector2D(m_Pos.X, m_Pos.Z); }
64 CFixedVector3D GetPreviousPosition() const override { return CFixedVector3D(); }
65 CFixedVector2D GetPreviousPosition2D() const override { return CFixedVector2D(); }
66 fixed GetTurnRate() const override { return fixed::Zero(); }
67 void TurnTo(entity_angle_t UNUSED(y)) override { }
68 void SetYRotation(entity_angle_t UNUSED(y)) override { }
69 void SetXZRotation(entity_angle_t UNUSED(x), entity_angle_t UNUSED(z)) override { }
70 CFixedVector3D GetRotation() const override { return CFixedVector3D(); }
71 fixed GetDistanceTravelled() const override { return fixed::Zero(); }
72 void GetInterpolatedPosition2D(float UNUSED(frameOffset), float& x, float& z, float& rotY) const override { x = z = rotY = 0; }
73 CMatrix3D GetInterpolatedTransform(float UNUSED(frameOffset)) const override { return CMatrix3D(); }
74
75 CFixedVector3D m_Pos;
76};
77
78class MockObstructionRgm : public ICmpObstruction
79{
80public:
81 DEFAULT_MOCK_COMPONENT();
82
83 MockObstructionRgm(entity_pos_t s) : m_Size(s) {};
84
85 ICmpObstructionManager::tag_t GetObstruction() const override { return {}; };
86 bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare&) const override { return false; };
87 bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare&) const override { return false; };
88 entity_pos_t GetSize() const override { return m_Size; };
89 CFixedVector2D GetStaticSize() const override { return {}; };
90 EObstructionType GetObstructionType() const override { return {}; };
91 void SetUnitClearance(const entity_pos_t&) override {};
92 bool IsControlPersistent() const override { return {}; };
93 bool CheckShorePlacement() const override { return {}; };
94 EFoundationCheck CheckFoundation(const std::string&) const override { return {}; };
95 EFoundationCheck CheckFoundation(const std::string& , bool) const override { return {}; };
96 std::string CheckFoundation_wrapper(const std::string&, bool) const override { return {}; };
97 bool CheckDuplicateFoundation() const override { return {}; };
98 std::vector<entity_id_t> GetEntitiesByFlags(ICmpObstructionManager::flags_t) const override { return {}; };
99 std::vector<entity_id_t> GetEntitiesBlockingMovement() const override { return {}; };
100 std::vector<entity_id_t> GetEntitiesBlockingConstruction() const override { return {}; };
101 std::vector<entity_id_t> GetEntitiesDeletedUponConstruction() const override { return {}; };
102 void ResolveFoundationCollisions() const override {};
103 void SetActive(bool) override {};
104 void SetMovingFlag(bool) override {};
105 void SetDisableBlockMovementPathfinding(bool, bool, int32_t) override {};
106 bool GetBlockMovementFlag(bool) const override { return {}; };
107 void SetControlGroup(entity_id_t) override {};
108 entity_id_t GetControlGroup() const override { return {}; };
109 void SetControlGroup2(entity_id_t) override {};
110 entity_id_t GetControlGroup2() const override { return {}; };
111private:
112 entity_pos_t m_Size;
113};
114
115class TestCmpRangeManager : public CxxTest::TestSuite
116{
117public:
118 void setUp()
119 {
120 CXeromyces::Startup();
121 }
122
123 void tearDown()
124 {
125 CXeromyces::Terminate();
126 }
127
128 // TODO It would be nice to call Verify() with the shore revealing system
129 // but that means testing on an actual map, with water and land.
130
131 void test_basic()
132 {
133 ComponentTestHelper test(*g_ScriptContext);
134
135 ICmpRangeManager* cmp = test.Add<ICmpRangeManager>(CID_RangeManager, "", SYSTEM_ENTITY);
136
137 MockVisionRgm vision;
138 test.AddMock(100, IID_Vision, vision);
139
140 MockPositionRgm position;
141 test.AddMock(100, IID_Position, position);
142
143 // This tests that the incremental computation produces the correct result
144 // in various edge cases
145
146 cmp->SetBounds(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0), entity_pos_t::FromInt(512), entity_pos_t::FromInt(512));
147 cmp->Verify();
148 { CMessageCreate msg(100); cmp->HandleMessage(msg, false); }
149 cmp->Verify();
150 { CMessageOwnershipChanged msg(100, -1, 1); cmp->HandleMessage(msg, false); }
151 cmp->Verify();
152 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(247), entity_pos_t::FromDouble(257.95), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
153 cmp->Verify();
154 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(247), entity_pos_t::FromInt(253), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
155 cmp->Verify();
156
157 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(256), entity_pos_t::FromInt(256), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
158 cmp->Verify();
159
160 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(256)+entity_pos_t::Epsilon(), entity_pos_t::FromInt(256), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
161 cmp->Verify();
162 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(256)-entity_pos_t::Epsilon(), entity_pos_t::FromInt(256), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
163 cmp->Verify();
164 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(256), entity_pos_t::FromInt(256)+entity_pos_t::Epsilon(), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
165 cmp->Verify();
166 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(256), entity_pos_t::FromInt(256)-entity_pos_t::Epsilon(), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
167 cmp->Verify();
168
169 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(383), entity_pos_t::FromInt(84), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
170 cmp->Verify();
171 { CMessagePositionChanged msg(100, true, entity_pos_t::FromInt(348), entity_pos_t::FromInt(83), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
172 cmp->Verify();
173
174 boost::mt19937 rng;
175 for (size_t i = 0; i < 1024; ++i)
176 {
177 double x = boost::random::uniform_real_distribution<double>(0.0, 512.0)(rng);
178 double z = boost::random::uniform_real_distribution<double>(0.0, 512.0)(rng);
179 { CMessagePositionChanged msg(100, true, entity_pos_t::FromDouble(x), entity_pos_t::FromDouble(z), entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
180 cmp->Verify();
181 }
182
183 // Test OwnershipChange, GetEntitiesByPlayer, GetNonGaiaEntities
184 {
185 player_id_t previousOwner = -1;
186 for (player_id_t newOwner = 0; newOwner < 8; ++newOwner)
187 {
188 CMessageOwnershipChanged msg(100, previousOwner, newOwner);
189 cmp->HandleMessage(msg, false);
190
191 for (player_id_t i = 0; i < 8; ++i)
192 TS_ASSERT_EQUALS(cmp->GetEntitiesByPlayer(i).size(), i == newOwner ? 1 : 0);
193
194 TS_ASSERT_EQUALS(cmp->GetNonGaiaEntities().size(), newOwner > 0 ? 1 : 0);
195 previousOwner = newOwner;
196 }
197 }
198 }
199
200 void test_queries()
201 {
202 ComponentTestHelper test(*g_ScriptContext);
203
204 ICmpRangeManager* cmp = test.Add<ICmpRangeManager>(CID_RangeManager, "", SYSTEM_ENTITY);
205
206 MockVisionRgm vision, vision2;
207 MockPositionRgm position, position2;
208 MockObstructionRgm obs(fixed::FromInt(2)), obs2(fixed::Zero());
209 test.AddMock(100, IID_Vision, vision);
210 test.AddMock(100, IID_Position, position);
211 test.AddMock(100, IID_Obstruction, obs);
212
213 test.AddMock(101, IID_Vision, vision2);
214 test.AddMock(101, IID_Position, position2);
215 test.AddMock(101, IID_Obstruction, obs2);
216
217 cmp->SetBounds(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0), entity_pos_t::FromInt(512), entity_pos_t::FromInt(512));
218 cmp->Verify();
219 { CMessageCreate msg(100); cmp->HandleMessage(msg, false); }
220 { CMessageCreate msg(101); cmp->HandleMessage(msg, false); }
221
222 { CMessageOwnershipChanged msg(100, -1, 1); cmp->HandleMessage(msg, false); }
223 { CMessageOwnershipChanged msg(101, -1, 1); cmp->HandleMessage(msg, false); }
224
225 auto move = [&cmp](entity_id_t ent, MockPositionRgm& pos, fixed x, fixed z) {
226 pos.m_Pos = CFixedVector3D(x, fixed::Zero(), z);
227 { CMessagePositionChanged msg(ent, true, x, z, entity_angle_t::Zero()); cmp->HandleMessage(msg, false); }
228 };
229
230 move(100, position, fixed::FromInt(10), fixed::FromInt(10));
231 move(101, position2, fixed::FromInt(10), fixed::FromInt(20));
232
233 std::vector<entity_id_t> nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
234 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
235 nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0, true);
236 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
237
238 move(101, position2, fixed::FromInt(10), fixed::FromInt(10));
239 nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
240 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
241 nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0, true);
242 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
243
244 move(101, position2, fixed::FromInt(10), fixed::FromInt(13));
245 nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
246 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
247 nearby = cmp->ExecuteQuery(100, fixed::FromInt(4), fixed::FromInt(50), {1}, 0, true);
248 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
249
250 move(101, position2, fixed::FromInt(10), fixed::FromInt(15));
251 // In range thanks to self obstruction size.
252 nearby = cmp->ExecuteQuery(100, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
253 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
254 // In range thanks to target obstruction size.
255 nearby = cmp->ExecuteQuery(101, fixed::FromInt(0), fixed::FromInt(4), {1}, 0, true);
256 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{100});
257
258 // Trickier: min-range is closest-to-closest, but rotation may change the real distance.
259 nearby = cmp->ExecuteQuery(100, fixed::FromInt(2), fixed::FromInt(50), {1}, 0, true);
260 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
261 nearby = cmp->ExecuteQuery(100, fixed::FromInt(5), fixed::FromInt(50), {1}, 0, true);
262 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{101});
263 nearby = cmp->ExecuteQuery(100, fixed::FromInt(6), fixed::FromInt(50), {1}, 0, true);
264 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
265 nearby = cmp->ExecuteQuery(101, fixed::FromInt(5), fixed::FromInt(50), {1}, 0, true);
266 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{100});
267 nearby = cmp->ExecuteQuery(101, fixed::FromInt(6), fixed::FromInt(50), {1}, 0, true);
268 TS_ASSERT_EQUALS(nearby, std::vector<entity_id_t>{});
269
270 }
271
272 void test_IsInTargetParabolicRange()
273 {
274 ComponentTestHelper test(*g_ScriptContext);
275 ICmpRangeManager* cmp = test.Add<ICmpRangeManager>(CID_RangeManager, "", SYSTEM_ENTITY);
276 const entity_id_t source = 200;
277 const entity_id_t target = 201;
278 entity_pos_t range = fixed::FromInt(-3);
279 entity_pos_t yOrigin = fixed::FromInt(-20);
280
281 // Invalid range.
282 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, range, yOrigin), range);
283
284 // No source ICmpPosition.
285 range = fixed::FromInt(10);
286 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, range, yOrigin), NEVER_IN_RANGE);
287
288 // No target ICmpPosition.
289 MockPositionRgm cmpSourcePosition;
290 test.AddMock(source, IID_Position, cmpSourcePosition);
291 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, range, yOrigin), NEVER_IN_RANGE);
292
293 // Too much height difference.
294 MockPositionRgm cmpTargetPosition;
295 test.AddMock(target, IID_Position, cmpTargetPosition);
296 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, range, yOrigin), NEVER_IN_RANGE);
297
298 // If no offset we get the range.
299 range = fixed::FromInt(20);
300 yOrigin = fixed::Zero();
301 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, range, yOrigin), range);
302 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, fixed::Zero(), yOrigin), fixed::Zero());
303
304 // Normal case.
305 yOrigin = fixed::FromInt(5);
306 range = fixed::FromInt(10);
307 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, range, yOrigin), fixed::FromFloat(14.142136f));
308
309 // Big range.
310 range = fixed::FromInt(260);
311 TS_ASSERT_EQUALS(cmp->GetEffectiveParabolicRange(source, target, range, yOrigin), fixed::FromFloat(264.952820f));
312 }
313};
Note: See TracBrowser for help on using the repository browser.