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/KartScale.hh"
9#include "game/kart/KartSub.hh"
10#include "game/kart/KartSuspension.hh"
12#include "game/field/CollisionDirector.hh"
15#include "game/item/ItemDirector.hh"
16#include "game/item/KartItem.hh"
18#include "game/system/CourseMap.hh"
19#include "game/system/RaceManager.hh"
20#include "game/system/map/MapdataCannonPoint.hh"
21#include "game/system/map/MapdataJugemPoint.hh"
23#include <egg/math/Math.hh>
24#include <egg/math/Quat.hh>
35static constexpr std::array<CannonParameter, 3> CANNON_PARAMETERS = {{
36 {500.0f, 0.0f, 6000.0f, -1.0f},
37 {500.0f, 5000.0f, 6000.0f, -1.0f},
38 {120.0f, 2000.0f, 1000.0f, 45.0f},
42KartMove::KartMove() : m_smoothedUp(
EGG::Vector3f::ey), m_scale(1.0f, 1.0f, 1.0f) {
45 m_padType.makeAllZero();
46 m_flags.makeAllZero();
51KartMove::~KartMove() {
58void KartMove::createSubsystems() {
59 m_jump =
new KartJump(
this);
71 if (state()->isInAction() || state()->isCannonStart() || state()->isInCannon() ||
72 state()->isOverZipper()) {
76 if (state()->isBeforeRespawn()) {
82 if (state()->isJumpPadMushroomCollision()) {
84 }
else if (state()->isAirtimeOver20()) {
92 if (state()->isDrifting()) {
103 if (!state()->isDrifting()) {
113void KartMove::setTurnParams() {
114 static constexpr std::array<DriftingParameters, 3> DRIFTING_PARAMS_ARRAY = {{
115 {10.0f, 0.5f, 0.5f, 1.0f},
116 {10.0f, 0.5f, 0.5f, 0.2f},
117 {10.0f, 0.22f, 0.5f, 0.2f},
124 m_landingDir = m_dir;
131void KartMove::init(
bool b1,
bool b2) {
139 m_up = EGG::Vector3f::ey;
141 m_vel1Dir = EGG::Vector3f::ez;
143 m_dir = EGG::Vector3f::ez;
144 m_landingDir = EGG::Vector3f::ez;
145 m_dirDiff = EGG::Vector3f::zero;
146 m_hasLandingDir =
false;
148 m_landingAngle = 0.0f;
166 m_standStillBoostRot = 0.0f;
167 m_driftState = DriftState::NotDrifting;
172 m_zipperBoostTimer = 0;
173 m_zipperBoostMax = 0;
179 m_nonZipperAirtime = 0;
186 m_hitboxScale = 1.0f;
192 m_jumpPadMaxSpeed = 0.0f;
193 m_jumpPadBoostMultiplier = 0.0f;
194 m_jumpPadProperties =
nullptr;
196 m_autoDriftAngle = 0.0f;
197 m_autoDriftStartFrameCounter = 0;
199 m_cannonEntryOfsLength = 0.0f;
200 m_cannonEntryPos.setZero();
201 m_cannonEntryOfs.setZero();
202 m_cannonOrthog.setZero();
203 m_cannonProgress.setZero();
222void KartMove::clear() {
223 if (state()->isOverZipper()) {
224 state()->setActionMidZipper(
true);
232 clearOffroadInvincibility();
241 EGG::Quatf quaternion = EGG::Quatf::FromRPY(angles * DEG2RAD);
246 bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos,
247 EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0);
250 newPos = newPos + info.tangentOff + (info.floorNrm * -100.0f);
251 newPos += info.floorNrm * bsp().initialYPos;
257 sub()->initPhysicsValues();
259 physics()->setPos(pos());
260 physics()->setVelocity(dynamics()->velocity());
265 dynamics()->setTop(
m_up);
267 for (
u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) {
268 suspension(tireIdx)->setInitialState();
278 if (state()->isInRespawn()) {
283 dynamics()->resetInternalVelocity();
293 m_bumpTimer = std::max(m_bumpTimer - 1, 0);
301 if (!state()->isAutoDrift()) {
313 if (state()->isInCannon()) {
324void KartMove::calcRespawnStart() {
325 constexpr float RESPAWN_HEIGHT = 700.0f;
327 const auto *jugemPoint = System::RaceManager::Instance()->jugemPoint();
332 respawnPos.y += RESPAWN_HEIGHT;
337 Item::ItemDirector::Instance()->kartItem(0).clear();
339 state()->setTriggerRespawn(
false);
340 state()->setInRespawn(
true);
344void KartMove::calcInRespawn() {
345 constexpr f32 LAKITU_VELOCITY = 1.5f;
346 constexpr u16 RESPAWN_DURATION = 110;
348 if (!state()->isInRespawn()) {
353 newPos.y -= LAKITU_VELOCITY;
354 dynamics()->setPos(newPos);
355 dynamics()->setNoGravity(
true);
358 state()->setInRespawn(
false);
359 state()->setAfterRespawn(
true);
360 state()->setRespawnKillY(
true);
363 dynamics()->setNoGravity(
false);
368void KartMove::calcRespawnBoost() {
369 constexpr s16 RESPAWN_BOOST_DURATION = 30;
370 constexpr s16 RESPAWN_BOOST_INPUT_LENIENCY = 4;
372 if (state()->isAfterRespawn()) {
373 if (state()->isTouchingGround()) {
375 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
376 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
377 m_respawnTimer = RESPAWN_BOOST_DURATION;
383 state()->setAfterRespawn(
false);
395 if (state()->isAccelerateStart()) {
396 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
397 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
398 m_respawnTimer = RESPAWN_BOOST_DURATION;
406 state()->setRespawnKillY(
false);
410 m_respawnTimer = std::max(0, m_respawnTimer - 1);
414void KartMove::calcTop() {
415 f32 stabilizationFactor = 0.1f;
416 m_hasLandingDir =
false;
419 if (state()->isGroundStart() && m_nonZipperAirtime >= 3) {
423 m_dirDiff = m_landingDir.
proj(m_landingDir);
424 m_hasLandingDir =
true;
426 if (state()->isHop() &&
m_hopPosY > 0.0f) {
428 }
else if (state()->isTouchingGround()) {
430 inputTop.
dot(m_dir) > 0.0f &&
m_speed > 50.0f &&
431 collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::NotTrickable)) {
439 if (state()->isHalfPipeRamp() ||
440 (!state()->isBoost() && !state()->isRampBoost() && !state()->isWheelie() &&
441 !state()->isOverZipper() &&
442 (!state()->isZipperBoost() || m_zipperBoostTimer > 15))) {
443 f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(inputTop.
dot(componentZAxis())));
444 scalar = std::min(0.8f, std::max(0.3f, topDotZ));
452 if (bodyDotFront < -0.1f) {
453 stabilizationFactor += std::min(0.2f, EGG::Mathf::abs(bodyDotFront) * 0.5f);
456 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
457 stabilizationFactor = 0.4f;
464 dynamics()->setStabilizationFactor(stabilizationFactor);
466 m_nonZipperAirtime = state()->isOverZipper() ? 0 : state()->airtime();
467 m_flags.
changeBit(collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::Trickable),
474 if (state()->isOverZipper() || !state()->isAirtimeOver20()) {
485 if (
m_up.y <= 0.99f) {
486 m_up += (EGG::Vector3f::ey -
m_up) * 0.03f;
489 m_up = EGG::Vector3f::ey;
497 const auto *raceMgr = System::RaceManager::Instance();
498 if (!raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
502 if (m_padType.
onBit(ePadType::BoostPanel)) {
503 tryStartBoostPanel();
506 if (m_padType.
onBit(ePadType::BoostRamp)) {
510 if (m_padType.
onBit(ePadType::JumpPad)) {
518void KartMove::calcDirs() {
522 m_flags.
setBit(eFlags::LaunchBoost);
524 if (!state()->isInATrick() && !state()->isOverZipper() &&
525 (((state()->isTouchingGround() || !state()->isRampBoost() ||
526 !m_jump->isBoostRampEnabled()) &&
527 !state()->isJumpPad() && state()->airtime() <= 5) ||
528 state()->isJumpPadMushroomCollision() || state()->isNoSparkInvisibleWall())) {
529 if (state()->isHop()) {
541 if (dirDiff.
squaredLength() <= std::numeric_limits<f32>::epsilon()) {
552 if (origDirCross.
dot(newDirCross) < 0.0f) {
559 m_flags.
resetBit(eFlags::LaunchBoost);
564 if (!state()->isOverZipper()) {
568 if (m_hasLandingDir) {
569 f32 dot = m_dir.dot(m_landingDir);
571 f32 crossDot = cross.
length();
572 f32 angle = EGG::Mathf::atan2(crossDot, dot);
573 angle = EGG::Mathf::abs(angle);
580 m_landingAngle += (angle * RAD2DEG) * fVar4;
583 if (m_landingAngle <= 0.0f) {
584 if (m_landingAngle < 0.0f) {
585 m_landingAngle = std::min(0.0f, m_landingAngle + 2.0f);
588 m_landingAngle = std::max(0.0f, m_landingAngle - 2.0f);
593void KartMove::calcStickyRoad() {
594 constexpr f32 STICKY_RADIUS = 200.0f;
598 if (state()->isOverZipper()) {
599 state()->setStickyRoad(
false);
603 if ((!state()->isStickyRoad() &&
604 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::Trickable)) ||
605 EGG::Mathf::abs(
m_speed) <= 20.0f) {
613 colInfo.bbox.setZero();
615 bool stickyRoad =
false;
617 for (
size_t i = 0; i < 3; ++i) {
619 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
620 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
621 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
622 dynamics()->setMovingObjVel(dynamics()->movingObjVel().rej(colInfo.floorNrm));
628 pos += -STICKY_RADIUS * componentYAxis();
632 state()->setStickyRoad(
false);
640 if (state()->isBoostOffroadInvincibility()) {
644 bool anyWheel = state()->isAnyWheelCollision();
651 if (state()->isVehicleBodyFloorCollision()) {
668void KartMove::calcBoost() {
669 if (m_boost.
calc()) {
670 state()->setAccelerate(
true);
672 state()->setBoost(
false);
679void KartMove::calcRampBoost() {
680 if (!state()->isRampBoost()) {
684 state()->setAccelerate(
true);
685 if (--m_rampBoost < 1) {
687 state()->setRampBoost(
false);
695 if (!state()->isDisableBackwardsAccel()) {
701 state()->setDisableBackwardsAccel(
false);
710 constexpr s16 MAX_SSMT_CHARGE = 75;
711 constexpr s16 SSMT_BOOST_FRAMES = 30;
712 constexpr s16 LEEWAY_FRAMES = 1;
713 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
717 if (state()->isChargingSsmt()) {
738 state()->setDisableBackwardsAccel(
true);
740 if (!state()->isAccelerate() && !state()->isBrake()) {
741 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
747 if (state()->isAccelerate() && !state()->isBrake()) {
748 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
754 state()->setDisableBackwardsAccel(
true);
765 if (!state()->isTouchingGround() && !state()->isHop() && !state()->isDriftManual()) {
766 if (state()->isStickLeft() || state()->isStickRight()) {
767 if (!state()->isDriftInput()) {
768 state()->setSlipdriftCharge(
false);
769 }
else if (!state()->isSlipdriftCharge()) {
771 if (state()->isStickRight()) {
773 }
else if (state()->isStickLeft()) {
776 state()->setSlipdriftCharge(
true);
783 if (state()->isHop()) {
785 if (state()->isStickRight()) {
787 }
else if (state()->isStickLeft()) {
794 }
else if (state()->isSlipdriftCharge()) {
798 return state()->isHop() || state()->isSlipdriftCharge();
807 state()->setHop(
false);
808 state()->setDriftManual(
false);
809 m_driftState = DriftState::NotDrifting;
821 m_driftState = DriftState::NotDrifting;
825 state()->setHop(
false);
826 state()->setSlipdriftCharge(
false);
827 state()->setDriftManual(
false);
828 state()->setDriftAuto(
false);
829 m_autoDriftAngle = 0.0f;
831 m_autoDriftStartFrameCounter = 0;
835void KartMove::clearJumpPad() {
837 state()->setJumpPad(
false);
841void KartMove::clearRampBoost() {
843 state()->setRampBoost(
false);
847void KartMove::clearZipperBoost() {
848 m_zipperBoostTimer = 0;
849 state()->setZipperBoost(
false);
853void KartMove::clearBoost() {
854 m_boost.resetActive();
855 state()->setBoost(
false);
859void KartMove::clearSsmt() {
867void KartMove::clearOffroadInvincibility() {
869 state()->setBoostOffroadInvincibility(
false);
872void KartMove::clearRejectRoad() {
873 state()->setRejectRoadTrigger(
false);
874 state()->setNoSparkInvisibleWall(
false);
881 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
883 if (!state()->isAutoDrift()) {
887 if (canStartDrift() && !state()->isOverZipper() && !state()->isRejectRoadTrigger() &&
888 !state()->isWheelie() && EGG::Mathf::abs(state()->stickX()) > 0.85f) {
889 m_autoDriftStartFrameCounter =
890 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
892 m_autoDriftStartFrameCounter = 0;
895 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
896 state()->setDriftAuto(
true);
898 if (state()->isTouchingGround()) {
899 if (state()->stickX() < 0.0f) {
909 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
910 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
912 state()->setDriftAuto(
false);
915 if (m_autoDriftAngle > 0.0f) {
917 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
920 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
926 physics()->composeExtraRot(angleAxis);
935 if (!state()->isOverZipper()) {
938 if (!state()->isTouchingGround() &&
939 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
940 !state()->isJumpPadMushroomCollision() &&
941 (state()->isDriftManual() || state()->isSlipdriftCharge()) &&
942 m_flags.
onBit(eFlags::LaunchBoost)) {
947 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
948 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
950 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
962 if (((!state()->isHop() ||
m_hopFrame < 3) && !state()->isSlipdriftCharge()) ||
963 (state()->isInAction() || !state()->isTouchingGround())) {
975 if (!state()->isDriftManual()) {
976 if (!isHopping && state()->isTouchingGround()) {
979 if (action()->flags().offBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
980 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
989 if (!state()->isOverZipper() &&
990 (!state()->isDriftInput() || !state()->isAccelerate() || state()->isInAction() ||
991 state()->isRejectRoadTrigger() || state()->isWall3Collision() ||
992 state()->isWallCollision() || !canStartDrift())) {
993 if (canStartDrift()) {
1009 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1011 const auto &stats = param()->stats();
1013 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1014 f32 driftAngle = 0.0f;
1016 if (state()->isHop()) {
1031 state()->setHop(
false);
1032 state()->setSlipdriftCharge(
false);
1034 if (!state()->isDriftInput()) {
1042 state()->setDriftManual(
true);
1043 state()->setHop(
false);
1044 m_driftState = DriftState::ChargingMt;
1052 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1054 if (m_driftState < DriftState::ChargedMt || state()->isBrake()) {
1055 m_driftState = DriftState::NotDrifting;
1061 if (m_driftState == DriftState::ChargedSmt) {
1062 mtLength *= SMT_LENGTH_FACTOR;
1065 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
1066 activateBoost(KartBoost::Type::AllMt, mtLength);
1069 m_driftState = DriftState::NotDrifting;
1076 if (state()->airtime() > 5) {
1080 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1083 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1084 if (angle > targetAngle) {
1086 }
else if (angle < targetAngle) {
1092 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1093 if (targetAngle > angle) {
1095 }
else if (targetAngle < angle) {
1110 bool drifting = state()->isDrifting() && !state()->isJumpPadMushroomCollision();
1111 bool autoDrift = state()->isAutoDrift();
1112 const auto &stats = param()->stats();
1115 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1117 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1120 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1125 bool forwards =
true;
1126 if (state()->isBrake() &&
m_speed <= 0.0f) {
1131 if (state()->isChargingSsmt()) {
1134 if (state()->isHop() &&
m_hopPosY > 0.0f) {
1139 bool noTurn =
false;
1140 if (!state()->isWallCollision() && !state()->isWall3Collision() &&
1141 EGG::Mathf::abs(
m_speed) < 1.0f) {
1142 if (!(state()->isHop() &&
m_hopPosY > 0.0f)) {
1147 if (forwards && !noTurn) {
1151 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1154 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1163 if (state()->isZipperBoost() && !state()->isDriftManual()) {
1167 f32 stickX = EGG::Mathf::abs(state()->stickX());
1168 if (autoDrift && stickX > 0.3f) {
1169 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1170 stickX = drifting ? 0.2f : 0.5f;
1175 if (!state()->isInAction() && !state()->isZipperTrick()) {
1176 if (!state()->isTouchingGround()) {
1177 if (state()->isRampBoost() && m_jump->isBoostRampEnabled()) {
1179 }
else if (!state()->isJumpPadMushroomCollision()) {
1180 u32 airtime = state()->airtime();
1181 if (airtime >= 70) {
1183 }
else if (airtime >= 30) {
1184 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1190 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1191 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1193 if (angle > 60.0f) {
1194 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1205 const auto *raceMgr = System::RaceManager::Instance();
1206 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1207 f32 speedFix = dynamics()->speedFix();
1208 if (state()->isInAction() ||
1209 ((state()->isWallCollisionStart() || state()->wallBonkTimer() == 0 ||
1210 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1211 !state()->isDriftManual())) {
1223 if (state()->isInAction()) {
1224 action()->calcVehicleSpeed();
1228 if ((state()->isSomethingWallCollision() && state()->isTouchingGround() &&
1229 !state()->isAnyWheelCollision()) ||
1230 !state()->isTouchingGround() || state()->isChargingSsmt()) {
1231 if (state()->isRampBoost() && state()->airtime() < 4) {
1234 if (state()->isJumpPad() && !state()->isAccelerate()) {
1237 if (state()->isOverZipper()) {
1240 if (state()->airtime() > 5) {
1248 }
else if (state()->isBoost()) {
1251 if (!state()->isJumpPad() && !state()->isRampBoost()) {
1252 if (state()->isAccelerate()) {
1255 if (!state()->isBrake() || state()->isDisableBackwardsAccel() ||
1256 state()->isSomethingWallCollision()) {
1269 if (!state()->isBoost() && !state()->isDriftManual() && !state()->isAutoDrift()) {
1270 const auto &stats = param()->stats();
1273 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1287 initialVel = std::min(initialVel * 2.0f, 2.0f);
1289 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1303 std::span<const f32> as;
1304 std::span<const f32> ts;
1305 if (state()->isDrifting()) {
1306 as = param()->stats().accelerationDriftA;
1307 ts = param()->stats().accelerationDriftT;
1309 as = param()->stats().accelerationStandardA;
1310 ts = param()->stats().accelerationStandardT;
1314 f32 acceleration = 0.0f;
1316 for (; i < ts.size(); ++i) {
1317 if (ratio < ts[i]) {
1318 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1325 return i < ts.size() ? acceleration : as.back();
1332 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1333 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1334 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1335 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1336 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1340 dynamics()->setKillExtVelY(state()->isRespawnKillY());
1342 if (state()->isBurnout()) {
1358 if (state()->isBeforeRespawn()) {
1361 if (state()->isChargingSsmt()) {
1372 f32 speedLimit = state()->isJumpPad() ? m_jumpPadMaxSpeed :
m_baseSpeed;
1373 const f32 boostMultiplier = m_boost.multiplier();
1374 const f32 boostSpdLimit = m_boost.speedLimit();
1375 m_jumpPadBoostMultiplier = boostMultiplier;
1377 f32 crushMultiplier = state()->isCrushed() ? 0.7f : 1.0f;
1379 speedLimit *= state()->isJumpPadFixedSpeed() ?
1383 bool ignoreCrushSpeed = state()->isRampBoost() || state()->isZipperInvisibleWall() ||
1384 state()->isOverZipper() || state()->isHalfPipeRamp();
1385 f32 boostSpeed = ignoreCrushSpeed ? 1.0f : crushMultiplier;
1388 if (!state()->isJumpPad() && boostSpeed > 0.0f && boostSpeed > speedLimit) {
1389 speedLimit = boostSpeed;
1394 if (state()->isRampBoost()) {
1395 speedLimit = std::max(speedLimit, 100.0f);
1400 f32 local_c8 = 1.0f;
1405 }
else if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1415 if (state()->isJumpPad()) {
1426 crossVec = -crossVec;
1429 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1430 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1431 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1432 }
else if (!state()->isTouchingGround()) {
1433 rotationScalar = ROTATION_SCALAR_MIDAIR;
1440 const auto *raceMgr = System::RaceManager::Instance();
1441 if (!state()->isInAction() && !state()->isDisableBackwardsAccel() &&
1442 state()->isTouchingGround() && !state()->isAccelerate() &&
1443 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1450 f32 maxSpeedY = state()->isOverZipper() ? KartHalfPipe::TerminalVelocity() : TERMINAL_VELOCITY;
1451 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1453 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1455 if (state()->isTouchingGround() && !state()->isDriftManual() && !state()->isHop()) {
1456 if (state()->isBrake()) {
1459 DrivingDirection::Backwards;
1475 if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1481 if (state()->isZipperInvisibleWall() || state()->isOverZipper()) {
1486 if (wallNrm.y > 0.0f) {
1494 f1 = std::max(0.0f, dot + 1.0f);
1496 return std::min(1.0f, f1 * (state()->isWallCollision() ? 0.4f : 0.7f));
1508 if (!state()->isWallCollisionStart()) {
1513 if (!state()->isInAction()) {
1516 m_landingDir = m_dir;
1519 if (!state()->isZipperInvisibleWall() && !state()->isOverZipper() && param_2 < 0.9f) {
1523 if (speedDiff > 30.0f) {
1526 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1530 speedDiff = std::min(60.0f, speedDiff);
1533 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1537 if (state()->isBoost()) {
1538 proj = EGG::Vector3f::zero;
1539 rej = EGG::Vector3f::zero;
1542 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1543 proj = EGG::Vector3f::zero;
1548 f32 bumpDeviation = 0.0f;
1550 bumpDeviation = param()->stats().bumpDeviationLevel;
1555 dynamics()->addForce(colData.wallNrm * 15.0f);
1556 collide()->startFloorMomentRate();
1568 if (state()->isTouchingGround()) {
1569 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1570 next = 0.015f * -state()->startBoostCharge();
1571 }
else if (!state()->isChargingSsmt()) {
1572 if (!state()->isJumpPad() && !state()->isRampBoost() && !state()->isSoftWallDrift()) {
1574 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1576 if (state()->isMushroomBoost()) {
1577 next = (scalar * 0.15f) * 0.25f;
1578 if (state()->isWheelie()) {
1582 next = (scalar * 0.15f) * 0.08f;
1587 constexpr s16 MAX_SSMT_CHARGE = 75;
1588 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1593 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1595 m_standStillBoostRot += scalar * (next - m_standStillBoostRot);
1603 constexpr f32 DIVE_LIMIT = 0.8f;
1607 if (state()->isTouchingGround() || state()->isCannonStart() || state()->isInCannon() ||
1608 state()->isInAction() || state()->isOverZipper()) {
1612 f32 stickY = state()->stickY();
1615 stickY = std::min(1.0f, stickY + 0.4f);
1618 u32 airtime = state()->airtime();
1621 if (EGG::Mathf::abs(stickY) < 0.1f) {
1625 stickY *= (airtime / 50.0f);
1632 dynamics()->setAngVel2(angVel2);
1634 if (state()->airtime() < 50) {
1640 f32 upDotTop =
m_up.
dot(topRotated);
1642 f32 crossNorm = upCrossTop.
length();
1643 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1645 f32 fVar1 = angle * RAD2DEG - 20.0f;
1646 if (fVar1 <= 0.0f) {
1650 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1651 if (forwardRotated.y > 0.0f) {
1652 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1654 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1662 if (EGG::Mathf::abs(
m_speed) >= 10.0f || state()->isBoost() || state()->isRampBoost() ||
1663 !state()->isAccelerate() || !state()->isBrake()) {
1664 state()->setChargingSsmt(
false);
1668 state()->setChargingSsmt(
true);
1669 state()->setHopStart(
false);
1670 state()->setDriftInput(
false);
1674void KartMove::calcHopPhysics() {
1685void KartMove::calcRejectRoad() {
1686 m_reject.calcRejectRoad();
1690bool KartMove::calcZipperCollision(f32 radius, f32 scale,
EGG::Vector3f &pos,
1694 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1696 auto *colDir = Field::CollisionDirector::Instance();
1697 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1702 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.
dot(to)));
1703 f32 acos = EGG::Mathf::acos(dotNorm);
1704 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1710 f32 tiltMagnitude = 0.0f;
1712 if (!state()->isInAction() && !state()->isSoftWallDrift() && state()->isAnyWheelCollision()) {
1716 f32 magnitude = tiltMagnitude;
1718 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1719 magnitude = frontSpeed.
length();
1721 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1722 magnitude = -magnitude;
1725 tiltMagnitude = -1.0f;
1726 if (-1.0f <= magnitude) {
1727 tiltMagnitude = std::min(1.0f, magnitude);
1730 }
else if (!state()->isHop() ||
m_hopPosY <= 0.0f) {
1733 dynamics()->setAngVel0(angVel0);
1736 f32 lean = EGG::Mathf::abs(
m_weightedTurn) * (tiltMagnitude * param()->stats().tilt);
1741 angVel0.x += m_standStillBoostRot;
1743 dynamics()->setAngVel0(angVel0);
1747 dynamics()->setAngVel2(angVel2);
1757 constexpr u16 MAX_MT_CHARGE = 270;
1758 constexpr u16 MAX_SMT_CHARGE = 300;
1759 constexpr u16 BASE_MT_CHARGE = 2;
1760 constexpr u16 BASE_SMT_CHARGE = 2;
1761 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1762 constexpr u16 EXTRA_MT_CHARGE = 3;
1764 if (m_driftState == DriftState::ChargedSmt) {
1768 f32 stickX = state()->stickX();
1770 if (m_driftState == DriftState::ChargingMt) {
1773 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1774 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1783 m_driftState = DriftState::ChargingSmt;
1787 if (m_driftState != DriftState::ChargingSmt) {
1793 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1794 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1803 m_driftState = DriftState::ChargedSmt;
1808void KartMove::initOob() {
1814 clearOffroadInvincibility();
1821 state()->setHop(
true);
1822 state()->setDriftManual(
false);
1827 m_driftState = DriftState::NotDrifting;
1839 dynamics()->setExtVel(extVel);
1842 totalForce.y = 0.0f;
1843 dynamics()->setTotalForce(totalForce);
1847void KartMove::tryStartBoostPanel() {
1848 constexpr s16 BOOST_PANEL_DURATION = 60;
1850 if (state()->isBeforeRespawn() || state()->isInAction()) {
1854 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
1862 constexpr s16 BOOST_RAMP_DURATION = 60;
1864 if (state()->isBeforeRespawn() || state()->isInAction()) {
1868 state()->setRampBoost(
true);
1869 m_rampBoost = BOOST_RAMP_DURATION;
1878 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
1879 {50.0f, 50.0f, 35.0f},
1880 {50.0f, 50.0f, 47.0f},
1881 {59.0f, 59.0f, 30.0f},
1882 {73.0f, 73.0f, 45.0f},
1883 {73.0f, 73.0f, 53.0f},
1884 {55.0f, 55.0f, 35.0f},
1885 {56.0f, 56.0f, 50.0f},
1888 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isHalfPipeRamp()) {
1892 state()->setJumpPad(
true);
1893 s32 jumpPadVariant = state()->jumpPadVariant();
1894 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
1896 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
1897 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
1900 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
1901 {100.0f, 100.0f, 70.0f},
1902 {100.0f, 100.0f, 65.0f},
1904 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
1906 state()->setJumpPadFixedSpeed(
true);
1909 if (jumpPadVariant == 4) {
1910 state()->setJumpPadMushroomTrigger(
true);
1911 state()->setJumpPadMushroomVelYInc(
true);
1912 state()->setJumpPadMushroomCollision(
true);
1917 extVel.y = m_jumpPadProperties->velY;
1918 totalForce.y = 0.0f;
1920 dynamics()->setExtVel(extVel);
1921 dynamics()->setTotalForce(totalForce);
1923 if (jumpPadVariant != 3) {
1930 state()->setJumpPadDisableYsusForce(
true);
1935 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
1940void KartMove::tryEndJumpPad() {
1941 if (state()->isJumpPadMushroomTrigger()) {
1942 if (state()->isGroundStart()) {
1943 state()->setJumpPadMushroomTrigger(
false);
1944 state()->setJumpPadFixedSpeed(
false);
1945 state()->setJumpPadMushroomVelYInc(
false);
1948 if (state()->isJumpPadMushroomVelYInc()) {
1950 newExtVel.y += 20.0f;
1951 if (m_jumpPadProperties->velY < newExtVel.y) {
1952 newExtVel.y = m_jumpPadProperties->velY;
1953 state()->setJumpPadMushroomVelYInc(
false);
1955 dynamics()->setExtVel(newExtVel);
1959 if (state()->isGroundStart() && !state()->isJumpPadMushroomTrigger()) {
1965void KartMove::cancelJumpPad() {
1967 state()->setJumpPad(
false);
1971void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
1972 if (m_boost.
activate(type, frames)) {
1973 state()->setBoost(
true);
1978void KartMove::applyStartBoost(s16 frames) {
1979 activateBoost(KartBoost::Type::AllMt, frames);
1983void KartMove::activateMushroom() {
1984 constexpr s16 MUSHROOM_DURATION = 90;
1986 if (state()->isBeforeRespawn() || state()->isInAction()) {
1990 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
1993 state()->setMushroomBoost(
true);
1998void KartMove::activateZipperBoost() {
1999 constexpr s16 BASE_DURATION = 50;
2000 constexpr s16 TRICK_DURATION = 100;
2002 if (state()->isBeforeRespawn() || state()->isInAction()) {
2006 s16 boostDuration = state()->isZipperTrick() ? TRICK_DURATION : BASE_DURATION;
2007 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
2010 m_zipperBoostTimer = 0;
2011 m_zipperBoostMax = boostDuration;
2012 state()->setZipperBoost(
true);
2024 state()->setBoostOffroadInvincibility(
true);
2031 if (!state()->isBoostOffroadInvincibility()) {
2039 state()->setBoostOffroadInvincibility(
false);
2045 if (!state()->isMushroomBoost()) {
2053 state()->setMushroomBoost(
false);
2057void KartMove::calcZipperBoost() {
2058 if (!state()->isZipperBoost()) {
2062 state()->setAccelerate(
true);
2064 if (!state()->isOverZipper() && ++m_zipperBoostTimer >= m_zipperBoostMax) {
2065 m_zipperBoostTimer = 0;
2066 state()->setZipperBoost(
false);
2069 if (m_zipperBoostTimer < 10) {
2072 dynamics()->setAngVel0(angVel);
2077void KartMove::landTrick() {
2078 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2083 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2089 if (state()->isBeforeRespawn() || state()->isInAction()) {
2095 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2097 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2100 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2104void KartMove::activateCrush(
u16 timer) {
2105 state()->setCrushed(
true);
2111void KartMove::calcCrushed() {
2112 if (!state()->isCrushed()) {
2117 state()->setCrushed(
false);
2123void KartMove::calcScale() {
2129void KartMove::applyBumpForce(f32 speed,
const EGG::Vector3f &hitDir,
bool resetSpeed) {
2130 constexpr s16 BUMP_COOLDOWN = 5;
2132 if (m_bumpTimer >= 1) {
2136 dynamics()->addForce(speed * hitDir.
perpInPlane(move()->up(),
true));
2137 collide()->startFloorMomentRate();
2139 m_bumpTimer = BUMP_COOLDOWN;
2147void KartMove::enterCannon() {
2149 physics()->clearDecayingRot();
2150 m_boost.resetActive();
2151 state()->setBoost(
false);
2157 clearOffroadInvincibility();
2159 dynamics()->reset();
2162 state()->setHop(
false);
2163 state()->setInCannon(
true);
2164 state()->setSkipWheelCalc(
true);
2165 state()->setCannonStart(
false);
2167 const auto [cannonPos, cannonDir] = getCannonPosRot();
2168 m_cannonEntryPos = pos();
2169 m_cannonEntryOfs = cannonPos - pos();
2170 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2172 m_dir = m_cannonEntryOfs;
2173 m_vel1Dir = m_cannonEntryOfs;
2174 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2175 m_cannonProgress.setZero();
2179void KartMove::calcCannon() {
2180 auto [cannonPos, cannonDir] = getCannonPosRot();
2181 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2183 f32 forwardLength = forward.
normalise();
2197 if (forwardLength < 30.0f || local94.
dot(forwardXZ) <= 0.0f) {
2202 const auto *cannonPoint =
2203 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2204 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2205 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2206 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2207 f32 newSpeed = cannonParams.speed;
2208 if (forwardLength < cannonParams.decelFactor) {
2209 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2211 newSpeed = cannonParams.endDecel;
2212 if (newSpeed <= 0.0f) {
2216 newSpeed += factor * (cannonParams.speed - newSpeed);
2217 if (cannonParams.endDecel > 0.0f) {
2222 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2225 if (cannonParams.height > 0.0f) {
2226 f32 fVar9 = EGG::Mathf::SinFIdx(
2227 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2228 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2231 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2232 m_dir = m_cannonEntryOfs;
2233 m_vel1Dir = m_cannonEntryOfs;
2235 calcRotCannon(forward);
2237 dynamics()->setExtVel(EGG::Vector3f::zero);
2245 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2251 local80 *= dynamics()->fullRot();
2256 dynamics()->setFullRot(newRot);
2257 dynamics()->setMainRot(newRot);
2261void KartMove::exitCannon() {
2262 if (!state()->isInCannon()) {
2266 state()->setInCannon(
false);
2267 state()->setSkipWheelCalc(
false);
2268 state()->setAfterCannon(
true);
2269 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2273void KartMove::triggerRespawn() {
2275 state()->setTriggerRespawn(
true);
2279KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2282KartMoveBike::~KartMoveBike() =
default;
2287 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2288 constexpr u16 WHEELIE_COOLDOWN = 20;
2290 state()->setWheelie(
true);
2295 m_autoHardStickXFrames = 0;
2302 state()->setWheelie(
false);
2304 m_autoHardStickXFrames = 0;
2308void KartMoveBike::createSubsystems() {
2320 const auto *raceManager = System::RaceManager::Instance();
2322 if (!state()->isChargingSsmt()) {
2323 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2324 EGG::Mathf::abs(
m_speed) < 5.0f) {
2336 f32 stickX = state()->stickX();
2337 f32 extVelXFactor = 0.0f;
2341 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isWheelie() ||
2342 state()->isOverZipper() || state()->isRejectRoadTrigger() ||
2343 state()->isAirtimeOver20() || state()->isSoftWallDrift() ||
2344 state()->isSomethingWallCollision() || state()->isHWG() || state()->isCannonStart() ||
2345 state()->isInCannon()) {
2347 }
else if (!state()->isDrifting()) {
2348 if (stickX <= 0.2f) {
2349 if (stickX >= -0.2f) {
2364 leanRotMin = -leanRotMax;
2368 if (stickX == 0.0f) {
2374 }
else if (stickX == 0.0f) {
2382 bool capped =
false;
2394 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2397 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2401 dynamics()->setAngVel2(dynamics()->angVel2() +
2402 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2409 if (!state()->isRejectRoad() && !state()->isHalfPipeRamp() && !state()->isOverZipper()) {
2411 scalar = std::min(1.0f, scalar);
2412 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2414 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2419 dynamics()->setTop_(top);
2427 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2428 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2429 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2432 KartMove::setTurnParams();
2434 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2436 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2440 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2450void KartMoveBike::init(
bool b1,
bool b2) {
2451 KartMove::init(b1, b2);
2460 m_autoHardStickXFrames = 0;
2464void KartMoveBike::clear() {
2472 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2473 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2478 if (state()->isWheelie()) {
2479 bool cancelAutoWheelie =
false;
2481 if (!state()->isAutoDrift() ||
2482 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2483 m_autoHardStickXFrames = 0;
2485 if (++m_autoHardStickXFrames > 15) {
2486 cancelAutoWheelie =
true;
2498 dynamics()->setAngVel0(angVel0);
2508 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2513 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2514 dynamics()->setAngVel2(angVel2);
2519 state()->setWheelieRot(
true);
2521 state()->setWheelieRot(
false);
2531 if (state()->isAutoDrift()) {
2548 constexpr u16 MAX_MT_CHARGE = 270;
2549 constexpr u16 BASE_MT_CHARGE = 2;
2550 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2551 constexpr u16 EXTRA_MT_CHARGE = 3;
2553 if (m_driftState != DriftState::ChargingMt) {
2559 f32 stickX = state()->stickX();
2560 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2561 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2570 m_driftState = DriftState::ChargedMt;
2575void KartMoveBike::initOob() {
2576 KartMove::initOob();
2583 constexpr s16 COOLDOWN_FRAMES = 20;
2584 bool dpadUp = inputs()->currentState().trickUp();
2586 if (!state()->isWheelie()) {
2587 if (dpadUp && state()->isTouchingGround()) {
2588 if (state()->isDriftManual() || state()->isWallCollision() ||
2589 state()->isWall3Collision() || state()->isHop() || state()->isDriftAuto() ||
2590 state()->isInAction()) {
@ COL_TYPE_MOVING_WATER
Koopa Cape and DS Yoshi Falls.
@ COL_TYPE_STICKY_ROAD
Player sticks if within 200 units (rBC stairs).
@ COL_TYPE_SPECIAL_WALL
Various other wall types, determined by variant.
#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.
u16 m_crushTimer
Number of frames until player will be uncrushed.
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.
KartScale * m_kartScale
Manages scaling due to TF stompers and MH cars.
@ 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
Normally the unit vector, but may vary due to crush animations.
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.
Mainly responsible for calculating scaling for the squish/unsquish animation.
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 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.