3#include "game/kart/KartCollide.hh"
4#include "game/kart/KartDynamics.hh"
5#include "game/kart/KartJump.hh"
6#include "game/kart/KartParam.hh"
7#include "game/kart/KartPhysics.hh"
8#include "game/kart/KartSub.hh"
9#include "game/kart/KartSuspension.hh"
11#include "game/field/CollisionDirector.hh"
14#include "game/item/ItemDirector.hh"
15#include "game/item/KartItem.hh"
17#include "game/system/CourseMap.hh"
18#include "game/system/RaceManager.hh"
19#include "game/system/map/MapdataCannonPoint.hh"
20#include "game/system/map/MapdataJugemPoint.hh"
22#include <egg/math/Math.hh>
23#include <egg/math/Quat.hh>
34static constexpr std::array<CannonParameter, 3> CANNON_PARAMETERS = {{
35 {500.0f, 0.0f, 6000.0f, -1.0f},
36 {500.0f, 5000.0f, 6000.0f, -1.0f},
37 {120.0f, 2000.0f, 1000.0f, 45.0f},
41KartMove::KartMove() : m_smoothedUp(
EGG::Vector3f::ey), m_scale(1.0f, 1.0f, 1.0f) {
44 m_padType.makeAllZero();
45 m_flags.makeAllZero();
50KartMove::~KartMove() {
56void KartMove::createSubsystems() {
57 m_jump =
new KartJump(
this);
68 if (state()->isInAction() || state()->isCannonStart() || state()->isInCannon() ||
69 state()->isOverZipper()) {
73 if (state()->isBeforeRespawn()) {
79 if (state()->isJumpPadMushroomCollision()) {
81 }
else if (state()->isAirtimeOver20()) {
89 if (state()->isDrifting()) {
100 if (!state()->isDrifting()) {
110void KartMove::setTurnParams() {
111 static constexpr std::array<DriftingParameters, 3> DRIFTING_PARAMS_ARRAY = {{
112 {10.0f, 0.5f, 0.5f, 1.0f},
113 {10.0f, 0.5f, 0.5f, 0.2f},
114 {10.0f, 0.22f, 0.5f, 0.2f},
121 m_landingDir = m_dir;
127void KartMove::init(
bool b1,
bool b2) {
135 m_up = EGG::Vector3f::ey;
137 m_vel1Dir = EGG::Vector3f::ez;
139 m_dir = EGG::Vector3f::ez;
140 m_landingDir = EGG::Vector3f::ez;
141 m_dirDiff = EGG::Vector3f::zero;
142 m_hasLandingDir =
false;
144 m_landingAngle = 0.0f;
162 m_standStillBoostRot = 0.0f;
163 m_driftState = DriftState::NotDrifting;
168 m_zipperBoostTimer = 0;
169 m_zipperBoostMax = 0;
175 m_nonZipperAirtime = 0;
182 m_hitboxScale = 1.0f;
187 m_jumpPadMaxSpeed = 0.0f;
188 m_jumpPadBoostMultiplier = 0.0f;
189 m_jumpPadProperties =
nullptr;
191 m_autoDriftAngle = 0.0f;
192 m_autoDriftStartFrameCounter = 0;
194 m_cannonEntryOfsLength = 0.0f;
195 m_cannonEntryPos.setZero();
196 m_cannonEntryOfs.setZero();
197 m_cannonOrthog.setZero();
198 m_cannonProgress.setZero();
216void KartMove::clear() {
217 if (state()->isOverZipper()) {
218 state()->setActionMidZipper(
true);
226 clearOffroadInvincibility();
236 quaternion.
setRPY(angles * DEG2RAD);
241 bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos,
242 EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0);
245 newPos = newPos + info.tangentOff + (info.floorNrm * -100.0f);
246 newPos += info.floorNrm * bsp().initialYPos;
252 sub()->initPhysicsValues();
254 physics()->setPos(pos());
255 physics()->setVelocity(dynamics()->velocity());
260 dynamics()->setTop(
m_up);
262 for (
u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) {
263 suspension(tireIdx)->setInitialState();
273 if (state()->isInRespawn()) {
278 dynamics()->resetInternalVelocity();
293 if (!state()->isAutoDrift()) {
303 if (state()->isInCannon()) {
314void KartMove::calcRespawnStart() {
315 constexpr float RESPAWN_HEIGHT = 700.0f;
317 const auto *jugemPoint = System::RaceManager::Instance()->jugemPoint();
322 respawnPos.y += RESPAWN_HEIGHT;
327 Item::ItemDirector::Instance()->kartItem(0).clear();
329 state()->setTriggerRespawn(
false);
330 state()->setInRespawn(
true);
334void KartMove::calcInRespawn() {
335 constexpr f32 LAKITU_VELOCITY = 1.5f;
336 constexpr u16 RESPAWN_DURATION = 110;
338 if (!state()->isInRespawn()) {
343 newPos.y -= LAKITU_VELOCITY;
344 dynamics()->setPos(newPos);
345 dynamics()->setNoGravity(
true);
348 state()->setInRespawn(
false);
349 state()->setAfterRespawn(
true);
350 state()->setRespawnKillY(
true);
353 dynamics()->setNoGravity(
false);
358void KartMove::calcRespawnBoost() {
359 constexpr s16 RESPAWN_BOOST_DURATION = 30;
360 constexpr s16 RESPAWN_BOOST_INPUT_LENIENCY = 4;
362 if (state()->isAfterRespawn()) {
363 if (state()->isTouchingGround()) {
365 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
366 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
367 m_respawnTimer = RESPAWN_BOOST_DURATION;
373 state()->setAfterRespawn(
false);
385 if (state()->isAccelerateStart()) {
386 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
387 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
388 m_respawnTimer = RESPAWN_BOOST_DURATION;
396 state()->setRespawnKillY(
false);
400 m_respawnTimer = std::max(0, m_respawnTimer - 1);
404void KartMove::calcTop() {
405 f32 stabilizationFactor = 0.1f;
406 m_hasLandingDir =
false;
409 if (state()->isGroundStart() && m_nonZipperAirtime >= 3) {
413 m_dirDiff = m_landingDir.
proj(m_landingDir);
414 m_hasLandingDir =
true;
416 if (state()->isHop() &&
m_hopPosY > 0.0f) {
418 }
else if (state()->isTouchingGround()) {
420 inputTop.
dot(m_dir) > 0.0f &&
m_speed > 50.0f &&
421 collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::NotTrickable)) {
429 if (state()->isHalfPipeRamp() ||
430 (!state()->isBoost() && !state()->isRampBoost() && !state()->isWheelie() &&
431 !state()->isOverZipper() &&
432 (!state()->isZipperBoost() || m_zipperBoostTimer > 15))) {
433 f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(inputTop.
dot(componentZAxis())));
434 scalar = std::min(0.8f, std::max(0.3f, topDotZ));
442 if (bodyDotFront < -0.1f) {
443 stabilizationFactor += std::min(0.2f, EGG::Mathf::abs(bodyDotFront) * 0.5f);
446 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
447 stabilizationFactor = 0.4f;
454 dynamics()->setStabilizationFactor(stabilizationFactor);
456 m_nonZipperAirtime = state()->isOverZipper() ? 0 : state()->airtime();
457 m_flags.
changeBit(collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::Trickable),
464 if (state()->isOverZipper() || !state()->isAirtimeOver20()) {
475 if (
m_up.y <= 0.99f) {
476 m_up += (EGG::Vector3f::ey -
m_up) * 0.03f;
479 m_up = EGG::Vector3f::ey;
487 const auto *raceMgr = System::RaceManager::Instance();
488 if (!raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
492 if (m_padType.
onBit(ePadType::BoostPanel)) {
493 tryStartBoostPanel();
496 if (m_padType.
onBit(ePadType::BoostRamp)) {
500 if (m_padType.
onBit(ePadType::JumpPad)) {
508void KartMove::calcDirs() {
512 m_flags.
setBit(eFlags::LaunchBoost);
514 if (!state()->isInATrick() && !state()->isOverZipper() &&
515 (((state()->isTouchingGround() || !state()->isRampBoost() ||
516 !m_jump->isBoostRampEnabled()) &&
517 !state()->isJumpPad() && state()->airtime() <= 5) ||
518 state()->isJumpPadMushroomCollision() || state()->isNoSparkInvisibleWall())) {
519 if (state()->isHop()) {
531 if (dirDiff.
squaredLength() <= std::numeric_limits<f32>::epsilon()) {
542 if (origDirCross.
dot(newDirCross) < 0.0f) {
549 m_flags.
resetBit(eFlags::LaunchBoost);
554 if (!state()->isOverZipper()) {
558 if (m_hasLandingDir) {
559 f32 dot = m_dir.dot(m_landingDir);
561 f32 crossDot = cross.
length();
562 f32 angle = EGG::Mathf::atan2(crossDot, dot);
563 angle = EGG::Mathf::abs(angle);
570 m_landingAngle += (angle * RAD2DEG) * fVar4;
573 if (m_landingAngle <= 0.0f) {
574 if (m_landingAngle < 0.0f) {
575 m_landingAngle = std::min(0.0f, m_landingAngle + 2.0f);
578 m_landingAngle = std::max(0.0f, m_landingAngle - 2.0f);
583void KartMove::calcStickyRoad() {
584 constexpr f32 STICKY_RADIUS = 200.0f;
588 if (state()->isOverZipper()) {
589 state()->setStickyRoad(
false);
593 if ((!state()->isStickyRoad() &&
594 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::Trickable)) ||
595 EGG::Mathf::abs(
m_speed) <= 20.0f) {
603 colInfo.bbox.setZero();
605 bool stickyRoad =
false;
607 for (
size_t i = 0; i < 3; ++i) {
609 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
610 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
611 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
617 pos += -STICKY_RADIUS * componentYAxis();
621 state()->setStickyRoad(
false);
629 if (state()->isBoostOffroadInvincibility()) {
633 bool anyWheel = state()->isAnyWheelCollision();
640 if (state()->isVehicleBodyFloorCollision()) {
657void KartMove::calcBoost() {
658 if (m_boost.
calc()) {
659 state()->setAccelerate(
true);
661 state()->setBoost(
false);
668void KartMove::calcRampBoost() {
669 if (!state()->isRampBoost()) {
673 state()->setAccelerate(
true);
674 if (--m_rampBoost < 1) {
676 state()->setRampBoost(
false);
684 if (!state()->isDisableBackwardsAccel()) {
690 state()->setDisableBackwardsAccel(
false);
699 constexpr s16 MAX_SSMT_CHARGE = 75;
700 constexpr s16 SSMT_BOOST_FRAMES = 30;
701 constexpr s16 LEEWAY_FRAMES = 1;
702 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
706 if (state()->isChargingSsmt()) {
727 state()->setDisableBackwardsAccel(
true);
729 if (!state()->isAccelerate() && !state()->isBrake()) {
730 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
736 if (state()->isAccelerate() && !state()->isBrake()) {
737 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
743 state()->setDisableBackwardsAccel(
true);
754 if (!state()->isTouchingGround() && !state()->isHop() && !state()->isDriftManual()) {
755 if (state()->isStickLeft() || state()->isStickRight()) {
756 if (!state()->isDriftInput()) {
757 state()->setSlipdriftCharge(
false);
758 }
else if (!state()->isSlipdriftCharge()) {
760 if (state()->isStickRight()) {
762 }
else if (state()->isStickLeft()) {
765 state()->setSlipdriftCharge(
true);
772 if (state()->isHop()) {
774 if (state()->isStickRight()) {
776 }
else if (state()->isStickLeft()) {
783 }
else if (state()->isSlipdriftCharge()) {
787 return state()->isHop() || state()->isSlipdriftCharge();
796 state()->setHop(
false);
797 state()->setDriftManual(
false);
798 m_driftState = DriftState::NotDrifting;
810 m_driftState = DriftState::NotDrifting;
814 state()->setHop(
false);
815 state()->setSlipdriftCharge(
false);
816 state()->setDriftManual(
false);
817 m_autoDriftStartFrameCounter = 0;
821void KartMove::clearJumpPad() {
823 state()->setJumpPad(
false);
827void KartMove::clearRampBoost() {
829 state()->setRampBoost(
false);
833void KartMove::clearZipperBoost() {
834 m_zipperBoostTimer = 0;
835 state()->setZipperBoost(
false);
839void KartMove::clearBoost() {
840 m_boost.resetActive();
841 state()->setBoost(
false);
845void KartMove::clearSsmt() {
853void KartMove::clearOffroadInvincibility() {
855 state()->setBoostOffroadInvincibility(
false);
858void KartMove::clearRejectRoad() {
859 state()->setRejectRoadTrigger(
false);
860 state()->setNoSparkInvisibleWall(
false);
867 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
869 if (!state()->isAutoDrift()) {
873 if (canStartDrift() && !state()->isOverZipper() && !state()->isRejectRoadTrigger() &&
874 !state()->isWheelie() && EGG::Mathf::abs(state()->stickX()) > 0.85f) {
875 m_autoDriftStartFrameCounter =
876 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
878 m_autoDriftStartFrameCounter = 0;
881 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
882 state()->setDriftAuto(
true);
884 if (state()->isTouchingGround()) {
885 if (state()->stickX() < 0.0f) {
895 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
896 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
898 state()->setDriftAuto(
false);
901 if (m_autoDriftAngle > 0.0f) {
903 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
906 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
912 physics()->composeExtraRot(angleAxis);
921 if (!state()->isOverZipper()) {
924 if (!state()->isTouchingGround() &&
925 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
926 !state()->isJumpPadMushroomCollision() &&
927 (state()->isDriftManual() || state()->isSlipdriftCharge()) &&
928 m_flags.
onBit(eFlags::LaunchBoost)) {
933 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
934 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
936 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
948 if (((!state()->isHop() ||
m_hopFrame < 3) && !state()->isSlipdriftCharge()) ||
949 (state()->isInAction() || !state()->isTouchingGround())) {
961 if (!state()->isDriftManual()) {
962 if (!isHopping && state()->isTouchingGround()) {
965 if (!action()->flags().onBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
966 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
975 if (!state()->isOverZipper() &&
976 (!state()->isDriftInput() || !state()->isAccelerate() || state()->isInAction() ||
977 state()->isRejectRoadTrigger() || state()->isWall3Collision() ||
978 state()->isWallCollision() || !canStartDrift())) {
979 if (canStartDrift()) {
995 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
997 const auto &stats = param()->stats();
999 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1000 f32 driftAngle = 0.0f;
1002 if (state()->isHop()) {
1017 state()->setHop(
false);
1018 state()->setSlipdriftCharge(
false);
1020 if (!state()->isDriftInput()) {
1028 state()->setDriftManual(
true);
1029 state()->setHop(
false);
1030 m_driftState = DriftState::ChargingMt;
1038 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1040 if (m_driftState < DriftState::ChargedMt || state()->isBrake()) {
1041 m_driftState = DriftState::NotDrifting;
1047 if (m_driftState == DriftState::ChargedSmt) {
1048 mtLength *= SMT_LENGTH_FACTOR;
1051 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
1052 activateBoost(KartBoost::Type::AllMt, mtLength);
1055 m_driftState = DriftState::NotDrifting;
1062 if (state()->airtime() > 5) {
1066 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1069 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1070 if (angle > targetAngle) {
1072 }
else if (angle < targetAngle) {
1078 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1079 if (targetAngle > angle) {
1081 }
else if (targetAngle < angle) {
1096 bool drifting = state()->isDrifting() && !state()->isJumpPadMushroomCollision();
1097 bool autoDrift = state()->isAutoDrift();
1098 const auto &stats = param()->stats();
1101 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1103 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1106 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1111 bool forwards =
true;
1112 if (state()->isBrake() &&
m_speed <= 0.0f) {
1117 if (state()->isChargingSsmt()) {
1120 if (state()->isHop() &&
m_hopPosY > 0.0f) {
1125 bool noTurn =
false;
1126 if (!state()->isWallCollision() && !state()->isWall3Collision() &&
1127 EGG::Mathf::abs(
m_speed) < 1.0f) {
1128 if (!(state()->isHop() &&
m_hopPosY > 0.0f)) {
1133 if (forwards && !noTurn) {
1137 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1140 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1149 if (state()->isZipperBoost() && !state()->isDriftManual()) {
1153 f32 stickX = EGG::Mathf::abs(state()->stickX());
1154 if (autoDrift && stickX > 0.3f) {
1155 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1156 stickX = drifting ? 0.2f : 0.5f;
1161 if (!state()->isInAction() && !state()->isZipperTrick()) {
1162 if (!state()->isTouchingGround()) {
1163 if (state()->isRampBoost() && m_jump->isBoostRampEnabled()) {
1165 }
else if (!state()->isJumpPadMushroomCollision()) {
1166 u32 airtime = state()->airtime();
1167 if (airtime >= 70) {
1169 }
else if (airtime >= 30) {
1170 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1176 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1177 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1179 if (angle > 60.0f) {
1180 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1191 const auto *raceMgr = System::RaceManager::Instance();
1192 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1193 f32 speedFix = dynamics()->speedFix();
1194 if (state()->isInAction() ||
1195 ((state()->isWallCollisionStart() || state()->wallBonkTimer() == 0 ||
1196 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1197 !state()->isDriftManual())) {
1209 if (state()->isInAction()) {
1210 action()->calcVehicleSpeed();
1214 if ((state()->isSomethingWallCollision() && state()->isTouchingGround() &&
1215 !state()->isAnyWheelCollision()) ||
1216 !state()->isTouchingGround() || state()->isChargingSsmt()) {
1217 if (state()->isRampBoost() && state()->airtime() < 4) {
1220 if (state()->isJumpPad() && !state()->isAccelerate()) {
1223 if (state()->isOverZipper()) {
1226 if (state()->airtime() > 5) {
1234 }
else if (state()->isBoost()) {
1237 if (!state()->isJumpPad() && !state()->isRampBoost()) {
1238 if (state()->isAccelerate()) {
1241 if (!state()->isBrake() || state()->isDisableBackwardsAccel() ||
1242 state()->isSomethingWallCollision()) {
1255 if (!state()->isBoost() && !state()->isDriftManual() && !state()->isAutoDrift()) {
1256 const auto &stats = param()->stats();
1259 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1273 initialVel = std::min(initialVel * 2.0f, 2.0f);
1275 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1289 std::span<const f32> as;
1290 std::span<const f32> ts;
1291 if (state()->isDrifting()) {
1292 as = param()->stats().accelerationDriftA;
1293 ts = param()->stats().accelerationDriftT;
1295 as = param()->stats().accelerationStandardA;
1296 ts = param()->stats().accelerationStandardT;
1300 f32 acceleration = 0.0f;
1302 for (; i < ts.size(); ++i) {
1303 if (ratio < ts[i]) {
1304 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1311 return i < ts.size() ? acceleration : as.back();
1318 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1319 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1320 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1321 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1322 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1326 dynamics()->setKillExtVelY(state()->isRespawnKillY());
1328 if (state()->isBurnout()) {
1344 if (state()->isBeforeRespawn()) {
1347 if (state()->isChargingSsmt()) {
1358 f32 speedLimit = state()->isJumpPad() ? m_jumpPadMaxSpeed :
m_baseSpeed;
1359 const f32 boostMultiplier = m_boost.multiplier();
1360 const f32 boostSpdLimit = m_boost.speedLimit();
1361 m_jumpPadBoostMultiplier = boostMultiplier;
1363 if (!state()->isJumpPadFixedSpeed()) {
1367 if (!state()->isJumpPad()) {
1372 if (state()->isRampBoost()) {
1373 speedLimit = std::max(speedLimit, 100.0f);
1378 f32 local_c8 = 1.0f;
1381 if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1391 if (state()->isJumpPad()) {
1402 crossVec = -crossVec;
1405 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1406 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1407 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1408 }
else if (!state()->isTouchingGround()) {
1409 rotationScalar = ROTATION_SCALAR_MIDAIR;
1416 const auto *raceMgr = System::RaceManager::Instance();
1417 if (!state()->isInAction() && !state()->isDisableBackwardsAccel() &&
1418 state()->isTouchingGround() && !state()->isAccelerate() &&
1419 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1426 f32 maxSpeedY = state()->isOverZipper() ? KartHalfPipe::TerminalVelocity() : TERMINAL_VELOCITY;
1427 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1429 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1431 if (state()->isTouchingGround() && !state()->isDriftManual() && !state()->isHop()) {
1432 if (state()->isBrake()) {
1435 DrivingDirection::Backwards;
1451 if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1457 if (state()->isZipperInvisibleWall() || state()->isOverZipper()) {
1462 if (wallNrm.y > 0.0f) {
1470 f1 = std::max(0.0f, dot + 1.0f);
1472 return std::min(1.0f, f1 * (state()->isWallCollision() ? 0.4f : 0.7f));
1484 if (!state()->isWallCollisionStart()) {
1489 if (!state()->isInAction()) {
1492 m_landingDir = m_dir;
1495 if (!state()->isOverZipper() && param_2 < 0.9f) {
1498 if (speedDiff > 30.0f) {
1502 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1506 speedDiff = std::min(60.0f, speedDiff);
1509 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1513 if (state()->isBoost()) {
1514 proj = EGG::Vector3f::zero;
1515 rej = EGG::Vector3f::zero;
1518 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1519 proj = EGG::Vector3f::zero;
1524 f32 bumpDeviation = 0.0f;
1526 bumpDeviation = param()->stats().bumpDeviationLevel;
1541 if (state()->isTouchingGround()) {
1542 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1543 next = 0.015f * -state()->startBoostCharge();
1544 }
else if (!state()->isChargingSsmt()) {
1545 if (!state()->isJumpPad() && !state()->isRampBoost() && !state()->isSoftWallDrift()) {
1547 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1549 if (state()->isMushroomBoost()) {
1550 next = (scalar * 0.15f) * 0.25f;
1551 if (state()->isWheelie()) {
1555 next = (scalar * 0.15f) * 0.08f;
1560 constexpr s16 MAX_SSMT_CHARGE = 75;
1561 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1566 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1568 m_standStillBoostRot += scalar * (next - m_standStillBoostRot);
1576 constexpr f32 DIVE_LIMIT = 0.8f;
1580 if (state()->isTouchingGround() || state()->isCannonStart() || state()->isInCannon() ||
1581 state()->isInAction() || state()->isOverZipper()) {
1585 f32 stickY = state()->stickY();
1588 stickY = std::min(1.0f, stickY + 0.4f);
1591 u32 airtime = state()->airtime();
1594 if (EGG::Mathf::abs(stickY) < 0.1f) {
1598 stickY *= (airtime / 50.0f);
1605 dynamics()->setAngVel2(angVel2);
1607 if (state()->airtime() < 50) {
1613 f32 upDotTop =
m_up.
dot(topRotated);
1615 f32 crossNorm = upCrossTop.
length();
1616 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1618 f32 fVar1 = angle * RAD2DEG - 20.0f;
1619 if (fVar1 <= 0.0f) {
1623 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1624 if (forwardRotated.y > 0.0f) {
1625 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1627 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1635 if (EGG::Mathf::abs(
m_speed) >= 10.0f || state()->isBoost() || state()->isRampBoost() ||
1636 !state()->isAccelerate() || !state()->isBrake()) {
1637 state()->setChargingSsmt(
false);
1641 state()->setChargingSsmt(
true);
1642 state()->setHopStart(
false);
1643 state()->setDriftInput(
false);
1647void KartMove::calcHopPhysics() {
1658void KartMove::calcRejectRoad() {
1659 m_reject.calcRejectRoad();
1663bool KartMove::calcZipperCollision(f32 radius, f32 scale,
EGG::Vector3f &pos,
1667 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1669 auto *colDir = Field::CollisionDirector::Instance();
1670 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1675 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.
dot(to)));
1676 f32 acos = EGG::Mathf::acos(dotNorm);
1677 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1683 f32 tiltMagnitude = 0.0f;
1685 if (!state()->isInAction() && !state()->isSoftWallDrift() && state()->isAnyWheelCollision()) {
1689 f32 magnitude = tiltMagnitude;
1691 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1692 magnitude = frontSpeed.
length();
1694 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1695 magnitude = -magnitude;
1698 tiltMagnitude = -1.0f;
1699 if (-1.0f <= magnitude) {
1700 tiltMagnitude = std::min(1.0f, magnitude);
1703 }
else if (!state()->isHop() ||
m_hopPosY <= 0.0f) {
1706 dynamics()->setAngVel0(angVel0);
1709 f32 lean = EGG::Mathf::abs(
m_weightedTurn) * (tiltMagnitude * param()->stats().tilt);
1714 angVel0.x += m_standStillBoostRot;
1716 dynamics()->setAngVel0(angVel0);
1720 dynamics()->setAngVel2(angVel2);
1730 constexpr u16 MAX_MT_CHARGE = 270;
1731 constexpr u16 MAX_SMT_CHARGE = 300;
1732 constexpr u16 BASE_MT_CHARGE = 2;
1733 constexpr u16 BASE_SMT_CHARGE = 2;
1734 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1735 constexpr u16 EXTRA_MT_CHARGE = 3;
1737 if (m_driftState == DriftState::ChargedSmt) {
1741 f32 stickX = state()->stickX();
1743 if (m_driftState == DriftState::ChargingMt) {
1746 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1747 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1756 m_driftState = DriftState::ChargingSmt;
1760 if (m_driftState != DriftState::ChargingSmt) {
1766 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1767 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1776 m_driftState = DriftState::ChargedSmt;
1781void KartMove::initOob() {
1787 clearOffroadInvincibility();
1794 state()->setHop(
true);
1795 state()->setDriftManual(
false);
1800 m_driftState = DriftState::NotDrifting;
1812 dynamics()->setExtVel(extVel);
1815 totalForce.y = 0.0f;
1816 dynamics()->setTotalForce(totalForce);
1820void KartMove::tryStartBoostPanel() {
1821 constexpr s16 BOOST_PANEL_DURATION = 60;
1823 if (state()->isBeforeRespawn() || state()->isInAction()) {
1827 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
1835 constexpr s16 BOOST_RAMP_DURATION = 60;
1837 if (state()->isBeforeRespawn() || state()->isInAction()) {
1841 state()->setRampBoost(
true);
1842 m_rampBoost = BOOST_RAMP_DURATION;
1851 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
1852 {50.0f, 50.0f, 35.0f},
1853 {50.0f, 50.0f, 47.0f},
1854 {59.0f, 59.0f, 30.0f},
1855 {73.0f, 73.0f, 45.0f},
1856 {73.0f, 73.0f, 53.0f},
1857 {55.0f, 55.0f, 35.0f},
1858 {56.0f, 56.0f, 50.0f},
1861 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isHalfPipeRamp()) {
1865 state()->setJumpPad(
true);
1866 s32 jumpPadVariant = state()->jumpPadVariant();
1867 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
1869 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
1870 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
1873 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
1874 {100.0f, 100.0f, 70.0f},
1875 {100.0f, 100.0f, 65.0f},
1877 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
1879 state()->setJumpPadFixedSpeed(
true);
1882 if (jumpPadVariant == 4) {
1883 state()->setJumpPadMushroomTrigger(
true);
1884 state()->setJumpPadMushroomVelYInc(
true);
1885 state()->setJumpPadMushroomCollision(
true);
1890 extVel.y = m_jumpPadProperties->velY;
1891 totalForce.y = 0.0f;
1893 dynamics()->setExtVel(extVel);
1894 dynamics()->setTotalForce(totalForce);
1896 if (jumpPadVariant != 3) {
1903 state()->setJumpPadDisableYsusForce(
true);
1908 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
1913void KartMove::tryEndJumpPad() {
1914 if (state()->isJumpPadMushroomTrigger()) {
1915 if (state()->isGroundStart()) {
1916 state()->setJumpPadMushroomTrigger(
false);
1917 state()->setJumpPadFixedSpeed(
false);
1918 state()->setJumpPadMushroomVelYInc(
false);
1921 if (state()->isJumpPadMushroomVelYInc()) {
1923 newExtVel.y += 20.0f;
1924 if (m_jumpPadProperties->velY < newExtVel.y) {
1925 newExtVel.y = m_jumpPadProperties->velY;
1926 state()->setJumpPadMushroomVelYInc(
false);
1928 dynamics()->setExtVel(newExtVel);
1932 if (state()->isGroundStart() && !state()->isJumpPadMushroomTrigger()) {
1938void KartMove::cancelJumpPad() {
1940 state()->setJumpPad(
false);
1944void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
1945 if (m_boost.
activate(type, frames)) {
1946 state()->setBoost(
true);
1951void KartMove::applyStartBoost(s16 frames) {
1952 activateBoost(KartBoost::Type::AllMt, frames);
1956void KartMove::activateMushroom() {
1957 constexpr s16 MUSHROOM_DURATION = 90;
1959 if (state()->isBeforeRespawn() || state()->isInAction()) {
1963 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
1966 state()->setMushroomBoost(
true);
1971void KartMove::activateZipperBoost() {
1972 constexpr s16 BASE_DURATION = 50;
1973 constexpr s16 TRICK_DURATION = 100;
1975 if (state()->isBeforeRespawn() || state()->isInAction()) {
1979 s16 boostDuration = state()->isZipperTrick() ? TRICK_DURATION : BASE_DURATION;
1980 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
1983 m_zipperBoostTimer = 0;
1984 m_zipperBoostMax = boostDuration;
1985 state()->setZipperBoost(
true);
1997 state()->setBoostOffroadInvincibility(
true);
2004 if (!state()->isBoostOffroadInvincibility()) {
2012 state()->setBoostOffroadInvincibility(
false);
2018 if (!state()->isMushroomBoost()) {
2026 state()->setMushroomBoost(
false);
2030void KartMove::calcZipperBoost() {
2031 if (!state()->isZipperBoost()) {
2035 state()->setAccelerate(
true);
2037 if (!state()->isOverZipper() && ++m_zipperBoostTimer >= m_zipperBoostMax) {
2038 m_zipperBoostTimer = 0;
2039 state()->setZipperBoost(
false);
2042 if (m_zipperBoostTimer < 10) {
2045 dynamics()->setAngVel0(angVel);
2050void KartMove::landTrick() {
2051 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2056 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2062 if (state()->isBeforeRespawn() || state()->isInAction()) {
2068 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2070 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2073 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2077void KartMove::enterCannon() {
2079 physics()->clearDecayingRot();
2080 m_boost.resetActive();
2081 state()->setBoost(
false);
2087 clearOffroadInvincibility();
2089 dynamics()->reset();
2092 state()->setHop(
false);
2093 state()->setInCannon(
true);
2094 state()->setSkipWheelCalc(
true);
2095 state()->setCannonStart(
false);
2097 const auto [cannonPos, cannonDir] = getCannonPosRot();
2098 m_cannonEntryPos = pos();
2099 m_cannonEntryOfs = cannonPos - pos();
2100 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2102 m_dir = m_cannonEntryOfs;
2103 m_vel1Dir = m_cannonEntryOfs;
2104 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2105 m_cannonProgress.setZero();
2109void KartMove::calcCannon() {
2110 auto [cannonPos, cannonDir] = getCannonPosRot();
2111 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2113 f32 forwardLength = forward.
normalise();
2127 if (forwardLength < 30.0f || local94.
dot(forwardXZ) <= 0.0f) {
2132 const auto *cannonPoint =
2133 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2134 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2135 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2136 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2137 f32 newSpeed = cannonParams.speed;
2138 if (forwardLength < cannonParams.decelFactor) {
2139 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2141 newSpeed = cannonParams.endDecel;
2142 if (newSpeed <= 0.0f) {
2146 newSpeed += factor * (cannonParams.speed - newSpeed);
2147 if (cannonParams.endDecel > 0.0f) {
2152 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2155 if (cannonParams.height > 0.0f) {
2156 f32 fVar9 = EGG::Mathf::SinFIdx(
2157 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2158 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2161 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2162 m_dir = m_cannonEntryOfs;
2163 m_vel1Dir = m_cannonEntryOfs;
2165 calcRotCannon(forward);
2167 dynamics()->setExtVel(EGG::Vector3f::zero);
2175 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2181 local80 *= dynamics()->fullRot();
2186 dynamics()->setFullRot(newRot);
2187 dynamics()->setMainRot(newRot);
2191void KartMove::exitCannon() {
2192 if (!state()->isInCannon()) {
2196 state()->setInCannon(
false);
2197 state()->setSkipWheelCalc(
false);
2198 state()->setAfterCannon(
true);
2199 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2203void KartMove::triggerRespawn() {
2205 state()->setTriggerRespawn(
true);
2209KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2212KartMoveBike::~KartMoveBike() =
default;
2217 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2218 constexpr u16 WHEELIE_COOLDOWN = 20;
2220 state()->setWheelie(
true);
2225 m_autoHardStickXFrames = 0;
2232 state()->setWheelie(
false);
2234 m_autoHardStickXFrames = 0;
2238void KartMoveBike::createSubsystems() {
2249 const auto *raceManager = System::RaceManager::Instance();
2251 if (!state()->isChargingSsmt()) {
2252 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2253 EGG::Mathf::abs(
m_speed) < 5.0f) {
2265 f32 stickX = state()->stickX();
2266 f32 extVelXFactor = 0.0f;
2270 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isWheelie() ||
2271 state()->isOverZipper() || state()->isRejectRoadTrigger() ||
2272 state()->isAirtimeOver20() || state()->isSoftWallDrift() ||
2273 state()->isSomethingWallCollision() || state()->isHWG() || state()->isCannonStart() ||
2274 state()->isInCannon()) {
2276 }
else if (!state()->isDrifting()) {
2277 if (stickX <= 0.2f) {
2278 if (stickX >= -0.2f) {
2293 leanRotMin = -leanRotMax;
2297 if (stickX == 0.0f) {
2303 }
else if (stickX == 0.0f) {
2311 bool capped =
false;
2323 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2326 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2330 dynamics()->setAngVel2(dynamics()->angVel2() +
2331 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2338 if (!state()->isRejectRoad() && !state()->isHalfPipeRamp() && !state()->isOverZipper()) {
2340 scalar = std::min(1.0f, scalar);
2341 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2343 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2348 dynamics()->setTop_(top);
2356 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2357 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2358 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2361 KartMove::setTurnParams();
2363 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2365 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2369 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2379void KartMoveBike::init(
bool b1,
bool b2) {
2380 KartMove::init(b1, b2);
2389 m_autoHardStickXFrames = 0;
2393void KartMoveBike::clear() {
2401 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2402 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2407 if (state()->isWheelie()) {
2408 bool cancelAutoWheelie =
false;
2410 if (!state()->isAutoDrift() ||
2411 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2412 m_autoHardStickXFrames = 0;
2414 if (++m_autoHardStickXFrames > 15) {
2415 cancelAutoWheelie =
true;
2427 dynamics()->setAngVel0(angVel0);
2437 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2442 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2443 dynamics()->setAngVel2(angVel2);
2448 state()->setWheelieRot(
true);
2450 state()->setWheelieRot(
false);
2460 if (state()->isAutoDrift()) {
2477 constexpr u16 MAX_MT_CHARGE = 270;
2478 constexpr u16 BASE_MT_CHARGE = 2;
2479 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2480 constexpr u16 EXTRA_MT_CHARGE = 3;
2482 if (m_driftState != DriftState::ChargingMt) {
2488 f32 stickX = state()->stickX();
2489 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2490 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2499 m_driftState = DriftState::ChargedMt;
2504void KartMoveBike::initOob() {
2505 KartMove::initOob();
2512 constexpr s16 COOLDOWN_FRAMES = 20;
2513 bool dpadUp = inputs()->currentState().trickUp();
2515 if (!state()->isWheelie()) {
2516 if (dpadUp && state()->isTouchingGround()) {
2517 if (state()->isDriftManual() || state()->isWallCollision() ||
2518 state()->isWall3Collision() || state()->isHop() || state()->isDriftAuto() ||
2519 state()->isInAction()) {
@ COL_TYPE_MOVING_WATER
Koopa Cape and DS Yoshi Falls.
@ COL_TYPE_STICKY_ROAD
Player sticks if within 200 units (rBC stairs).
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
void makeOrthonormalBasis(const Vector3f &v0, const Vector3f &v1)
Sets a 3x3 orthonormal basis for a local coordinate system.
void setAxisRotation(f32 angle, const Vector3f &axis)
Rotates the matrix about an axis.
Vector3f multVector33(const Vector3f &vec) const
Multiplies a 3x3 matrix by a vector.
Vector3f multVector(const Vector3f &vec) const
Multiplies a vector by a matrix.
bool activate(Type type, s16 frames)
Starts/restarts a boost of the given type.
bool calc()
Computes the current frame's boost multiplier, acceleration, and speed limit.
void applyWrenchScaled(const EGG::Vector3f &p, const EGG::Vector3f &f, f32 scale)
Applies a force linearly and rotationally to the kart.
Handles the physics and boosts associated with zippers.
f32 m_leanRot
Z-axis rotation of the bike from leaning.
f32 m_leanRotCap
The maximum leaning rotation.
void calcWheelie() override
STAGE 1+ - Every frame, checks player input for wheelies and computes wheelie rotation.
void calcMtCharge() override
Every frame during a drift, calculates MT charge based on player input.
virtual void startWheelie()
STAGE 1+ - Sets the wheelie bit flag and some wheelie-related variables.
f32 m_wheelieRotDec
The wheelie rotation decrementor, used after a wheelie has ended.
u32 m_wheelieFrames
Tracks wheelie duration and cancels the wheelie after 180 frames.
f32 m_wheelieRot
X-axis rotation from wheeling.
const TurningParameters * m_turningParams
Inside/outside drifting bike turn info.
void setTurnParams() override
On init, sets the bike's lean rotation cap and increment.In addition to setting the lean rotation cap...
f32 m_maxWheelieRot
The maximum wheelie rotation.
void tryStartWheelie()
STAGE 1+ - Every frame, checks player input to see if we should start or stop a wheelie.
void onWallCollision() override
Called when you collide with a wall. All it does for bikes is cancel wheelies.
void onHop() override
Virtual function that just cancels wheelies when you hop.
bool canWheelie() const override
Checks if the kart is going fast enough to wheelie.
void calcVehicleRotation(f32) override
Every frame, calculates rotation, EV, and angular velocity for the bike.
s16 m_wheelieCooldown
The number of frames before another wheelie can start.
f32 m_leanRotInc
The incrementor for leaning rotation.
virtual void cancelWheelie()
Clears the wheelie bit flag and resets the rotation decrement.
f32 m_baseSpeed
The speed associated with the current character/vehicle stats.
void calcRotation()
Every frame, calculates kart rotation based on player input.
s16 m_ssmtLeewayTimer
Frames to forgive letting go of A before clearing SSMT charge.
s32 m_hopFrame
A timer that can prevent subsequent hops until reset.
void calcDisableBackwardsAccel()
Computes the current cooldown duration between braking and reversing.
EGG::Vector3f m_hopUp
The up vector when hopping.
u16 m_mushroomBoostTimer
Number of frames until the mushroom boost runs out.
void calcSpecialFloor()
Every frame, calculates any boost resulting from a boost panel.
void calcWallCollisionStart(f32 param_2)
If we started to collide with a wall this frame, applies rotation.
KartHalfPipe * m_halfPipe
Pertains to zipper physics.
f32 m_kclRotFactor
Float between 0-1 that scales the player's turning radius on offroad.
f32 m_outsideDriftBonus
Added to angular velocity when outside drifting.
void tryStartBoostRamp()
Sets offroad invincibility and and enables the ramp boost bitfield flag.
u16 m_smtCharge
A value between 0 and 300 representing current SMT charge.
f32 m_speedRatio
The ratio between current speed and the player's base speed stat.
@ DriftReset
Set when a wall bonk should cancel your drift.
@ SsmtCharged
Set after holding a stand-still mini-turbo for 75 frames.
@ TrickableSurface
Set when driving on a trickable surface.
@ SsmtLeeway
If set, activates SSMT when not pressing A or B.
@ WallBounce
Set when our speed loss from wall collision is > 30.0f.
@ Respawned
Set when Lakitu lets go of the player, cleared when landing.
void tryStartJumpPad()
Applies calculations to start interacting with a jump pad.
f32 m_jumpPadMinSpeed
Snaps the player to a minimum speed when first touching a jump pad.
f32 m_hopPosY
Relative position as the result of a hop. Starts at 0.
DrivingDirection m_drivingDirection
Current state of driver's direction.
bool calcPreDrift()
Each frame, checks for hop or slipdrift. Computes drift direction based on player input.
f32 m_speed
Current speed, restricted to the soft speed limit.
s16 m_offroadInvincibility
How many frames until the player is affected by offroad.
void calcAirtimeTop()
Calculates rotation of the bike due to excessive airtime.
void startManualDrift()
Called when the player lands from a drift hop, or to start a slipdrift.
void controlOutsideDriftAngle()
Every frame, handles mini-turbo charging and outside drifting bike rotation.
f32 m_softSpeedLimit
Base speed + boosts + wheelies, restricted to the hard speed limit.
virtual void hop()
Initializes hop information, resets upwards EV and clears upwards force.
void calcStandstillBoostRot()
STAGE Computes the x-component of angular velocity based on the kart's speed.
EGG::Vector3f m_outsideDriftLastDir
Used to compute the next m_outsideDriftAngle.
void calcManualDrift()
Each frame, handles hopping, drifting, and mini-turbos.
virtual void calcMtCharge()
Every frame during a drift, calculates MT/SMT charge based on player input.
void calcSsmt()
Calculates standstill mini-turbo components, if applicable.
void calcAcceleration()
Every frame, applies acceleration to the kart's internal velocity.
f32 m_processedSpeed
Offset 0x28. It's only ever just a copy of m_speed.
void releaseMt()
Stops charging a mini-turbo, and applies boost if charged.
f32 m_kclSpeedFactor
Float between 0-1 that scales the player's speed on offroad.
f32 m_weightedTurn
Magnitude+direction of stick input, factoring in the kart's stats.
void calcVehicleSpeed()
Every frame, computes speed based on acceleration and any active boosts.
f32 m_lastSpeed
Last frame's speed, cached to calculate angular velocity.
s16 m_ssmtDisableAccelTimer
Counter that tracks delay before starting to reverse.
void calcOffroadInvincibility()
Checks a timer to see if we are still ignoring offroad slowdown.
KartBurnout m_burnout
Manages the state of start boost burnout.
f32 calcVehicleAcceleration() const
Every frame, computes acceleration based off the character/vehicle stats.
void calcAutoDrift()
Each frame, handles automatic transmission drifting.
f32 m_realTurn
The "true" turn magnitude. Equal to m_weightedTurn unless drifting.
const DriftingParameters * m_driftingParams
Drift-type-specific parameters.
void calc()
Each frame, calculates the kart's movement.
EGG::Vector3f m_up
Vector perpendicular to the floor, pointing upwards.
s16 m_ssmtCharge
Increments every frame up to 75 when charging stand-still MT.
f32 m_speedDragMultiplier
After 5 frames of airtime, this causes speed to slowly decay.
u16 m_mtCharge
A value between 0 and 270 representing current MT charge.
void calcSsmtStart()
Calculates whether we are starting a standstill mini-turbo.
s16 m_respawnPostLandTimer
Counts up to 4 if not accelerating after respawn landing.
virtual f32 getWheelieSoftSpeedLimitBonus() const
Returns the % speed boost from wheelies. For karts, this is always 0.
f32 m_kclWheelRotFactor
The slowest rotation multiplier of each wheel's floor collision.
void resetDriftManual()
Clears drift state. Called when touching ground and drift is canceled.
f32 m_totalScale
[Unused] Always 1.0f
f32 m_acceleration
Captures the acceleration from player input and boosts.
virtual void calcVehicleRotation(f32 turn)
Every frame, calculates rotation, EV, and angular velocity for the kart.
s16 m_respawnPreLandTimer
Counts down from 4 when pressing A before landing from respawn.
virtual void calcTurn()
Each frame, looks at player input and kart stats. Saves turn-related info.
f32 m_divingRot
Induces x-axis angular velocity based on up/down stick input.
f32 m_outsideDriftAngle
The facing angle of an outward-drifting vehicle.
EGG::Vector3f m_lastDir
m_speed from the previous frame but with signed magnitude.
EGG::Vector3f m_smoothedUp
A smoothed up vector, mostly used after significant airtime.
s32 getAppliedHopStickX() const
Factors in vehicle speed to retrieve our hop direction and magnitude.
u16 m_floorCollisionCount
The number of tires colliding with the floor.
void calcDive()
Responds to player input to handle up/down kart tilt mid-air.
void calcOffroad()
Each frame, computes rotation and speed scalars from the floor KCL.
@ WaitingForBackwards
Holding reverse but waiting on a 15 frame delay.
s16 m_timeInRespawn
The number of frames elapsed after position snap from respawn.
s32 m_hopStickX
A ternary for the direction of our hop, 0 if still neutral hopping.
s16 m_backwardsAllowCounter
Tracks the 15f delay before reversing.
f32 calcWallCollisionSpeedFactor(f32 &f1)
Every frame, computes a speed scalar if we are colliding with a wall.
void calcMushroomBoost()
Checks a timer to see if we are still boosting from a mushroom.
f32 m_hopGravity
Always main gravity (-1.3f).
f32 m_hopVelY
Relative velocity due to a hop. Starts at 10 and decreases with gravity.
f32 m_hardSpeedLimit
Absolute speed cap. It's 120, unless you're in a bullet (140).
void setInitialPhysicsValues(const EGG::Vector3f &position, const EGG::Vector3f &angles)
Initializes the kart's position and rotation. Calls tire suspension initializers.
f32 m_rawTurn
Float in range [-1, 1]. Represents stick magnitude + direction.
void setOffroadInvincibility(s16 timer)
Ignores offroad KCL collision for a set amount of time.
f32 m_speedRatioCapped
m_speedRatio but capped at 1.0f.
EGG::Vector3f m_scale
[Unused] Always 1.0f
f32 m_kclWheelSpeedFactor
The slowest speed multiplier of each wheel's floor collision.
EGG::Vector3f m_hopDir
Used for outward drift. Tracks the forward vector of our rotation.
EGG::Vector3f bodyUp() const
Returns the second column of the rotation matrix, which is the "up" direction.
EGG::Vector3f bodyFront() const
Returns the third column of the rotation matrix, which is the facing vector.
Pertains to kart-related functionality.
@ BikeSideStuntTrick
A side StuntTrick with a bike.
A quaternion, used to represent 3D rotation.
void normalise()
Scales the quaternion to a unit length.
Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
Quatf slerpTo(const Quatf &q2, f32 t) const
Performs spherical linear interpolation.
void setAxisRotation(f32 angle, const Vector3f &axis)
Set the quat given angle and axis.
f32 dot(const Quatf &q) const
Computes .
void setRPY(const Vector3f &rpy)
Sets roll, pitch, and yaw.
void makeVectorRotation(const Vector3f &from, const Vector3f &to)
Captures rotation between two vectors.
constexpr TBitFlag< T, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
constexpr TBitFlag< T, E > & changeBit(bool on, Es... es)
Changes the state of the corresponding bits for the provided enum values.
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
constexpr void makeAllZero()
Resets all the bits to zero.
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
f32 normalise()
Normalizes the vector and returns the original length.
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
f32 length() const
The square root of the vector's dot product.
f32 squaredLength() const
The dot product between the vector and itself.
Vector3f proj(const Vector3f &rhs) const
The projection of this vector onto rhs.
Vector3f perpInPlane(const EGG::Vector3f &rhs, bool normalise) const
Calculates the orthogonal vector, based on the plane defined by this vector and rhs.
Vector3f rej(const Vector3f &rhs) const
The rejection of this vector onto rhs.
Information about the current collision and its properties.
f32 driftManualTightness
Affects turn radius when manual drifting.
std::array< f32, 32 > kclRot
Rotation scalars, indexed using KCL attributes.
f32 driftAutomaticTightness
Affects turn radius when automatic drifting.
f32 driftReactivity
A weight applied to turn radius when drifting.
f32 speed
Base full speed of the character/vehicle combo.
f32 handlingReactivity
A weight applied to turn radius when not drifting.
u32 miniTurbo
The framecount duration of a charged mini-turbo.