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"
14#include "game/field/ObjectDirector.hh"
16#include "game/item/ItemDirector.hh"
17#include "game/item/KartItem.hh"
19#include "game/system/CourseMap.hh"
20#include "game/system/RaceManager.hh"
21#include "game/system/map/MapdataCannonPoint.hh"
22#include "game/system/map/MapdataJugemPoint.hh"
24#include <egg/math/Math.hh>
25#include <egg/math/Quat.hh>
36static constexpr std::array<CannonParameter, 3> CANNON_PARAMETERS = {{
37 {500.0f, 0.0f, 6000.0f, -1.0f},
38 {500.0f, 5000.0f, 6000.0f, -1.0f},
39 {120.0f, 2000.0f, 1000.0f, 45.0f},
43KartMove::KartMove() : m_smoothedUp(
EGG::Vector3f::ey), m_scale(1.0f, 1.0f, 1.0f) {
46 m_shockSpeedMultiplier = 1.0f;
48 m_padType.makeAllZero();
49 m_flags.makeAllZero();
54KartMove::~KartMove() {
61void KartMove::createSubsystems(
const KartParam::Stats &stats) {
62 m_jump =
new KartJump(
this);
74 auto &status = KartObjectProxy::status();
76 if (status.
onBit(eStatus::InAction, eStatus::CannonStart, eStatus::InCannon,
87 if (status.
onBit(eStatus::JumpPadMushroomCollision)) {
97 if (state()->isDrifting()) {
108 if (!state()->isDrifting()) {
118void KartMove::setTurnParams() {
119 static constexpr std::array<DriftingParameters, 3> DRIFTING_PARAMS_ARRAY = {{
120 {10.0f, 0.5f, 0.5f, 1.0f},
121 {10.0f, 0.5f, 0.5f, 0.2f},
122 {10.0f, 0.22f, 0.5f, 0.2f},
129 m_landingDir = m_dir;
131 m_driftingParams = &DRIFTING_PARAMS_ARRAY[
static_cast<u32
>(param()->stats().driftType)];
136void KartMove::init(
bool b1,
bool b2) {
144 m_up = EGG::Vector3f::ey;
146 m_vel1Dir = EGG::Vector3f::ez;
148 m_dir = EGG::Vector3f::ez;
149 m_landingDir = EGG::Vector3f::ez;
150 m_dirDiff = EGG::Vector3f::zero;
151 m_hasLandingDir =
false;
153 m_landingAngle = 0.0f;
171 m_standStillBoostRot = 0.0f;
172 m_driftState = DriftState::NotDrifting;
177 m_zipperBoostTimer = 0;
178 m_zipperBoostMax = 0;
184 m_nonZipperAirtime = 0;
191 m_hitboxScale = 1.0f;
192 m_shockSpeedMultiplier = 1.0f;
200 m_jumpPadMaxSpeed = 0.0f;
201 m_jumpPadBoostMultiplier = 0.0f;
202 m_jumpPadProperties =
nullptr;
204 m_autoDriftAngle = 0.0f;
205 m_autoDriftStartFrameCounter = 0;
207 m_cannonEntryOfsLength = 0.0f;
208 m_cannonEntryPos.setZero();
209 m_cannonEntryOfs.setZero();
210 m_cannonOrthog.setZero();
211 m_cannonProgress.setZero();
230void KartMove::clear() {
231 auto &status = KartObjectProxy::status();
242 clearOffroadInvincibility();
251 EGG::Quatf quaternion = EGG::Quatf::FromRPY(angles * DEG2RAD);
254 Field::KCLTypeMask kcl_flags = KCL_NONE;
256 bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos,
257 EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0);
260 newPos = newPos + info.tangentOff + (info.floorNrm * -100.0f);
261 newPos += info.floorNrm * bsp().initialYPos;
267 sub()->initPhysicsValues();
269 physics()->setPos(pos());
270 physics()->setVelocity(dynamics()->velocity());
275 dynamics()->setTop(
m_up);
277 for (
u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) {
278 suspension(tireIdx)->setInitialState();
288 auto &status = KartObjectProxy::status();
290 if (status.
onBit(eStatus::InRespawn)) {
295 dynamics()->resetInternalVelocity();
326 if (status.
onBit(eStatus::InCannon)) {
337void KartMove::calcRespawnStart() {
338 constexpr float RESPAWN_HEIGHT = 700.0f;
340 const auto *jugemPoint = System::RaceManager::Instance()->jugemPoint();
345 respawnPos.y += RESPAWN_HEIGHT;
350 Item::ItemDirector::Instance()->kartItem(0).clear();
352 status().
resetBit(eStatus::TriggerRespawn).setBit(eStatus::InRespawn);
356void KartMove::calcInRespawn() {
357 constexpr f32 LAKITU_VELOCITY = 1.5f;
358 constexpr u16 RESPAWN_DURATION = 110;
360 auto &status = KartObjectProxy::status();
362 if (status.
offBit(eStatus::InRespawn)) {
367 newPos.y -= LAKITU_VELOCITY;
368 dynamics()->setPos(newPos);
369 dynamics()->setNoGravity(
true);
375 dynamics()->setNoGravity(
false);
380void KartMove::calcRespawnBoost() {
381 constexpr s16 RESPAWN_BOOST_DURATION = 30;
382 constexpr s16 RESPAWN_BOOST_INPUT_LENIENCY = 4;
384 auto &status = KartObjectProxy::status();
386 if (status.
onBit(eStatus::AfterRespawn)) {
390 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
391 m_respawnTimer = RESPAWN_BOOST_DURATION;
397 status.
resetBit(eStatus::AfterRespawn);
411 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
412 m_respawnTimer = RESPAWN_BOOST_DURATION;
424 m_respawnTimer = std::max(0, m_respawnTimer - 1);
428void KartMove::calcTop() {
429 f32 stabilizationFactor = 0.1f;
430 m_hasLandingDir =
false;
432 auto &status = KartObjectProxy::status();
438 m_dirDiff = m_landingDir.
proj(m_landingDir);
439 m_hasLandingDir =
true;
445 inputTop.
dot(m_dir) > 0.0f &&
m_speed > 50.0f &&
446 collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::NotTrickable)) {
458 f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(inputTop.
dot(componentZAxis())));
459 scalar = std::min(0.8f, std::max(0.3f, topDotZ));
467 if (bodyDotFront < -0.1f) {
468 stabilizationFactor += std::min(0.2f, EGG::Mathf::abs(bodyDotFront) * 0.5f);
471 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
472 stabilizationFactor = 0.4f;
479 dynamics()->setStabilizationFactor(stabilizationFactor);
482 m_flags.
changeBit(collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::Trickable),
489 auto &status = KartObjectProxy::status();
502 if (
m_up.y <= 0.99f) {
503 m_up += (EGG::Vector3f::ey -
m_up) * 0.03f;
506 m_up = EGG::Vector3f::ey;
514 const auto *raceMgr = System::RaceManager::Instance();
515 if (!raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
519 if (m_padType.
onBit(ePadType::BoostPanel)) {
520 tryStartBoostPanel();
523 if (m_padType.
onBit(ePadType::BoostRamp)) {
527 if (m_padType.
onBit(ePadType::JumpPad)) {
535void KartMove::calcDirs() {
539 m_flags.
setBit(eFlags::LaunchBoost);
540 auto &status = KartObjectProxy::status();
544 !m_jump->isBoostRampEnabled()) &&
545 status.
offBit(eStatus::JumpPad) && state()->airtime() <= 5) ||
546 status.
onBit(eStatus::JumpPadMushroomCollision,
547 eStatus::NoSparkInvisibleWall))) {
560 if (dirDiff.
squaredLength() <= std::numeric_limits<f32>::epsilon()) {
571 if (origDirCross.
dot(newDirCross) < 0.0f) {
578 m_flags.
resetBit(eFlags::LaunchBoost);
587 if (m_hasLandingDir) {
588 f32 dot = m_dir.dot(m_landingDir);
590 f32 crossDot = cross.
length();
591 f32 angle = EGG::Mathf::atan2(crossDot, dot);
592 angle = EGG::Mathf::abs(angle);
599 m_landingAngle += (angle * RAD2DEG) * fVar4;
602 if (m_landingAngle <= 0.0f) {
603 if (m_landingAngle < 0.0f) {
604 m_landingAngle = std::min(0.0f, m_landingAngle + 2.0f);
607 m_landingAngle = std::max(0.0f, m_landingAngle - 2.0f);
612void KartMove::calcStickyRoad() {
613 constexpr f32 STICKY_RADIUS = 200.0f;
614 constexpr Field::KCLTypeMask STICKY_MASK =
617 auto &status = KartObjectProxy::status();
625 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::Trickable)) ||
626 EGG::Mathf::abs(
m_speed) <= 20.0f) {
634 colInfo.bbox.setZero();
635 Field::KCLTypeMask kcl_flags = KCL_NONE;
636 bool stickyRoad =
false;
638 for (
size_t i = 0; i < 3; ++i) {
640 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
641 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
642 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
643 dynamics()->setMovingObjVel(dynamics()->movingObjVel().rej(colInfo.floorNrm));
644 dynamics()->setMovingRoadVel(dynamics()->movingRoadVel().rej(colInfo.floorNrm));
646 if (status.
onBit(eStatus::MovingWaterStickyRoad)) {
647 m_up = colInfo.floorNrm;
656 pos += -STICKY_RADIUS * componentYAxis();
668 auto &status = KartObjectProxy::status();
700void KartMove::calcRisingWater() {
701 auto *objDir = Field::ObjectDirector::Instance();
702 auto *psea = objDir->psea();
707 f32 pos = wheelPos(0).y;
708 u16 count = tireCount();
709 for (
u16 wheelIdx = 0; wheelIdx < count; ++wheelIdx) {
710 f32 tmp = wheelEdgePos(wheelIdx).y;
711 if (wheelIdx == 0 || tmp < pos) {
716 if (objDir->risingWaterKillPlaneHeight() > pos) {
717 collide()->activateOob(
true,
nullptr,
false,
false);
720 f32 dist = -objDir->distAboveRisingWater(pos);
722 f32 speedScale = std::min(1.0f, dist / 100.0f);
728void KartMove::calcBoost() {
729 auto &status = KartObjectProxy::status();
731 if (m_boost.
calc()) {
741void KartMove::calcRampBoost() {
742 auto &status = KartObjectProxy::status();
744 if (status.
offBit(eStatus::RampBoost)) {
749 if (--m_rampBoost < 1) {
751 status.
resetBit(eStatus::RampBoost);
759 auto &status = KartObjectProxy::status();
776 constexpr s16 MAX_SSMT_CHARGE = 75;
777 constexpr s16 SSMT_BOOST_FRAMES = 30;
778 constexpr s16 LEEWAY_FRAMES = 1;
779 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
783 auto &status = KartObjectProxy::status();
809 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
816 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
833 auto &status = KartObjectProxy::status();
878 m_driftState = DriftState::NotDrifting;
890 m_driftState = DriftState::NotDrifting;
896 m_autoDriftAngle = 0.0f;
898 m_autoDriftStartFrameCounter = 0;
902void KartMove::clearJumpPad() {
904 status().
resetBit(eStatus::JumpPad);
908void KartMove::clearRampBoost() {
910 status().
resetBit(eStatus::RampBoost);
914void KartMove::clearZipperBoost() {
915 m_zipperBoostTimer = 0;
920void KartMove::clearBoost() {
921 m_boost.resetActive();
926void KartMove::clearSsmt() {
934void KartMove::clearOffroadInvincibility() {
939void KartMove::clearRejectRoad() {
947 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
949 auto &status = KartObjectProxy::status();
957 EGG::Mathf::abs(state()->stickX()) > 0.85f) {
958 m_autoDriftStartFrameCounter =
959 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
961 m_autoDriftStartFrameCounter = 0;
964 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
965 status.
setBit(eStatus::DriftAuto);
968 if (state()->stickX() < 0.0f) {
978 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
979 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
981 status.
resetBit(eStatus::DriftAuto);
984 if (m_autoDriftAngle > 0.0f) {
986 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
989 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
995 physics()->composeExtraRot(angleAxis);
1003 auto &status = KartObjectProxy::status();
1009 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
1010 status.
offBit(eStatus::JumpPadMushroomCollision) &&
1012 m_flags.
onBit(eFlags::LaunchBoost)) {
1017 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
1018 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
1020 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
1050 if (action()->flags().offBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
1051 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
1084 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1086 const auto &stats = param()->stats();
1087 auto &status = KartObjectProxy::status();
1089 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1090 f32 driftAngle = 0.0f;
1118 m_driftState = DriftState::ChargingMt;
1126 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1128 auto &status = KartObjectProxy::status();
1131 m_driftState = DriftState::NotDrifting;
1137 if (m_driftState == DriftState::ChargedSmt) {
1138 mtLength *= SMT_LENGTH_FACTOR;
1142 activateBoost(KartBoost::Type::AllMt, mtLength);
1145 m_driftState = DriftState::NotDrifting;
1152 if (state()->airtime() > 5) {
1156 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1159 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1160 if (angle > targetAngle) {
1162 }
else if (angle < targetAngle) {
1168 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1169 if (targetAngle > angle) {
1171 }
else if (targetAngle < angle) {
1186 auto &status = KartObjectProxy::status();
1187 bool drifting = state()->isDrifting() && status.
offBit(eStatus::JumpPadMushroomCollision);
1189 const auto &stats = param()->stats();
1192 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1194 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1197 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1202 bool forwards =
true;
1216 bool noTurn =
false;
1218 EGG::Mathf::abs(
m_speed) < 1.0f) {
1224 if (forwards && !noTurn) {
1228 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1231 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1244 f32 stickX = EGG::Mathf::abs(state()->stickX());
1245 if (autoDrift && stickX > 0.3f) {
1246 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1247 stickX = drifting ? 0.2f : 0.5f;
1254 if (status.
onBit(eStatus::RampBoost) && m_jump->isBoostRampEnabled()) {
1256 }
else if (status.
offBit(eStatus::JumpPadMushroomCollision)) {
1257 u32 airtime = state()->airtime();
1258 if (airtime >= 70) {
1260 }
else if (airtime >= 30) {
1261 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1267 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1268 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1270 if (angle > 60.0f) {
1271 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1282 const auto *raceMgr = System::RaceManager::Instance();
1283 auto &status = KartObjectProxy::status();
1285 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1286 f32 speedFix = dynamics()->speedFix();
1287 if (status.
onBit(eStatus::InAction) ||
1289 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1301 if (status.
onBit(eStatus::MovingWaterVertical) ||
1302 (status.
onBit(eStatus::MovingWaterDecaySpeed) &&
1305 m_speed *= collide()->pullPath().roadSpeedDecay();
1311 if (status.
onBit(eStatus::InAction)) {
1312 action()->calcVehicleSpeed();
1320 if (status.
onBit(eStatus::RampBoost) && state()->airtime() < 4) {
1329 if (state()->airtime() > 5) {
1337 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost)) {
1344 eStatus::SomethingWallCollision)) {
1358 const auto &stats = param()->stats();
1361 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1377 initialVel = std::min(initialVel * 2.0f, 2.0f);
1379 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1393 std::span<const f32> as;
1394 std::span<const f32> ts;
1395 if (state()->isDrifting()) {
1396 as = param()->stats().accelerationDriftA;
1397 ts = param()->stats().accelerationDriftT;
1399 as = param()->stats().accelerationStandardA;
1400 ts = param()->stats().accelerationStandardT;
1404 f32 acceleration = 0.0f;
1406 for (; i < ts.size(); ++i) {
1407 if (ratio < ts[i]) {
1408 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1415 return i < ts.size() ? acceleration : as.back();
1422 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1423 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1424 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1425 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1426 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1429 auto &status = KartObjectProxy::status();
1431 if (status.
offBit(eStatus::InAction)) {
1465 f32 speedLimit = status.
onBit(eStatus::JumpPad) ? m_jumpPadMaxSpeed :
m_baseSpeed;
1466 const f32 boostMultiplier = m_boost.multiplier();
1467 const f32 boostSpdLimit = m_boost.speedLimit();
1468 m_jumpPadBoostMultiplier = boostMultiplier;
1470 f32 scaleMultiplier = m_shockSpeedMultiplier;
1471 if (status.
onBit(eStatus::Crushed)) {
1472 scaleMultiplier *= 0.7f;
1476 speedLimit *= status.
onBit(eStatus::JumpPadFixedSpeed) ?
1482 f32 boostSpeed = ignoreScale ? 1.0f : scaleMultiplier;
1485 if (status.
offBit(eStatus::JumpPad) && boostSpeed > 0.0f && boostSpeed > speedLimit) {
1486 speedLimit = boostSpeed;
1491 if (status.
onBit(eStatus::RampBoost)) {
1492 speedLimit = std::max(speedLimit, 100.0f);
1497 f32 local_c8 = 1.0f;
1512 if (status.
onBit(eStatus::JumpPad)) {
1523 crossVec = -crossVec;
1526 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1527 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1528 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1530 rotationScalar = ROTATION_SCALAR_MIDAIR;
1537 const auto *raceMgr = System::RaceManager::Instance();
1540 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1549 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1551 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1558 DrivingDirection::Backwards;
1574 auto &status = KartObjectProxy::status();
1587 if (wallNrm.y > 0.0f) {
1595 f1 = std::max(0.0f, dot + 1.0f);
1609 auto &status = KartObjectProxy::status();
1616 if (status.
offBit(eStatus::InAction)) {
1619 m_landingDir = m_dir;
1626 if (speedDiff > 30.0f) {
1629 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1633 speedDiff = std::min(60.0f, speedDiff);
1636 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1641 proj = EGG::Vector3f::zero;
1642 rej = EGG::Vector3f::zero;
1645 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1646 proj = EGG::Vector3f::zero;
1651 f32 bumpDeviation = 0.0f;
1653 bumpDeviation = param()->stats().bumpDeviationLevel;
1658 dynamics()->addForce(colData.wallNrm * 15.0f);
1659 collide()->startFloorMomentRate();
1663 dynamics()->addForce(colData.wallNrm * 15.0f);
1664 collide()->startFloorMomentRate();
1676 auto &status = KartObjectProxy::status();
1679 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1680 next = 0.015f * -state()->startBoostCharge();
1682 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost, eStatus::SoftWallDrift)) {
1684 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1687 next = (scalar * 0.15f) * 0.25f;
1692 next = (scalar * 0.15f) * 0.08f;
1697 constexpr s16 MAX_SSMT_CHARGE = 75;
1698 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1703 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1705 m_standStillBoostRot += scalar * (next * m_invScale - m_standStillBoostRot);
1713 constexpr f32 DIVE_LIMIT = 0.8f;
1717 auto &status = KartObjectProxy::status();
1724 f32 stickY = state()->stickY();
1727 stickY = std::min(1.0f, stickY + 0.4f);
1730 u32 airtime = state()->airtime();
1733 if (EGG::Mathf::abs(stickY) < 0.1f) {
1737 stickY *= (airtime / 50.0f);
1744 dynamics()->setAngVel2(angVel2);
1746 if (state()->airtime() < 50) {
1752 f32 upDotTop =
m_up.
dot(topRotated);
1754 f32 crossNorm = upCrossTop.
length();
1755 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1757 f32 fVar1 = angle * RAD2DEG - 20.0f;
1758 if (fVar1 <= 0.0f) {
1762 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1763 if (forwardRotated.y > 0.0f) {
1764 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1766 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1774 auto &status = KartObjectProxy::status();
1786void KartMove::calcHopPhysics() {
1797void KartMove::calcRejectRoad() {
1798 m_reject.calcRejectRoad();
1802bool KartMove::calcZipperCollision(f32 radius, f32 scale,
EGG::Vector3f &pos,
1804 Field::KCLTypeMask *maskOut, Field::KCLTypeMask flags)
const {
1806 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1808 auto *colDir = Field::CollisionDirector::Instance();
1809 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1814 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.
dot(to)));
1815 f32 acos = EGG::Mathf::acos(dotNorm);
1816 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1820void KartMove::applyForce(f32 force,
const EGG::Vector3f &hitDir,
bool stop) {
1821 constexpr s16 BUMP_COOLDOWN = 5;
1828 collide()->startFloorMomentRate();
1840 f32 tiltMagnitude = 0.0f;
1841 auto &status = KartObjectProxy::status();
1843 if (status.
offBit(eStatus::InAction, eStatus::SoftWallDrift) &&
1848 f32 magnitude = tiltMagnitude;
1850 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1851 magnitude = frontSpeed.
length();
1853 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1854 magnitude = -magnitude;
1857 tiltMagnitude = -1.0f;
1858 if (-1.0f <= magnitude) {
1859 tiltMagnitude = std::min(1.0f, magnitude);
1865 dynamics()->setAngVel0(angVel0);
1869 m_invScale * (tiltMagnitude * param()->stats().tilt * EGG::Mathf::abs(
m_weightedTurn));
1874 angVel0.x += m_standStillBoostRot;
1876 dynamics()->setAngVel0(angVel0);
1880 dynamics()->setAngVel2(angVel2);
1890 constexpr u16 MAX_MT_CHARGE = 270;
1891 constexpr u16 MAX_SMT_CHARGE = 300;
1892 constexpr u16 BASE_MT_CHARGE = 2;
1893 constexpr u16 BASE_SMT_CHARGE = 2;
1894 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1895 constexpr u16 EXTRA_MT_CHARGE = 3;
1897 if (m_driftState == DriftState::ChargedSmt) {
1901 f32 stickX = state()->stickX();
1903 if (m_driftState == DriftState::ChargingMt) {
1906 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1907 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1916 m_driftState = DriftState::ChargingSmt;
1920 if (m_driftState != DriftState::ChargingSmt) {
1926 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1927 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1936 m_driftState = DriftState::ChargedSmt;
1941void KartMove::initOob() {
1947 clearOffroadInvincibility();
1959 m_driftState = DriftState::NotDrifting;
1971 dynamics()->setExtVel(extVel);
1974 totalForce.y = 0.0f;
1975 dynamics()->setTotalForce(totalForce);
1979void KartMove::tryStartBoostPanel() {
1980 constexpr s16 BOOST_PANEL_DURATION = 60;
1986 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
1994 constexpr s16 BOOST_RAMP_DURATION = 60;
1996 auto &status = KartObjectProxy::status();
2002 status.
setBit(eStatus::RampBoost);
2003 m_rampBoost = BOOST_RAMP_DURATION;
2012 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
2013 {50.0f, 50.0f, 35.0f},
2014 {50.0f, 50.0f, 47.0f},
2015 {59.0f, 59.0f, 30.0f},
2016 {73.0f, 73.0f, 45.0f},
2017 {73.0f, 73.0f, 53.0f},
2018 {56.0f, 56.0f, 50.0f},
2019 {55.0f, 55.0f, 35.0f},
2020 {56.0f, 56.0f, 50.0f},
2023 auto &status = KartObjectProxy::status();
2029 status.
setBit(eStatus::JumpPad);
2030 s32 jumpPadVariant = state()->jumpPadVariant();
2031 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
2033 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
2034 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
2037 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
2038 {100.0f, 100.0f, 70.0f},
2039 {100.0f, 100.0f, 65.0f},
2041 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
2044 status.
setBit(eStatus::JumpPadFixedSpeed);
2047 if (jumpPadVariant == 4) {
2048 status.
setBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadMushroomVelYInc,
2049 eStatus::JumpPadMushroomCollision);
2054 extVel.y = m_jumpPadProperties->velY;
2055 totalForce.y = 0.0f;
2057 dynamics()->setExtVel(extVel);
2058 dynamics()->setTotalForce(totalForce);
2060 if (jumpPadVariant != 3) {
2067 status.
setBit(eStatus::JumpPadDisableYsusForce);
2072 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
2077void KartMove::tryEndJumpPad() {
2078 auto &status = KartObjectProxy::status();
2079 if (status.
onBit(eStatus::JumpPadMushroomTrigger)) {
2081 status.
resetBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadFixedSpeed,
2082 eStatus::JumpPadMushroomVelYInc);
2085 if (status.
onBit(eStatus::JumpPadMushroomVelYInc)) {
2087 newExtVel.y += 20.0f;
2088 if (m_jumpPadProperties->velY < newExtVel.y) {
2089 newExtVel.y = m_jumpPadProperties->velY;
2090 status.
resetBit(eStatus::JumpPadMushroomVelYInc);
2092 dynamics()->setExtVel(newExtVel);
2102void KartMove::cancelJumpPad() {
2104 status().
resetBit(eStatus::JumpPad);
2108void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
2109 if (m_boost.
activate(type, frames)) {
2115void KartMove::applyStartBoost(s16 frames) {
2116 activateBoost(KartBoost::Type::AllMt, frames);
2120void KartMove::activateMushroom() {
2121 constexpr s16 MUSHROOM_DURATION = 90;
2123 auto &status = KartObjectProxy::status();
2129 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
2137void KartMove::activateZipperBoost() {
2138 constexpr s16 BASE_DURATION = 50;
2139 constexpr s16 TRICK_DURATION = 100;
2141 auto &status = KartObjectProxy::status();
2148 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
2151 m_zipperBoostTimer = 0;
2152 m_zipperBoostMax = boostDuration;
2172 auto &status = KartObjectProxy::status();
2188 auto &status = KartObjectProxy::status();
2202void KartMove::calcZipperBoost() {
2203 auto &status = KartObjectProxy::status();
2212 m_zipperBoostTimer = 0;
2216 if (m_zipperBoostTimer < 10) {
2219 dynamics()->setAngVel0(angVel);
2224void KartMove::landTrick() {
2225 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2230 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2242 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2244 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2247 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2251void KartMove::activateCrush(
u16 timer) {
2252 status().
setBit(eStatus::Crushed);
2258void KartMove::calcCrushed() {
2259 if (status().offBit(eStatus::Crushed)) {
2264 status().
resetBit(eStatus::Crushed);
2270void KartMove::calcScale() {
2278 if (sizeScale.z != 1.0f) {
2286void KartMove::applyShrink(
u16 timer) {
2287 auto &status = state()->status();
2289 if (status.
onBit(eStatus::InRespawn, eStatus::AfterRespawn, eStatus::CannonStart)) {
2293 action()->
start(Action::UNK_15);
2294 Item::ItemDirector::Instance()->kartItem(0).clear();
2295 status.
setBit(eStatus::Shocked);
2297 if (timer > m_shockTimer) {
2298 m_shockTimer = timer;
2304void KartMove::calcShock() {
2305 auto &status = state()->status();
2307 if (status.
onBit(eStatus::Shocked)) {
2308 if (--m_shockTimer == 0) {
2309 deactivateShock(
false);
2312 m_shockSpeedMultiplier = std::max(0.7f, m_shockSpeedMultiplier - 0.03f);
2314 m_shockSpeedMultiplier = std::min(1.0f, m_shockSpeedMultiplier + 0.05f);
2319void KartMove::deactivateShock(
bool resetSpeed) {
2320 status().
resetBit(eStatus::Shocked);
2325 m_shockSpeedMultiplier = 1.0f;
2330void KartMove::enterCannon() {
2332 physics()->clearDecayingRot();
2333 m_boost.resetActive();
2335 auto &status = KartObjectProxy::status();
2343 clearOffroadInvincibility();
2345 dynamics()->reset();
2350 .setBit(eStatus::InCannon, eStatus::SkipWheelCalc);
2352 const auto [cannonPos, cannonDir] = getCannonPosRot();
2353 m_cannonEntryPos = pos();
2354 m_cannonEntryOfs = cannonPos - pos();
2355 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2357 m_dir = m_cannonEntryOfs;
2358 m_vel1Dir = m_cannonEntryOfs;
2359 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2360 m_cannonProgress.setZero();
2364void KartMove::calcCannon() {
2365 auto [cannonPos, cannonDir] = getCannonPosRot();
2366 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2368 f32 forwardLength = forward.
normalise();
2382 if (forwardLength < 30.0f || local94.
dot(forwardXZ) <= 0.0f) {
2387 const auto *cannonPoint =
2388 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2389 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2390 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2391 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2392 f32 newSpeed = cannonParams.speed;
2393 if (forwardLength < cannonParams.decelFactor) {
2394 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2396 newSpeed = cannonParams.endDecel;
2397 if (newSpeed <= 0.0f) {
2401 newSpeed += factor * (cannonParams.speed - newSpeed);
2402 if (cannonParams.endDecel > 0.0f) {
2407 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2410 if (cannonParams.height > 0.0f) {
2411 f32 fVar9 = EGG::Mathf::SinFIdx(
2412 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2413 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2416 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2417 m_dir = m_cannonEntryOfs;
2418 m_vel1Dir = m_cannonEntryOfs;
2420 calcRotCannon(forward);
2422 dynamics()->setExtVel(EGG::Vector3f::zero);
2430 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2436 local80 *= dynamics()->fullRot();
2441 dynamics()->setFullRot(newRot);
2442 dynamics()->setMainRot(newRot);
2446void KartMove::exitCannon() {
2447 auto &status = KartObjectProxy::status();
2449 if (status.
offBit(eStatus::InCannon)) {
2453 status.
resetBit(eStatus::InCannon, eStatus::SkipWheelCalc).setBit(eStatus::AfterCannon);
2454 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2458void KartMove::triggerRespawn() {
2460 status().
setBit(eStatus::TriggerRespawn);
2464KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2467KartMoveBike::~KartMoveBike() =
default;
2472 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2473 constexpr u16 WHEELIE_COOLDOWN = 20;
2480 m_autoHardStickXFrames = 0;
2489 m_autoHardStickXFrames = 0;
2505 const auto *raceManager = System::RaceManager::Instance();
2507 auto &status = KartObjectProxy::status();
2510 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2511 EGG::Mathf::abs(
m_speed) < 5.0f) {
2523 f32 stickX = state()->stickX();
2524 f32 extVelXFactor = 0.0f;
2530 eStatus::SoftWallDrift, eStatus::SomethingWallCollision,
eStatus::HWG,
2531 eStatus::CannonStart, eStatus::InCannon)) {
2533 }
else if (!state()->isDrifting()) {
2534 if (stickX <= 0.2f) {
2535 if (stickX >= -0.2f) {
2550 leanRotMin = -leanRotMax;
2554 if (stickX == 0.0f) {
2560 }
else if (stickX == 0.0f) {
2568 bool capped =
false;
2580 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2583 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2587 dynamics()->setAngVel2(dynamics()->angVel2() +
2588 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2597 scalar = std::min(1.0f, scalar);
2598 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2600 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2605 dynamics()->setTop_(top);
2613 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2614 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2615 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2618 KartMove::setTurnParams();
2620 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2622 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2626 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2636void KartMoveBike::init(
bool b1,
bool b2) {
2637 KartMove::init(b1, b2);
2646 m_autoHardStickXFrames = 0;
2650void KartMoveBike::clear() {
2658 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2659 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2664 auto &status = KartObjectProxy::status();
2667 bool cancelAutoWheelie =
false;
2670 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2671 m_autoHardStickXFrames = 0;
2673 if (++m_autoHardStickXFrames > 15) {
2674 cancelAutoWheelie =
true;
2686 dynamics()->setAngVel0(angVel0);
2696 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2701 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2702 dynamics()->setAngVel2(angVel2);
2707 status.
setBit(eStatus::WheelieRot);
2709 status.
resetBit(eStatus::WheelieRot);
2736 constexpr u16 MAX_MT_CHARGE = 270;
2737 constexpr u16 BASE_MT_CHARGE = 2;
2738 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2739 constexpr u16 EXTRA_MT_CHARGE = 3;
2741 if (m_driftState != DriftState::ChargingMt) {
2747 f32 stickX = state()->stickX();
2748 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2749 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2758 m_driftState = DriftState::ChargedMt;
2763void KartMoveBike::initOob() {
2764 KartMove::initOob();
2771 constexpr s16 COOLDOWN_FRAMES = 20;
2772 bool dpadUp = inputs()->currentState().trickUp();
2773 auto &status = KartObjectProxy::status();
2778 eStatus::Hop, eStatus::DriftAuto, eStatus::InAction)) {
@ 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.
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
constexpr TBitFlagExt< N, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
constexpr bool onAllBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are on.
constexpr bool offAnyBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are off.
constexpr TBitFlagExt< N, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
bool start(Action action)
Starts an action.
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 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.
bool canStartDrift() const
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.
s16 m_bumpTimer
Set when a Reaction::SmallBump collision occurs.
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.
@ Wheelie
Set while we are in a wheelie (even during the countdown).
@ HopStart
Set if m_bDriftInput was toggled on this frame.
@ RejectRoad
Collision which causes a change in the player's pos and rot.
@ DisableBackwardsAccel
Enforces a 20f delay when reversing after charging SSMT.
@ HalfPipeRamp
Set while colliding with zipper KCL.
@ Boost
Set while in a boost.
@ BoostOffroadInvincibility
Set if we should ignore offroad slowdown this frame.
@ HWG
Set when "Horizontal Wall Glitch" is active.
@ ZipperTrick
Set while tricking mid-air from a zipper.
@ SlipdriftCharge
Currently in a drift w/ automatic.
@ Hop
Set while we are in a drift hop. Clears when we land.
@ MushroomBoost
Set while we are in a mushroom boost.
@ StickyRoad
Like the rBC stairs.
@ StickLeft
Set on left stick input. Mutually exclusive to m_bStickRight.
@ RespawnKillY
Set while respawning to cap external velocity at 0.
@ VehicleBodyFloorCollision
Set if the vehicle body is colliding with the floor.
@ RejectRoadTrigger
e.g. DK Summit ending, and Maple Treeway side walls.
@ GroundStart
Set first frame landing from airtime.
@ Accelerate
Accel button is pressed.
@ StickRight
Set on right stick input. Mutually exclusive to m_bStickLeft.
@ ActionMidZipper
Set when we enter an action while mid-air from a zipper.
@ DriftInput
A "fake" button, normally set if you meet the speed requirement to hop.
@ AirtimeOver20
Set after 20 frames of airtime, resets on landing.
@ ZipperBoost
Set when boosting after landing from a zipper.
@ DriftManual
Currently in a drift w/ manual.
@ OverZipper
Set while mid-air from a zipper.
@ ChargingSSMT
Tracks whether we are charging a stand-still mini-turbo.
@ AccelerateStart
Set if m_bAccelerate was toggled on this frame.
@ Burnout
Set during a burnout on race start.
@ WallCollisionStart
Set if we have just started colliding with a wall.
@ AnyWheelCollision
Set when any wheel is touching floor collision.
@ AutoDrift
True if auto transmission, false if manual.
@ Wall3Collision
Set when colliding with wall KCL COL_TYPE_WALL_2.
@ TouchingGround
Set when any part of the vehicle is colliding with floor KCL.
@ ZipperInvisibleWall
Set when colliding with invisible wall above a zipper.
@ BeforeRespawn
Set on respawn collision, cleared on position snap.
@ WallCollision
Set if we are colliding with a wall.
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.
Various character/vehicle-related handling and speed stats.
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.
std::array< f32, 32 > kclSpeed
Speed multipliers, indexed using KCL attributes.
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.