| 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 |
|
|---|
| 28 | class MockVisionRgm : public ICmpVision
|
|---|
| 29 | {
|
|---|
| 30 | public:
|
|---|
| 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 |
|
|---|
| 37 | class MockPositionRgm : public ICmpPosition
|
|---|
| 38 | {
|
|---|
| 39 | public:
|
|---|
| 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 |
|
|---|
| 78 | class MockObstructionRgm : public ICmpObstruction
|
|---|
| 79 | {
|
|---|
| 80 | public:
|
|---|
| 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 {}; };
|
|---|
| 111 | private:
|
|---|
| 112 | entity_pos_t m_Size;
|
|---|
| 113 | };
|
|---|
| 114 |
|
|---|
| 115 | class TestCmpRangeManager : public CxxTest::TestSuite
|
|---|
| 116 | {
|
|---|
| 117 | public:
|
|---|
| 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 | };
|
|---|