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 state()->setDriftAuto(
false);
818 m_autoDriftAngle = 0.0f;
820 m_autoDriftStartFrameCounter = 0;
824void KartMove::clearJumpPad() {
826 state()->setJumpPad(
false);
830void KartMove::clearRampBoost() {
832 state()->setRampBoost(
false);
836void KartMove::clearZipperBoost() {
837 m_zipperBoostTimer = 0;
838 state()->setZipperBoost(
false);
842void KartMove::clearBoost() {
843 m_boost.resetActive();
844 state()->setBoost(
false);
848void KartMove::clearSsmt() {
856void KartMove::clearOffroadInvincibility() {
858 state()->setBoostOffroadInvincibility(
false);
861void KartMove::clearRejectRoad() {
862 state()->setRejectRoadTrigger(
false);
863 state()->setNoSparkInvisibleWall(
false);
870 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
872 if (!state()->isAutoDrift()) {
876 if (canStartDrift() && !state()->isOverZipper() && !state()->isRejectRoadTrigger() &&
877 !state()->isWheelie() && EGG::Mathf::abs(state()->stickX()) > 0.85f) {
878 m_autoDriftStartFrameCounter =
879 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
881 m_autoDriftStartFrameCounter = 0;
884 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
885 state()->setDriftAuto(
true);
887 if (state()->isTouchingGround()) {
888 if (state()->stickX() < 0.0f) {
898 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
899 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
901 state()->setDriftAuto(
false);
904 if (m_autoDriftAngle > 0.0f) {
906 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
909 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
915 physics()->composeExtraRot(angleAxis);
924 if (!state()->isOverZipper()) {
927 if (!state()->isTouchingGround() &&
928 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
929 !state()->isJumpPadMushroomCollision() &&
930 (state()->isDriftManual() || state()->isSlipdriftCharge()) &&
931 m_flags.
onBit(eFlags::LaunchBoost)) {
936 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
937 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
939 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
951 if (((!state()->isHop() ||
m_hopFrame < 3) && !state()->isSlipdriftCharge()) ||
952 (state()->isInAction() || !state()->isTouchingGround())) {
964 if (!state()->isDriftManual()) {
965 if (!isHopping && state()->isTouchingGround()) {
968 if (!action()->flags().onBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
969 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
978 if (!state()->isOverZipper() &&
979 (!state()->isDriftInput() || !state()->isAccelerate() || state()->isInAction() ||
980 state()->isRejectRoadTrigger() || state()->isWall3Collision() ||
981 state()->isWallCollision() || !canStartDrift())) {
982 if (canStartDrift()) {
998 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1000 const auto &stats = param()->stats();
1002 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1003 f32 driftAngle = 0.0f;
1005 if (state()->isHop()) {
1020 state()->setHop(
false);
1021 state()->setSlipdriftCharge(
false);
1023 if (!state()->isDriftInput()) {
1031 state()->setDriftManual(
true);
1032 state()->setHop(
false);
1033 m_driftState = DriftState::ChargingMt;
1041 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1043 if (m_driftState < DriftState::ChargedMt || state()->isBrake()) {
1044 m_driftState = DriftState::NotDrifting;
1050 if (m_driftState == DriftState::ChargedSmt) {
1051 mtLength *= SMT_LENGTH_FACTOR;
1054 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
1055 activateBoost(KartBoost::Type::AllMt, mtLength);
1058 m_driftState = DriftState::NotDrifting;
1065 if (state()->airtime() > 5) {
1069 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1072 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1073 if (angle > targetAngle) {
1075 }
else if (angle < targetAngle) {
1081 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1082 if (targetAngle > angle) {
1084 }
else if (targetAngle < angle) {
1099 bool drifting = state()->isDrifting() && !state()->isJumpPadMushroomCollision();
1100 bool autoDrift = state()->isAutoDrift();
1101 const auto &stats = param()->stats();
1104 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1106 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1109 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1114 bool forwards =
true;
1115 if (state()->isBrake() &&
m_speed <= 0.0f) {
1120 if (state()->isChargingSsmt()) {
1123 if (state()->isHop() &&
m_hopPosY > 0.0f) {
1128 bool noTurn =
false;
1129 if (!state()->isWallCollision() && !state()->isWall3Collision() &&
1130 EGG::Mathf::abs(
m_speed) < 1.0f) {
1131 if (!(state()->isHop() &&
m_hopPosY > 0.0f)) {
1136 if (forwards && !noTurn) {
1140 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1143 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1152 if (state()->isZipperBoost() && !state()->isDriftManual()) {
1156 f32 stickX = EGG::Mathf::abs(state()->stickX());
1157 if (autoDrift && stickX > 0.3f) {
1158 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1159 stickX = drifting ? 0.2f : 0.5f;
1164 if (!state()->isInAction() && !state()->isZipperTrick()) {
1165 if (!state()->isTouchingGround()) {
1166 if (state()->isRampBoost() && m_jump->isBoostRampEnabled()) {
1168 }
else if (!state()->isJumpPadMushroomCollision()) {
1169 u32 airtime = state()->airtime();
1170 if (airtime >= 70) {
1172 }
else if (airtime >= 30) {
1173 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1179 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1180 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1182 if (angle > 60.0f) {
1183 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1194 const auto *raceMgr = System::RaceManager::Instance();
1195 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1196 f32 speedFix = dynamics()->speedFix();
1197 if (state()->isInAction() ||
1198 ((state()->isWallCollisionStart() || state()->wallBonkTimer() == 0 ||
1199 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1200 !state()->isDriftManual())) {
1212 if (state()->isInAction()) {
1213 action()->calcVehicleSpeed();
1217 if ((state()->isSomethingWallCollision() && state()->isTouchingGround() &&
1218 !state()->isAnyWheelCollision()) ||
1219 !state()->isTouchingGround() || state()->isChargingSsmt()) {
1220 if (state()->isRampBoost() && state()->airtime() < 4) {
1223 if (state()->isJumpPad() && !state()->isAccelerate()) {
1226 if (state()->isOverZipper()) {
1229 if (state()->airtime() > 5) {
1237 }
else if (state()->isBoost()) {
1240 if (!state()->isJumpPad() && !state()->isRampBoost()) {
1241 if (state()->isAccelerate()) {
1244 if (!state()->isBrake() || state()->isDisableBackwardsAccel() ||
1245 state()->isSomethingWallCollision()) {
1258 if (!state()->isBoost() && !state()->isDriftManual() && !state()->isAutoDrift()) {
1259 const auto &stats = param()->stats();
1262 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1276 initialVel = std::min(initialVel * 2.0f, 2.0f);
1278 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1292 std::span<const f32> as;
1293 std::span<const f32> ts;
1294 if (state()->isDrifting()) {
1295 as = param()->stats().accelerationDriftA;
1296 ts = param()->stats().accelerationDriftT;
1298 as = param()->stats().accelerationStandardA;
1299 ts = param()->stats().accelerationStandardT;
1303 f32 acceleration = 0.0f;
1305 for (; i < ts.size(); ++i) {
1306 if (ratio < ts[i]) {
1307 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1314 return i < ts.size() ? acceleration : as.back();
1321 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1322 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1323 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1324 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1325 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1329 dynamics()->setKillExtVelY(state()->isRespawnKillY());
1331 if (state()->isBurnout()) {
1347 if (state()->isBeforeRespawn()) {
1350 if (state()->isChargingSsmt()) {
1361 f32 speedLimit = state()->isJumpPad() ? m_jumpPadMaxSpeed :
m_baseSpeed;
1362 const f32 boostMultiplier = m_boost.multiplier();
1363 const f32 boostSpdLimit = m_boost.speedLimit();
1364 m_jumpPadBoostMultiplier = boostMultiplier;
1366 if (!state()->isJumpPadFixedSpeed()) {
1370 if (!state()->isJumpPad()) {
1375 if (state()->isRampBoost()) {
1376 speedLimit = std::max(speedLimit, 100.0f);
1381 f32 local_c8 = 1.0f;
1384 if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1394 if (state()->isJumpPad()) {
1405 crossVec = -crossVec;
1408 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1409 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1410 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1411 }
else if (!state()->isTouchingGround()) {
1412 rotationScalar = ROTATION_SCALAR_MIDAIR;
1419 const auto *raceMgr = System::RaceManager::Instance();
1420 if (!state()->isInAction() && !state()->isDisableBackwardsAccel() &&
1421 state()->isTouchingGround() && !state()->isAccelerate() &&
1422 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1429 f32 maxSpeedY = state()->isOverZipper() ? KartHalfPipe::TerminalVelocity() : TERMINAL_VELOCITY;
1430 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1432 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1434 if (state()->isTouchingGround() && !state()->isDriftManual() && !state()->isHop()) {
1435 if (state()->isBrake()) {
1438 DrivingDirection::Backwards;
1454 if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1460 if (state()->isZipperInvisibleWall() || state()->isOverZipper()) {
1465 if (wallNrm.y > 0.0f) {
1473 f1 = std::max(0.0f, dot + 1.0f);
1475 return std::min(1.0f, f1 * (state()->isWallCollision() ? 0.4f : 0.7f));
1487 if (!state()->isWallCollisionStart()) {
1492 if (!state()->isInAction()) {
1495 m_landingDir = m_dir;
1498 if (!state()->isOverZipper() && param_2 < 0.9f) {
1501 if (speedDiff > 30.0f) {
1505 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1509 speedDiff = std::min(60.0f, speedDiff);
1512 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1516 if (state()->isBoost()) {
1517 proj = EGG::Vector3f::zero;
1518 rej = EGG::Vector3f::zero;
1521 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1522 proj = EGG::Vector3f::zero;
1527 f32 bumpDeviation = 0.0f;
1529 bumpDeviation = param()->stats().bumpDeviationLevel;
1544 if (state()->isTouchingGround()) {
1545 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1546 next = 0.015f * -state()->startBoostCharge();
1547 }
else if (!state()->isChargingSsmt()) {
1548 if (!state()->isJumpPad() && !state()->isRampBoost() && !state()->isSoftWallDrift()) {
1550 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1552 if (state()->isMushroomBoost()) {
1553 next = (scalar * 0.15f) * 0.25f;
1554 if (state()->isWheelie()) {
1558 next = (scalar * 0.15f) * 0.08f;
1563 constexpr s16 MAX_SSMT_CHARGE = 75;
1564 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1569 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1571 m_standStillBoostRot += scalar * (next - m_standStillBoostRot);
1579 constexpr f32 DIVE_LIMIT = 0.8f;
1583 if (state()->isTouchingGround() || state()->isCannonStart() || state()->isInCannon() ||
1584 state()->isInAction() || state()->isOverZipper()) {
1588 f32 stickY = state()->stickY();
1591 stickY = std::min(1.0f, stickY + 0.4f);
1594 u32 airtime = state()->airtime();
1597 if (EGG::Mathf::abs(stickY) < 0.1f) {
1601 stickY *= (airtime / 50.0f);
1608 dynamics()->setAngVel2(angVel2);
1610 if (state()->airtime() < 50) {
1616 f32 upDotTop =
m_up.
dot(topRotated);
1618 f32 crossNorm = upCrossTop.
length();
1619 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1621 f32 fVar1 = angle * RAD2DEG - 20.0f;
1622 if (fVar1 <= 0.0f) {
1626 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1627 if (forwardRotated.y > 0.0f) {
1628 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1630 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1638 if (EGG::Mathf::abs(
m_speed) >= 10.0f || state()->isBoost() || state()->isRampBoost() ||
1639 !state()->isAccelerate() || !state()->isBrake()) {
1640 state()->setChargingSsmt(
false);
1644 state()->setChargingSsmt(
true);
1645 state()->setHopStart(
false);
1646 state()->setDriftInput(
false);
1650void KartMove::calcHopPhysics() {
1661void KartMove::calcRejectRoad() {
1662 m_reject.calcRejectRoad();
1666bool KartMove::calcZipperCollision(f32 radius, f32 scale,
EGG::Vector3f &pos,
1670 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1672 auto *colDir = Field::CollisionDirector::Instance();
1673 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1678 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.
dot(to)));
1679 f32 acos = EGG::Mathf::acos(dotNorm);
1680 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1686 f32 tiltMagnitude = 0.0f;
1688 if (!state()->isInAction() && !state()->isSoftWallDrift() && state()->isAnyWheelCollision()) {
1692 f32 magnitude = tiltMagnitude;
1694 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1695 magnitude = frontSpeed.
length();
1697 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1698 magnitude = -magnitude;
1701 tiltMagnitude = -1.0f;
1702 if (-1.0f <= magnitude) {
1703 tiltMagnitude = std::min(1.0f, magnitude);
1706 }
else if (!state()->isHop() ||
m_hopPosY <= 0.0f) {
1709 dynamics()->setAngVel0(angVel0);
1712 f32 lean = EGG::Mathf::abs(
m_weightedTurn) * (tiltMagnitude * param()->stats().tilt);
1717 angVel0.x += m_standStillBoostRot;
1719 dynamics()->setAngVel0(angVel0);
1723 dynamics()->setAngVel2(angVel2);
1733 constexpr u16 MAX_MT_CHARGE = 270;
1734 constexpr u16 MAX_SMT_CHARGE = 300;
1735 constexpr u16 BASE_MT_CHARGE = 2;
1736 constexpr u16 BASE_SMT_CHARGE = 2;
1737 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1738 constexpr u16 EXTRA_MT_CHARGE = 3;
1740 if (m_driftState == DriftState::ChargedSmt) {
1744 f32 stickX = state()->stickX();
1746 if (m_driftState == DriftState::ChargingMt) {
1749 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1750 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1759 m_driftState = DriftState::ChargingSmt;
1763 if (m_driftState != DriftState::ChargingSmt) {
1769 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1770 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1779 m_driftState = DriftState::ChargedSmt;
1784void KartMove::initOob() {
1790 clearOffroadInvincibility();
1797 state()->setHop(
true);
1798 state()->setDriftManual(
false);
1803 m_driftState = DriftState::NotDrifting;
1815 dynamics()->setExtVel(extVel);
1818 totalForce.y = 0.0f;
1819 dynamics()->setTotalForce(totalForce);
1823void KartMove::tryStartBoostPanel() {
1824 constexpr s16 BOOST_PANEL_DURATION = 60;
1826 if (state()->isBeforeRespawn() || state()->isInAction()) {
1830 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
1838 constexpr s16 BOOST_RAMP_DURATION = 60;
1840 if (state()->isBeforeRespawn() || state()->isInAction()) {
1844 state()->setRampBoost(
true);
1845 m_rampBoost = BOOST_RAMP_DURATION;
1854 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
1855 {50.0f, 50.0f, 35.0f},
1856 {50.0f, 50.0f, 47.0f},
1857 {59.0f, 59.0f, 30.0f},
1858 {73.0f, 73.0f, 45.0f},
1859 {73.0f, 73.0f, 53.0f},
1860 {55.0f, 55.0f, 35.0f},
1861 {56.0f, 56.0f, 50.0f},
1864 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isHalfPipeRamp()) {
1868 state()->setJumpPad(
true);
1869 s32 jumpPadVariant = state()->jumpPadVariant();
1870 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
1872 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
1873 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
1876 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
1877 {100.0f, 100.0f, 70.0f},
1878 {100.0f, 100.0f, 65.0f},
1880 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
1882 state()->setJumpPadFixedSpeed(
true);
1885 if (jumpPadVariant == 4) {
1886 state()->setJumpPadMushroomTrigger(
true);
1887 state()->setJumpPadMushroomVelYInc(
true);
1888 state()->setJumpPadMushroomCollision(
true);
1893 extVel.y = m_jumpPadProperties->velY;
1894 totalForce.y = 0.0f;
1896 dynamics()->setExtVel(extVel);
1897 dynamics()->setTotalForce(totalForce);
1899 if (jumpPadVariant != 3) {
1906 state()->setJumpPadDisableYsusForce(
true);
1911 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
1916void KartMove::tryEndJumpPad() {
1917 if (state()->isJumpPadMushroomTrigger()) {
1918 if (state()->isGroundStart()) {
1919 state()->setJumpPadMushroomTrigger(
false);
1920 state()->setJumpPadFixedSpeed(
false);
1921 state()->setJumpPadMushroomVelYInc(
false);
1924 if (state()->isJumpPadMushroomVelYInc()) {
1926 newExtVel.y += 20.0f;
1927 if (m_jumpPadProperties->velY < newExtVel.y) {
1928 newExtVel.y = m_jumpPadProperties->velY;
1929 state()->setJumpPadMushroomVelYInc(
false);
1931 dynamics()->setExtVel(newExtVel);
1935 if (state()->isGroundStart() && !state()->isJumpPadMushroomTrigger()) {
1941void KartMove::cancelJumpPad() {
1943 state()->setJumpPad(
false);
1947void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
1948 if (m_boost.
activate(type, frames)) {
1949 state()->setBoost(
true);
1954void KartMove::applyStartBoost(s16 frames) {
1955 activateBoost(KartBoost::Type::AllMt, frames);
1959void KartMove::activateMushroom() {
1960 constexpr s16 MUSHROOM_DURATION = 90;
1962 if (state()->isBeforeRespawn() || state()->isInAction()) {
1966 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
1969 state()->setMushroomBoost(
true);
1974void KartMove::activateZipperBoost() {
1975 constexpr s16 BASE_DURATION = 50;
1976 constexpr s16 TRICK_DURATION = 100;
1978 if (state()->isBeforeRespawn() || state()->isInAction()) {
1982 s16 boostDuration = state()->isZipperTrick() ? TRICK_DURATION : BASE_DURATION;
1983 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
1986 m_zipperBoostTimer = 0;
1987 m_zipperBoostMax = boostDuration;
1988 state()->setZipperBoost(
true);
2000 state()->setBoostOffroadInvincibility(
true);
2007 if (!state()->isBoostOffroadInvincibility()) {
2015 state()->setBoostOffroadInvincibility(
false);
2021 if (!state()->isMushroomBoost()) {
2029 state()->setMushroomBoost(
false);
2033void KartMove::calcZipperBoost() {
2034 if (!state()->isZipperBoost()) {
2038 state()->setAccelerate(
true);
2040 if (!state()->isOverZipper() && ++m_zipperBoostTimer >= m_zipperBoostMax) {
2041 m_zipperBoostTimer = 0;
2042 state()->setZipperBoost(
false);
2045 if (m_zipperBoostTimer < 10) {
2048 dynamics()->setAngVel0(angVel);
2053void KartMove::landTrick() {
2054 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2059 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2065 if (state()->isBeforeRespawn() || state()->isInAction()) {
2071 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2073 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2076 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2080void KartMove::enterCannon() {
2082 physics()->clearDecayingRot();
2083 m_boost.resetActive();
2084 state()->setBoost(
false);
2090 clearOffroadInvincibility();
2092 dynamics()->reset();
2095 state()->setHop(
false);
2096 state()->setInCannon(
true);
2097 state()->setSkipWheelCalc(
true);
2098 state()->setCannonStart(
false);
2100 const auto [cannonPos, cannonDir] = getCannonPosRot();
2101 m_cannonEntryPos = pos();
2102 m_cannonEntryOfs = cannonPos - pos();
2103 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2105 m_dir = m_cannonEntryOfs;
2106 m_vel1Dir = m_cannonEntryOfs;
2107 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2108 m_cannonProgress.setZero();
2112void KartMove::calcCannon() {
2113 auto [cannonPos, cannonDir] = getCannonPosRot();
2114 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2116 f32 forwardLength = forward.
normalise();
2130 if (forwardLength < 30.0f || local94.
dot(forwardXZ) <= 0.0f) {
2135 const auto *cannonPoint =
2136 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2137 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2138 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2139 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2140 f32 newSpeed = cannonParams.speed;
2141 if (forwardLength < cannonParams.decelFactor) {
2142 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2144 newSpeed = cannonParams.endDecel;
2145 if (newSpeed <= 0.0f) {
2149 newSpeed += factor * (cannonParams.speed - newSpeed);
2150 if (cannonParams.endDecel > 0.0f) {
2155 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2158 if (cannonParams.height > 0.0f) {
2159 f32 fVar9 = EGG::Mathf::SinFIdx(
2160 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2161 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2164 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2165 m_dir = m_cannonEntryOfs;
2166 m_vel1Dir = m_cannonEntryOfs;
2168 calcRotCannon(forward);
2170 dynamics()->setExtVel(EGG::Vector3f::zero);
2178 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2184 local80 *= dynamics()->fullRot();
2189 dynamics()->setFullRot(newRot);
2190 dynamics()->setMainRot(newRot);
2194void KartMove::exitCannon() {
2195 if (!state()->isInCannon()) {
2199 state()->setInCannon(
false);
2200 state()->setSkipWheelCalc(
false);
2201 state()->setAfterCannon(
true);
2202 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2206void KartMove::triggerRespawn() {
2208 state()->setTriggerRespawn(
true);
2212KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2215KartMoveBike::~KartMoveBike() =
default;
2220 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2221 constexpr u16 WHEELIE_COOLDOWN = 20;
2223 state()->setWheelie(
true);
2228 m_autoHardStickXFrames = 0;
2235 state()->setWheelie(
false);
2237 m_autoHardStickXFrames = 0;
2241void KartMoveBike::createSubsystems() {
2252 const auto *raceManager = System::RaceManager::Instance();
2254 if (!state()->isChargingSsmt()) {
2255 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2256 EGG::Mathf::abs(
m_speed) < 5.0f) {
2268 f32 stickX = state()->stickX();
2269 f32 extVelXFactor = 0.0f;
2273 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isWheelie() ||
2274 state()->isOverZipper() || state()->isRejectRoadTrigger() ||
2275 state()->isAirtimeOver20() || state()->isSoftWallDrift() ||
2276 state()->isSomethingWallCollision() || state()->isHWG() || state()->isCannonStart() ||
2277 state()->isInCannon()) {
2279 }
else if (!state()->isDrifting()) {
2280 if (stickX <= 0.2f) {
2281 if (stickX >= -0.2f) {
2296 leanRotMin = -leanRotMax;
2300 if (stickX == 0.0f) {
2306 }
else if (stickX == 0.0f) {
2314 bool capped =
false;
2326 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2329 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2333 dynamics()->setAngVel2(dynamics()->angVel2() +
2334 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2341 if (!state()->isRejectRoad() && !state()->isHalfPipeRamp() && !state()->isOverZipper()) {
2343 scalar = std::min(1.0f, scalar);
2344 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2346 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2351 dynamics()->setTop_(top);
2359 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2360 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2361 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2364 KartMove::setTurnParams();
2366 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2368 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2372 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2382void KartMoveBike::init(
bool b1,
bool b2) {
2383 KartMove::init(b1, b2);
2392 m_autoHardStickXFrames = 0;
2396void KartMoveBike::clear() {
2404 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2405 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2410 if (state()->isWheelie()) {
2411 bool cancelAutoWheelie =
false;
2413 if (!state()->isAutoDrift() ||
2414 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2415 m_autoHardStickXFrames = 0;
2417 if (++m_autoHardStickXFrames > 15) {
2418 cancelAutoWheelie =
true;
2430 dynamics()->setAngVel0(angVel0);
2440 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2445 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2446 dynamics()->setAngVel2(angVel2);
2451 state()->setWheelieRot(
true);
2453 state()->setWheelieRot(
false);
2463 if (state()->isAutoDrift()) {
2480 constexpr u16 MAX_MT_CHARGE = 270;
2481 constexpr u16 BASE_MT_CHARGE = 2;
2482 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2483 constexpr u16 EXTRA_MT_CHARGE = 3;
2485 if (m_driftState != DriftState::ChargingMt) {
2491 f32 stickX = state()->stickX();
2492 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2493 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2502 m_driftState = DriftState::ChargedMt;
2507void KartMoveBike::initOob() {
2508 KartMove::initOob();
2515 constexpr s16 COOLDOWN_FRAMES = 20;
2516 bool dpadUp = inputs()->currentState().trickUp();
2518 if (!state()->isWheelie()) {
2519 if (dpadUp && state()->isTouchingGround()) {
2520 if (state()->isDriftManual() || state()->isWallCollision() ||
2521 state()->isWall3Collision() || state()->isHop() || state()->isDriftAuto() ||
2522 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.