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;
130 m_smoothedForward = m_dir;
132 m_driftingParams = &DRIFTING_PARAMS_ARRAY[
static_cast<u32
>(param()->stats().driftType)];
137void KartMove::init(
bool b1,
bool b2) {
145 m_up = EGG::Vector3f::ey;
147 m_smoothedForward = EGG::Vector3f::ez;
148 m_vel1Dir = EGG::Vector3f::ez;
150 m_dir = EGG::Vector3f::ez;
151 m_landingDir = EGG::Vector3f::ez;
152 m_dirDiff = EGG::Vector3f::zero;
153 m_hasLandingDir =
false;
155 m_landingAngle = 0.0f;
173 m_standStillBoostRot = 0.0f;
174 m_driftState = DriftState::NotDrifting;
179 m_zipperBoostTimer = 0;
180 m_zipperBoostMax = 0;
186 m_nonZipperAirtime = 0;
193 m_hitboxScale = 1.0f;
194 m_shockSpeedMultiplier = 1.0f;
202 m_jumpPadMaxSpeed = 0.0f;
203 m_jumpPadBoostMultiplier = 0.0f;
204 m_jumpPadProperties =
nullptr;
206 m_autoDriftAngle = 0.0f;
207 m_autoDriftStartFrameCounter = 0;
209 m_cannonEntryOfsLength = 0.0f;
210 m_cannonEntryPos.setZero();
211 m_cannonEntryOfs.setZero();
212 m_cannonOrthog.setZero();
213 m_cannonProgress.setZero();
232void KartMove::clear() {
233 auto &status = KartObjectProxy::status();
244 clearOffroadInvincibility();
253 EGG::Quatf quaternion = EGG::Quatf::FromRPY(angles * DEG2RAD);
256 Field::KCLTypeMask kcl_flags = KCL_NONE;
258 bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos,
259 EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0);
262 newPos = newPos + info.tangentOff + (info.floorNrm * -100.0f);
263 newPos += info.floorNrm * bsp().initialYPos;
269 sub()->initPhysicsValues();
271 physics()->setPos(pos());
272 physics()->setVelocity(dynamics()->velocity());
278 dynamics()->setTop(
m_up);
280 for (
u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) {
281 suspension(tireIdx)->setInitialState();
291 auto &status = KartObjectProxy::status();
293 if (status.
onBit(eStatus::InRespawn)) {
298 dynamics()->resetInternalVelocity();
329 if (status.
onBit(eStatus::InCannon)) {
340void KartMove::calcRespawnStart() {
341 constexpr float RESPAWN_HEIGHT = 700.0f;
343 const auto *jugemPoint = System::RaceManager::Instance()->jugemPoint();
348 respawnPos.y += RESPAWN_HEIGHT;
353 Item::ItemDirector::Instance()->kartItem(0).clear();
355 status().
resetBit(eStatus::TriggerRespawn).setBit(eStatus::InRespawn);
359void KartMove::calcInRespawn() {
360 constexpr f32 LAKITU_VELOCITY = 1.5f;
361 constexpr u16 RESPAWN_DURATION = 110;
363 auto &status = KartObjectProxy::status();
365 if (status.
offBit(eStatus::InRespawn)) {
369 EGG::Vector3f newPos = pos();
370 newPos.y -= LAKITU_VELOCITY;
371 dynamics()->setPos(newPos);
372 dynamics()->setNoGravity(
true);
378 dynamics()->setNoGravity(
false);
383void KartMove::calcRespawnBoost() {
384 constexpr s16 RESPAWN_BOOST_DURATION = 30;
385 constexpr s16 RESPAWN_BOOST_INPUT_LENIENCY = 4;
387 auto &status = KartObjectProxy::status();
389 if (status.
onBit(eStatus::AfterRespawn)) {
393 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
394 m_respawnTimer = RESPAWN_BOOST_DURATION;
400 status.
resetBit(eStatus::AfterRespawn);
414 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
415 m_respawnTimer = RESPAWN_BOOST_DURATION;
427 m_respawnTimer = std::max(0, m_respawnTimer - 1);
431void KartMove::calcTop() {
432 f32 stabilizationFactor = 0.1f;
433 m_hasLandingDir =
false;
434 EGG::Vector3f inputTop = state()->top();
435 auto &status = KartObjectProxy::status();
441 m_dirDiff = m_landingDir.
proj(m_landingDir);
442 m_hasLandingDir =
true;
448 inputTop.dot(m_dir) > 0.0f &&
m_speed > 50.0f &&
449 collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::NotTrickable)) {
461 f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(inputTop.dot(componentZAxis())));
462 scalar = std::min(0.8f, std::max(0.3f, topDotZ));
470 if (bodyDotFront < -0.1f) {
471 stabilizationFactor += std::min(0.2f, EGG::Mathf::abs(bodyDotFront) * 0.5f);
474 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
475 stabilizationFactor = 0.4f;
482 dynamics()->setStabilizationFactor(stabilizationFactor);
485 m_flags.
changeBit(collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::Trickable),
492 auto &status = KartObjectProxy::status();
505 if (
m_up.y <= 0.99f) {
506 m_up += (EGG::Vector3f::ey -
m_up) * 0.03f;
509 m_up = EGG::Vector3f::ey;
517 const auto *raceMgr = System::RaceManager::Instance();
518 if (!raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
522 if (m_padType.
onBit(ePadType::BoostPanel)) {
523 tryStartBoostPanel();
526 if (m_padType.
onBit(ePadType::BoostRamp)) {
530 if (m_padType.
onBit(ePadType::JumpPad)) {
538void KartMove::calcDirs() {
542 m_flags.
setBit(eFlags::LaunchBoost);
543 auto &status = KartObjectProxy::status();
547 !m_jump->isBoostRampEnabled()) &&
548 status.
offBit(eStatus::JumpPad) && state()->airtime() <= 5) ||
549 status.
onBit(eStatus::JumpPadMushroomCollision,
550 eStatus::NoSparkInvisibleWall))) {
564 if (dirDiff.
squaredLength() <= std::numeric_limits<f32>::epsilon()) {
568 EGG::Vector3f origDirCross = m_dir.cross(local_b8);
573 EGG::Vector3f newDirCross = m_dir.cross(local_b8);
575 if (origDirCross.dot(newDirCross) < 0.0f) {
582 m_flags.
resetBit(eFlags::LaunchBoost);
591 EGG::Vector3f nextDir =
m_up.cross(local_88);
592 m_smoothedForward = nextDir.cross(
m_up);
595 if (m_hasLandingDir) {
596 f32 dot = m_dir.dot(m_landingDir);
597 EGG::Vector3f cross = m_dir.cross(m_landingDir);
598 f32 crossDot = cross.length();
599 f32 angle = EGG::Mathf::atan2(crossDot, dot);
600 angle = EGG::Mathf::abs(angle);
607 m_landingAngle += (angle * RAD2DEG) * fVar4;
610 if (m_landingAngle <= 0.0f) {
611 if (m_landingAngle < 0.0f) {
612 m_landingAngle = std::min(0.0f, m_landingAngle + 2.0f);
615 m_landingAngle = std::max(0.0f, m_landingAngle - 2.0f);
620void KartMove::calcStickyRoad() {
621 constexpr f32 STICKY_RADIUS = 200.0f;
622 constexpr Field::KCLTypeMask STICKY_MASK =
625 auto &status = KartObjectProxy::status();
633 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::Trickable)) ||
634 EGG::Mathf::abs(
m_speed) <= 20.0f) {
638 EGG::Vector3f pos = dynamics()->pos();
639 EGG::Vector3f vel = dynamics()->movingObjVel() +
m_speed * m_vel1Dir;
640 Field::CollisionInfo colInfo;
641 colInfo.bbox.setZero();
642 Field::KCLTypeMask kcl_flags = KCL_NONE;
643 bool stickyRoad =
false;
645 for (
size_t i = 0; i < 3; ++i) {
646 EGG::Vector3f newPos = pos + vel;
647 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
648 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
649 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
650 dynamics()->setMovingObjVel(dynamics()->movingObjVel().rej(colInfo.floorNrm));
651 dynamics()->setMovingRoadVel(dynamics()->movingRoadVel().rej(colInfo.floorNrm));
653 if (status.
onBit(eStatus::MovingWaterStickyRoad)) {
654 m_up = colInfo.floorNrm;
663 pos += -STICKY_RADIUS * componentYAxis();
675 auto &status = KartObjectProxy::status();
707void KartMove::calcRisingWater() {
708 auto *objDir = Field::ObjectDirector::Instance();
709 auto *psea = objDir->psea();
714 f32 pos = wheelPos(0).y;
715 u16 count = tireCount();
716 for (
u16 wheelIdx = 0; wheelIdx < count; ++wheelIdx) {
717 f32 tmp = wheelEdgePos(wheelIdx).y;
718 if (wheelIdx == 0 || tmp < pos) {
723 if (objDir->risingWaterKillPlaneHeight() > pos) {
724 collide()->activateOob(
true,
nullptr,
false,
false);
727 f32 dist = -objDir->distAboveRisingWater(pos);
729 f32 speedScale = std::min(1.0f, dist / 100.0f);
735void KartMove::calcBoost() {
736 auto &status = KartObjectProxy::status();
738 if (m_boost.
calc()) {
748void KartMove::calcRampBoost() {
749 auto &status = KartObjectProxy::status();
751 if (status.
offBit(eStatus::RampBoost)) {
756 if (--m_rampBoost < 1) {
758 status.
resetBit(eStatus::RampBoost);
766 auto &status = KartObjectProxy::status();
783 constexpr s16 MAX_SSMT_CHARGE = 75;
784 constexpr s16 SSMT_BOOST_FRAMES = 30;
785 constexpr s16 LEEWAY_FRAMES = 1;
786 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
790 auto &status = KartObjectProxy::status();
816 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
823 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
840 auto &status = KartObjectProxy::status();
885 m_driftState = DriftState::NotDrifting;
897 m_driftState = DriftState::NotDrifting;
903 m_autoDriftAngle = 0.0f;
905 m_autoDriftStartFrameCounter = 0;
909void KartMove::clearJumpPad() {
911 status().
resetBit(eStatus::JumpPad);
915void KartMove::clearRampBoost() {
917 status().
resetBit(eStatus::RampBoost);
921void KartMove::clearZipperBoost() {
922 m_zipperBoostTimer = 0;
927void KartMove::clearBoost() {
928 m_boost.resetActive();
933void KartMove::clearSsmt() {
941void KartMove::clearOffroadInvincibility() {
946void KartMove::clearRejectRoad() {
954 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
956 auto &status = KartObjectProxy::status();
964 EGG::Mathf::abs(state()->stickX()) > 0.85f) {
965 m_autoDriftStartFrameCounter =
966 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
968 m_autoDriftStartFrameCounter = 0;
971 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
972 status.
setBit(eStatus::DriftAuto);
975 if (state()->stickX() < 0.0f) {
985 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
986 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
988 status.
resetBit(eStatus::DriftAuto);
991 if (m_autoDriftAngle > 0.0f) {
993 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
996 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
1002 physics()->composeExtraRot(angleAxis);
1010 auto &status = KartObjectProxy::status();
1016 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
1017 status.
offBit(eStatus::JumpPadMushroomCollision) &&
1019 m_flags.
onBit(eFlags::LaunchBoost)) {
1024 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
1025 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
1027 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
1057 if (action()->flags().offBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
1058 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
1091 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1093 const auto &stats = param()->stats();
1094 auto &status = KartObjectProxy::status();
1096 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1097 f32 driftAngle = 0.0f;
1125 m_driftState = DriftState::ChargingMt;
1133 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1135 auto &status = KartObjectProxy::status();
1138 m_driftState = DriftState::NotDrifting;
1144 if (m_driftState == DriftState::ChargedSmt) {
1145 mtLength *= SMT_LENGTH_FACTOR;
1149 activateBoost(KartBoost::Type::AllMt, mtLength);
1152 m_driftState = DriftState::NotDrifting;
1159 if (state()->airtime() > 5) {
1163 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1166 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1167 if (angle > targetAngle) {
1169 }
else if (angle < targetAngle) {
1175 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1176 if (targetAngle > angle) {
1178 }
else if (targetAngle < angle) {
1193 auto &status = KartObjectProxy::status();
1194 bool drifting = state()->isDrifting() && status.
offBit(eStatus::JumpPadMushroomCollision);
1196 const auto &stats = param()->stats();
1199 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1201 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1204 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1209 bool forwards =
true;
1223 bool noTurn =
false;
1225 EGG::Mathf::abs(
m_speed) < 1.0f) {
1231 if (forwards && !noTurn) {
1235 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1238 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1251 f32 stickX = EGG::Mathf::abs(state()->stickX());
1252 if (autoDrift && stickX > 0.3f) {
1253 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1254 stickX = drifting ? 0.2f : 0.5f;
1261 if (status.
onBit(eStatus::RampBoost) && m_jump->isBoostRampEnabled()) {
1263 }
else if (status.
offBit(eStatus::JumpPadMushroomCollision)) {
1264 u32 airtime = state()->airtime();
1265 if (airtime >= 70) {
1267 }
else if (airtime >= 30) {
1268 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1274 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1275 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1277 if (angle > 60.0f) {
1278 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1289 const auto *raceMgr = System::RaceManager::Instance();
1290 auto &status = KartObjectProxy::status();
1292 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1293 f32 speedFix = dynamics()->speedFix();
1294 if (status.
onBit(eStatus::InAction) ||
1296 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1308 if (status.
onBit(eStatus::MovingWaterVertical) ||
1309 (status.
onBit(eStatus::MovingWaterDecaySpeed) &&
1312 m_speed *= collide()->pullPath().roadSpeedDecay();
1318 if (status.
onBit(eStatus::InAction)) {
1319 action()->calcVehicleSpeed();
1327 if (status.
onBit(eStatus::RampBoost) && state()->airtime() < 4) {
1336 if (state()->airtime() > 5) {
1344 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost)) {
1351 eStatus::SomethingWallCollision)) {
1365 const auto &stats = param()->stats();
1368 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1384 initialVel = std::min(initialVel * 2.0f, 2.0f);
1386 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1400 std::span<const f32> as;
1401 std::span<const f32> ts;
1402 if (state()->isDrifting()) {
1403 as = param()->stats().accelerationDriftA;
1404 ts = param()->stats().accelerationDriftT;
1406 as = param()->stats().accelerationStandardA;
1407 ts = param()->stats().accelerationStandardT;
1411 f32 acceleration = 0.0f;
1413 for (; i < ts.size(); ++i) {
1414 if (ratio < ts[i]) {
1415 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1422 return i < ts.size() ? acceleration : as.back();
1429 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1430 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1431 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1432 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1433 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1436 auto &status = KartObjectProxy::status();
1438 if (status.
offBit(eStatus::InAction)) {
1472 f32 speedLimit = status.
onBit(eStatus::JumpPad) ? m_jumpPadMaxSpeed :
m_baseSpeed;
1473 const f32 boostMultiplier = m_boost.multiplier();
1474 const f32 boostSpdLimit = m_boost.speedLimit();
1475 m_jumpPadBoostMultiplier = boostMultiplier;
1477 f32 scaleMultiplier = m_shockSpeedMultiplier;
1478 if (status.
onBit(eStatus::Crushed)) {
1479 scaleMultiplier *= 0.7f;
1483 speedLimit *= status.
onBit(eStatus::JumpPadFixedSpeed) ?
1489 f32 boostSpeed = ignoreScale ? 1.0f : scaleMultiplier;
1492 if (status.
offBit(eStatus::JumpPad) && boostSpeed > 0.0f && boostSpeed > speedLimit) {
1493 speedLimit = boostSpeed;
1498 if (status.
onBit(eStatus::RampBoost)) {
1499 speedLimit = std::max(speedLimit, 100.0f);
1504 f32 local_c8 = 1.0f;
1519 if (status.
onBit(eStatus::JumpPad)) {
1530 crossVec = -crossVec;
1533 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1534 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1535 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1537 rotationScalar = ROTATION_SCALAR_MIDAIR;
1544 const auto *raceMgr = System::RaceManager::Instance();
1547 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1556 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1558 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1565 DrivingDirection::Backwards;
1581 auto &status = KartObjectProxy::status();
1594 if (wallNrm.y > 0.0f) {
1602 f1 = std::max(0.0f, dot + 1.0f);
1616 auto &status = KartObjectProxy::status();
1623 if (status.
offBit(eStatus::InAction)) {
1626 m_landingDir = m_dir;
1627 m_smoothedForward = m_dir;
1634 if (speedDiff > 30.0f) {
1637 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1641 speedDiff = std::min(60.0f, speedDiff);
1644 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1649 proj = EGG::Vector3f::zero;
1650 rej = EGG::Vector3f::zero;
1653 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1654 proj = EGG::Vector3f::zero;
1659 f32 bumpDeviation = 0.0f;
1661 bumpDeviation = param()->stats().bumpDeviationLevel;
1666 dynamics()->addForce(colData.wallNrm * 15.0f);
1667 collide()->startFloorMomentRate();
1671 dynamics()->addForce(colData.wallNrm * 15.0f);
1672 collide()->startFloorMomentRate();
1684 auto &status = KartObjectProxy::status();
1687 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1688 next = 0.015f * -state()->startBoostCharge();
1690 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost, eStatus::SoftWallDrift)) {
1692 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1695 next = (scalar * 0.15f) * 0.25f;
1700 next = (scalar * 0.15f) * 0.08f;
1705 constexpr s16 MAX_SSMT_CHARGE = 75;
1706 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1711 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1713 m_standStillBoostRot += scalar * (next * m_invScale - m_standStillBoostRot);
1721 constexpr f32 DIVE_LIMIT = 0.8f;
1725 auto &status = KartObjectProxy::status();
1732 f32 stickY = state()->stickY();
1735 stickY = std::min(1.0f, stickY + 0.4f);
1738 u32 airtime = state()->airtime();
1741 if (EGG::Mathf::abs(stickY) < 0.1f) {
1745 stickY *= (airtime / 50.0f);
1752 dynamics()->setAngVel2(angVel2);
1754 if (state()->airtime() < 50) {
1760 f32 upDotTop =
m_up.
dot(topRotated);
1762 f32 crossNorm = upCrossTop.
length();
1763 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1765 f32 fVar1 = angle * RAD2DEG - 20.0f;
1766 if (fVar1 <= 0.0f) {
1770 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1771 if (forwardRotated.y > 0.0f) {
1772 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1774 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1782 auto &status = KartObjectProxy::status();
1794void KartMove::calcHopPhysics() {
1805void KartMove::calcRejectRoad() {
1806 m_reject.calcRejectRoad();
1810bool KartMove::calcZipperCollision(f32 radius, f32 scale, EGG::Vector3f &pos,
1811 EGG::Vector3f &upLocal,
const EGG::Vector3f &prevPos, Field::CollisionInfo *colInfo,
1812 Field::KCLTypeMask *maskOut, Field::KCLTypeMask flags)
const {
1814 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1816 auto *colDir = Field::CollisionDirector::Instance();
1817 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1821f32 KartMove::calcSlerpRate(f32 scale,
const EGG::Quatf &from,
const EGG::Quatf &to)
const {
1822 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.dot(to)));
1823 f32 acos = EGG::Mathf::acos(dotNorm);
1824 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1828void KartMove::applyForce(f32 force,
const EGG::Vector3f &hitDir,
bool stop) {
1829 constexpr s16 BUMP_COOLDOWN = 5;
1835 dynamics()->addForce(force * hitDir.perpInPlane(
m_up,
true));
1836 collide()->startFloorMomentRate();
1848 f32 tiltMagnitude = 0.0f;
1849 auto &status = KartObjectProxy::status();
1851 if (status.
offBit(eStatus::InAction, eStatus::SoftWallDrift) &&
1856 f32 magnitude = tiltMagnitude;
1858 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1859 magnitude = frontSpeed.
length();
1861 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1862 magnitude = -magnitude;
1865 tiltMagnitude = -1.0f;
1866 if (-1.0f <= magnitude) {
1867 tiltMagnitude = std::min(1.0f, magnitude);
1873 dynamics()->setAngVel0(angVel0);
1877 m_invScale * (tiltMagnitude * param()->stats().tilt * EGG::Mathf::abs(
m_weightedTurn));
1882 angVel0.x += m_standStillBoostRot;
1884 dynamics()->setAngVel0(angVel0);
1888 dynamics()->setAngVel2(angVel2);
1898 constexpr u16 MAX_MT_CHARGE = 270;
1899 constexpr u16 MAX_SMT_CHARGE = 300;
1900 constexpr u16 BASE_MT_CHARGE = 2;
1901 constexpr u16 BASE_SMT_CHARGE = 2;
1902 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1903 constexpr u16 EXTRA_MT_CHARGE = 3;
1905 if (m_driftState == DriftState::ChargedSmt) {
1909 f32 stickX = state()->stickX();
1911 if (m_driftState == DriftState::ChargingMt) {
1914 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1915 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1924 m_driftState = DriftState::ChargingSmt;
1928 if (m_driftState != DriftState::ChargingSmt) {
1934 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1935 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1944 m_driftState = DriftState::ChargedSmt;
1949void KartMove::initOob() {
1955 clearOffroadInvincibility();
1967 m_driftState = DriftState::NotDrifting;
1979 dynamics()->setExtVel(extVel);
1982 totalForce.y = 0.0f;
1983 dynamics()->setTotalForce(totalForce);
1987void KartMove::tryStartBoostPanel() {
1988 constexpr s16 BOOST_PANEL_DURATION = 60;
1994 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
2002 constexpr s16 BOOST_RAMP_DURATION = 60;
2004 auto &status = KartObjectProxy::status();
2010 status.
setBit(eStatus::RampBoost);
2011 m_rampBoost = BOOST_RAMP_DURATION;
2020 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
2021 {50.0f, 50.0f, 35.0f},
2022 {50.0f, 50.0f, 47.0f},
2023 {59.0f, 59.0f, 30.0f},
2024 {73.0f, 73.0f, 45.0f},
2025 {73.0f, 73.0f, 53.0f},
2026 {56.0f, 56.0f, 50.0f},
2027 {55.0f, 55.0f, 35.0f},
2028 {56.0f, 56.0f, 50.0f},
2031 auto &status = KartObjectProxy::status();
2037 status.
setBit(eStatus::JumpPad);
2038 s32 jumpPadVariant = state()->jumpPadVariant();
2039 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
2041 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
2042 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
2045 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
2046 {100.0f, 100.0f, 70.0f},
2047 {100.0f, 100.0f, 65.0f},
2049 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
2052 status.
setBit(eStatus::JumpPadFixedSpeed);
2055 if (jumpPadVariant == 4) {
2056 status.
setBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadMushroomVelYInc,
2057 eStatus::JumpPadMushroomCollision);
2062 extVel.y = m_jumpPadProperties->velY;
2063 totalForce.y = 0.0f;
2065 dynamics()->setExtVel(extVel);
2066 dynamics()->setTotalForce(totalForce);
2068 if (jumpPadVariant != 3) {
2075 status.
setBit(eStatus::JumpPadDisableYsusForce);
2080 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
2085void KartMove::tryEndJumpPad() {
2086 auto &status = KartObjectProxy::status();
2087 if (status.
onBit(eStatus::JumpPadMushroomTrigger)) {
2089 status.
resetBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadFixedSpeed,
2090 eStatus::JumpPadMushroomVelYInc);
2093 if (status.
onBit(eStatus::JumpPadMushroomVelYInc)) {
2095 newExtVel.y += 20.0f;
2096 if (m_jumpPadProperties->velY < newExtVel.y) {
2097 newExtVel.y = m_jumpPadProperties->velY;
2098 status.
resetBit(eStatus::JumpPadMushroomVelYInc);
2100 dynamics()->setExtVel(newExtVel);
2110void KartMove::cancelJumpPad() {
2112 status().
resetBit(eStatus::JumpPad);
2116void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
2117 if (m_boost.
activate(type, frames)) {
2123void KartMove::applyStartBoost(s16 frames) {
2124 activateBoost(KartBoost::Type::AllMt, frames);
2128void KartMove::activateMushroom() {
2129 constexpr s16 MUSHROOM_DURATION = 90;
2131 auto &status = KartObjectProxy::status();
2137 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
2145void KartMove::activateZipperBoost() {
2146 constexpr s16 BASE_DURATION = 50;
2147 constexpr s16 TRICK_DURATION = 100;
2149 auto &status = KartObjectProxy::status();
2156 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
2159 m_zipperBoostTimer = 0;
2160 m_zipperBoostMax = boostDuration;
2180 auto &status = KartObjectProxy::status();
2196 auto &status = KartObjectProxy::status();
2210void KartMove::calcZipperBoost() {
2211 auto &status = KartObjectProxy::status();
2220 m_zipperBoostTimer = 0;
2224 if (m_zipperBoostTimer < 10) {
2225 EGG::Vector3f angVel = dynamics()->angVel0();
2227 dynamics()->setAngVel0(angVel);
2232void KartMove::landTrick() {
2233 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2238 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2250 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2252 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2255 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2259void KartMove::activateCrush(u16 timer) {
2260 status().
setBit(eStatus::Crushed);
2266void KartMove::calcCrushed() {
2267 if (status().offBit(eStatus::Crushed)) {
2272 status().
resetBit(eStatus::Crushed);
2278void KartMove::calcScale() {
2281 const EGG::Vector3f sizeScale =
m_kartScale->sizeScale();
2286 if (sizeScale.z != 1.0f) {
2294void KartMove::applyShrink(u16 timer) {
2295 auto &status = state()->status();
2297 if (status.
onBit(eStatus::InRespawn, eStatus::AfterRespawn, eStatus::CannonStart)) {
2301 action()->
start(Action::UNK_15);
2302 Item::ItemDirector::Instance()->kartItem(0).clear();
2303 status.
setBit(eStatus::Shocked);
2305 if (timer > m_shockTimer) {
2306 m_shockTimer = timer;
2312void KartMove::calcShock() {
2313 auto &status = state()->status();
2315 if (status.
onBit(eStatus::Shocked)) {
2316 if (--m_shockTimer == 0) {
2317 deactivateShock(
false);
2320 m_shockSpeedMultiplier = std::max(0.7f, m_shockSpeedMultiplier - 0.03f);
2322 m_shockSpeedMultiplier = std::min(1.0f, m_shockSpeedMultiplier + 0.05f);
2327void KartMove::deactivateShock(
bool resetSpeed) {
2328 status().
resetBit(eStatus::Shocked);
2333 m_shockSpeedMultiplier = 1.0f;
2338void KartMove::enterCannon() {
2340 physics()->clearDecayingRot();
2341 m_boost.resetActive();
2343 auto &status = KartObjectProxy::status();
2351 clearOffroadInvincibility();
2353 dynamics()->reset();
2358 .setBit(eStatus::InCannon, eStatus::SkipWheelCalc);
2360 const auto [cannonPos, cannonDir] = getCannonPosRot();
2361 m_cannonEntryPos = pos();
2362 m_cannonEntryOfs = cannonPos - pos();
2363 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2365 m_dir = m_cannonEntryOfs;
2366 m_vel1Dir = m_cannonEntryOfs;
2367 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2368 m_cannonProgress.setZero();
2372void KartMove::calcCannon() {
2373 auto [cannonPos, cannonDir] = getCannonPosRot();
2374 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2375 EGG::Vector3f forward = forwardXZ;
2376 f32 forwardLength = forward.normalise();
2378 forwardXZ.normalise();
2379 EGG::Vector3f local94 = m_cannonEntryOfs;
2381 local94.normalise();
2384 EGG::Matrix34f cannonOrientation;
2385 cannonOrientation.makeOrthonormalBasis(forward, EGG::Vector3f::ey);
2386 EGG::Vector3f up = cannonOrientation.multVector33(EGG::Vector3f::ey);
2390 if (forwardLength < 30.0f || local94.dot(forwardXZ) <= 0.0f) {
2395 m_smoothedForward = cannonOrientation.multVector33(EGG::Vector3f::ez);
2397 const auto *cannonPoint =
2398 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2399 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2400 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2401 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2402 f32 newSpeed = cannonParams.speed;
2403 if (forwardLength < cannonParams.decelFactor) {
2404 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2406 newSpeed = cannonParams.endDecel;
2407 if (newSpeed <= 0.0f) {
2411 newSpeed += factor * (cannonParams.speed - newSpeed);
2412 if (cannonParams.endDecel > 0.0f) {
2417 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2419 EGG::Vector3f newPos = EGG::Vector3f::zero;
2420 if (cannonParams.height > 0.0f) {
2421 f32 fVar9 = EGG::Mathf::SinFIdx(
2422 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2423 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2426 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2427 m_dir = m_cannonEntryOfs;
2428 m_vel1Dir = m_cannonEntryOfs;
2430 calcRotCannon(forward);
2432 dynamics()->setExtVel(EGG::Vector3f::zero);
2436void KartMove::calcRotCannon(
const EGG::Vector3f &forward) {
2437 EGG::Vector3f local48 = forward;
2438 local48.normalise();
2440 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2441 local54.normalise();
2442 local60.normalise();
2445 local80.makeVectorRotation(local54, local60);
2446 local80 *= dynamics()->fullRot();
2449 localB8.makeVectorRotation(local80.rotateVector(EGG::Vector3f::ey), smoothedUp());
2450 EGG::Quatf newRot = local80.slerpTo(localB8.multSwap(local80), 0.3f);
2451 dynamics()->setFullRot(newRot);
2452 dynamics()->setMainRot(newRot);
2456void KartMove::exitCannon() {
2457 auto &status = KartObjectProxy::status();
2459 if (status.
offBit(eStatus::InCannon)) {
2463 status.
resetBit(eStatus::InCannon, eStatus::SkipWheelCalc).setBit(eStatus::AfterCannon);
2464 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2468void KartMove::triggerRespawn() {
2470 status().
setBit(eStatus::TriggerRespawn);
2474KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2477KartMoveBike::~KartMoveBike() =
default;
2482 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2483 constexpr u16 WHEELIE_COOLDOWN = 20;
2490 m_autoHardStickXFrames = 0;
2499 m_autoHardStickXFrames = 0;
2515 const auto *raceManager = System::RaceManager::Instance();
2517 auto &status = KartObjectProxy::status();
2520 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2521 EGG::Mathf::abs(
m_speed) < 5.0f) {
2533 f32 stickX = state()->stickX();
2534 f32 extVelXFactor = 0.0f;
2540 eStatus::SoftWallDrift, eStatus::SomethingWallCollision,
eStatus::HWG,
2541 eStatus::CannonStart, eStatus::InCannon)) {
2543 }
else if (!state()->isDrifting()) {
2544 if (stickX <= 0.2f) {
2545 if (stickX >= -0.2f) {
2560 leanRotMin = -leanRotMax;
2564 if (stickX == 0.0f) {
2570 }
else if (stickX == 0.0f) {
2578 bool capped =
false;
2590 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2593 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2597 dynamics()->setAngVel2(dynamics()->angVel2() +
2598 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2607 scalar = std::min(1.0f, scalar);
2608 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2610 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2615 dynamics()->setTop_(top);
2623 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2624 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2625 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2628 KartMove::setTurnParams();
2630 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2632 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2636 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2646void KartMoveBike::init(
bool b1,
bool b2) {
2647 KartMove::init(b1, b2);
2656 m_autoHardStickXFrames = 0;
2660void KartMoveBike::clear() {
2668 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2669 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2674 auto &status = KartObjectProxy::status();
2677 bool cancelAutoWheelie =
false;
2680 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2681 m_autoHardStickXFrames = 0;
2683 if (++m_autoHardStickXFrames > 15) {
2684 cancelAutoWheelie =
true;
2696 dynamics()->setAngVel0(angVel0);
2706 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2711 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2712 dynamics()->setAngVel2(angVel2);
2717 status.
setBit(eStatus::WheelieRot);
2719 status.
resetBit(eStatus::WheelieRot);
2746 constexpr u16 MAX_MT_CHARGE = 270;
2747 constexpr u16 BASE_MT_CHARGE = 2;
2748 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2749 constexpr u16 EXTRA_MT_CHARGE = 3;
2751 if (m_driftState != DriftState::ChargingMt) {
2757 f32 stickX = state()->stickX();
2758 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2759 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2768 m_driftState = DriftState::ChargedMt;
2773void KartMoveBike::initOob() {
2774 KartMove::initOob();
2781 constexpr s16 COOLDOWN_FRAMES = 20;
2782 bool dpadUp = inputs()->currentState().trickUp();
2783 auto &status = KartObjectProxy::status();
2788 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.
constexpr void setAxisRotation(f32 angle, const EGG::Vector3f &axis)
Rotates the matrix about an axis.
constexpr Vector3f multVector(const Vector3f &vec) const
Multiplies a vector by a matrix.
constexpr Vector3f multVector33(const Vector3f &vec) const
Multiplies a 3x3 matrix by a vector.
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
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.
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
constexpr bool onAllBit(Es... es) const
Checks if all 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.
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_leanRotCap
The maximum leaning rotation.
const TurningParameters * m_turningParams
Inside/outside drifting bike turn info.
void calcVehicleRotation(f32) override
Every frame, calculates rotation, EV, and angular velocity for the bike.
void setTurnParams() override
On init, sets the bike's lean rotation cap and increment.In addition to setting the lean rotation cap...
virtual void cancelWheelie()
Clears the wheelie bit flag and resets the rotation decrement.
void tryStartWheelie()
STAGE 1+ - Every frame, checks player input to see if we should start or stop a wheelie.
f32 m_leanRotInc
The incrementor for leaning rotation.
s16 m_wheelieCooldown
The number of frames before another wheelie can start.
void onWallCollision() override
Called when you collide with a wall. All it does for bikes is cancel wheelies.
bool canWheelie() const override
Checks if the kart is going fast enough to wheelie.
f32 m_maxWheelieRot
The maximum wheelie rotation.
void calcWheelie() override
STAGE 1+ - Every frame, checks player input for wheelies and computes wheelie rotation.
u32 m_wheelieFrames
Tracks wheelie duration and cancels the wheelie after 180 frames.
f32 m_leanRot
Z-axis rotation of the bike from leaning.
f32 m_wheelieRotDec
The wheelie rotation decrementor, used after a wheelie has ended.
virtual void startWheelie()
STAGE 1+ - Sets the wheelie bit flag and some wheelie-related variables.
f32 m_wheelieRot
X-axis rotation from wheeling.
void calcMtCharge() override
Every frame during a drift, calculates MT charge based on player input.
void onHop() override
Virtual function that just cancels wheelies when you hop.
void releaseMt()
Stops charging a mini-turbo, and applies boost if charged.
s16 m_offroadInvincibility
How many frames until the player is affected by offroad.
bool canStartDrift() const
void calcOffroad()
Each frame, computes rotation and speed scalars from the floor KCL.
void controlOutsideDriftAngle()
Every frame, handles mini-turbo charging and outside drifting bike rotation.
f32 m_speed
Current speed, restricted to the soft speed limit.
KartScale * m_kartScale
Manages scaling due to TF stompers and MH cars.
u16 m_mtCharge
A value between 0 and 270 representing current MT charge.
virtual void calcTurn()
Each frame, looks at player input and kart stats. Saves turn-related info.
s16 m_backwardsAllowCounter
Tracks the 15f delay before reversing.
s16 m_bumpTimer
Set when a Reaction::SmallBump collision occurs.
void calcDive()
Responds to player input to handle up/down kart tilt mid-air.
EGG::Vector3f m_lastDir
m_speed from the previous frame but with signed magnitude.
f32 calcWallCollisionSpeedFactor(f32 &f1)
Every frame, computes a speed scalar if we are colliding with a wall.
s16 m_respawnPreLandTimer
Counts down from 4 when pressing A before landing from respawn.
s32 getAppliedHopStickX() const
Factors in vehicle speed to retrieve our hop direction and magnitude.
void resetDriftManual()
Clears drift state. Called when touching ground and drift is canceled.
void calcSsmt()
Calculates standstill mini-turbo components, if applicable.
void calcAcceleration()
Every frame, applies acceleration to the kart's internal velocity.
void startManualDrift()
Called when the player lands from a drift hop, or to start a slipdrift.
void setOffroadInvincibility(s16 timer)
Ignores offroad KCL collision for a set amount of time.
f32 calcVehicleAcceleration() const
Every frame, computes acceleration based off the character/vehicle stats.
void calcRotation()
Every frame, calculates kart rotation based on player input.
EGG::Vector3f m_up
Vector perpendicular to the floor, pointing upwards.
const DriftingParameters * m_driftingParams
Drift-type-specific parameters.
f32 m_weightedTurn
Magnitude+direction of stick input, factoring in the kart's stats.
f32 m_softSpeedLimit
Base speed + boosts + wheelies, restricted to the hard speed limit.
void calcVehicleSpeed()
Every frame, computes speed based on acceleration and any active boosts.
f32 m_kclWheelRotFactor
The slowest rotation multiplier of each wheel's floor collision.
s32 m_hopStickX
A ternary for the direction of our hop, 0 if still neutral hopping.
void calcDisableBackwardsAccel()
Computes the current cooldown duration between braking and reversing.
f32 m_outsideDriftBonus
Added to angular velocity when outside drifting.
f32 m_kclRotFactor
Float between 0-1 that scales the player's turning radius on offroad.
bool calcPreDrift()
Each frame, checks for hop or slipdrift. Computes drift direction based on player input.
f32 m_jumpPadMinSpeed
Snaps the player to a minimum speed when first touching a jump pad.
f32 m_divingRot
Induces x-axis angular velocity based on up/down stick input.
void calcAutoDrift()
Each frame, handles automatic transmission drifting.
f32 m_lastSpeed
Last frame's speed, cached to calculate angular velocity.
DrivingDirection m_drivingDirection
Current state of driver's direction.
void calcOffroadInvincibility()
Checks a timer to see if we are still ignoring offroad slowdown.
f32 m_hopGravity
Always main gravity (-1.3f).
s16 m_respawnPostLandTimer
Counts up to 4 if not accelerating after respawn landing.
f32 m_hopPosY
Relative position as the result of a hop. Starts at 0.
EGG::Vector3f m_hopDir
Used for outward drift. Tracks the forward vector of our rotation.
void calcStandstillBoostRot()
STAGE Computes the x-component of angular velocity based on the kart's speed.
EGG::Vector3f m_scale
Normally the unit vector, but may vary due to crush animations.
f32 m_speedDragMultiplier
After 5 frames of airtime, this causes speed to slowly decay.
u16 m_floorCollisionCount
The number of tires colliding with the floor.
u16 m_crushTimer
Number of frames until player will be uncrushed.
f32 m_hardSpeedLimit
Absolute speed cap. It's 120, unless you're in a bullet (140).
u16 m_mushroomBoostTimer
Number of frames until the mushroom boost runs out.
f32 m_rawTurn
Float in range [-1, 1]. Represents stick magnitude + direction.
f32 m_hopVelY
Relative velocity due to a hop. Starts at 10 and decreases with gravity.
void tryStartJumpPad()
Applies calculations to start interacting with a jump pad.
s16 m_ssmtCharge
Increments every frame up to 75 when charging stand-still MT.
void calc()
Each frame, calculates the kart's movement.
f32 m_kclWheelSpeedFactor
The slowest speed multiplier of each wheel's floor collision.
s32 m_hopFrame
A timer that can prevent subsequent hops until reset.
s16 m_ssmtDisableAccelTimer
Counter that tracks delay before starting to reverse.
f32 m_outsideDriftAngle
The facing angle of an outward-drifting vehicle.
void calcSpecialFloor()
Every frame, calculates any boost resulting from a boost panel.
void calcAirtimeTop()
Calculates rotation of the bike due to excessive airtime.
void calcMushroomBoost()
Checks a timer to see if we are still boosting from a mushroom.
f32 m_kclSpeedFactor
Float between 0-1 that scales the player's speed on offroad.
KartBurnout m_burnout
Manages the state of start boost burnout.
s16 m_timeInRespawn
The number of frames elapsed after position snap from respawn.
f32 m_speedRatioCapped
m_speedRatio but capped at 1.0f.
f32 m_processedSpeed
Offset 0x28. It's only ever just a copy of m_speed.
EGG::Vector3f m_hopUp
The up vector when hopping.
void setInitialPhysicsValues(const EGG::Vector3f &position, const EGG::Vector3f &angles)
Initializes the kart's position and rotation. Calls tire suspension initializers.
f32 m_realTurn
The "true" turn magnitude. Equal to m_weightedTurn unless drifting.
@ WaitingForBackwards
Holding reverse but waiting on a 15 frame delay.
void calcSsmtStart()
Calculates whether we are starting a standstill mini-turbo.
EGG::Vector3f m_smoothedUp
A smoothed up vector, mostly used after significant airtime.
virtual void calcMtCharge()
Every frame during a drift, calculates MT/SMT charge based on player input.
virtual void calcVehicleRotation(f32 turn)
Every frame, calculates rotation, EV, and angular velocity for the kart.
f32 m_speedRatio
The ratio between current speed and the player's base speed stat.
KartHalfPipe * m_halfPipe
Pertains to zipper physics.
void calcWallCollisionStart(f32 param_2)
If we started to collide with a wall this frame, applies rotation.
virtual void hop()
Initializes hop information, resets upwards EV and clears upwards force.
EGG::Vector3f m_outsideDriftLastDir
Used to compute the next m_outsideDriftAngle.
s16 m_ssmtLeewayTimer
Frames to forgive letting go of A before clearing SSMT charge.
f32 m_totalScale
[Unused] Always 1.0f
f32 m_baseSpeed
The speed associated with the current character/vehicle stats.
f32 m_acceleration
Captures the acceleration from player input and boosts.
virtual f32 getWheelieSoftSpeedLimitBonus() const
Returns the % speed boost from wheelies. For karts, this is always 0.
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.
@ 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 calcManualDrift()
Each frame, handles hopping, drifting, and mini-turbos.
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.
@ 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.
@ BikeSideStuntTrick
A side StuntTrick with a bike.
A quaternion, used to represent 3D rotation.
constexpr Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
constexpr void normalise()
Scales the quaternion to a unit length.
constexpr void setAxisRotation(f32 angle, const EGG::Vector3f &axis)
Set the quat given angle and axis.
constexpr void makeAllZero()
Resets all the bits to zero.
constexpr TBitFlag< T, E > & resetBit(Es... es)
Resets 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 TBitFlag< T, E > & changeBit(bool on, Es... es)
Changes the state of the corresponding bits for the provided enum values.
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets 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 Vector3f rej(const Vector3f &rhs) const
The rejection of this vector onto rhs.
constexpr f32 normalise()
Normalizes the vector and returns the original length.
constexpr f32 squaredLength() const
The dot product between the vector and itself.
constexpr f32 length() const
The square root of the vector's dot product.
constexpr Vector3f proj(const Vector3f &rhs) const
The projection of this vector onto rhs.
constexpr f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
constexpr Vector3f perpInPlane(const EGG::Vector3f &rhs, bool normalise) const
Calculates the orthogonal vector, based on the plane defined by this vector and rhs.
Information about the current collision and its properties.
Various character/vehicle-related handling and speed stats.
std::array< f32, 32 > kclSpeed
Speed multipliers, indexed using KCL attributes.
f32 speed
Base full speed of the character/vehicle combo.
f32 driftReactivity
A weight applied to turn radius when drifting.
std::array< f32, 32 > kclRot
Rotation scalars, indexed using KCL attributes.
f32 driftManualTightness
Affects turn radius when manual drifting.
u32 miniTurbo
The framecount duration of a charged mini-turbo.
f32 driftAutomaticTightness
Affects turn radius when automatic drifting.
f32 handlingReactivity
A weight applied to turn radius when not drifting.