| 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 "simulation2/system/ComponentTest.h"
|
|---|
| 19 |
|
|---|
| 20 | #include "maths/Matrix3D.h"
|
|---|
| 21 | #include "simulation2/components/ICmpPosition.h"
|
|---|
| 22 | #include "simulation2/components/ICmpWaterManager.h"
|
|---|
| 23 |
|
|---|
| 24 | class MockWater : public ICmpWaterManager
|
|---|
| 25 | {
|
|---|
| 26 | public:
|
|---|
| 27 | DEFAULT_MOCK_COMPONENT()
|
|---|
| 28 |
|
|---|
| 29 | entity_pos_t GetWaterLevel(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) const override
|
|---|
| 30 | {
|
|---|
| 31 | return entity_pos_t::FromInt(100);
|
|---|
| 32 | }
|
|---|
| 33 |
|
|---|
| 34 | float GetExactWaterLevel(float UNUSED(x), float UNUSED(z)) const override
|
|---|
| 35 | {
|
|---|
| 36 | return 100.f;
|
|---|
| 37 | }
|
|---|
| 38 |
|
|---|
| 39 | void RecomputeWaterData() override
|
|---|
| 40 | {
|
|---|
| 41 | }
|
|---|
| 42 |
|
|---|
| 43 | void SetWaterLevel(entity_pos_t UNUSED(h)) override
|
|---|
| 44 | {
|
|---|
| 45 | }
|
|---|
| 46 | };
|
|---|
| 47 |
|
|---|
| 48 | class TestCmpPosition : public CxxTest::TestSuite
|
|---|
| 49 | {
|
|---|
| 50 | public:
|
|---|
| 51 | void setUp()
|
|---|
| 52 | {
|
|---|
| 53 | CXeromyces::Startup();
|
|---|
| 54 | }
|
|---|
| 55 |
|
|---|
| 56 | void tearDown()
|
|---|
| 57 | {
|
|---|
| 58 | CXeromyces::Terminate();
|
|---|
| 59 | }
|
|---|
| 60 |
|
|---|
| 61 | static CFixedVector3D fixedvec(int x, int y, int z)
|
|---|
| 62 | {
|
|---|
| 63 | return CFixedVector3D(fixed::FromInt(x), fixed::FromInt(y), fixed::FromInt(z));
|
|---|
| 64 | }
|
|---|
| 65 |
|
|---|
| 66 | void test_basic()
|
|---|
| 67 | {
|
|---|
| 68 | ComponentTestHelper test(*g_ScriptContext);
|
|---|
| 69 |
|
|---|
| 70 | MockTerrain terrain;
|
|---|
| 71 | test.AddMock(SYSTEM_ENTITY, IID_Terrain, terrain);
|
|---|
| 72 |
|
|---|
| 73 | ICmpPosition* cmp = test.Add<ICmpPosition>(CID_Position, "<Anchor>upright</Anchor><Altitude>23</Altitude><Floating>false</Floating>");
|
|---|
| 74 |
|
|---|
| 75 | // Defaults
|
|---|
| 76 | TS_ASSERT(!cmp->IsInWorld());
|
|---|
| 77 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(23));
|
|---|
| 78 | TS_ASSERT_EQUALS(cmp->GetRotation(), fixedvec(0, 0, 0));
|
|---|
| 79 |
|
|---|
| 80 | // Change height offset
|
|---|
| 81 | cmp->SetHeightOffset(entity_pos_t::FromInt(10));
|
|---|
| 82 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(10));
|
|---|
| 83 |
|
|---|
| 84 | // Move out of world, while currently out of world
|
|---|
| 85 | cmp->MoveOutOfWorld();
|
|---|
| 86 | TS_ASSERT(!cmp->IsInWorld());
|
|---|
| 87 |
|
|---|
| 88 | // Jump into world
|
|---|
| 89 | cmp->JumpTo(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0));
|
|---|
| 90 | TS_ASSERT(cmp->IsInWorld());
|
|---|
| 91 | // Move out of world, while currently in world
|
|---|
| 92 | cmp->MoveOutOfWorld();
|
|---|
| 93 | TS_ASSERT(!cmp->IsInWorld());
|
|---|
| 94 |
|
|---|
| 95 | // Move into world
|
|---|
| 96 | cmp->MoveTo(entity_pos_t::FromInt(100), entity_pos_t::FromInt(200));
|
|---|
| 97 | TS_ASSERT(cmp->IsInWorld());
|
|---|
| 98 | // Position computed from move plus terrain
|
|---|
| 99 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 60, 200));
|
|---|
| 100 | // Interpolated position should be constant
|
|---|
| 101 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(100, 60, 200));
|
|---|
| 102 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(100, 60, 200));
|
|---|
| 103 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(100, 60, 200));
|
|---|
| 104 |
|
|---|
| 105 | // No TurnStart message, so this move doesn't affect the interpolation
|
|---|
| 106 | cmp->MoveTo(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0));
|
|---|
| 107 | // Move smoothly to new position
|
|---|
| 108 | cmp->MoveTo(entity_pos_t::FromInt(200), entity_pos_t::FromInt(0));
|
|---|
| 109 | // Position computed from move plus terrain
|
|---|
| 110 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(200, 60, 0));
|
|---|
| 111 | // Interpolated position should vary, from original move into world to new move
|
|---|
| 112 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(100, 60, 200));
|
|---|
| 113 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(150, 60, 100));
|
|---|
| 114 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(200, 60, 0));
|
|---|
| 115 |
|
|---|
| 116 | // Latch new position for interpolation
|
|---|
| 117 | CMessageTurnStart msg;
|
|---|
| 118 | test.HandleMessage(cmp, msg, false);
|
|---|
| 119 |
|
|---|
| 120 | // Move smoothly to new position
|
|---|
| 121 | cmp->MoveTo(entity_pos_t::FromInt(400), entity_pos_t::FromInt(300));
|
|---|
| 122 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(200, 60, 0));
|
|---|
| 123 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(300, 60, 150));
|
|---|
| 124 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(400, 60, 300));
|
|---|
| 125 |
|
|---|
| 126 | // Jump to new position
|
|---|
| 127 | cmp->JumpTo(entity_pos_t::FromInt(300), entity_pos_t::FromInt(100));
|
|---|
| 128 | // Position computed from jump plus terrain
|
|---|
| 129 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(300, 60, 100));
|
|---|
| 130 | // Interpolated position should be constant after jump
|
|---|
| 131 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(300, 60, 100));
|
|---|
| 132 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(300, 60, 100));
|
|---|
| 133 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(300, 60, 100));
|
|---|
| 134 |
|
|---|
| 135 | // TODO: Test the rotation methods
|
|---|
| 136 | }
|
|---|
| 137 |
|
|---|
| 138 | void test_water()
|
|---|
| 139 | {
|
|---|
| 140 | ComponentTestHelper test(*g_ScriptContext);
|
|---|
| 141 |
|
|---|
| 142 | MockTerrain terrain;
|
|---|
| 143 | test.AddMock(SYSTEM_ENTITY, IID_Terrain, terrain);
|
|---|
| 144 |
|
|---|
| 145 | MockWater water;
|
|---|
| 146 | test.AddMock(SYSTEM_ENTITY, IID_WaterManager, water);
|
|---|
| 147 |
|
|---|
| 148 | ICmpPosition* cmp = test.Add<ICmpPosition>(CID_Position, "<Anchor>upright</Anchor><Altitude>23</Altitude><Floating>true</Floating><FloatDepth>1</FloatDepth>");
|
|---|
| 149 |
|
|---|
| 150 | // Move into the world, the fixed height uses the water level minus the float depth as a base
|
|---|
| 151 | cmp->JumpTo(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0));
|
|---|
| 152 | TS_ASSERT(cmp->IsInWorld());
|
|---|
| 153 | TS_ASSERT(cmp->CanFloat());
|
|---|
| 154 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(23));
|
|---|
| 155 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(122));
|
|---|
| 156 |
|
|---|
| 157 | // Change height offset, the fixed height changes too
|
|---|
| 158 | cmp->SetHeightOffset(entity_pos_t::FromInt(11));
|
|---|
| 159 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(11));
|
|---|
| 160 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(110));
|
|---|
| 161 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(0, 110, 0));
|
|---|
| 162 |
|
|---|
| 163 | // Move
|
|---|
| 164 | cmp->MoveTo(entity_pos_t::FromInt(100), entity_pos_t::FromInt(200));
|
|---|
| 165 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(0, 122, 0));
|
|---|
| 166 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(50, 116, 100));
|
|---|
| 167 | TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(100, 110, 200));
|
|---|
| 168 |
|
|---|
| 169 | // Change fixed height, the height offset changes too
|
|---|
| 170 | cmp->SetHeightFixed(entity_pos_t::FromInt(122));
|
|---|
| 171 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(122));
|
|---|
| 172 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(23));
|
|---|
| 173 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 122, 200));
|
|---|
| 174 |
|
|---|
| 175 | // The entity can't float anymore, the fixed height is computed from the terrain base
|
|---|
| 176 | cmp->SetFloating(false);
|
|---|
| 177 | TS_ASSERT(!cmp->CanFloat());
|
|---|
| 178 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(73));
|
|---|
| 179 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(23));
|
|---|
| 180 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 73, 200));
|
|---|
| 181 |
|
|---|
| 182 | // The entity can float again
|
|---|
| 183 | cmp->SetFloating(true);
|
|---|
| 184 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(122));
|
|---|
| 185 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(23));
|
|---|
| 186 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 122, 200));
|
|---|
| 187 |
|
|---|
| 188 | // Use non relative height, entity will not follow terrain/water height.
|
|---|
| 189 | TS_ASSERT(cmp->IsHeightRelative());
|
|---|
| 190 | cmp->SetHeightRelative(false);
|
|---|
| 191 | TS_ASSERT(!cmp->IsHeightRelative());
|
|---|
| 192 |
|
|---|
| 193 | cmp->SetHeightOffset(entity_pos_t::FromInt(11));
|
|---|
| 194 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(11));
|
|---|
| 195 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(110));
|
|---|
| 196 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 110, 200));
|
|---|
| 197 |
|
|---|
| 198 | cmp->SetHeightFixed(entity_pos_t::FromInt(122));
|
|---|
| 199 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(122));
|
|---|
| 200 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(23));
|
|---|
| 201 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 122, 200));
|
|---|
| 202 |
|
|---|
| 203 | // The entity can't float anymore and height is not relative, fixed height doesn't change
|
|---|
| 204 | cmp->SetFloating(false);
|
|---|
| 205 | TS_ASSERT(!cmp->CanFloat());
|
|---|
| 206 | TS_ASSERT_EQUALS(cmp->GetHeightFixed(), entity_pos_t::FromInt(122));
|
|---|
| 207 | TS_ASSERT_EQUALS(cmp->GetHeightOffset(), entity_pos_t::FromInt(72));
|
|---|
| 208 | TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 122, 200));
|
|---|
| 209 | }
|
|---|
| 210 |
|
|---|
| 211 | void test_serialize()
|
|---|
| 212 | {
|
|---|
| 213 | ComponentTestHelper test(*g_ScriptContext);
|
|---|
| 214 |
|
|---|
| 215 | MockTerrain terrain;
|
|---|
| 216 | test.AddMock(SYSTEM_ENTITY, IID_Terrain, terrain);
|
|---|
| 217 |
|
|---|
| 218 | ICmpPosition* cmp = test.Add<ICmpPosition>(CID_Position, "<Anchor>upright</Anchor><Altitude>5</Altitude><Floating>false</Floating>");
|
|---|
| 219 |
|
|---|
| 220 | test.Roundtrip();
|
|---|
| 221 |
|
|---|
| 222 | cmp->SetHeightOffset(entity_pos_t::FromInt(20));
|
|---|
| 223 | cmp->SetXZRotation(entity_angle_t::FromInt(1), entity_angle_t::FromInt(2));
|
|---|
| 224 | cmp->SetYRotation(entity_angle_t::FromInt(3));
|
|---|
| 225 |
|
|---|
| 226 | test.Roundtrip();
|
|---|
| 227 |
|
|---|
| 228 | cmp->JumpTo(entity_pos_t::FromInt(10), entity_pos_t::FromInt(20));
|
|---|
| 229 | cmp->MoveTo(entity_pos_t::FromInt(123), entity_pos_t::FromInt(456));
|
|---|
| 230 |
|
|---|
| 231 | test.Roundtrip();
|
|---|
| 232 | }
|
|---|
| 233 | };
|
|---|