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