A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
KartAction.cc
1#include "KartAction.hh"
2
3#include "game/kart/KartMove.hh"
4#include "game/kart/KartPhysics.hh"
5#include "game/kart/KartState.hh"
6
7#include "game/item/ItemDirector.hh"
8#include "game/item/KartItem.hh"
9
10#include <egg/math/Math.hh>
11
12namespace Kart {
13
15KartAction::KartAction()
16 : m_currentAction(Action::None), m_hitDepth(EGG::Vector3f::zero),
17 m_translation(EGG::Vector3f::ez), m_onStart(nullptr), m_onCalc(nullptr), m_onEnd(nullptr),
18 m_actionParams(nullptr), m_rotationParams(nullptr), m_priority(0) {}
19
21KartAction::~KartAction() = default;
22
24void KartAction::init() {
25 m_currentAction = Action::None;
26 m_flags.makeAllZero();
27}
28
30void KartAction::calc() {
31 if (m_currentAction == Action::None || !m_onCalc) {
32 return;
33 }
34
35 if (calcCurrentAction()) {
36 calcEndAction(false);
37 }
38}
39
41void KartAction::calcVehicleSpeed() {
42 move()->setSpeed(m_actionParams->calcSpeedMult * move()->speed());
43}
44
49bool KartAction::start(Action action) {
50 ASSERT(action != Action::None);
51
52 auto &status = KartObjectProxy::status();
53
54 if (status.onBit(eStatus::InRespawn, eStatus::AfterRespawn, eStatus::BeforeRespawn,
55 eStatus::InCannon)) {
56 return false;
57 }
58
59 if (status.onBit(eStatus::ZipperStick)) {
60 switch (action) {
61 case Action::UNK_2:
62 case Action::UNK_3:
63 case Action::UNK_4:
64 case Action::UNK_5:
65 case Action::UNK_6:
66 action = Action::UNK_1;
67 break;
68 default:
69 break;
70 }
71 }
72
73 size_t actionIdx = static_cast<size_t>(action);
74
75 if (m_currentAction != Action::None && s_actionParams[actionIdx].priority <= m_priority) {
76 return false;
77 }
78
79 calcEndAction(true);
80 m_currentAction = action;
81 m_actionParams = &s_actionParams[actionIdx];
82 m_priority = m_actionParams->priority;
83 m_onStart = s_onStart[actionIdx];
84 m_onCalc = s_onCalc[actionIdx];
85 m_onEnd = s_onEnd[actionIdx];
86 status.setBit(eStatus::InAction);
87 m_frame = 0;
88 m_flags.makeAllZero();
89 m_up = move()->up();
90 move()->clear();
91
92 applyStartSpeed();
93 (this->*m_onStart)();
94 return true;
95}
96
102void KartAction::startRotation(size_t idx) {
103 m_rotation = EGG::Quatf::ident;
104
105 EGG::Vector3f dir = move()->dir();
106 if (speed() < 0.0f) {
107 dir = -dir;
108 }
109
110 m_rotationDirection = dir.cross(bodyFront()).dot(bodyUp()) > 0.0f ? 1.0f : -1.0f;
111 setRotation(idx);
112 m_flags.setBit(eFlags::Rotating);
113}
114
116void KartAction::calcSideFromHitDepth() {
117 m_hitDepth.normalise();
118 m_side = m_hitDepth.perpInPlane(move()->smoothedUp(), true);
119
120 if (m_side.squaredLength() <= std::numeric_limits<f32>::epsilon()) {
121 m_side = EGG::Vector3f::ey;
122 }
123}
124
126void KartAction::calcSideFromHitDepthAndTranslation() {
127 calcSideFromHitDepth();
128
129 EGG::Vector3f cross = m_translation.cross(m_side);
130 f32 sign = (cross.y > 0.0f) ? 1.0f : -1.0f;
131
132 EGG::Vector3f worldSide = EGG::Vector3f::ey.cross(m_translation);
133 worldSide.normalise();
134
135 m_side = worldSide.perpInPlane(move()->smoothedUp(), true);
136
137 if (m_side.squaredLength() > std::numeric_limits<f32>::epsilon()) {
138 m_side *= sign;
139 } else {
140 m_side = EGG::Vector3f::ey;
141 }
142}
143
145void KartAction::end() {
146 status().resetBit(eStatus::InAction, eStatus::LargeFlipHit);
147 dynamics()->setForceUpright(true);
148
149 m_currentAction = Action::None;
150 m_priority = 0;
151 m_flags.makeAllZero();
152}
153
157bool KartAction::calcCurrentAction() {
158 ++m_frame;
159 return (this->*m_onCalc)();
160}
161
163void KartAction::calcEndAction(bool endArg) {
164 if (m_currentAction == Action::None) {
165 return;
166 }
167
168 if (m_onEnd) {
169 (this->*m_onEnd)(endArg);
170 end();
171 }
172}
173
175bool KartAction::calcRotation() {
176 if (!m_rotationParams) {
177 return false;
178 }
179
180 // Slow the rotation down as we approach the end of the spinout
181 if (m_currentAngle > m_finalAngle * m_rotationParams->slowdownThreshold) {
182 m_angleIncrement *= m_multiplier;
183 if (m_rotationParams->minAngleIncrement > m_angleIncrement) {
184 m_angleIncrement = m_rotationParams->minAngleIncrement;
185 }
186
187 m_multiplier -= m_multiplierDecrement;
188 if (m_rotationParams->minMultiplier > m_multiplier) {
189 m_multiplier = m_rotationParams->minMultiplier;
190 }
191 }
192
193 m_currentAngle += m_angleIncrement;
194 if (m_finalAngle < m_currentAngle) {
195 m_currentAngle = m_finalAngle;
196 return true;
197 }
198
199 return false;
200}
201
203void KartAction::calcUp() {
204 m_up += (move()->up() - m_up) * 0.3f;
205 m_up.normalise();
206}
207
208void KartAction::calcLanding() {
209 if (m_currentAngle < m_targetRot || status().offBit(eStatus::TouchingGround)) {
210 return;
211 }
212
213 m_flags.setBit(eFlags::Landing);
214
215 if (!isBike()) {
216 dynamics()->setForceUpright(false);
217 }
218
219 physics()->composeDecayingExtraRot(m_rotation);
220}
221
223void KartAction::startLaunch(f32 extVelScalar, f32 extVelKart, f32 extVelBike, f32 numRotations,
224 u32 param6) {
225 m_targetRot = 360.0f * numRotations;
226
227 EGG::Vector3f extVel = EGG::Vector3f::zero;
228 extVel.y = isBike() ? extVelBike : extVelKart;
229
230 if (param6 == 0) {
231 m_hitDepth = move()->dir();
232 calcSideFromHitDepth();
233 } else if (param6 == 1) {
234 calcSideFromHitDepth();
235 extVel += extVelScalar * m_side;
236 } else if (param6 == 2) {
237 calcSideFromHitDepthAndTranslation();
238 extVel += extVelScalar * m_side;
239 }
240
241 setRotation(static_cast<size_t>(numRotations + 3.0f));
242 m_rotAxis = move()->smoothedUp().cross(m_side);
243
244 dynamics()->setExtVel(dynamics()->extVel() + extVel);
245}
246
248void KartAction::activateCrush(u16 timer) {
249 move()->activateCrush(m_crushTimer + timer);
250 Item::ItemDirector::Instance()->kartItem(0).clear();
251}
252
254void KartAction::applyStartSpeed() {
255 move()->setSpeed(m_actionParams->startSpeedMult * move()->speed());
256 if (m_actionParams->startSpeedMult == 0.0f) {
257 move()->clearDrift();
258 }
259}
260
262void KartAction::setRotation(size_t idx) {
263 ASSERT(idx - 1 < s_rotationParams.size());
264 m_rotationParams = &s_rotationParams[--idx];
265
266 m_finalAngle = m_rotationParams->finalAngle;
267 m_angleIncrement = m_rotationParams->initialAngleIncrement;
268 m_multiplierDecrement = m_rotationParams->initialMultiplierDecrement;
269 m_currentAngle = 0.0f;
270 m_multiplier = 1.0f;
271}
272
273/* ================================ *
274 * START FUNCTIONS
275 * ================================ */
276
277void KartAction::startStub() {}
278
280void KartAction::startAction1() {
281 startRotation(2);
282}
283
285void KartAction::startAction2() {
286 constexpr f32 EXT_VEL_SCALAR = 0.0f;
287 constexpr f32 EXT_VEL_KART = 30.0f;
288 constexpr f32 EXT_VEL_BIKE = 30.0f;
289 constexpr f32 NUM_ROTATIONS = 1.0f;
290
291 startLaunch(EXT_VEL_SCALAR, EXT_VEL_KART, EXT_VEL_BIKE, NUM_ROTATIONS, 0);
292}
293
295void KartAction::startAction3() {
296 constexpr f32 EXT_VEL_SCALAR = 25.0f;
297 constexpr f32 EXT_VEL_KART = 30.0f;
298 constexpr f32 EXT_VEL_BIKE = 30.0f;
299 constexpr f32 NUM_ROTATIONS = 1.0f;
300
301 startLaunch(EXT_VEL_SCALAR, EXT_VEL_KART, EXT_VEL_BIKE, NUM_ROTATIONS, 1);
302 Item::ItemDirector::Instance()->kartItem(0).clear();
303}
304
306void KartAction::startAction5() {
307 constexpr f32 EXT_VEL_SCALAR = 13.0f;
308 constexpr f32 EXT_VEL_KART = 40.0f;
309 constexpr f32 EXT_VEL_BIKE = 45.0f;
310 constexpr f32 NUM_ROTATIONS = 1.0f;
311
312 startLaunch(EXT_VEL_SCALAR, EXT_VEL_KART, EXT_VEL_BIKE, NUM_ROTATIONS, 2);
313}
314
316void KartAction::startLargeFlipAction() {
317 constexpr EGG::Vector3f INIT_VEL = EGG::Vector3f(0.0f, 60.0f, 0.0f);
318
319 dynamics()->setExtVel(INIT_VEL);
320 dynamics()->setAngVel0(EGG::Vector3f::zero);
321
322 if (m_currentAction == Action::UNK_8) {
323 calcSideFromHitDepth();
324 dynamics()->setExtVel(dynamics()->extVel() + m_side * -20.0f);
325 }
326
327 Item::ItemDirector::Instance()->kartItem(0).clear();
328
329 m_deltaPitch = 0.0f;
330 m_pitch = 0.0f;
331 m_velPitch = 22.0f;
332 m_flipPhase = 0.0f;
333 m_framesFlipping = 0;
334
335 status().setBit(eStatus::LargeFlipHit);
336}
337
339void KartAction::startAction9() {
340 startRotation(2);
341}
342
344void KartAction::startLongPressAction() {
345 constexpr u32 ACTION_DURATION = 90;
346 constexpr u16 CRUSH_DURATION = 480;
347
348 m_crushTimer = ACTION_DURATION;
349 activateCrush(CRUSH_DURATION);
350}
351
353void KartAction::startShortPressAction() {
354 constexpr u32 ACTION_DURATION = 30;
355 constexpr u16 CRUSH_DURATION = 240;
356
357 m_crushTimer = ACTION_DURATION;
358 activateCrush(CRUSH_DURATION);
359}
360
361/* ================================ *
362 * CALC FUNCTIONS
363 * ================================ */
364
365bool KartAction::calcStub() {
366 return false;
367}
368
370bool KartAction::calcAction1() {
371 calcUp();
372 bool finished = calcRotation();
373
374 m_rotation.setAxisRotation(DEG2RAD * (m_currentAngle * m_rotationDirection), m_up);
375 physics()->composeExtraRot(m_rotation);
376 return finished;
377}
378
380bool KartAction::calcLaunchAction() {
381 constexpr u32 ACTION_DURATION = 100;
382
383 if (m_flags.offBit(eFlags::Landing)) {
384 calcRotation();
385 calcLanding();
386 }
387
388 bool actionEnded = m_frame >= ACTION_DURATION;
389
390 if (actionEnded) {
391 if (m_flags.offBit(eFlags::Landing)) {
392 physics()->composeDecayingExtraRot(m_rotation);
393 }
394 } else if (m_flags.offBit(eFlags::Landing)) {
395 m_rotation.setAxisRotation(DEG2RAD * m_currentAngle, m_rotAxis);
396 physics()->composeExtraRot(m_rotation);
397 }
398
399 return actionEnded;
400}
401
403bool KartAction::calcLargeFlipAction() {
404 constexpr f32 PITCH_DECAY = 0.971f;
405 constexpr f32 TOTAL_DELTA_PITCH = 720.0f;
406 constexpr f32 PHASE_DELTA = 4.0f;
407 constexpr f32 WOBBLE_AMPLITUDE = 18.1f;
408 constexpr f32 BOUNCE_FACTOR = 5.0f;
409
410 bool decayingRot = false;
411 bool stuntRot = false;
412
413 if (m_flags.onBit(eFlags::LargeFlip)) {
414 ++m_framesFlipping;
415 } else {
416 if (m_deltaPitch < TOTAL_DELTA_PITCH) {
417 m_deltaPitch += m_velPitch;
418 m_pitch -= m_velPitch;
419 m_velPitch *= (m_velPitch > 1.0f) ? PITCH_DECAY : 1.0f;
420 } else {
421 m_pitch = 0.0f;
422 }
423
424 f32 sin;
425
426 if (EGG::Mathf::abs(m_flipPhase) < 360.0f) {
427 m_flipPhase += PHASE_DELTA;
428 sin = EGG::Mathf::SinFIdx(DEG2FIDX * m_flipPhase);
429 } else {
430 sin = 0.0f;
431 }
432
433 EGG::Matrix34f mat;
434 mat.setAxisRotation(DEG2RAD * (WOBBLE_AMPLITUDE * sin), EGG::Vector3f::ez);
435 m_rotation.setAxisRotation(DEG2RAD * m_pitch, mat.ps_multVector(EGG::Vector3f::ex));
436
437 stuntRot = true;
438 }
439
440 bool actionEnded = false;
441 auto &status = KartObjectProxy::status();
442 bool touchingGround = status.onBit(eStatus::TouchingGround);
443
444 if (m_flags.offBit(eFlags::LandingFromFlip) && touchingGround && move()->up().y > 0.0f &&
445 m_frame > 50) {
446 m_flags.setBit(eFlags::LandingFromFlip);
447 dynamics()->setExtVel(move()->up().proj(EGG::Vector3f::ey) * BOUNCE_FACTOR);
448 }
449
450 if ((m_currentAction != Action::UNK_8 && m_frame < 10) || !touchingGround) {
451 dynamics()->setExtVel(EGG::Vector3f(0.0f, dynamics()->extVel().y, 0.0f));
452 }
453
454 if ((touchingGround && move()->up().dot(EGG::Vector3f::ey) > 0.0f) || m_frame >= 300) {
455 if (m_frame >= 40) {
456 status.resetBit(eStatus::LargeFlipHit);
457 }
458
459 if (m_frame <= 120) {
460 if (m_flags.onBit(eFlags::LargeFlip)) {
461 if (m_framesFlipping > 30) {
462 actionEnded = true;
463 }
464 } else {
465 if (m_frame >= 40 && m_frame <= 80) {
466 decayingRot = true;
467 m_flags.setBit(eFlags::LargeFlip);
468 status.resetBit(eStatus::LargeFlipHit);
469 }
470 }
471 } else {
472 actionEnded = true;
473 }
474 }
475
476 if (decayingRot) {
477 physics()->composeDecayingStuntRot(m_rotation);
478 } else if (stuntRot) {
479 physics()->composeStuntRot(m_rotation);
480 }
481
482 return actionEnded;
483}
484
486bool KartAction::calcPressAction() {
487 EGG::Vector3f extVel = KartObjectProxy::extVel();
488 extVel.y = std::min(0.0f, extVel.y);
489 dynamics()->setExtVel(extVel);
490
491 return m_frame > m_crushTimer;
492}
493
494/* ================================ *
495 * END FUNCTIONS
496 * ================================ */
497
498void KartAction::endStub(bool /*arg*/) {}
499
501void KartAction::endAction1(bool arg) {
502 if (arg) {
503 physics()->composeDecayingExtraRot(m_rotation);
504 }
505}
506
508void KartAction::endLaunchAction(bool arg) {
509 if (arg) {
510 physics()->composeDecayingExtraRot(m_rotation);
511 }
512}
513
514/* ================================ *
515 * ACTION TABLES
516 * ================================ */
517
518const std::array<KartAction::ActionParams, KartAction::MAX_ACTION> KartAction::s_actionParams = {{
519 {0.98f, 0.98f, 1},
520 {0.98f, 0.98f, 2},
521 {0.96f, 0.96f, 4},
522 {0.0f, 0.96f, 4},
523 {0.0f, 0.98f, 4},
524 {0.0f, 0.96f, 4},
525 {0.0f, 0.96f, 4},
526 {0.0f, 0.0f, 6},
527 {0.0f, 0.99f, 6},
528 {0.98f, 0.98f, 3},
529 {0.98f, 0.98f, 3},
530 {1.0f, 1.0f, 5},
531 {0.0f, 0.0f, 3},
532 {0.0f, 0.0f, 3},
533 {0.0f, 0.0f, 3},
534 {0.98f, 0.98f, 3},
535 {0.0f, 0.0f, 3},
536 {0.98f, 0.98f, 3},
537}};
538
539const std::array<KartAction::RotationParams, 5> KartAction::s_rotationParams = {{
540 {10.0f, 1.5f, 0.9f, 0.005f, 0.6f, 360.0f},
541 {11.0f, 1.5f, 0.9f, 0.0028f, 0.7f, 720.0f},
542 {11.0f, 1.5f, 0.9f, 0.0028f, 0.8f, 1080.0f},
543 {7.0f, 1.5f, 0.9f, 0.005f, 0.6f, 450.0f},
544 {9.0f, 1.5f, 0.9f, 0.0028f, 0.7f, 810.0f},
545}};
546
547const std::array<KartAction::StartActionFunc, KartAction::MAX_ACTION> KartAction::s_onStart = {{
548 &KartAction::startStub,
549 &KartAction::startAction1,
550 &KartAction::startAction2,
551 &KartAction::startAction3,
552 &KartAction::startStub,
553 &KartAction::startAction5,
554 &KartAction::startStub,
555 &KartAction::startLargeFlipAction,
556 &KartAction::startLargeFlipAction,
557 &KartAction::startAction9,
558 &KartAction::startStub,
559 &KartAction::startStub,
560 &KartAction::startLongPressAction,
561 &KartAction::startStub,
562 &KartAction::startShortPressAction,
563 &KartAction::startStub,
564 &KartAction::startStub,
565 &KartAction::startStub,
566}};
567
568const std::array<KartAction::CalcActionFunc, KartAction::MAX_ACTION> KartAction::s_onCalc = {{
569 &KartAction::calcStub,
570 &KartAction::calcAction1,
571 &KartAction::calcLaunchAction,
572 &KartAction::calcLaunchAction,
573 &KartAction::calcStub,
574 &KartAction::calcLaunchAction,
575 &KartAction::calcStub,
576 &KartAction::calcLargeFlipAction,
577 &KartAction::calcLargeFlipAction,
578 &KartAction::calcAction1,
579 &KartAction::calcStub,
580 &KartAction::calcStub,
581 &KartAction::calcPressAction,
582 &KartAction::calcStub,
583 &KartAction::calcPressAction,
584 &KartAction::calcStub,
585 &KartAction::calcStub,
586 &KartAction::calcStub,
587}};
588
589const std::array<KartAction::EndActionFunc, KartAction::MAX_ACTION> KartAction::s_onEnd = {{
590 &KartAction::endStub,
591 &KartAction::endAction1,
592 &KartAction::endLaunchAction,
593 &KartAction::endLaunchAction,
594 &KartAction::endStub,
595 &KartAction::endLaunchAction,
596 &KartAction::endStub,
597 &KartAction::endStub,
598 &KartAction::endStub,
599 &KartAction::endAction1,
600 &KartAction::endStub,
601 &KartAction::endStub,
602 &KartAction::endStub,
603 &KartAction::endStub,
604 &KartAction::endStub,
605 &KartAction::endStub,
606 &KartAction::endStub,
607 &KartAction::endStub,
608}};
609
610} // namespace Kart
A 3 x 4 matrix.
Definition Matrix.hh:8
void setAxisRotation(f32 angle, const Vector3f &axis)
Rotates the matrix about an axis.
Definition Matrix.cc:180
Vector3f ps_multVector(const Vector3f &vec) const
Paired-singles impl. of multVector.
Definition Matrix.cc:237
EGG core library.
Definition Archive.cc:6
Pertains to kart-related functionality.
A 3D float vector.
Definition Vector.hh:88
f32 normalise()
Normalizes the vector and returns the original length.
Definition Vector.cc:52
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
Definition Vector.hh:187
Vector3f perpInPlane(const EGG::Vector3f &rhs, bool normalise) const
Calculates the orthogonal vector, based on the plane defined by this vector and rhs.
Definition Vector.cc:114