A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectChoropu.cc
1#include "ObjectChoropu.hh"
2
3#include "game/field/ObjectDirector.hh"
4#include "game/field/RailManager.hh"
5
6#include "game/system/RaceManager.hh"
7
8namespace Field {
9
11ObjectChoropu::ObjectChoropu(const System::MapdataGeoObj &params)
12 : ObjectCollidable(params), StateManager(this) {
13 constexpr f32 MAX_SPEED = 20.0f;
14
15 m_startFrameOffset = static_cast<s16>(params.setting(1));
16 m_idleDuration = params.setting(0);
17 m_isStationary = strcmp(getName(), "choropu") != 0;
18
19 s16 railIdx = params.pathId();
20 if (railIdx != -1) {
21 auto *rail = RailManager::Instance()->rail(railIdx);
22 rail->checkSphereFull();
23 }
24
25 // If the mole moves around, then we need to create the dirt trail.
26 if (!m_isStationary) {
27 const auto &flowTable = ObjectDirector::Instance()->flowTable();
28 const auto *collisionSet =
29 flowTable.set(flowTable.slot(flowTable.getIdFromName("choropu_ground")));
30 ASSERT(collisionSet);
31
32 s16 height = parse<s16>(collisionSet->params.cylinder.height);
33 size_t groundCount = static_cast<size_t>(MAX_GROUND_LEN / EGG::Mathf::abs(height * 2)) + 1;
34 m_groundObjs = std::span<ObjectChoropuGround *>(new ObjectChoropuGround *[groundCount],
35 groundCount);
36
37 for (auto *&obj : m_groundObjs) {
38 obj = new ObjectChoropuGround(m_pos, m_rot, m_scale);
39 obj->load();
40 obj->resize(RADIUS, MAX_SPEED);
41 }
42
43 m_groundHeight = m_groundObjs.front()->height();
44 }
45
46 m_objHoll = new ObjectChoropuHoll(params);
47 m_objHoll->load();
48}
49
51ObjectChoropu::~ObjectChoropu() {
52 delete[] m_groundObjs.data();
53}
54
56void ObjectChoropu::init() {
57 if (m_isStationary) {
58 disableCollision();
59
60 m_scale.x = 1.0f;
61 m_objHoll->setScale(EGG::Vector3f(1.0f, m_objHoll->scale().y, 1.0f));
62 m_nextStateId = 0;
63 m_groundLength = 0.0f;
64
65 calcTransform();
66 m_transMat = m_transform;
67 } else {
68 if (m_mapObj->pathId() == -1) {
69 return;
70 }
71
72 m_railInterpolator->init(0.0f, 0);
73 m_railInterpolator->setPerPointVelocities(true);
74
75 m_railMat = RailOrthonormalBasis(*m_railInterpolator);
76 m_pos = m_railMat.base(3);
77 m_flags.setBit(eFlags::Position);
78
79 disableCollision();
80 m_objHoll->disableCollision();
81
82 m_nextStateId = 0;
83 }
84}
85
87void ObjectChoropu::calc() {
88 constexpr u32 START_DELAY = 300;
89
90 // Nothing to do if the mole hasn't spawned yet
91 u32 t = System::RaceManager::Instance()->timer();
92 if (t < m_startFrameOffset + START_DELAY) {
93 return;
94 }
95
96 if (!m_isStationary) {
97 if (m_mapObj->pathId() == -1) {
98 return;
99 }
100
101 m_railMat = RailOrthonormalBasis(*m_railInterpolator);
102 }
103
104 if (m_nextStateId >= 0) {
105 m_currentStateId = m_nextStateId;
106 m_nextStateId = -1;
107 m_currentFrame = 0;
108
109 auto enterFunc = m_entries[m_entryIds[m_currentStateId]].onEnter;
110 (this->*enterFunc)();
111 } else {
112 ++m_currentFrame;
113 }
114
115 auto calcFunc = m_entries[m_entryIds[m_currentStateId]].onCalc;
116 (this->*calcFunc)();
117
118 m_objHoll->setScale(EGG::Vector3f(1.0f, m_objHoll->scale().y, 1.0f));
119}
120
122Kart::Reaction ObjectChoropu::onCollision(Kart::KartObject * /*kartObj*/,
123 Kart::Reaction reactionOnKart, Kart::Reaction /*reactionOnObj*/,
124 EGG::Vector3f & /*hitDepth*/) {
125 return m_currentStateId == 1 ? Kart::Reaction::SmallBump : reactionOnKart;
126}
127
128void ObjectChoropu::enterStateStub() {}
129
131void ObjectChoropu::enterDigging() {
132 if (m_isStationary) {
133 disableCollision();
134 } else {
135 for (auto *&obj : m_groundObjs) {
136 obj->enableCollision();
137 }
138
139 disableCollision();
140 m_objHoll->disableCollision();
141 m_groundLength = 0.0f;
142 }
143}
144
146void ObjectChoropu::enterPeeking() {
147 if (m_isStationary) {
148 m_pos = m_transMat.base(3);
149 m_flags.setBit(eFlags::Position, eFlags::Rotation);
150 m_rot.z = 0.0f;
151
152 enableCollision();
153 } else {
154 m_pos = m_railMat.base(3);
155 m_flags.setBit(eFlags::Position, eFlags::Rotation);
156 m_rot.z = 0.0f;
157 m_rot.y = 0.0f;
158
159 enableCollision();
160
161 const auto &curTanDir = m_railInterpolator->curTangentDir();
162 s16 curPointIdx = m_railInterpolator->curPointIdx();
163 EGG::Matrix34f mat;
164 SetRotTangentHorizontal(mat, m_railInterpolator->floorNrm(curPointIdx), curTanDir);
165 mat.setBase(3, m_railInterpolator->curPos());
166
167 m_objHoll->setTransform(mat);
168 m_objHoll->setPos(mat.base(3));
169 m_objHoll->enableCollision();
170 }
171}
172
174void ObjectChoropu::enterJumping() {
175 enableCollision();
176
177 m_pos = m_isStationary ? m_transMat.base(3) : m_railMat.base(3);
178 m_flags.setBit(eFlags::Position, eFlags::Rotation);
179 m_rot.z = 0.0f;
180}
181
182void ObjectChoropu::calcStateStub() {}
183
185void ObjectChoropu::calcDigging() {
186 if (m_isStationary) {
187 if (m_currentFrame > m_idleDuration) {
188 m_nextStateId = 1;
189 }
190
191 return;
192 }
193
194 m_pos = m_railInterpolator->curPos();
195 m_flags.setBit(eFlags::Position);
196
197 if (m_railInterpolator->calc() == RailInterpolator::Status::SegmentEnd) {
198 if (m_railInterpolator->curPoint().setting[1] == 1) {
199 m_nextStateId = 1;
200 } else {
201 calcGround();
202 }
203 } else {
204 bool skipGroundCalc = false;
205
206 if (m_railInterpolator->nextPoint().setting[1] == 1) {
207 f32 invT = 1.0f - m_railInterpolator->segmentT();
208 if (invT * m_railInterpolator->getCurrSegmentLength() < 250.0f) {
209 skipGroundCalc = true;
210 }
211 }
212
213 if (!skipGroundCalc) {
214 calcGround();
215 }
216 }
217}
218
220void ObjectChoropu::calcPeeking() {
221 constexpr s16 PEEK_DURATION = 40;
222 constexpr s16 STATE_DURATION = 100;
223
224 if (!m_isStationary) {
225 m_groundLength = std::max(0.0f, m_groundLength - m_railInterpolator->speed());
226
227 calcGroundObjs();
228 }
229
230 if (m_currentFrame > STATE_DURATION) {
231 m_nextStateId = 3;
232 }
233
234 if (m_currentFrame > PEEK_DURATION) {
235 disableCollision();
236 }
237}
238
240void ObjectChoropu::calcJumping() {
241 constexpr f32 JUMP_LINEAR_COEFFICIENT = 65.0f;
242 constexpr f32 JUMP_QUADRATIC_COEFFICIENT = 2.7f;
243
244 if (!m_isStationary) {
245 m_groundLength = std::max(0.0f, m_groundLength - m_railInterpolator->speed());
246 calcGroundObjs();
247 }
248
249 // y = -1.35t^2 + 65.0t
250 f32 pos = JUMP_LINEAR_COEFFICIENT * static_cast<f32>(m_currentFrame) -
251 static_cast<f32>(m_currentFrame) * 0.5f * JUMP_QUADRATIC_COEFFICIENT *
252 static_cast<f32>(m_currentFrame);
253
254 if (pos < 0.0f) {
255 m_nextStateId = 0;
256 }
257
258 m_pos.y = pos + (m_isStationary ? m_transMat.base(3).y : m_railInterpolator->curPos().y);
259 m_flags.setBit(eFlags::Position);
260}
261
263void ObjectChoropu::calcGround() {
264 m_groundLength += m_railInterpolator->getCurrVel();
265 if (m_groundLength > MAX_GROUND_LEN) {
266 m_groundLength = MAX_GROUND_LEN - 1.0f;
267 }
268
269 calcGroundObjs();
270}
271
273void ObjectChoropu::calcGroundObjs() {
274 size_t idx =
275 std::min(static_cast<size_t>(m_groundLength / m_groundHeight) + 1, m_groundObjs.size());
276
277 for (auto *&obj : m_groundObjs) {
278 obj->enableCollision();
279 }
280
281 for (size_t i = idx; i < m_groundObjs.size(); ++i) {
282 m_groundObjs[i]->disableCollision();
283 }
284
285 if (m_groundLength > RADIUS) {
286 f32 height = std::min(m_groundHeight, m_groundLength) - RADIUS;
287 m_groundObjs[0]->calcPosAndMat(height, calcInterpolatedPose(RADIUS + 0.5f * height));
288 }
289
290 for (size_t i = 1; i < idx - 1; ++i) {
291 f32 height = 0.5f * m_groundHeight + m_groundHeight * static_cast<f32>(i);
292 m_groundObjs[i]->calcPosAndMat(m_groundHeight, calcInterpolatedPose(height));
293 }
294
295 f32 height = m_groundLength - m_groundHeight * static_cast<f32>(idx - 1);
296 EGG::Matrix34f mat =
297 calcInterpolatedPose(0.5f * height + m_groundHeight * static_cast<f32>(idx - 1));
298 m_groundObjs[idx - 1]->calcPosAndMat(height, mat);
299}
300
302EGG::Matrix34f ObjectChoropu::calcInterpolatedPose(f32 t) const {
303 EGG::Vector3f curDir;
304 EGG::Vector3f curTanDir;
305 m_railInterpolator->evalCubicBezierOnPath(t, curDir, curTanDir);
306 EGG::Matrix34f mat = OrthonormalBasis(curTanDir);
307 mat.setBase(3, curDir);
308 return mat;
309}
310
311const std::array<StateManagerEntry<ObjectChoropu>, 5> StateManager<ObjectChoropu>::STATE_ENTRIES = {
312 {
313 {0, &ObjectChoropu::enterDigging, &ObjectChoropu::calcDigging},
314 {1, &ObjectChoropu::enterPeeking, &ObjectChoropu::calcPeeking},
315 {2, &ObjectChoropu::enterStateStub, &ObjectChoropu::calcStateStub},
316 {3, &ObjectChoropu::enterJumping, &ObjectChoropu::calcJumping},
317 {4, &ObjectChoropu::enterStateStub, &ObjectChoropu::calcStateStub},
318 }};
319
320StateManager<ObjectChoropu>::StateManager(ObjectChoropu *obj) {
321 constexpr size_t ENTRY_COUNT = 5;
322
323 m_obj = obj;
324 m_entries = std::span{STATE_ENTRIES};
325 m_entryIds = std::span(new u16[ENTRY_COUNT], ENTRY_COUNT);
326
327 // The base game initializes all entries to 0xffff, possibly to avoid an uninitialized value
328 for (auto &id : m_entryIds) {
329 id = 0xffff;
330 }
331
332 for (size_t i = 0; i < m_entryIds.size(); ++i) {
333 m_entryIds[STATE_ENTRIES[i].id] = i;
334 }
335}
336
337StateManager<ObjectChoropu>::~StateManager() {
338 delete[] m_entryIds.data();
339}
340
342ObjectChoropuGround::ObjectChoropuGround(const EGG::Vector3f &pos, const EGG::Vector3f &rot,
343 const EGG::Vector3f &scale)
344 : ObjectCollidable("choropu_ground", pos, rot, scale) {
345 const auto &flowTable = ObjectDirector::Instance()->flowTable();
346 const auto *collisionSet =
347 flowTable.set(flowTable.slot(flowTable.getIdFromName("choropu_ground")));
348 ASSERT(collisionSet);
349
350 s16 height = parse<s16>(collisionSet->params.cylinder.height);
351 m_height = 2.0f * EGG::Mathf::abs(static_cast<f32>(height));
352}
353
355ObjectChoropuGround::~ObjectChoropuGround() = default;
356
358void ObjectChoropuGround::calcPosAndMat(f32 height, const EGG::Matrix34f &mat) {
359 EGG::Matrix34f matTemp;
360 SetRotTangentHorizontal(matTemp, mat.base(2), EGG::Vector3f::ey);
361 matTemp.setBase(1, matTemp.base(1) * (height / m_height));
362 matTemp.setBase(3, mat.base(3));
363 m_flags.setBit(eFlags::Matrix);
364 m_transform = matTemp;
365 m_pos = matTemp.base(3);
366}
367
369ObjectChoropuHoll::ObjectChoropuHoll(const System::MapdataGeoObj &params)
370 : ObjectCollidable(params) {}
371
373ObjectChoropuHoll::~ObjectChoropuHoll() = default;
374
375} // namespace Field
A 3 x 4 matrix.
Definition Matrix.hh:8
void setBase(size_t col, const Vector3f &base)
Sets one column of a matrix.
Definition Matrix.cc:181
Vector3f base(size_t col) const
Get a particular column from a matrix.
Definition Matrix.hh:70
The highest level abstraction for a kart.
Definition KartObject.hh:11
Pertains to collision.
A 3D float vector.
Definition Vector.hh:87