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) {
633 colInfo.bbox.setZero();
634 Field::KCLTypeMask kcl_flags = KCL_NONE;
635 bool stickyRoad =
false;
637 for (
size_t i = 0; i < 3; ++i) {
639 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
640 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
641 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
642 dynamics()->setMovingObjVel(dynamics()->movingObjVel().rej(colInfo.floorNrm));
643 dynamics()->setMovingRoadVel(dynamics()->movingRoadVel().rej(colInfo.floorNrm));
645 if (status.
onBit(eStatus::MovingWaterStickyRoad)) {
646 m_up = colInfo.floorNrm;
655 pos += -STICKY_RADIUS * componentYAxis();
667 auto &status = KartObjectProxy::status();
699void KartMove::calcRisingWater() {
700 auto *objDir = Field::ObjectDirector::Instance();
701 auto *psea = objDir->psea();
706 f32 pos = wheelPos(0).y;
707 u16 count = tireCount();
708 for (
u16 wheelIdx = 0; wheelIdx < count; ++wheelIdx) {
709 f32 tmp = wheelEdgePos(wheelIdx).y;
710 if (wheelIdx == 0 || tmp < pos) {
715 if (objDir->risingWaterKillPlaneHeight() > pos) {
716 collide()->activateOob(
true,
nullptr,
false,
false);
719 f32 dist = -objDir->distAboveRisingWater(pos);
721 f32 speedScale = std::min(1.0f, dist / 100.0f);
727void KartMove::calcBoost() {
728 auto &status = KartObjectProxy::status();
730 if (m_boost.
calc()) {
740void KartMove::calcRampBoost() {
741 auto &status = KartObjectProxy::status();
743 if (status.
offBit(eStatus::RampBoost)) {
748 if (--m_rampBoost < 1) {
750 status.
resetBit(eStatus::RampBoost);
758 auto &status = KartObjectProxy::status();
775 constexpr s16 MAX_SSMT_CHARGE = 75;
776 constexpr s16 SSMT_BOOST_FRAMES = 30;
777 constexpr s16 LEEWAY_FRAMES = 1;
778 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
782 auto &status = KartObjectProxy::status();
808 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
815 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
832 auto &status = KartObjectProxy::status();
877 m_driftState = DriftState::NotDrifting;
889 m_driftState = DriftState::NotDrifting;
895 m_autoDriftAngle = 0.0f;
897 m_autoDriftStartFrameCounter = 0;
901void KartMove::clearJumpPad() {
903 status().
resetBit(eStatus::JumpPad);
907void KartMove::clearRampBoost() {
909 status().
resetBit(eStatus::RampBoost);
913void KartMove::clearZipperBoost() {
914 m_zipperBoostTimer = 0;
919void KartMove::clearBoost() {
920 m_boost.resetActive();
925void KartMove::clearSsmt() {
933void KartMove::clearOffroadInvincibility() {
938void KartMove::clearRejectRoad() {
946 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
948 auto &status = KartObjectProxy::status();
956 EGG::Mathf::abs(state()->stickX()) > 0.85f) {
957 m_autoDriftStartFrameCounter =
958 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
960 m_autoDriftStartFrameCounter = 0;
963 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
964 status.
setBit(eStatus::DriftAuto);
967 if (state()->stickX() < 0.0f) {
977 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
978 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
980 status.
resetBit(eStatus::DriftAuto);
983 if (m_autoDriftAngle > 0.0f) {
985 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
988 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
994 physics()->composeExtraRot(angleAxis);
1002 auto &status = KartObjectProxy::status();
1008 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
1009 status.
offBit(eStatus::JumpPadMushroomCollision) &&
1011 m_flags.
onBit(eFlags::LaunchBoost)) {
1016 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
1017 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
1019 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
1049 if (action()->flags().offBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
1050 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
1083 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1085 const auto &stats = param()->stats();
1086 auto &status = KartObjectProxy::status();
1088 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1089 f32 driftAngle = 0.0f;
1117 m_driftState = DriftState::ChargingMt;
1125 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1127 auto &status = KartObjectProxy::status();
1130 m_driftState = DriftState::NotDrifting;
1136 if (m_driftState == DriftState::ChargedSmt) {
1137 mtLength *= SMT_LENGTH_FACTOR;
1141 activateBoost(KartBoost::Type::AllMt, mtLength);
1144 m_driftState = DriftState::NotDrifting;
1151 if (state()->airtime() > 5) {
1155 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1158 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1159 if (angle > targetAngle) {
1161 }
else if (angle < targetAngle) {
1167 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1168 if (targetAngle > angle) {
1170 }
else if (targetAngle < angle) {
1185 auto &status = KartObjectProxy::status();
1186 bool drifting = state()->isDrifting() && status.
offBit(eStatus::JumpPadMushroomCollision);
1188 const auto &stats = param()->stats();
1191 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1193 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1196 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1201 bool forwards =
true;
1215 bool noTurn =
false;
1217 EGG::Mathf::abs(
m_speed) < 1.0f) {
1223 if (forwards && !noTurn) {
1227 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1230 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1243 f32 stickX = EGG::Mathf::abs(state()->stickX());
1244 if (autoDrift && stickX > 0.3f) {
1245 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1246 stickX = drifting ? 0.2f : 0.5f;
1253 if (status.
onBit(eStatus::RampBoost) && m_jump->isBoostRampEnabled()) {
1255 }
else if (status.
offBit(eStatus::JumpPadMushroomCollision)) {
1256 u32 airtime = state()->airtime();
1257 if (airtime >= 70) {
1259 }
else if (airtime >= 30) {
1260 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1266 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1267 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1269 if (angle > 60.0f) {
1270 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1281 const auto *raceMgr = System::RaceManager::Instance();
1282 auto &status = KartObjectProxy::status();
1284 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1285 f32 speedFix = dynamics()->speedFix();
1286 if (status.
onBit(eStatus::InAction) ||
1288 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1300 if (status.
onBit(eStatus::MovingWaterVertical) ||
1301 (status.
onBit(eStatus::MovingWaterDecaySpeed) &&
1304 m_speed *= collide()->pullPath().roadSpeedDecay();
1310 if (status.
onBit(eStatus::InAction)) {
1311 action()->calcVehicleSpeed();
1319 if (status.
onBit(eStatus::RampBoost) && state()->airtime() < 4) {
1328 if (state()->airtime() > 5) {
1336 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost)) {
1343 eStatus::SomethingWallCollision)) {
1357 const auto &stats = param()->stats();
1360 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1376 initialVel = std::min(initialVel * 2.0f, 2.0f);
1378 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1392 std::span<const f32> as;
1393 std::span<const f32> ts;
1394 if (state()->isDrifting()) {
1395 as = param()->stats().accelerationDriftA;
1396 ts = param()->stats().accelerationDriftT;
1398 as = param()->stats().accelerationStandardA;
1399 ts = param()->stats().accelerationStandardT;
1403 f32 acceleration = 0.0f;
1405 for (; i < ts.size(); ++i) {
1406 if (ratio < ts[i]) {
1407 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1414 return i < ts.size() ? acceleration : as.back();
1421 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1422 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1423 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1424 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1425 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1428 auto &status = KartObjectProxy::status();
1430 if (status.
offBit(eStatus::InAction)) {
1464 f32 speedLimit = status.
onBit(eStatus::JumpPad) ? m_jumpPadMaxSpeed :
m_baseSpeed;
1465 const f32 boostMultiplier = m_boost.multiplier();
1466 const f32 boostSpdLimit = m_boost.speedLimit();
1467 m_jumpPadBoostMultiplier = boostMultiplier;
1469 f32 scaleMultiplier = m_shockSpeedMultiplier;
1470 if (status.
onBit(eStatus::Crushed)) {
1471 scaleMultiplier *= 0.7f;
1475 speedLimit *= status.
onBit(eStatus::JumpPadFixedSpeed) ?
1481 f32 boostSpeed = ignoreScale ? 1.0f : scaleMultiplier;
1484 if (status.
offBit(eStatus::JumpPad) && boostSpeed > 0.0f && boostSpeed > speedLimit) {
1485 speedLimit = boostSpeed;
1490 if (status.
onBit(eStatus::RampBoost)) {
1491 speedLimit = std::max(speedLimit, 100.0f);
1496 f32 local_c8 = 1.0f;
1511 if (status.
onBit(eStatus::JumpPad)) {
1522 crossVec = -crossVec;
1525 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1526 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1527 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1529 rotationScalar = ROTATION_SCALAR_MIDAIR;
1536 const auto *raceMgr = System::RaceManager::Instance();
1539 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1548 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1550 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1557 DrivingDirection::Backwards;
1573 auto &status = KartObjectProxy::status();
1586 if (wallNrm.y > 0.0f) {
1594 f1 = std::max(0.0f, dot + 1.0f);
1608 auto &status = KartObjectProxy::status();
1615 if (status.
offBit(eStatus::InAction)) {
1618 m_landingDir = m_dir;
1625 if (speedDiff > 30.0f) {
1628 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1632 speedDiff = std::min(60.0f, speedDiff);
1635 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1640 proj = EGG::Vector3f::zero;
1641 rej = EGG::Vector3f::zero;
1644 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1645 proj = EGG::Vector3f::zero;
1650 f32 bumpDeviation = 0.0f;
1652 bumpDeviation = param()->stats().bumpDeviationLevel;
1657 dynamics()->addForce(colData.wallNrm * 15.0f);
1658 collide()->startFloorMomentRate();
1662 dynamics()->addForce(colData.wallNrm * 15.0f);
1663 collide()->startFloorMomentRate();
1675 auto &status = KartObjectProxy::status();
1678 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1679 next = 0.015f * -state()->startBoostCharge();
1681 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost, eStatus::SoftWallDrift)) {
1683 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1686 next = (scalar * 0.15f) * 0.25f;
1691 next = (scalar * 0.15f) * 0.08f;
1696 constexpr s16 MAX_SSMT_CHARGE = 75;
1697 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1702 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1704 m_standStillBoostRot += scalar * (next * m_invScale - m_standStillBoostRot);
1712 constexpr f32 DIVE_LIMIT = 0.8f;
1716 auto &status = KartObjectProxy::status();
1723 f32 stickY = state()->stickY();
1726 stickY = std::min(1.0f, stickY + 0.4f);
1729 u32 airtime = state()->airtime();
1732 if (EGG::Mathf::abs(stickY) < 0.1f) {
1736 stickY *= (airtime / 50.0f);
1743 dynamics()->setAngVel2(angVel2);
1745 if (state()->airtime() < 50) {
1751 f32 upDotTop =
m_up.
dot(topRotated);
1753 f32 crossNorm = upCrossTop.
length();
1754 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1756 f32 fVar1 = angle * RAD2DEG - 20.0f;
1757 if (fVar1 <= 0.0f) {
1761 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1762 if (forwardRotated.y > 0.0f) {
1763 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1765 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1773 auto &status = KartObjectProxy::status();
1785void KartMove::calcHopPhysics() {
1796void KartMove::calcRejectRoad() {
1797 m_reject.calcRejectRoad();
1801bool KartMove::calcZipperCollision(f32 radius, f32 scale,
EGG::Vector3f &pos,
1803 Field::KCLTypeMask *maskOut, Field::KCLTypeMask flags)
const {
1805 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1807 auto *colDir = Field::CollisionDirector::Instance();
1808 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1813 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.
dot(to)));
1814 f32 acos = EGG::Mathf::acos(dotNorm);
1815 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1819void KartMove::applyForce(f32 force,
const EGG::Vector3f &hitDir,
bool stop) {
1820 constexpr s16 BUMP_COOLDOWN = 5;
1827 collide()->startFloorMomentRate();
1839 f32 tiltMagnitude = 0.0f;
1840 auto &status = KartObjectProxy::status();
1842 if (status.
offBit(eStatus::InAction, eStatus::SoftWallDrift) &&
1847 f32 magnitude = tiltMagnitude;
1849 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1850 magnitude = frontSpeed.
length();
1852 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1853 magnitude = -magnitude;
1856 tiltMagnitude = -1.0f;
1857 if (-1.0f <= magnitude) {
1858 tiltMagnitude = std::min(1.0f, magnitude);
1864 dynamics()->setAngVel0(angVel0);
1868 m_invScale * (tiltMagnitude * param()->stats().tilt * EGG::Mathf::abs(
m_weightedTurn));
1873 angVel0.x += m_standStillBoostRot;
1875 dynamics()->setAngVel0(angVel0);
1879 dynamics()->setAngVel2(angVel2);
1889 constexpr u16 MAX_MT_CHARGE = 270;
1890 constexpr u16 MAX_SMT_CHARGE = 300;
1891 constexpr u16 BASE_MT_CHARGE = 2;
1892 constexpr u16 BASE_SMT_CHARGE = 2;
1893 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1894 constexpr u16 EXTRA_MT_CHARGE = 3;
1896 if (m_driftState == DriftState::ChargedSmt) {
1900 f32 stickX = state()->stickX();
1902 if (m_driftState == DriftState::ChargingMt) {
1905 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1906 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1915 m_driftState = DriftState::ChargingSmt;
1919 if (m_driftState != DriftState::ChargingSmt) {
1925 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1926 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1935 m_driftState = DriftState::ChargedSmt;
1940void KartMove::initOob() {
1946 clearOffroadInvincibility();
1958 m_driftState = DriftState::NotDrifting;
1970 dynamics()->setExtVel(extVel);
1973 totalForce.y = 0.0f;
1974 dynamics()->setTotalForce(totalForce);
1978void KartMove::tryStartBoostPanel() {
1979 constexpr s16 BOOST_PANEL_DURATION = 60;
1985 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
1993 constexpr s16 BOOST_RAMP_DURATION = 60;
1995 auto &status = KartObjectProxy::status();
2001 status.
setBit(eStatus::RampBoost);
2002 m_rampBoost = BOOST_RAMP_DURATION;
2011 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
2012 {50.0f, 50.0f, 35.0f},
2013 {50.0f, 50.0f, 47.0f},
2014 {59.0f, 59.0f, 30.0f},
2015 {73.0f, 73.0f, 45.0f},
2016 {73.0f, 73.0f, 53.0f},
2017 {56.0f, 56.0f, 50.0f},
2018 {55.0f, 55.0f, 35.0f},
2019 {56.0f, 56.0f, 50.0f},
2022 auto &status = KartObjectProxy::status();
2028 status.
setBit(eStatus::JumpPad);
2029 s32 jumpPadVariant = state()->jumpPadVariant();
2030 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
2032 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
2033 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
2036 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
2037 {100.0f, 100.0f, 70.0f},
2038 {100.0f, 100.0f, 65.0f},
2040 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
2043 status.
setBit(eStatus::JumpPadFixedSpeed);
2046 if (jumpPadVariant == 4) {
2047 status.
setBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadMushroomVelYInc,
2048 eStatus::JumpPadMushroomCollision);
2053 extVel.y = m_jumpPadProperties->velY;
2054 totalForce.y = 0.0f;
2056 dynamics()->setExtVel(extVel);
2057 dynamics()->setTotalForce(totalForce);
2059 if (jumpPadVariant != 3) {
2066 status.
setBit(eStatus::JumpPadDisableYsusForce);
2071 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
2076void KartMove::tryEndJumpPad() {
2077 auto &status = KartObjectProxy::status();
2078 if (status.
onBit(eStatus::JumpPadMushroomTrigger)) {
2080 status.
resetBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadFixedSpeed,
2081 eStatus::JumpPadMushroomVelYInc);
2084 if (status.
onBit(eStatus::JumpPadMushroomVelYInc)) {
2086 newExtVel.y += 20.0f;
2087 if (m_jumpPadProperties->velY < newExtVel.y) {
2088 newExtVel.y = m_jumpPadProperties->velY;
2089 status.
resetBit(eStatus::JumpPadMushroomVelYInc);
2091 dynamics()->setExtVel(newExtVel);
2101void KartMove::cancelJumpPad() {
2103 status().
resetBit(eStatus::JumpPad);
2107void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
2108 if (m_boost.
activate(type, frames)) {
2114void KartMove::applyStartBoost(s16 frames) {
2115 activateBoost(KartBoost::Type::AllMt, frames);
2119void KartMove::activateMushroom() {
2120 constexpr s16 MUSHROOM_DURATION = 90;
2122 auto &status = KartObjectProxy::status();
2128 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
2136void KartMove::activateZipperBoost() {
2137 constexpr s16 BASE_DURATION = 50;
2138 constexpr s16 TRICK_DURATION = 100;
2140 auto &status = KartObjectProxy::status();
2147 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
2150 m_zipperBoostTimer = 0;
2151 m_zipperBoostMax = boostDuration;
2171 auto &status = KartObjectProxy::status();
2187 auto &status = KartObjectProxy::status();
2201void KartMove::calcZipperBoost() {
2202 auto &status = KartObjectProxy::status();
2211 m_zipperBoostTimer = 0;
2215 if (m_zipperBoostTimer < 10) {
2218 dynamics()->setAngVel0(angVel);
2223void KartMove::landTrick() {
2224 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2229 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2241 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2243 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2246 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2250void KartMove::activateCrush(
u16 timer) {
2251 status().
setBit(eStatus::Crushed);
2257void KartMove::calcCrushed() {
2258 if (status().offBit(eStatus::Crushed)) {
2263 status().
resetBit(eStatus::Crushed);
2269void KartMove::calcScale() {
2277 if (sizeScale.z != 1.0f) {
2285void KartMove::applyShrink(
u16 timer) {
2286 auto &status = state()->status();
2288 if (status.
onBit(eStatus::InRespawn, eStatus::AfterRespawn, eStatus::CannonStart)) {
2292 action()->
start(Action::UNK_15);
2293 Item::ItemDirector::Instance()->kartItem(0).clear();
2294 status.
setBit(eStatus::Shocked);
2296 if (timer > m_shockTimer) {
2297 m_shockTimer = timer;
2303void KartMove::calcShock() {
2304 auto &status = state()->status();
2306 if (status.
onBit(eStatus::Shocked)) {
2307 if (--m_shockTimer == 0) {
2308 deactivateShock(
false);
2311 m_shockSpeedMultiplier = std::max(0.7f, m_shockSpeedMultiplier - 0.03f);
2313 m_shockSpeedMultiplier = std::min(1.0f, m_shockSpeedMultiplier + 0.05f);
2318void KartMove::deactivateShock(
bool resetSpeed) {
2319 status().
resetBit(eStatus::Shocked);
2324 m_shockSpeedMultiplier = 1.0f;
2329void KartMove::enterCannon() {
2331 physics()->clearDecayingRot();
2332 m_boost.resetActive();
2334 auto &status = KartObjectProxy::status();
2342 clearOffroadInvincibility();
2344 dynamics()->reset();
2349 .setBit(eStatus::InCannon, eStatus::SkipWheelCalc);
2351 const auto [cannonPos, cannonDir] = getCannonPosRot();
2352 m_cannonEntryPos = pos();
2353 m_cannonEntryOfs = cannonPos - pos();
2354 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2356 m_dir = m_cannonEntryOfs;
2357 m_vel1Dir = m_cannonEntryOfs;
2358 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2359 m_cannonProgress.setZero();
2363void KartMove::calcCannon() {
2364 auto [cannonPos, cannonDir] = getCannonPosRot();
2365 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2367 f32 forwardLength = forward.
normalise();
2381 if (forwardLength < 30.0f || local94.
dot(forwardXZ) <= 0.0f) {
2386 const auto *cannonPoint =
2387 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2388 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2389 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2390 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2391 f32 newSpeed = cannonParams.speed;
2392 if (forwardLength < cannonParams.decelFactor) {
2393 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2395 newSpeed = cannonParams.endDecel;
2396 if (newSpeed <= 0.0f) {
2400 newSpeed += factor * (cannonParams.speed - newSpeed);
2401 if (cannonParams.endDecel > 0.0f) {
2406 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2409 if (cannonParams.height > 0.0f) {
2410 f32 fVar9 = EGG::Mathf::SinFIdx(
2411 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2412 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2415 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2416 m_dir = m_cannonEntryOfs;
2417 m_vel1Dir = m_cannonEntryOfs;
2419 calcRotCannon(forward);
2421 dynamics()->setExtVel(EGG::Vector3f::zero);
2429 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2435 local80 *= dynamics()->fullRot();
2440 dynamics()->setFullRot(newRot);
2441 dynamics()->setMainRot(newRot);
2445void KartMove::exitCannon() {
2446 auto &status = KartObjectProxy::status();
2448 if (status.
offBit(eStatus::InCannon)) {
2452 status.
resetBit(eStatus::InCannon, eStatus::SkipWheelCalc).setBit(eStatus::AfterCannon);
2453 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2457void KartMove::triggerRespawn() {
2459 status().
setBit(eStatus::TriggerRespawn);
2463KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2466KartMoveBike::~KartMoveBike() =
default;
2471 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2472 constexpr u16 WHEELIE_COOLDOWN = 20;
2479 m_autoHardStickXFrames = 0;
2488 m_autoHardStickXFrames = 0;
2504 const auto *raceManager = System::RaceManager::Instance();
2506 auto &status = KartObjectProxy::status();
2509 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2510 EGG::Mathf::abs(
m_speed) < 5.0f) {
2522 f32 stickX = state()->stickX();
2523 f32 extVelXFactor = 0.0f;
2529 eStatus::SoftWallDrift, eStatus::SomethingWallCollision,
eStatus::HWG,
2530 eStatus::CannonStart, eStatus::InCannon)) {
2532 }
else if (!state()->isDrifting()) {
2533 if (stickX <= 0.2f) {
2534 if (stickX >= -0.2f) {
2549 leanRotMin = -leanRotMax;
2553 if (stickX == 0.0f) {
2559 }
else if (stickX == 0.0f) {
2567 bool capped =
false;
2579 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2582 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2586 dynamics()->setAngVel2(dynamics()->angVel2() +
2587 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2596 scalar = std::min(1.0f, scalar);
2597 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2599 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2604 dynamics()->setTop_(top);
2612 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2613 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2614 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2617 KartMove::setTurnParams();
2619 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2621 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2625 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2635void KartMoveBike::init(
bool b1,
bool b2) {
2636 KartMove::init(b1, b2);
2645 m_autoHardStickXFrames = 0;
2649void KartMoveBike::clear() {
2657 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2658 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2663 auto &status = KartObjectProxy::status();
2666 bool cancelAutoWheelie =
false;
2669 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2670 m_autoHardStickXFrames = 0;
2672 if (++m_autoHardStickXFrames > 15) {
2673 cancelAutoWheelie =
true;
2685 dynamics()->setAngVel0(angVel0);
2695 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2700 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2701 dynamics()->setAngVel2(angVel2);
2706 status.
setBit(eStatus::WheelieRot);
2708 status.
resetBit(eStatus::WheelieRot);
2735 constexpr u16 MAX_MT_CHARGE = 270;
2736 constexpr u16 BASE_MT_CHARGE = 2;
2737 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2738 constexpr u16 EXTRA_MT_CHARGE = 3;
2740 if (m_driftState != DriftState::ChargingMt) {
2746 f32 stickX = state()->stickX();
2747 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2748 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2757 m_driftState = DriftState::ChargedMt;
2762void KartMoveBike::initOob() {
2763 KartMove::initOob();
2770 constexpr s16 COOLDOWN_FRAMES = 20;
2771 bool dpadUp = inputs()->currentState().trickUp();
2772 auto &status = KartObjectProxy::status();
2777 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.