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();
221void KartMove::clear() {
222 if (state()->isOverZipper()) {
223 state()->setActionMidZipper(
true);
231 clearOffroadInvincibility();
240 EGG::Quatf quaternion = EGG::Quatf::FromRPY(angles * DEG2RAD);
245 bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos,
246 EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0);
249 newPos = newPos + info.tangentOff + (info.floorNrm * -100.0f);
250 newPos += info.floorNrm * bsp().initialYPos;
256 sub()->initPhysicsValues();
258 physics()->setPos(pos());
259 physics()->setVelocity(dynamics()->velocity());
264 dynamics()->setTop(
m_up);
266 for (
u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) {
267 suspension(tireIdx)->setInitialState();
277 if (state()->isInRespawn()) {
282 dynamics()->resetInternalVelocity();
297 if (!state()->isAutoDrift()) {
309 if (state()->isInCannon()) {
320void KartMove::calcRespawnStart() {
321 constexpr float RESPAWN_HEIGHT = 700.0f;
323 const auto *jugemPoint = System::RaceManager::Instance()->jugemPoint();
328 respawnPos.y += RESPAWN_HEIGHT;
333 Item::ItemDirector::Instance()->kartItem(0).clear();
335 state()->setTriggerRespawn(
false);
336 state()->setInRespawn(
true);
340void KartMove::calcInRespawn() {
341 constexpr f32 LAKITU_VELOCITY = 1.5f;
342 constexpr u16 RESPAWN_DURATION = 110;
344 if (!state()->isInRespawn()) {
349 newPos.y -= LAKITU_VELOCITY;
350 dynamics()->setPos(newPos);
351 dynamics()->setNoGravity(
true);
354 state()->setInRespawn(
false);
355 state()->setAfterRespawn(
true);
356 state()->setRespawnKillY(
true);
359 dynamics()->setNoGravity(
false);
364void KartMove::calcRespawnBoost() {
365 constexpr s16 RESPAWN_BOOST_DURATION = 30;
366 constexpr s16 RESPAWN_BOOST_INPUT_LENIENCY = 4;
368 if (state()->isAfterRespawn()) {
369 if (state()->isTouchingGround()) {
371 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
372 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
373 m_respawnTimer = RESPAWN_BOOST_DURATION;
379 state()->setAfterRespawn(
false);
391 if (state()->isAccelerateStart()) {
392 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
393 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
394 m_respawnTimer = RESPAWN_BOOST_DURATION;
402 state()->setRespawnKillY(
false);
406 m_respawnTimer = std::max(0, m_respawnTimer - 1);
410void KartMove::calcTop() {
411 f32 stabilizationFactor = 0.1f;
412 m_hasLandingDir =
false;
415 if (state()->isGroundStart() && m_nonZipperAirtime >= 3) {
419 m_dirDiff = m_landingDir.
proj(m_landingDir);
420 m_hasLandingDir =
true;
422 if (state()->isHop() &&
m_hopPosY > 0.0f) {
424 }
else if (state()->isTouchingGround()) {
426 inputTop.
dot(m_dir) > 0.0f &&
m_speed > 50.0f &&
427 collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::NotTrickable)) {
435 if (state()->isHalfPipeRamp() ||
436 (!state()->isBoost() && !state()->isRampBoost() && !state()->isWheelie() &&
437 !state()->isOverZipper() &&
438 (!state()->isZipperBoost() || m_zipperBoostTimer > 15))) {
439 f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(inputTop.
dot(componentZAxis())));
440 scalar = std::min(0.8f, std::max(0.3f, topDotZ));
448 if (bodyDotFront < -0.1f) {
449 stabilizationFactor += std::min(0.2f, EGG::Mathf::abs(bodyDotFront) * 0.5f);
452 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
453 stabilizationFactor = 0.4f;
460 dynamics()->setStabilizationFactor(stabilizationFactor);
462 m_nonZipperAirtime = state()->isOverZipper() ? 0 : state()->airtime();
463 m_flags.
changeBit(collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::Trickable),
470 if (state()->isOverZipper() || !state()->isAirtimeOver20()) {
481 if (
m_up.y <= 0.99f) {
482 m_up += (EGG::Vector3f::ey -
m_up) * 0.03f;
485 m_up = EGG::Vector3f::ey;
493 const auto *raceMgr = System::RaceManager::Instance();
494 if (!raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
498 if (m_padType.
onBit(ePadType::BoostPanel)) {
499 tryStartBoostPanel();
502 if (m_padType.
onBit(ePadType::BoostRamp)) {
506 if (m_padType.
onBit(ePadType::JumpPad)) {
514void KartMove::calcDirs() {
518 m_flags.
setBit(eFlags::LaunchBoost);
520 if (!state()->isInATrick() && !state()->isOverZipper() &&
521 (((state()->isTouchingGround() || !state()->isRampBoost() ||
522 !m_jump->isBoostRampEnabled()) &&
523 !state()->isJumpPad() && state()->airtime() <= 5) ||
524 state()->isJumpPadMushroomCollision() || state()->isNoSparkInvisibleWall())) {
525 if (state()->isHop()) {
537 if (dirDiff.
squaredLength() <= std::numeric_limits<f32>::epsilon()) {
548 if (origDirCross.
dot(newDirCross) < 0.0f) {
555 m_flags.
resetBit(eFlags::LaunchBoost);
560 if (!state()->isOverZipper()) {
564 if (m_hasLandingDir) {
565 f32 dot = m_dir.dot(m_landingDir);
567 f32 crossDot = cross.
length();
568 f32 angle = EGG::Mathf::atan2(crossDot, dot);
569 angle = EGG::Mathf::abs(angle);
576 m_landingAngle += (angle * RAD2DEG) * fVar4;
579 if (m_landingAngle <= 0.0f) {
580 if (m_landingAngle < 0.0f) {
581 m_landingAngle = std::min(0.0f, m_landingAngle + 2.0f);
584 m_landingAngle = std::max(0.0f, m_landingAngle - 2.0f);
589void KartMove::calcStickyRoad() {
590 constexpr f32 STICKY_RADIUS = 200.0f;
594 if (state()->isOverZipper()) {
595 state()->setStickyRoad(
false);
599 if ((!state()->isStickyRoad() &&
600 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::Trickable)) ||
601 EGG::Mathf::abs(
m_speed) <= 20.0f) {
609 colInfo.bbox.setZero();
611 bool stickyRoad =
false;
613 for (
size_t i = 0; i < 3; ++i) {
615 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
616 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
617 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
618 dynamics()->setMovingObjVel(dynamics()->movingObjVel().rej(colInfo.floorNrm));
624 pos += -STICKY_RADIUS * componentYAxis();
628 state()->setStickyRoad(
false);
636 if (state()->isBoostOffroadInvincibility()) {
640 bool anyWheel = state()->isAnyWheelCollision();
647 if (state()->isVehicleBodyFloorCollision()) {
664void KartMove::calcBoost() {
665 if (m_boost.
calc()) {
666 state()->setAccelerate(
true);
668 state()->setBoost(
false);
675void KartMove::calcRampBoost() {
676 if (!state()->isRampBoost()) {
680 state()->setAccelerate(
true);
681 if (--m_rampBoost < 1) {
683 state()->setRampBoost(
false);
691 if (!state()->isDisableBackwardsAccel()) {
697 state()->setDisableBackwardsAccel(
false);
706 constexpr s16 MAX_SSMT_CHARGE = 75;
707 constexpr s16 SSMT_BOOST_FRAMES = 30;
708 constexpr s16 LEEWAY_FRAMES = 1;
709 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
713 if (state()->isChargingSsmt()) {
734 state()->setDisableBackwardsAccel(
true);
736 if (!state()->isAccelerate() && !state()->isBrake()) {
737 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
743 if (state()->isAccelerate() && !state()->isBrake()) {
744 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
750 state()->setDisableBackwardsAccel(
true);
761 if (!state()->isTouchingGround() && !state()->isHop() && !state()->isDriftManual()) {
762 if (state()->isStickLeft() || state()->isStickRight()) {
763 if (!state()->isDriftInput()) {
764 state()->setSlipdriftCharge(
false);
765 }
else if (!state()->isSlipdriftCharge()) {
767 if (state()->isStickRight()) {
769 }
else if (state()->isStickLeft()) {
772 state()->setSlipdriftCharge(
true);
779 if (state()->isHop()) {
781 if (state()->isStickRight()) {
783 }
else if (state()->isStickLeft()) {
790 }
else if (state()->isSlipdriftCharge()) {
794 return state()->isHop() || state()->isSlipdriftCharge();
803 state()->setHop(
false);
804 state()->setDriftManual(
false);
805 m_driftState = DriftState::NotDrifting;
817 m_driftState = DriftState::NotDrifting;
821 state()->setHop(
false);
822 state()->setSlipdriftCharge(
false);
823 state()->setDriftManual(
false);
824 state()->setDriftAuto(
false);
825 m_autoDriftAngle = 0.0f;
827 m_autoDriftStartFrameCounter = 0;
831void KartMove::clearJumpPad() {
833 state()->setJumpPad(
false);
837void KartMove::clearRampBoost() {
839 state()->setRampBoost(
false);
843void KartMove::clearZipperBoost() {
844 m_zipperBoostTimer = 0;
845 state()->setZipperBoost(
false);
849void KartMove::clearBoost() {
850 m_boost.resetActive();
851 state()->setBoost(
false);
855void KartMove::clearSsmt() {
863void KartMove::clearOffroadInvincibility() {
865 state()->setBoostOffroadInvincibility(
false);
868void KartMove::clearRejectRoad() {
869 state()->setRejectRoadTrigger(
false);
870 state()->setNoSparkInvisibleWall(
false);
877 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
879 if (!state()->isAutoDrift()) {
883 if (canStartDrift() && !state()->isOverZipper() && !state()->isRejectRoadTrigger() &&
884 !state()->isWheelie() && EGG::Mathf::abs(state()->stickX()) > 0.85f) {
885 m_autoDriftStartFrameCounter =
886 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
888 m_autoDriftStartFrameCounter = 0;
891 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
892 state()->setDriftAuto(
true);
894 if (state()->isTouchingGround()) {
895 if (state()->stickX() < 0.0f) {
905 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
906 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
908 state()->setDriftAuto(
false);
911 if (m_autoDriftAngle > 0.0f) {
913 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
916 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
922 physics()->composeExtraRot(angleAxis);
931 if (!state()->isOverZipper()) {
934 if (!state()->isTouchingGround() &&
935 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
936 !state()->isJumpPadMushroomCollision() &&
937 (state()->isDriftManual() || state()->isSlipdriftCharge()) &&
938 m_flags.
onBit(eFlags::LaunchBoost)) {
943 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
944 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
946 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
958 if (((!state()->isHop() ||
m_hopFrame < 3) && !state()->isSlipdriftCharge()) ||
959 (state()->isInAction() || !state()->isTouchingGround())) {
971 if (!state()->isDriftManual()) {
972 if (!isHopping && state()->isTouchingGround()) {
975 if (action()->flags().offBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
976 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
985 if (!state()->isOverZipper() &&
986 (!state()->isDriftInput() || !state()->isAccelerate() || state()->isInAction() ||
987 state()->isRejectRoadTrigger() || state()->isWall3Collision() ||
988 state()->isWallCollision() || !canStartDrift())) {
989 if (canStartDrift()) {
1005 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1007 const auto &stats = param()->stats();
1009 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1010 f32 driftAngle = 0.0f;
1012 if (state()->isHop()) {
1027 state()->setHop(
false);
1028 state()->setSlipdriftCharge(
false);
1030 if (!state()->isDriftInput()) {
1038 state()->setDriftManual(
true);
1039 state()->setHop(
false);
1040 m_driftState = DriftState::ChargingMt;
1048 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1050 if (m_driftState < DriftState::ChargedMt || state()->isBrake()) {
1051 m_driftState = DriftState::NotDrifting;
1057 if (m_driftState == DriftState::ChargedSmt) {
1058 mtLength *= SMT_LENGTH_FACTOR;
1061 if (!state()->isBeforeRespawn() && !state()->isInAction()) {
1062 activateBoost(KartBoost::Type::AllMt, mtLength);
1065 m_driftState = DriftState::NotDrifting;
1072 if (state()->airtime() > 5) {
1076 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1079 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1080 if (angle > targetAngle) {
1082 }
else if (angle < targetAngle) {
1088 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1089 if (targetAngle > angle) {
1091 }
else if (targetAngle < angle) {
1106 bool drifting = state()->isDrifting() && !state()->isJumpPadMushroomCollision();
1107 bool autoDrift = state()->isAutoDrift();
1108 const auto &stats = param()->stats();
1111 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1113 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1116 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1121 bool forwards =
true;
1122 if (state()->isBrake() &&
m_speed <= 0.0f) {
1127 if (state()->isChargingSsmt()) {
1130 if (state()->isHop() &&
m_hopPosY > 0.0f) {
1135 bool noTurn =
false;
1136 if (!state()->isWallCollision() && !state()->isWall3Collision() &&
1137 EGG::Mathf::abs(
m_speed) < 1.0f) {
1138 if (!(state()->isHop() &&
m_hopPosY > 0.0f)) {
1143 if (forwards && !noTurn) {
1147 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1150 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1159 if (state()->isZipperBoost() && !state()->isDriftManual()) {
1163 f32 stickX = EGG::Mathf::abs(state()->stickX());
1164 if (autoDrift && stickX > 0.3f) {
1165 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1166 stickX = drifting ? 0.2f : 0.5f;
1171 if (!state()->isInAction() && !state()->isZipperTrick()) {
1172 if (!state()->isTouchingGround()) {
1173 if (state()->isRampBoost() && m_jump->isBoostRampEnabled()) {
1175 }
else if (!state()->isJumpPadMushroomCollision()) {
1176 u32 airtime = state()->airtime();
1177 if (airtime >= 70) {
1179 }
else if (airtime >= 30) {
1180 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1186 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1187 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1189 if (angle > 60.0f) {
1190 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1201 const auto *raceMgr = System::RaceManager::Instance();
1202 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1203 f32 speedFix = dynamics()->speedFix();
1204 if (state()->isInAction() ||
1205 ((state()->isWallCollisionStart() || state()->wallBonkTimer() == 0 ||
1206 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1207 !state()->isDriftManual())) {
1219 if (state()->isInAction()) {
1220 action()->calcVehicleSpeed();
1224 if ((state()->isSomethingWallCollision() && state()->isTouchingGround() &&
1225 !state()->isAnyWheelCollision()) ||
1226 !state()->isTouchingGround() || state()->isChargingSsmt()) {
1227 if (state()->isRampBoost() && state()->airtime() < 4) {
1230 if (state()->isJumpPad() && !state()->isAccelerate()) {
1233 if (state()->isOverZipper()) {
1236 if (state()->airtime() > 5) {
1244 }
else if (state()->isBoost()) {
1247 if (!state()->isJumpPad() && !state()->isRampBoost()) {
1248 if (state()->isAccelerate()) {
1251 if (!state()->isBrake() || state()->isDisableBackwardsAccel() ||
1252 state()->isSomethingWallCollision()) {
1265 if (!state()->isBoost() && !state()->isDriftManual() && !state()->isAutoDrift()) {
1266 const auto &stats = param()->stats();
1269 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1283 initialVel = std::min(initialVel * 2.0f, 2.0f);
1285 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1299 std::span<const f32> as;
1300 std::span<const f32> ts;
1301 if (state()->isDrifting()) {
1302 as = param()->stats().accelerationDriftA;
1303 ts = param()->stats().accelerationDriftT;
1305 as = param()->stats().accelerationStandardA;
1306 ts = param()->stats().accelerationStandardT;
1310 f32 acceleration = 0.0f;
1312 for (; i < ts.size(); ++i) {
1313 if (ratio < ts[i]) {
1314 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1321 return i < ts.size() ? acceleration : as.back();
1328 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1329 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1330 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1331 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1332 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1336 dynamics()->setKillExtVelY(state()->isRespawnKillY());
1338 if (state()->isBurnout()) {
1354 if (state()->isBeforeRespawn()) {
1357 if (state()->isChargingSsmt()) {
1368 f32 speedLimit = state()->isJumpPad() ? m_jumpPadMaxSpeed :
m_baseSpeed;
1369 const f32 boostMultiplier = m_boost.multiplier();
1370 const f32 boostSpdLimit = m_boost.speedLimit();
1371 m_jumpPadBoostMultiplier = boostMultiplier;
1373 f32 crushMultiplier = state()->isCrushed() ? 0.7f : 1.0f;
1375 speedLimit *= state()->isJumpPadFixedSpeed() ?
1379 bool ignoreCrushSpeed = state()->isRampBoost() || state()->isZipperInvisibleWall() ||
1380 state()->isOverZipper() || state()->isHalfPipeRamp();
1381 f32 boostSpeed = ignoreCrushSpeed ? 1.0f : crushMultiplier;
1384 if (!state()->isJumpPad() && boostSpeed > 0.0f && boostSpeed > speedLimit) {
1385 speedLimit = boostSpeed;
1390 if (state()->isRampBoost()) {
1391 speedLimit = std::max(speedLimit, 100.0f);
1396 f32 local_c8 = 1.0f;
1399 if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1409 if (state()->isJumpPad()) {
1420 crossVec = -crossVec;
1423 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1424 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1425 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1426 }
else if (!state()->isTouchingGround()) {
1427 rotationScalar = ROTATION_SCALAR_MIDAIR;
1434 const auto *raceMgr = System::RaceManager::Instance();
1435 if (!state()->isInAction() && !state()->isDisableBackwardsAccel() &&
1436 state()->isTouchingGround() && !state()->isAccelerate() &&
1437 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1444 f32 maxSpeedY = state()->isOverZipper() ? KartHalfPipe::TerminalVelocity() : TERMINAL_VELOCITY;
1445 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1447 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1449 if (state()->isTouchingGround() && !state()->isDriftManual() && !state()->isHop()) {
1450 if (state()->isBrake()) {
1453 DrivingDirection::Backwards;
1469 if (!state()->isWallCollision() && !state()->isWall3Collision()) {
1475 if (state()->isZipperInvisibleWall() || state()->isOverZipper()) {
1480 if (wallNrm.y > 0.0f) {
1488 f1 = std::max(0.0f, dot + 1.0f);
1490 return std::min(1.0f, f1 * (state()->isWallCollision() ? 0.4f : 0.7f));
1502 if (!state()->isWallCollisionStart()) {
1507 if (!state()->isInAction()) {
1510 m_landingDir = m_dir;
1513 if (!state()->isZipperInvisibleWall() && !state()->isOverZipper() && param_2 < 0.9f) {
1517 if (speedDiff > 30.0f) {
1520 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1524 speedDiff = std::min(60.0f, speedDiff);
1527 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1531 if (state()->isBoost()) {
1532 proj = EGG::Vector3f::zero;
1533 rej = EGG::Vector3f::zero;
1536 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1537 proj = EGG::Vector3f::zero;
1542 f32 bumpDeviation = 0.0f;
1544 bumpDeviation = param()->stats().bumpDeviationLevel;
1549 dynamics()->addForce(colData.wallNrm * 15.0f);
1550 collide()->startFloorMomentRate();
1562 if (state()->isTouchingGround()) {
1563 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1564 next = 0.015f * -state()->startBoostCharge();
1565 }
else if (!state()->isChargingSsmt()) {
1566 if (!state()->isJumpPad() && !state()->isRampBoost() && !state()->isSoftWallDrift()) {
1568 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1570 if (state()->isMushroomBoost()) {
1571 next = (scalar * 0.15f) * 0.25f;
1572 if (state()->isWheelie()) {
1576 next = (scalar * 0.15f) * 0.08f;
1581 constexpr s16 MAX_SSMT_CHARGE = 75;
1582 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1587 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1589 m_standStillBoostRot += scalar * (next - m_standStillBoostRot);
1597 constexpr f32 DIVE_LIMIT = 0.8f;
1601 if (state()->isTouchingGround() || state()->isCannonStart() || state()->isInCannon() ||
1602 state()->isInAction() || state()->isOverZipper()) {
1606 f32 stickY = state()->stickY();
1609 stickY = std::min(1.0f, stickY + 0.4f);
1612 u32 airtime = state()->airtime();
1615 if (EGG::Mathf::abs(stickY) < 0.1f) {
1619 stickY *= (airtime / 50.0f);
1626 dynamics()->setAngVel2(angVel2);
1628 if (state()->airtime() < 50) {
1634 f32 upDotTop =
m_up.
dot(topRotated);
1636 f32 crossNorm = upCrossTop.
length();
1637 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1639 f32 fVar1 = angle * RAD2DEG - 20.0f;
1640 if (fVar1 <= 0.0f) {
1644 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1645 if (forwardRotated.y > 0.0f) {
1646 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1648 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1656 if (EGG::Mathf::abs(
m_speed) >= 10.0f || state()->isBoost() || state()->isRampBoost() ||
1657 !state()->isAccelerate() || !state()->isBrake()) {
1658 state()->setChargingSsmt(
false);
1662 state()->setChargingSsmt(
true);
1663 state()->setHopStart(
false);
1664 state()->setDriftInput(
false);
1668void KartMove::calcHopPhysics() {
1679void KartMove::calcRejectRoad() {
1680 m_reject.calcRejectRoad();
1684bool KartMove::calcZipperCollision(f32 radius, f32 scale,
EGG::Vector3f &pos,
1688 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1690 auto *colDir = Field::CollisionDirector::Instance();
1691 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1696 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.
dot(to)));
1697 f32 acos = EGG::Mathf::acos(dotNorm);
1698 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1704 f32 tiltMagnitude = 0.0f;
1706 if (!state()->isInAction() && !state()->isSoftWallDrift() && state()->isAnyWheelCollision()) {
1710 f32 magnitude = tiltMagnitude;
1712 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1713 magnitude = frontSpeed.
length();
1715 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1716 magnitude = -magnitude;
1719 tiltMagnitude = -1.0f;
1720 if (-1.0f <= magnitude) {
1721 tiltMagnitude = std::min(1.0f, magnitude);
1724 }
else if (!state()->isHop() ||
m_hopPosY <= 0.0f) {
1727 dynamics()->setAngVel0(angVel0);
1730 f32 lean = EGG::Mathf::abs(
m_weightedTurn) * (tiltMagnitude * param()->stats().tilt);
1735 angVel0.x += m_standStillBoostRot;
1737 dynamics()->setAngVel0(angVel0);
1741 dynamics()->setAngVel2(angVel2);
1751 constexpr u16 MAX_MT_CHARGE = 270;
1752 constexpr u16 MAX_SMT_CHARGE = 300;
1753 constexpr u16 BASE_MT_CHARGE = 2;
1754 constexpr u16 BASE_SMT_CHARGE = 2;
1755 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1756 constexpr u16 EXTRA_MT_CHARGE = 3;
1758 if (m_driftState == DriftState::ChargedSmt) {
1762 f32 stickX = state()->stickX();
1764 if (m_driftState == DriftState::ChargingMt) {
1767 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1768 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1777 m_driftState = DriftState::ChargingSmt;
1781 if (m_driftState != DriftState::ChargingSmt) {
1787 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1788 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1797 m_driftState = DriftState::ChargedSmt;
1802void KartMove::initOob() {
1808 clearOffroadInvincibility();
1815 state()->setHop(
true);
1816 state()->setDriftManual(
false);
1821 m_driftState = DriftState::NotDrifting;
1833 dynamics()->setExtVel(extVel);
1836 totalForce.y = 0.0f;
1837 dynamics()->setTotalForce(totalForce);
1841void KartMove::tryStartBoostPanel() {
1842 constexpr s16 BOOST_PANEL_DURATION = 60;
1844 if (state()->isBeforeRespawn() || state()->isInAction()) {
1848 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
1856 constexpr s16 BOOST_RAMP_DURATION = 60;
1858 if (state()->isBeforeRespawn() || state()->isInAction()) {
1862 state()->setRampBoost(
true);
1863 m_rampBoost = BOOST_RAMP_DURATION;
1872 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
1873 {50.0f, 50.0f, 35.0f},
1874 {50.0f, 50.0f, 47.0f},
1875 {59.0f, 59.0f, 30.0f},
1876 {73.0f, 73.0f, 45.0f},
1877 {73.0f, 73.0f, 53.0f},
1878 {55.0f, 55.0f, 35.0f},
1879 {56.0f, 56.0f, 50.0f},
1882 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isHalfPipeRamp()) {
1886 state()->setJumpPad(
true);
1887 s32 jumpPadVariant = state()->jumpPadVariant();
1888 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
1890 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
1891 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
1894 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
1895 {100.0f, 100.0f, 70.0f},
1896 {100.0f, 100.0f, 65.0f},
1898 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
1900 state()->setJumpPadFixedSpeed(
true);
1903 if (jumpPadVariant == 4) {
1904 state()->setJumpPadMushroomTrigger(
true);
1905 state()->setJumpPadMushroomVelYInc(
true);
1906 state()->setJumpPadMushroomCollision(
true);
1911 extVel.y = m_jumpPadProperties->velY;
1912 totalForce.y = 0.0f;
1914 dynamics()->setExtVel(extVel);
1915 dynamics()->setTotalForce(totalForce);
1917 if (jumpPadVariant != 3) {
1924 state()->setJumpPadDisableYsusForce(
true);
1929 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
1934void KartMove::tryEndJumpPad() {
1935 if (state()->isJumpPadMushroomTrigger()) {
1936 if (state()->isGroundStart()) {
1937 state()->setJumpPadMushroomTrigger(
false);
1938 state()->setJumpPadFixedSpeed(
false);
1939 state()->setJumpPadMushroomVelYInc(
false);
1942 if (state()->isJumpPadMushroomVelYInc()) {
1944 newExtVel.y += 20.0f;
1945 if (m_jumpPadProperties->velY < newExtVel.y) {
1946 newExtVel.y = m_jumpPadProperties->velY;
1947 state()->setJumpPadMushroomVelYInc(
false);
1949 dynamics()->setExtVel(newExtVel);
1953 if (state()->isGroundStart() && !state()->isJumpPadMushroomTrigger()) {
1959void KartMove::cancelJumpPad() {
1961 state()->setJumpPad(
false);
1965void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
1966 if (m_boost.
activate(type, frames)) {
1967 state()->setBoost(
true);
1972void KartMove::applyStartBoost(s16 frames) {
1973 activateBoost(KartBoost::Type::AllMt, frames);
1977void KartMove::activateMushroom() {
1978 constexpr s16 MUSHROOM_DURATION = 90;
1980 if (state()->isBeforeRespawn() || state()->isInAction()) {
1984 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
1987 state()->setMushroomBoost(
true);
1992void KartMove::activateZipperBoost() {
1993 constexpr s16 BASE_DURATION = 50;
1994 constexpr s16 TRICK_DURATION = 100;
1996 if (state()->isBeforeRespawn() || state()->isInAction()) {
2000 s16 boostDuration = state()->isZipperTrick() ? TRICK_DURATION : BASE_DURATION;
2001 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
2004 m_zipperBoostTimer = 0;
2005 m_zipperBoostMax = boostDuration;
2006 state()->setZipperBoost(
true);
2018 state()->setBoostOffroadInvincibility(
true);
2025 if (!state()->isBoostOffroadInvincibility()) {
2033 state()->setBoostOffroadInvincibility(
false);
2039 if (!state()->isMushroomBoost()) {
2047 state()->setMushroomBoost(
false);
2051void KartMove::calcZipperBoost() {
2052 if (!state()->isZipperBoost()) {
2056 state()->setAccelerate(
true);
2058 if (!state()->isOverZipper() && ++m_zipperBoostTimer >= m_zipperBoostMax) {
2059 m_zipperBoostTimer = 0;
2060 state()->setZipperBoost(
false);
2063 if (m_zipperBoostTimer < 10) {
2066 dynamics()->setAngVel0(angVel);
2071void KartMove::landTrick() {
2072 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2077 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2083 if (state()->isBeforeRespawn() || state()->isInAction()) {
2089 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2091 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32>(m_jump->variant())];
2094 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2098void KartMove::activateCrush(
u16 timer) {
2099 state()->setCrushed(
true);
2105void KartMove::calcCrushed() {
2106 if (!state()->isCrushed()) {
2111 state()->setCrushed(
false);
2117void KartMove::calcScale() {
2123void KartMove::enterCannon() {
2125 physics()->clearDecayingRot();
2126 m_boost.resetActive();
2127 state()->setBoost(
false);
2133 clearOffroadInvincibility();
2135 dynamics()->reset();
2138 state()->setHop(
false);
2139 state()->setInCannon(
true);
2140 state()->setSkipWheelCalc(
true);
2141 state()->setCannonStart(
false);
2143 const auto [cannonPos, cannonDir] = getCannonPosRot();
2144 m_cannonEntryPos = pos();
2145 m_cannonEntryOfs = cannonPos - pos();
2146 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2148 m_dir = m_cannonEntryOfs;
2149 m_vel1Dir = m_cannonEntryOfs;
2150 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2151 m_cannonProgress.setZero();
2155void KartMove::calcCannon() {
2156 auto [cannonPos, cannonDir] = getCannonPosRot();
2157 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2159 f32 forwardLength = forward.
normalise();
2173 if (forwardLength < 30.0f || local94.
dot(forwardXZ) <= 0.0f) {
2178 const auto *cannonPoint =
2179 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2180 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2181 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2182 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2183 f32 newSpeed = cannonParams.speed;
2184 if (forwardLength < cannonParams.decelFactor) {
2185 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2187 newSpeed = cannonParams.endDecel;
2188 if (newSpeed <= 0.0f) {
2192 newSpeed += factor * (cannonParams.speed - newSpeed);
2193 if (cannonParams.endDecel > 0.0f) {
2198 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2201 if (cannonParams.height > 0.0f) {
2202 f32 fVar9 = EGG::Mathf::SinFIdx(
2203 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2204 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2207 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2208 m_dir = m_cannonEntryOfs;
2209 m_vel1Dir = m_cannonEntryOfs;
2211 calcRotCannon(forward);
2213 dynamics()->setExtVel(EGG::Vector3f::zero);
2221 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2227 local80 *= dynamics()->fullRot();
2232 dynamics()->setFullRot(newRot);
2233 dynamics()->setMainRot(newRot);
2237void KartMove::exitCannon() {
2238 if (!state()->isInCannon()) {
2242 state()->setInCannon(
false);
2243 state()->setSkipWheelCalc(
false);
2244 state()->setAfterCannon(
true);
2245 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2249void KartMove::triggerRespawn() {
2251 state()->setTriggerRespawn(
true);
2255KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2258KartMoveBike::~KartMoveBike() =
default;
2263 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2264 constexpr u16 WHEELIE_COOLDOWN = 20;
2266 state()->setWheelie(
true);
2271 m_autoHardStickXFrames = 0;
2278 state()->setWheelie(
false);
2280 m_autoHardStickXFrames = 0;
2284void KartMoveBike::createSubsystems() {
2296 const auto *raceManager = System::RaceManager::Instance();
2298 if (!state()->isChargingSsmt()) {
2299 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2300 EGG::Mathf::abs(
m_speed) < 5.0f) {
2312 f32 stickX = state()->stickX();
2313 f32 extVelXFactor = 0.0f;
2317 if (state()->isBeforeRespawn() || state()->isInAction() || state()->isWheelie() ||
2318 state()->isOverZipper() || state()->isRejectRoadTrigger() ||
2319 state()->isAirtimeOver20() || state()->isSoftWallDrift() ||
2320 state()->isSomethingWallCollision() || state()->isHWG() || state()->isCannonStart() ||
2321 state()->isInCannon()) {
2323 }
else if (!state()->isDrifting()) {
2324 if (stickX <= 0.2f) {
2325 if (stickX >= -0.2f) {
2340 leanRotMin = -leanRotMax;
2344 if (stickX == 0.0f) {
2350 }
else if (stickX == 0.0f) {
2358 bool capped =
false;
2370 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2373 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2377 dynamics()->setAngVel2(dynamics()->angVel2() +
2378 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2385 if (!state()->isRejectRoad() && !state()->isHalfPipeRamp() && !state()->isOverZipper()) {
2387 scalar = std::min(1.0f, scalar);
2388 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2390 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2395 dynamics()->setTop_(top);
2403 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2404 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2405 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2408 KartMove::setTurnParams();
2410 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2412 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2416 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2426void KartMoveBike::init(
bool b1,
bool b2) {
2427 KartMove::init(b1, b2);
2436 m_autoHardStickXFrames = 0;
2440void KartMoveBike::clear() {
2448 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2449 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2454 if (state()->isWheelie()) {
2455 bool cancelAutoWheelie =
false;
2457 if (!state()->isAutoDrift() ||
2458 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2459 m_autoHardStickXFrames = 0;
2461 if (++m_autoHardStickXFrames > 15) {
2462 cancelAutoWheelie =
true;
2474 dynamics()->setAngVel0(angVel0);
2484 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2489 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2490 dynamics()->setAngVel2(angVel2);
2495 state()->setWheelieRot(
true);
2497 state()->setWheelieRot(
false);
2507 if (state()->isAutoDrift()) {
2524 constexpr u16 MAX_MT_CHARGE = 270;
2525 constexpr u16 BASE_MT_CHARGE = 2;
2526 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2527 constexpr u16 EXTRA_MT_CHARGE = 3;
2529 if (m_driftState != DriftState::ChargingMt) {
2535 f32 stickX = state()->stickX();
2536 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2537 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2546 m_driftState = DriftState::ChargedMt;
2551void KartMoveBike::initOob() {
2552 KartMove::initOob();
2559 constexpr s16 COOLDOWN_FRAMES = 20;
2560 bool dpadUp = inputs()->currentState().trickUp();
2562 if (!state()->isWheelie()) {
2563 if (dpadUp && state()->isTouchingGround()) {
2564 if (state()->isDriftManual() || state()->isWallCollision() ||
2565 state()->isWall3Collision() || state()->isHop() || state()->isDriftAuto() ||
2566 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.