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_groundStartLaunchTimer = 0;
243 m_rotAxis = move()->smoothedUp().cross(m_side);
244
245 dynamics()->setExtVel(dynamics()->extVel() + extVel);
246}
247
249void KartAction::activateCrush(u16 timer) {
250 move()->activateCrush(m_crushTimer + timer);
251 Item::ItemDirector::Instance()->kartItem(0).clear();
252}
253
255void KartAction::applyStartSpeed() {
256 move()->setSpeed(m_actionParams->startSpeedMult * move()->speed());
257 if (m_actionParams->startSpeedMult == 0.0f) {
258 move()->clearDrift();
259 }
260}
261
263void KartAction::setRotation(size_t idx) {
264 ASSERT(idx - 1 < s_rotationParams.size());
265 m_rotationParams = &s_rotationParams[--idx];
266
267 m_finalAngle = m_rotationParams->finalAngle;
268 m_angleIncrement = m_rotationParams->initialAngleIncrement;
269 m_multiplierDecrement = m_rotationParams->initialMultiplierDecrement;
270 m_currentAngle = 0.0f;
271 m_multiplier = 1.0f;
272}
273
274/* ================================ *
275 * START FUNCTIONS
276 * ================================ */
277
278void KartAction::startStub() {}
279
281void KartAction::startAction1() {
282 startRotation(2);
283}
284
286void KartAction::startAction2() {
287 constexpr f32 EXT_VEL_SCALAR = 0.0f;
288 constexpr f32 EXT_VEL_KART = 30.0f;
289 constexpr f32 EXT_VEL_BIKE = 30.0f;
290 constexpr f32 NUM_ROTATIONS = 1.0f;
291
292 startLaunch(EXT_VEL_SCALAR, EXT_VEL_KART, EXT_VEL_BIKE, NUM_ROTATIONS, 0);
293}
294
296void KartAction::startAction3() {
297 constexpr f32 EXT_VEL_SCALAR = 25.0f;
298 constexpr f32 EXT_VEL_KART = 30.0f;
299 constexpr f32 EXT_VEL_BIKE = 30.0f;
300 constexpr f32 NUM_ROTATIONS = 1.0f;
301
302 startLaunch(EXT_VEL_SCALAR, EXT_VEL_KART, EXT_VEL_BIKE, NUM_ROTATIONS, 1);
303 Item::ItemDirector::Instance()->kartItem(0).clear();
304}
305
307void KartAction::startAction4() {
308 constexpr f32 EXT_VEL_SCALAR = 25.0f;
309 constexpr f32 EXT_VEL_KART = 30.0f;
310 constexpr f32 EXT_VEL_BIKE = 30.0f;
311 constexpr f32 NUM_ROTATIONS = 2.0f;
312
313 startLaunch(EXT_VEL_SCALAR, EXT_VEL_KART, EXT_VEL_BIKE, NUM_ROTATIONS, 1);
314 Item::ItemDirector::Instance()->kartItem(0).clear();
315}
316
318void KartAction::startAction5() {
319 constexpr f32 EXT_VEL_SCALAR = 13.0f;
320 constexpr f32 EXT_VEL_KART = 40.0f;
321 constexpr f32 EXT_VEL_BIKE = 45.0f;
322 constexpr f32 NUM_ROTATIONS = 1.0f;
323
324 startLaunch(EXT_VEL_SCALAR, EXT_VEL_KART, EXT_VEL_BIKE, NUM_ROTATIONS, 2);
325}
326
328void KartAction::startLargeFlipAction() {
329 constexpr EGG::Vector3f INIT_VEL = EGG::Vector3f(0.0f, 60.0f, 0.0f);
330
331 dynamics()->setExtVel(INIT_VEL);
332 dynamics()->setAngVel0(EGG::Vector3f::zero);
333
334 if (m_currentAction == Action::UNK_8) {
335 calcSideFromHitDepth();
336 dynamics()->setExtVel(dynamics()->extVel() + m_side * -20.0f);
337 }
338
339 Item::ItemDirector::Instance()->kartItem(0).clear();
340
341 m_deltaPitch = 0.0f;
342 m_pitch = 0.0f;
343 m_velPitch = 22.0f;
344 m_flipPhase = 0.0f;
345 m_framesFlipping = 0;
346
347 status().setBit(eStatus::LargeFlipHit);
348}
349
351void KartAction::startAction9() {
352 startRotation(2);
353}
354
356void KartAction::startLongPressAction() {
357 constexpr u32 ACTION_DURATION = 90;
358 constexpr u16 CRUSH_DURATION = 480;
359
360 m_crushTimer = ACTION_DURATION;
361 activateCrush(CRUSH_DURATION);
362}
363
365void KartAction::startShortPressAction() {
366 constexpr u32 ACTION_DURATION = 30;
367 constexpr u16 CRUSH_DURATION = 240;
368
369 m_crushTimer = ACTION_DURATION;
370 activateCrush(CRUSH_DURATION);
371}
372
374void KartAction::startSpinShrinkAction() {
375 startRotation(1);
376}
377
378/* ================================ *
379 * CALC FUNCTIONS
380 * ================================ */
381
382bool KartAction::calcStub() {
383 return false;
384}
385
387bool KartAction::calcAction1() {
388 calcUp();
389 bool finished = calcRotation();
390
391 m_rotation.setAxisRotation(DEG2RAD * (m_currentAngle * m_rotationDirection), m_up);
392 physics()->composeExtraRot(m_rotation);
393 return finished;
394}
395
397bool KartAction::calcLaunchAction() {
398 constexpr u32 ACTION_DURATION = 100;
399
400 if (m_flags.offBit(eFlags::Landing)) {
401 calcRotation();
402 calcLanding();
403 }
404
405 bool actionEnded = m_frame >= ACTION_DURATION;
406
407 if (actionEnded) {
408 if (m_flags.offBit(eFlags::Landing)) {
409 physics()->composeDecayingExtraRot(m_rotation);
410 }
411 } else if (m_flags.offBit(eFlags::Landing)) {
412 m_rotation.setAxisRotation(DEG2RAD * m_currentAngle, m_rotAxis);
413 physics()->composeExtraRot(m_rotation);
414 }
415
416 return actionEnded;
417}
418
420bool KartAction::calcAction4() {
421 constexpr u32 ACTION_DURATION = 140;
422
423 auto &status = state()->status();
424 if (status.onBit(eStatus::GroundStart)) {
425 if (m_groundStartLaunchTimer++ == 0) {
426 EGG::Vector3f extVel = dynamics()->extVel();
427 extVel.y = 25.0f;
428 dynamics()->setExtVel(extVel);
429 }
430 }
431
432 if (m_flags.offBit(eFlags::Landing)) {
433 calcRotation();
434 calcLanding();
435 }
436
437 bool actionEnded = m_frame >= ACTION_DURATION;
438
439 if (actionEnded) {
440 if (m_flags.offBit(eFlags::Landing)) {
441 physics()->composeDecayingExtraRot(m_rotation);
442 }
443 } else if (m_flags.offBit(eFlags::Landing)) {
444 m_rotation.setAxisRotation(DEG2RAD * m_currentAngle, m_rotAxis);
445 physics()->composeExtraRot(m_rotation);
446 }
447
448 return actionEnded;
449}
450
452bool KartAction::calcLargeFlipAction() {
453 constexpr f32 PITCH_DECAY = 0.971f;
454 constexpr f32 TOTAL_DELTA_PITCH = 720.0f;
455 constexpr f32 PHASE_DELTA = 4.0f;
456 constexpr f32 WOBBLE_AMPLITUDE = 18.1f;
457 constexpr f32 BOUNCE_FACTOR = 5.0f;
458
459 bool decayingRot = false;
460 bool stuntRot = false;
461
462 if (m_flags.onBit(eFlags::LargeFlip)) {
463 ++m_framesFlipping;
464 } else {
465 if (m_deltaPitch < TOTAL_DELTA_PITCH) {
466 m_deltaPitch += m_velPitch;
467 m_pitch -= m_velPitch;
468 m_velPitch *= (m_velPitch > 1.0f) ? PITCH_DECAY : 1.0f;
469 } else {
470 m_pitch = 0.0f;
471 }
472
473 f32 sin;
474
475 if (EGG::Mathf::abs(m_flipPhase) < 360.0f) {
476 m_flipPhase += PHASE_DELTA;
477 sin = EGG::Mathf::SinFIdx(DEG2FIDX * m_flipPhase);
478 } else {
479 sin = 0.0f;
480 }
481
482 EGG::Matrix34f mat;
483 mat.setAxisRotation(DEG2RAD * (WOBBLE_AMPLITUDE * sin), EGG::Vector3f::ez);
484 m_rotation.setAxisRotation(DEG2RAD * m_pitch, mat.ps_multVector(EGG::Vector3f::ex));
485
486 stuntRot = true;
487 }
488
489 bool actionEnded = false;
490 auto &status = KartObjectProxy::status();
491 bool touchingGround = status.onBit(eStatus::TouchingGround);
492
493 if (m_flags.offBit(eFlags::LandingFromFlip) && touchingGround && move()->up().y > 0.0f &&
494 m_frame > 50) {
495 m_flags.setBit(eFlags::LandingFromFlip);
496 dynamics()->setExtVel(move()->up().proj(EGG::Vector3f::ey) * BOUNCE_FACTOR);
497 }
498
499 if ((m_currentAction != Action::UNK_8 && m_frame < 10) || !touchingGround) {
500 dynamics()->setExtVel(EGG::Vector3f(0.0f, dynamics()->extVel().y, 0.0f));
501 }
502
503 if ((touchingGround && move()->up().dot(EGG::Vector3f::ey) > 0.0f) || m_frame >= 300) {
504 if (m_frame >= 40) {
505 status.resetBit(eStatus::LargeFlipHit);
506 }
507
508 if (m_frame <= 120) {
509 if (m_flags.onBit(eFlags::LargeFlip)) {
510 if (m_framesFlipping > 30) {
511 actionEnded = true;
512 }
513 } else {
514 if (m_frame >= 40 && m_frame <= 80) {
515 decayingRot = true;
516 m_flags.setBit(eFlags::LargeFlip);
517 status.resetBit(eStatus::LargeFlipHit);
518 }
519 }
520 } else {
521 actionEnded = true;
522 }
523 }
524
525 if (decayingRot) {
526 physics()->composeDecayingStuntRot(m_rotation);
527 } else if (stuntRot) {
528 physics()->composeStuntRot(m_rotation);
529 }
530
531 return actionEnded;
532}
533
535bool KartAction::calcPressAction() {
536 EGG::Vector3f extVel = KartObjectProxy::extVel();
537 extVel.y = std::min(0.0f, extVel.y);
538 dynamics()->setExtVel(extVel);
539
540 return m_frame > m_crushTimer;
541}
542
543/* ================================ *
544 * END FUNCTIONS
545 * ================================ */
546
547void KartAction::endStub(bool /*arg*/) {}
548
550void KartAction::endAction1(bool arg) {
551 if (arg) {
552 physics()->composeDecayingExtraRot(m_rotation);
553 }
554}
555
557void KartAction::endLaunchAction(bool arg) {
558 if (arg) {
559 physics()->composeDecayingExtraRot(m_rotation);
560 }
561}
562
563/* ================================ *
564 * ACTION TABLES
565 * ================================ */
566
567const std::array<KartAction::ActionParams, KartAction::MAX_ACTION> KartAction::s_actionParams = {{
568 {0.98f, 0.98f, 1},
569 {0.98f, 0.98f, 2},
570 {0.96f, 0.96f, 4},
571 {0.0f, 0.96f, 4},
572 {0.0f, 0.98f, 4},
573 {0.0f, 0.96f, 4},
574 {0.0f, 0.96f, 4},
575 {0.0f, 0.0f, 6},
576 {0.0f, 0.99f, 6},
577 {0.98f, 0.98f, 3},
578 {0.98f, 0.98f, 3},
579 {1.0f, 1.0f, 5},
580 {0.0f, 0.0f, 3},
581 {0.0f, 0.0f, 3},
582 {0.0f, 0.0f, 3},
583 {0.98f, 0.98f, 3},
584 {0.0f, 0.0f, 3},
585 {0.98f, 0.98f, 3},
586}};
587
588const std::array<KartAction::RotationParams, 5> KartAction::s_rotationParams = {{
589 {10.0f, 1.5f, 0.9f, 0.005f, 0.6f, 360.0f},
590 {11.0f, 1.5f, 0.9f, 0.0028f, 0.7f, 720.0f},
591 {11.0f, 1.5f, 0.9f, 0.0028f, 0.8f, 1080.0f},
592 {7.0f, 1.5f, 0.9f, 0.005f, 0.6f, 450.0f},
593 {9.0f, 1.5f, 0.9f, 0.0028f, 0.7f, 810.0f},
594}};
595
596const std::array<KartAction::StartActionFunc, KartAction::MAX_ACTION> KartAction::s_onStart = {{
597 &KartAction::startStub,
598 &KartAction::startAction1,
599 &KartAction::startAction2,
600 &KartAction::startAction3,
601 &KartAction::startAction4,
602 &KartAction::startAction5,
603 &KartAction::startStub,
604 &KartAction::startLargeFlipAction,
605 &KartAction::startLargeFlipAction,
606 &KartAction::startAction9,
607 &KartAction::startStub,
608 &KartAction::startStub,
609 &KartAction::startLongPressAction,
610 &KartAction::startStub,
611 &KartAction::startShortPressAction,
612 &KartAction::startSpinShrinkAction,
613 &KartAction::startStub,
614 &KartAction::startStub,
615}};
616
617const std::array<KartAction::CalcActionFunc, KartAction::MAX_ACTION> KartAction::s_onCalc = {{
618 &KartAction::calcStub,
619 &KartAction::calcAction1,
620 &KartAction::calcLaunchAction,
621 &KartAction::calcLaunchAction,
622 &KartAction::calcAction4,
623 &KartAction::calcLaunchAction,
624 &KartAction::calcStub,
625 &KartAction::calcLargeFlipAction,
626 &KartAction::calcLargeFlipAction,
627 &KartAction::calcAction1,
628 &KartAction::calcStub,
629 &KartAction::calcStub,
630 &KartAction::calcPressAction,
631 &KartAction::calcStub,
632 &KartAction::calcPressAction,
633 &KartAction::calcAction1,
634 &KartAction::calcStub,
635 &KartAction::calcStub,
636}};
637
638const std::array<KartAction::EndActionFunc, KartAction::MAX_ACTION> KartAction::s_onEnd = {{
639 &KartAction::endStub,
640 &KartAction::endAction1,
641 &KartAction::endLaunchAction,
642 &KartAction::endLaunchAction,
643 &KartAction::endLaunchAction,
644 &KartAction::endLaunchAction,
645 &KartAction::endStub,
646 &KartAction::endStub,
647 &KartAction::endStub,
648 &KartAction::endAction1,
649 &KartAction::endStub,
650 &KartAction::endStub,
651 &KartAction::endStub,
652 &KartAction::endStub,
653 &KartAction::endStub,
654 &KartAction::endAction1,
655 &KartAction::endStub,
656 &KartAction::endStub,
657}};
658
659} // 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