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 Kinoko::Field {
9
11ObjectChoropu::ObjectChoropu(const System::MapdataGeoObj &params)
12 : ObjectCollidable(params), StateManager(this, STATE_ENTRIES) {
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 = owning_span<ObjectChoropuGround *>(groundCount);
35
36 for (auto *&obj : m_groundObjs) {
37 obj = EGG::egg_new<ObjectChoropuGround>(pos(), rot(), scale());
38 obj->load();
39 obj->resize(RADIUS, MAX_SPEED);
40 }
41
42 m_groundHeight = m_groundObjs.front()->height();
43 }
44
45 m_objHoll = EGG::egg_new<ObjectChoropuHoll>(params);
46 m_objHoll->load();
47}
48
50ObjectChoropu::~ObjectChoropu() = default;
51
53void ObjectChoropu::init() {
54 if (m_isStationary) {
55 disableCollision();
56
57 m_objHoll->setScale(EGG::Vector3f(1.0f, m_objHoll->scale().y, 1.0f));
58 m_nextStateId = 0;
59 m_groundLength = 0.0f;
60
61 calcTransform();
62 m_transMat = transform();
63 } else {
64 if (m_mapObj->pathId() == -1) {
65 return;
66 }
67
68 m_railInterpolator->init(0.0f, 0);
69 m_railInterpolator->setPerPointVelocities(true);
70
71 m_railMat = RailOrthonormalBasis(*m_railInterpolator);
72 setPos(m_railMat.base(3));
73
74 disableCollision();
75 m_objHoll->disableCollision();
76
77 m_nextStateId = 0;
78 }
79}
80
82void ObjectChoropu::calc() {
83 constexpr u32 START_DELAY = 300;
84
85 // Nothing to do if the mole hasn't spawned yet
86 u32 t = System::RaceManager::Instance()->timer();
87 if (t < m_startFrameOffset + START_DELAY) {
88 return;
89 }
90
91 if (!m_isStationary) {
92 if (m_mapObj->pathId() == -1) {
93 return;
94 }
95
96 m_railMat = RailOrthonormalBasis(*m_railInterpolator);
97 }
98
99 StateManager::calc();
100
101 m_objHoll->setScale(EGG::Vector3f(1.0f, m_objHoll->scale().y, 1.0f));
102}
103
105Kart::Reaction ObjectChoropu::onCollision(Kart::KartObject * /*kartObj*/,
106 Kart::Reaction reactionOnKart, Kart::Reaction /*reactionOnObj*/,
107 EGG::Vector3f & /*hitDepth*/) {
108 return m_currentStateId == 1 ? Kart::Reaction::SmallBump : reactionOnKart;
109}
110
111void ObjectChoropu::enterStateStub() {}
112
114void ObjectChoropu::enterDigging() {
115 if (m_isStationary) {
116 disableCollision();
117 } else {
118 for (auto *&obj : m_groundObjs) {
119 obj->enableCollision();
120 }
121
122 disableCollision();
123 m_objHoll->disableCollision();
124 m_groundLength = 0.0f;
125 }
126}
127
129void ObjectChoropu::enterPeeking() {
130 if (m_isStationary) {
131 setPos(m_transMat.base(3));
132 setRot(EGG::Vector3f(rot().x, rot().y, 0.0f));
133
134 enableCollision();
135 } else {
136 setPos(m_railMat.base(3));
137 setRot(EGG::Vector3f(rot().x, 0.0f, 0.0f));
138 enableCollision();
139
140 const auto &curTanDir = m_railInterpolator->curTangentDir();
141 s16 curPointIdx = m_railInterpolator->curPointIdx();
142 EGG::Matrix34f mat;
143 SetRotTangentHorizontal(mat, m_railInterpolator->floorNrm(curPointIdx), curTanDir);
144 mat.setBase(3, m_railInterpolator->curPos());
145
146 m_objHoll->setTransform(mat);
147 m_objHoll->enableCollision();
148 }
149}
150
152void ObjectChoropu::enterJumping() {
153 enableCollision();
154
155 setPos(m_isStationary ? m_transMat.base(3) : m_railMat.base(3));
156 setRot(EGG::Vector3f(rot().x, rot().y, 0.0f));
157}
158
159void ObjectChoropu::calcStateStub() {}
160
162void ObjectChoropu::calcDigging() {
163 if (m_isStationary) {
164 if (m_currentFrame > m_idleDuration) {
165 m_nextStateId = 1;
166 }
167
168 return;
169 }
170
171 setPos(m_railInterpolator->curPos());
172
173 if (m_railInterpolator->calc() == RailInterpolator::Status::SegmentEnd) {
174 if (m_railInterpolator->curPoint().setting[1] == 1) {
175 m_nextStateId = 1;
176 } else {
177 calcGround();
178 }
179 } else {
180 bool skipGroundCalc = false;
181
182 if (m_railInterpolator->nextPoint().setting[1] == 1) {
183 f32 invT = 1.0f - m_railInterpolator->segmentT();
184 if (invT * m_railInterpolator->getCurrSegmentLength() < 250.0f) {
185 skipGroundCalc = true;
186 }
187 }
188
189 if (!skipGroundCalc) {
190 calcGround();
191 }
192 }
193}
194
196void ObjectChoropu::calcPeeking() {
197 constexpr s16 PEEK_DURATION = 40;
198 constexpr s16 STATE_DURATION = 100;
199
200 if (!m_isStationary) {
201 m_groundLength = std::max(0.0f, m_groundLength - m_railInterpolator->speed());
202
203 calcGroundObjs();
204 }
205
206 if (m_currentFrame > STATE_DURATION) {
207 m_nextStateId = 3;
208 }
209
210 if (m_currentFrame > PEEK_DURATION) {
211 disableCollision();
212 }
213}
214
216void ObjectChoropu::calcJumping() {
217 constexpr f32 JUMP_LINEAR_COEFFICIENT = 65.0f;
218 constexpr f32 JUMP_QUADRATIC_COEFFICIENT = 2.7f;
219
220 if (!m_isStationary) {
221 m_groundLength = std::max(0.0f, m_groundLength - m_railInterpolator->speed());
222 calcGroundObjs();
223 }
224
225 // y = -1.35t^2 + 65.0t
226 f32 posY = JUMP_LINEAR_COEFFICIENT * static_cast<f32>(m_currentFrame) -
227 static_cast<f32>(m_currentFrame) * 0.5f * JUMP_QUADRATIC_COEFFICIENT *
228 static_cast<f32>(m_currentFrame);
229
230 if (posY < 0.0f) {
231 m_nextStateId = 0;
232 }
233
234 posY += (m_isStationary ? m_transMat.base(3).y : m_railInterpolator->curPos().y);
235 setPos(EGG::Vector3f(pos().x, posY, pos().z));
236}
237
239void ObjectChoropu::calcGround() {
240 m_groundLength += m_railInterpolator->getCurrVel();
241 if (m_groundLength > MAX_GROUND_LEN) {
242 m_groundLength = MAX_GROUND_LEN - 1.0f;
243 }
244
245 calcGroundObjs();
246}
247
249void ObjectChoropu::calcGroundObjs() {
250 size_t idx =
251 std::min(static_cast<size_t>(m_groundLength / m_groundHeight) + 1, m_groundObjs.size());
252
253 for (auto *&obj : m_groundObjs) {
254 obj->enableCollision();
255 }
256
257 for (size_t i = idx; i < m_groundObjs.size(); ++i) {
258 m_groundObjs[i]->disableCollision();
259 }
260
261 if (m_groundLength > RADIUS) {
262 f32 height = std::min(m_groundHeight, m_groundLength) - RADIUS;
263 m_groundObjs[0]->calcPosAndMat(height, calcInterpolatedPose(RADIUS + 0.5f * height));
264 }
265
266 for (size_t i = 1; i < idx - 1; ++i) {
267 f32 height = 0.5f * m_groundHeight + m_groundHeight * static_cast<f32>(i);
268 m_groundObjs[i]->calcPosAndMat(m_groundHeight, calcInterpolatedPose(height));
269 }
270
271 f32 height = m_groundLength - m_groundHeight * static_cast<f32>(idx - 1);
272 EGG::Matrix34f mat =
273 calcInterpolatedPose(0.5f * height + m_groundHeight * static_cast<f32>(idx - 1));
274 m_groundObjs[idx - 1]->calcPosAndMat(height, mat);
275}
276
278EGG::Matrix34f ObjectChoropu::calcInterpolatedPose(f32 t) const {
279 EGG::Vector3f curDir;
280 EGG::Vector3f curTanDir;
281 m_railInterpolator->evalCubicBezierOnPath(t, curDir, curTanDir);
282 EGG::Matrix34f mat = OrthonormalBasis(curTanDir);
283 mat.setBase(3, curDir);
284 return mat;
285}
286
288ObjectChoropuGround::ObjectChoropuGround(const EGG::Vector3f &pos, const EGG::Vector3f &rot,
289 const EGG::Vector3f &scale)
290 : ObjectCollidable("choropu_ground", pos, rot, scale) {
291 const auto &flowTable = ObjectDirector::Instance()->flowTable();
292 const auto *collisionSet =
293 flowTable.set(flowTable.slot(flowTable.getIdFromName("choropu_ground")));
294 ASSERT(collisionSet);
295
296 s16 height = parse<s16>(collisionSet->params.cylinder.height);
297 m_height = 2.0f * EGG::Mathf::abs(static_cast<f32>(height));
298}
299
301ObjectChoropuGround::~ObjectChoropuGround() = default;
302
304void ObjectChoropuGround::calcPosAndMat(f32 height, const EGG::Matrix34f &mat) {
305 EGG::Matrix34f matTemp;
306 SetRotTangentHorizontal(matTemp, mat.base(2), EGG::Vector3f::ey);
307 matTemp.setBase(1, matTemp.base(1) * (height / m_height));
308 matTemp.setBase(3, mat.base(3));
309 setTransform(matTemp);
310}
311
313ObjectChoropuHoll::ObjectChoropuHoll(const System::MapdataGeoObj &params)
314 : ObjectCollidable(params) {}
315
317ObjectChoropuHoll::~ObjectChoropuHoll() = default;
318
319} // namespace Kinoko::Field
Base class that represents different "states" for an object.
Pertains to collision.