3#include "game/kart/KartCollide.hh"
4#include "game/kart/KartDynamics.hh"
5#include "game/kart/KartJump.hh"
6#include "game/kart/KartParam.hh"
7#include "game/kart/KartPhysics.hh"
8#include "game/kart/KartScale.hh"
9#include "game/kart/KartSub.hh"
10#include "game/kart/KartSuspension.hh"
12#include "game/field/CollisionDirector.hh"
15#include "game/item/ItemDirector.hh"
16#include "game/item/KartItem.hh"
18#include "game/system/CourseMap.hh"
19#include "game/system/RaceManager.hh"
20#include "game/system/map/MapdataCannonPoint.hh"
21#include "game/system/map/MapdataJugemPoint.hh"
23#include <egg/math/Math.hh>
24#include <egg/math/Quat.hh>
35static constexpr std::array<CannonParameter, 3> CANNON_PARAMETERS = {{
36 {500.0f, 0.0f, 6000.0f, -1.0f},
37 {500.0f, 5000.0f, 6000.0f, -1.0f},
38 {120.0f, 2000.0f, 1000.0f, 45.0f},
42KartMove::KartMove() : m_smoothedUp(
EGG::Vector3f::ey), m_scale(1.0f, 1.0f, 1.0f) {
45 m_padType.makeAllZero();
46 m_flags.makeAllZero();
51KartMove::~KartMove() {
58void KartMove::createSubsystems() {
59 m_jump =
new KartJump(
this);
71 auto &status = KartObjectProxy::status();
73 if (status.
onBit(eStatus::InAction, eStatus::CannonStart, eStatus::InCannon,
84 if (status.
onBit(eStatus::JumpPadMushroomCollision)) {
94 if (state()->isDrifting()) {
105 if (!state()->isDrifting()) {
115void KartMove::setTurnParams() {
116 static constexpr std::array<DriftingParameters, 3> DRIFTING_PARAMS_ARRAY = {{
117 {10.0f, 0.5f, 0.5f, 1.0f},
118 {10.0f, 0.5f, 0.5f, 0.2f},
119 {10.0f, 0.22f, 0.5f, 0.2f},
126 m_landingDir = m_dir;
128 m_driftingParams = &DRIFTING_PARAMS_ARRAY[
static_cast<u32
>(param()->stats().driftType)];
133void KartMove::init(
bool b1,
bool b2) {
141 m_up = EGG::Vector3f::ey;
143 m_vel1Dir = EGG::Vector3f::ez;
145 m_dir = EGG::Vector3f::ez;
146 m_landingDir = EGG::Vector3f::ez;
147 m_dirDiff = EGG::Vector3f::zero;
148 m_hasLandingDir =
false;
150 m_landingAngle = 0.0f;
168 m_standStillBoostRot = 0.0f;
169 m_driftState = DriftState::NotDrifting;
174 m_zipperBoostTimer = 0;
175 m_zipperBoostMax = 0;
181 m_nonZipperAirtime = 0;
188 m_hitboxScale = 1.0f;
194 m_jumpPadMaxSpeed = 0.0f;
195 m_jumpPadBoostMultiplier = 0.0f;
196 m_jumpPadProperties =
nullptr;
198 m_autoDriftAngle = 0.0f;
199 m_autoDriftStartFrameCounter = 0;
201 m_cannonEntryOfsLength = 0.0f;
202 m_cannonEntryPos.setZero();
203 m_cannonEntryOfs.setZero();
204 m_cannonOrthog.setZero();
205 m_cannonProgress.setZero();
224void KartMove::clear() {
225 auto &status = KartObjectProxy::status();
236 clearOffroadInvincibility();
245 EGG::Quatf quaternion = EGG::Quatf::FromRPY(angles * DEG2RAD);
248 Field::KCLTypeMask kcl_flags = KCL_NONE;
250 bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos,
251 EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0);
254 newPos = newPos + info.tangentOff + (info.floorNrm * -100.0f);
255 newPos += info.floorNrm * bsp().initialYPos;
261 sub()->initPhysicsValues();
263 physics()->setPos(pos());
264 physics()->setVelocity(dynamics()->velocity());
269 dynamics()->setTop(
m_up);
271 for (
u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) {
272 suspension(tireIdx)->setInitialState();
282 auto &status = KartObjectProxy::status();
284 if (status.
onBit(eStatus::InRespawn)) {
289 dynamics()->resetInternalVelocity();
319 if (status.
onBit(eStatus::InCannon)) {
330void KartMove::calcRespawnStart() {
331 constexpr float RESPAWN_HEIGHT = 700.0f;
333 const auto *jugemPoint = System::RaceManager::Instance()->jugemPoint();
338 respawnPos.y += RESPAWN_HEIGHT;
343 Item::ItemDirector::Instance()->kartItem(0).clear();
345 status().
resetBit(eStatus::TriggerRespawn).setBit(eStatus::InRespawn);
349void KartMove::calcInRespawn() {
350 constexpr f32 LAKITU_VELOCITY = 1.5f;
351 constexpr u16 RESPAWN_DURATION = 110;
353 auto &status = KartObjectProxy::status();
355 if (status.
offBit(eStatus::InRespawn)) {
360 newPos.y -= LAKITU_VELOCITY;
361 dynamics()->setPos(newPos);
362 dynamics()->setNoGravity(
true);
368 dynamics()->setNoGravity(
false);
373void KartMove::calcRespawnBoost() {
374 constexpr s16 RESPAWN_BOOST_DURATION = 30;
375 constexpr s16 RESPAWN_BOOST_INPUT_LENIENCY = 4;
377 auto &status = KartObjectProxy::status();
379 if (status.
onBit(eStatus::AfterRespawn)) {
383 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
384 m_respawnTimer = RESPAWN_BOOST_DURATION;
390 status.
resetBit(eStatus::AfterRespawn);
404 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
405 m_respawnTimer = RESPAWN_BOOST_DURATION;
417 m_respawnTimer = std::max(0, m_respawnTimer - 1);
421void KartMove::calcTop() {
422 f32 stabilizationFactor = 0.1f;
423 m_hasLandingDir =
false;
425 auto &status = KartObjectProxy::status();
431 m_dirDiff = m_landingDir.
proj(m_landingDir);
432 m_hasLandingDir =
true;
438 inputTop.
dot(m_dir) > 0.0f &&
m_speed > 50.0f &&
439 collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::NotTrickable)) {
451 f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(inputTop.
dot(componentZAxis())));
452 scalar = std::min(0.8f, std::max(0.3f, topDotZ));
460 if (bodyDotFront < -0.1f) {
461 stabilizationFactor += std::min(0.2f, EGG::Mathf::abs(bodyDotFront) * 0.5f);
464 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
465 stabilizationFactor = 0.4f;
472 dynamics()->setStabilizationFactor(stabilizationFactor);
475 m_flags.
changeBit(collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::Trickable),
482 auto &status = KartObjectProxy::status();
495 if (
m_up.y <= 0.99f) {
496 m_up += (EGG::Vector3f::ey -
m_up) * 0.03f;
499 m_up = EGG::Vector3f::ey;
507 const auto *raceMgr = System::RaceManager::Instance();
508 if (!raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
512 if (m_padType.
onBit(ePadType::BoostPanel)) {
513 tryStartBoostPanel();
516 if (m_padType.
onBit(ePadType::BoostRamp)) {
520 if (m_padType.
onBit(ePadType::JumpPad)) {
528void KartMove::calcDirs() {
532 m_flags.
setBit(eFlags::LaunchBoost);
533 auto &status = KartObjectProxy::status();
537 !m_jump->isBoostRampEnabled()) &&
538 status.
offBit(eStatus::JumpPad) && state()->airtime() <= 5) ||
539 status.
onBit(eStatus::JumpPadMushroomCollision,
540 eStatus::NoSparkInvisibleWall))) {
553 if (dirDiff.
squaredLength() <= std::numeric_limits<f32>::epsilon()) {
564 if (origDirCross.
dot(newDirCross) < 0.0f) {
571 m_flags.
resetBit(eFlags::LaunchBoost);
580 if (m_hasLandingDir) {
581 f32 dot = m_dir.dot(m_landingDir);
583 f32 crossDot = cross.
length();
584 f32 angle = EGG::Mathf::atan2(crossDot, dot);
585 angle = EGG::Mathf::abs(angle);
592 m_landingAngle += (angle * RAD2DEG) * fVar4;
595 if (m_landingAngle <= 0.0f) {
596 if (m_landingAngle < 0.0f) {
597 m_landingAngle = std::min(0.0f, m_landingAngle + 2.0f);
600 m_landingAngle = std::max(0.0f, m_landingAngle - 2.0f);
605void KartMove::calcStickyRoad() {
606 constexpr f32 STICKY_RADIUS = 200.0f;
607 constexpr Field::KCLTypeMask STICKY_MASK =
610 auto &status = KartObjectProxy::status();
618 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::Trickable)) ||
619 EGG::Mathf::abs(
m_speed) <= 20.0f) {
627 colInfo.bbox.setZero();
628 Field::KCLTypeMask kcl_flags = KCL_NONE;
629 bool stickyRoad =
false;
631 for (
size_t i = 0; i < 3; ++i) {
633 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
634 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
635 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
636 dynamics()->setMovingObjVel(dynamics()->movingObjVel().rej(colInfo.floorNrm));
642 pos += -STICKY_RADIUS * componentYAxis();
654 auto &status = KartObjectProxy::status();
684void KartMove::calcBoost() {
685 auto &status = KartObjectProxy::status();
687 if (m_boost.
calc()) {
697void KartMove::calcRampBoost() {
698 auto &status = KartObjectProxy::status();
700 if (status.
offBit(eStatus::RampBoost)) {
705 if (--m_rampBoost < 1) {
707 status.
resetBit(eStatus::RampBoost);
715 auto &status = KartObjectProxy::status();
732 constexpr s16 MAX_SSMT_CHARGE = 75;
733 constexpr s16 SSMT_BOOST_FRAMES = 30;
734 constexpr s16 LEEWAY_FRAMES = 1;
735 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
739 auto &status = KartObjectProxy::status();
765 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
772 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
789 auto &status = KartObjectProxy::status();
834 m_driftState = DriftState::NotDrifting;
846 m_driftState = DriftState::NotDrifting;
852 m_autoDriftAngle = 0.0f;
854 m_autoDriftStartFrameCounter = 0;
858void KartMove::clearJumpPad() {
860 status().
resetBit(eStatus::JumpPad);
864void KartMove::clearRampBoost() {
866 status().
resetBit(eStatus::RampBoost);
870void KartMove::clearZipperBoost() {
871 m_zipperBoostTimer = 0;
876void KartMove::clearBoost() {
877 m_boost.resetActive();
882void KartMove::clearSsmt() {
890void KartMove::clearOffroadInvincibility() {
895void KartMove::clearRejectRoad() {
903 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
905 auto &status = KartObjectProxy::status();
913 EGG::Mathf::abs(state()->stickX()) > 0.85f) {
914 m_autoDriftStartFrameCounter =
915 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
917 m_autoDriftStartFrameCounter = 0;
920 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
921 status.
setBit(eStatus::DriftAuto);
924 if (state()->stickX() < 0.0f) {
934 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
935 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
937 status.
resetBit(eStatus::DriftAuto);
940 if (m_autoDriftAngle > 0.0f) {
942 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
945 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
951 physics()->composeExtraRot(angleAxis);
959 auto &status = KartObjectProxy::status();
965 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
966 status.
offBit(eStatus::JumpPadMushroomCollision) &&
968 m_flags.
onBit(eFlags::LaunchBoost)) {
973 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
974 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
976 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
1006 if (action()->flags().offBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
1007 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
1040 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1042 const auto &stats = param()->stats();
1043 auto &status = KartObjectProxy::status();
1045 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1046 f32 driftAngle = 0.0f;
1074 m_driftState = DriftState::ChargingMt;
1082 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1084 auto &status = KartObjectProxy::status();
1087 m_driftState = DriftState::NotDrifting;
1093 if (m_driftState == DriftState::ChargedSmt) {
1094 mtLength *= SMT_LENGTH_FACTOR;
1098 activateBoost(KartBoost::Type::AllMt, mtLength);
1101 m_driftState = DriftState::NotDrifting;
1108 if (state()->airtime() > 5) {
1112 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1115 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1116 if (angle > targetAngle) {
1118 }
else if (angle < targetAngle) {
1124 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1125 if (targetAngle > angle) {
1127 }
else if (targetAngle < angle) {
1142 auto &status = KartObjectProxy::status();
1143 bool drifting = state()->isDrifting() && status.
offBit(eStatus::JumpPadMushroomCollision);
1145 const auto &stats = param()->stats();
1148 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1150 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1153 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1158 bool forwards =
true;
1172 bool noTurn =
false;
1174 EGG::Mathf::abs(
m_speed) < 1.0f) {
1180 if (forwards && !noTurn) {
1184 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1187 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1200 f32 stickX = EGG::Mathf::abs(state()->stickX());
1201 if (autoDrift && stickX > 0.3f) {
1202 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1203 stickX = drifting ? 0.2f : 0.5f;
1210 if (status.
onBit(eStatus::RampBoost) && m_jump->isBoostRampEnabled()) {
1212 }
else if (status.
offBit(eStatus::JumpPadMushroomCollision)) {
1213 u32 airtime = state()->airtime();
1214 if (airtime >= 70) {
1216 }
else if (airtime >= 30) {
1217 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1223 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1224 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1226 if (angle > 60.0f) {
1227 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1238 const auto *raceMgr = System::RaceManager::Instance();
1239 auto &status = KartObjectProxy::status();
1241 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1242 f32 speedFix = dynamics()->speedFix();
1243 if (status.
onBit(eStatus::InAction) ||
1245 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1258 if (status.
onBit(eStatus::InAction)) {
1259 action()->calcVehicleSpeed();
1266 if (status.
onBit(eStatus::RampBoost) && state()->airtime() < 4) {
1275 if (state()->airtime() > 5) {
1286 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost)) {
1293 eStatus::SomethingWallCollision)) {
1307 const auto &stats = param()->stats();
1310 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1324 initialVel = std::min(initialVel * 2.0f, 2.0f);
1326 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1340 std::span<const f32> as;
1341 std::span<const f32> ts;
1342 if (state()->isDrifting()) {
1343 as = param()->stats().accelerationDriftA;
1344 ts = param()->stats().accelerationDriftT;
1346 as = param()->stats().accelerationStandardA;
1347 ts = param()->stats().accelerationStandardT;
1351 f32 acceleration = 0.0f;
1353 for (; i < ts.size(); ++i) {
1354 if (ratio < ts[i]) {
1355 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1362 return i < ts.size() ? acceleration : as.back();
1369 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1370 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1371 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1372 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1373 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1376 auto &status = KartObjectProxy::status();
1410 f32 speedLimit = status.
onBit(eStatus::JumpPad) ? m_jumpPadMaxSpeed :
m_baseSpeed;
1411 const f32 boostMultiplier = m_boost.multiplier();
1412 const f32 boostSpdLimit = m_boost.speedLimit();
1413 m_jumpPadBoostMultiplier = boostMultiplier;
1415 f32 crushMultiplier = status.
onBit(eStatus::Crushed) ? 0.7f : 1.0f;
1417 speedLimit *= status.
onBit(eStatus::JumpPadFixedSpeed) ?
1423 f32 boostSpeed = ignoreCrushSpeed ? 1.0f : crushMultiplier;
1426 if (status.
offBit(eStatus::JumpPad) && boostSpeed > 0.0f && boostSpeed > speedLimit) {
1427 speedLimit = boostSpeed;
1432 if (status.
onBit(eStatus::RampBoost)) {
1433 speedLimit = std::max(speedLimit, 100.0f);
1438 f32 local_c8 = 1.0f;
1453 if (status.
onBit(eStatus::JumpPad)) {
1464 crossVec = -crossVec;
1467 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1468 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1469 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1471 rotationScalar = ROTATION_SCALAR_MIDAIR;
1478 const auto *raceMgr = System::RaceManager::Instance();
1481 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1490 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1492 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1499 DrivingDirection::Backwards;
1515 auto &status = KartObjectProxy::status();
1528 if (wallNrm.y > 0.0f) {
1536 f1 = std::max(0.0f, dot + 1.0f);
1550 auto &status = KartObjectProxy::status();
1557 if (status.
offBit(eStatus::InAction)) {
1560 m_landingDir = m_dir;
1567 if (speedDiff > 30.0f) {
1570 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1574 speedDiff = std::min(60.0f, speedDiff);
1577 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1582 proj = EGG::Vector3f::zero;
1583 rej = EGG::Vector3f::zero;
1586 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1587 proj = EGG::Vector3f::zero;
1592 f32 bumpDeviation = 0.0f;
1594 bumpDeviation = param()->stats().bumpDeviationLevel;
1599 dynamics()->addForce(colData.wallNrm * 15.0f);
1600 collide()->startFloorMomentRate();
1604 dynamics()->addForce(colData.wallNrm * 15.0f);
1605 collide()->startFloorMomentRate();
1617 auto &status = KartObjectProxy::status();
1620 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1621 next = 0.015f * -state()->startBoostCharge();
1623 if (status.
offBit(eStatus::JumpPad, eStatus::RampBoost, eStatus::SoftWallDrift)) {
1625 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1628 next = (scalar * 0.15f) * 0.25f;
1633 next = (scalar * 0.15f) * 0.08f;
1638 constexpr s16 MAX_SSMT_CHARGE = 75;
1639 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1644 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1646 m_standStillBoostRot += scalar * (next - m_standStillBoostRot);
1654 constexpr f32 DIVE_LIMIT = 0.8f;
1658 auto &status = KartObjectProxy::status();
1665 f32 stickY = state()->stickY();
1668 stickY = std::min(1.0f, stickY + 0.4f);
1671 u32 airtime = state()->airtime();
1674 if (EGG::Mathf::abs(stickY) < 0.1f) {
1678 stickY *= (airtime / 50.0f);
1685 dynamics()->setAngVel2(angVel2);
1687 if (state()->airtime() < 50) {
1693 f32 upDotTop =
m_up.
dot(topRotated);
1695 f32 crossNorm = upCrossTop.
length();
1696 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1698 f32 fVar1 = angle * RAD2DEG - 20.0f;
1699 if (fVar1 <= 0.0f) {
1703 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1704 if (forwardRotated.y > 0.0f) {
1705 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1707 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1715 auto &status = KartObjectProxy::status();
1727void KartMove::calcHopPhysics() {
1738void KartMove::calcRejectRoad() {
1739 m_reject.calcRejectRoad();
1743bool KartMove::calcZipperCollision(f32 radius, f32 scale,
EGG::Vector3f &pos,
1745 Field::KCLTypeMask *maskOut, Field::KCLTypeMask flags)
const {
1747 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1749 auto *colDir = Field::CollisionDirector::Instance();
1750 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1755 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.
dot(to)));
1756 f32 acos = EGG::Mathf::acos(dotNorm);
1757 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1761void KartMove::applyForce(f32 force,
const EGG::Vector3f &hitDir,
bool stop) {
1767 collide()->startFloorMomentRate();
1779 f32 tiltMagnitude = 0.0f;
1780 auto &status = KartObjectProxy::status();
1782 if (status.
offBit(eStatus::InAction, eStatus::SoftWallDrift) &&
1787 f32 magnitude = tiltMagnitude;
1789 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1790 magnitude = frontSpeed.
length();
1792 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1793 magnitude = -magnitude;
1796 tiltMagnitude = -1.0f;
1797 if (-1.0f <= magnitude) {
1798 tiltMagnitude = std::min(1.0f, magnitude);
1804 dynamics()->setAngVel0(angVel0);
1807 f32 lean = EGG::Mathf::abs(
m_weightedTurn) * (tiltMagnitude * param()->stats().tilt);
1812 angVel0.x += m_standStillBoostRot;
1814 dynamics()->setAngVel0(angVel0);
1818 dynamics()->setAngVel2(angVel2);
1828 constexpr u16 MAX_MT_CHARGE = 270;
1829 constexpr u16 MAX_SMT_CHARGE = 300;
1830 constexpr u16 BASE_MT_CHARGE = 2;
1831 constexpr u16 BASE_SMT_CHARGE = 2;
1832 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1833 constexpr u16 EXTRA_MT_CHARGE = 3;
1835 if (m_driftState == DriftState::ChargedSmt) {
1839 f32 stickX = state()->stickX();
1841 if (m_driftState == DriftState::ChargingMt) {
1844 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1845 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1854 m_driftState = DriftState::ChargingSmt;
1858 if (m_driftState != DriftState::ChargingSmt) {
1864 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1865 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1874 m_driftState = DriftState::ChargedSmt;
1879void KartMove::initOob() {
1885 clearOffroadInvincibility();
1897 m_driftState = DriftState::NotDrifting;
1909 dynamics()->setExtVel(extVel);
1912 totalForce.y = 0.0f;
1913 dynamics()->setTotalForce(totalForce);
1917void KartMove::tryStartBoostPanel() {
1918 constexpr s16 BOOST_PANEL_DURATION = 60;
1924 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
1932 constexpr s16 BOOST_RAMP_DURATION = 60;
1934 auto &status = KartObjectProxy::status();
1940 status.
setBit(eStatus::RampBoost);
1941 m_rampBoost = BOOST_RAMP_DURATION;
1950 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
1951 {50.0f, 50.0f, 35.0f},
1952 {50.0f, 50.0f, 47.0f},
1953 {59.0f, 59.0f, 30.0f},
1954 {73.0f, 73.0f, 45.0f},
1955 {73.0f, 73.0f, 53.0f},
1956 {55.0f, 55.0f, 35.0f},
1957 {56.0f, 56.0f, 50.0f},
1960 auto &status = KartObjectProxy::status();
1966 status.
setBit(eStatus::JumpPad);
1967 s32 jumpPadVariant = state()->jumpPadVariant();
1968 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
1970 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
1971 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
1974 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
1975 {100.0f, 100.0f, 70.0f},
1976 {100.0f, 100.0f, 65.0f},
1978 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
1981 status.
setBit(eStatus::JumpPadFixedSpeed);
1984 if (jumpPadVariant == 4) {
1985 status.
setBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadMushroomVelYInc,
1986 eStatus::JumpPadMushroomCollision);
1991 extVel.y = m_jumpPadProperties->velY;
1992 totalForce.y = 0.0f;
1994 dynamics()->setExtVel(extVel);
1995 dynamics()->setTotalForce(totalForce);
1997 if (jumpPadVariant != 3) {
2004 status.
setBit(eStatus::JumpPadDisableYsusForce);
2009 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
2014void KartMove::tryEndJumpPad() {
2015 auto &status = KartObjectProxy::status();
2016 if (status.
onBit(eStatus::JumpPadMushroomTrigger)) {
2018 status.
resetBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadFixedSpeed,
2019 eStatus::JumpPadMushroomVelYInc);
2022 if (status.
onBit(eStatus::JumpPadMushroomVelYInc)) {
2024 newExtVel.y += 20.0f;
2025 if (m_jumpPadProperties->velY < newExtVel.y) {
2026 newExtVel.y = m_jumpPadProperties->velY;
2027 status.
resetBit(eStatus::JumpPadMushroomVelYInc);
2029 dynamics()->setExtVel(newExtVel);
2039void KartMove::cancelJumpPad() {
2041 status().
resetBit(eStatus::JumpPad);
2045void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
2046 if (m_boost.
activate(type, frames)) {
2052void KartMove::applyStartBoost(s16 frames) {
2053 activateBoost(KartBoost::Type::AllMt, frames);
2057void KartMove::activateMushroom() {
2058 constexpr s16 MUSHROOM_DURATION = 90;
2060 auto &status = KartObjectProxy::status();
2066 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
2074void KartMove::activateZipperBoost() {
2075 constexpr s16 BASE_DURATION = 50;
2076 constexpr s16 TRICK_DURATION = 100;
2078 auto &status = KartObjectProxy::status();
2085 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
2088 m_zipperBoostTimer = 0;
2089 m_zipperBoostMax = boostDuration;
2109 auto &status = KartObjectProxy::status();
2125 auto &status = KartObjectProxy::status();
2139void KartMove::calcZipperBoost() {
2140 auto &status = KartObjectProxy::status();
2149 m_zipperBoostTimer = 0;
2153 if (m_zipperBoostTimer < 10) {
2156 dynamics()->setAngVel0(angVel);
2161void KartMove::landTrick() {
2162 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2167 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2179 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2181 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2184 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2188void KartMove::activateCrush(
u16 timer) {
2189 status().
setBit(eStatus::Crushed);
2195void KartMove::calcCrushed() {
2196 if (status().offBit(eStatus::Crushed)) {
2201 status().
resetBit(eStatus::Crushed);
2207void KartMove::calcScale() {
2213void KartMove::applyBumpForce(f32 speed,
const EGG::Vector3f &hitDir,
bool resetSpeed) {
2214 constexpr s16 BUMP_COOLDOWN = 5;
2220 dynamics()->addForce(speed * hitDir.
perpInPlane(move()->up(),
true));
2221 collide()->startFloorMomentRate();
2231void KartMove::enterCannon() {
2233 physics()->clearDecayingRot();
2234 m_boost.resetActive();
2236 auto &status = KartObjectProxy::status();
2244 clearOffroadInvincibility();
2246 dynamics()->reset();
2251 .setBit(eStatus::InCannon, eStatus::SkipWheelCalc);
2253 const auto [cannonPos, cannonDir] = getCannonPosRot();
2254 m_cannonEntryPos = pos();
2255 m_cannonEntryOfs = cannonPos - pos();
2256 m_cannonEntryOfsLength = m_cannonEntryOfs.
normalise();
2258 m_dir = m_cannonEntryOfs;
2259 m_vel1Dir = m_cannonEntryOfs;
2260 m_cannonOrthog = EGG::Vector3f::ey.
perpInPlane(m_cannonEntryOfs,
true);
2261 m_cannonProgress.setZero();
2265void KartMove::calcCannon() {
2266 auto [cannonPos, cannonDir] = getCannonPosRot();
2267 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2269 f32 forwardLength = forward.
normalise();
2283 if (forwardLength < 30.0f || local94.
dot(forwardXZ) <= 0.0f) {
2288 const auto *cannonPoint =
2289 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2290 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2291 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2292 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2293 f32 newSpeed = cannonParams.speed;
2294 if (forwardLength < cannonParams.decelFactor) {
2295 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2297 newSpeed = cannonParams.endDecel;
2298 if (newSpeed <= 0.0f) {
2302 newSpeed += factor * (cannonParams.speed - newSpeed);
2303 if (cannonParams.endDecel > 0.0f) {
2308 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2311 if (cannonParams.height > 0.0f) {
2312 f32 fVar9 = EGG::Mathf::SinFIdx(
2313 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2314 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2317 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2318 m_dir = m_cannonEntryOfs;
2319 m_vel1Dir = m_cannonEntryOfs;
2321 calcRotCannon(forward);
2323 dynamics()->setExtVel(EGG::Vector3f::zero);
2331 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2337 local80 *= dynamics()->fullRot();
2342 dynamics()->setFullRot(newRot);
2343 dynamics()->setMainRot(newRot);
2347void KartMove::exitCannon() {
2348 auto &status = KartObjectProxy::status();
2350 if (status.
offBit(eStatus::InCannon)) {
2354 status.
resetBit(eStatus::InCannon, eStatus::SkipWheelCalc).setBit(eStatus::AfterCannon);
2355 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2359void KartMove::triggerRespawn() {
2361 status().
setBit(eStatus::TriggerRespawn);
2365KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2368KartMoveBike::~KartMoveBike() =
default;
2373 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2374 constexpr u16 WHEELIE_COOLDOWN = 20;
2381 m_autoHardStickXFrames = 0;
2390 m_autoHardStickXFrames = 0;
2394void KartMoveBike::createSubsystems() {
2406 const auto *raceManager = System::RaceManager::Instance();
2408 auto &status = KartObjectProxy::status();
2411 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2412 EGG::Mathf::abs(
m_speed) < 5.0f) {
2424 f32 stickX = state()->stickX();
2425 f32 extVelXFactor = 0.0f;
2431 eStatus::SoftWallDrift, eStatus::SomethingWallCollision,
eStatus::HWG,
2432 eStatus::CannonStart, eStatus::InCannon)) {
2434 }
else if (!state()->isDrifting()) {
2435 if (stickX <= 0.2f) {
2436 if (stickX >= -0.2f) {
2451 leanRotMin = -leanRotMax;
2455 if (stickX == 0.0f) {
2461 }
else if (stickX == 0.0f) {
2469 bool capped =
false;
2481 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2484 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2488 dynamics()->setAngVel2(dynamics()->angVel2() +
2489 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2498 scalar = std::min(1.0f, scalar);
2499 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2501 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2506 dynamics()->setTop_(top);
2514 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2515 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2516 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2519 KartMove::setTurnParams();
2521 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2523 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2527 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2537void KartMoveBike::init(
bool b1,
bool b2) {
2538 KartMove::init(b1, b2);
2547 m_autoHardStickXFrames = 0;
2551void KartMoveBike::clear() {
2559 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2560 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2565 auto &status = KartObjectProxy::status();
2568 bool cancelAutoWheelie =
false;
2571 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2572 m_autoHardStickXFrames = 0;
2574 if (++m_autoHardStickXFrames > 15) {
2575 cancelAutoWheelie =
true;
2587 dynamics()->setAngVel0(angVel0);
2597 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2602 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2603 dynamics()->setAngVel2(angVel2);
2608 status.
setBit(eStatus::WheelieRot);
2610 status.
resetBit(eStatus::WheelieRot);
2637 constexpr u16 MAX_MT_CHARGE = 270;
2638 constexpr u16 BASE_MT_CHARGE = 2;
2639 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2640 constexpr u16 EXTRA_MT_CHARGE = 3;
2642 if (m_driftState != DriftState::ChargingMt) {
2648 f32 stickX = state()->stickX();
2649 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2650 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2659 m_driftState = DriftState::ChargedMt;
2664void KartMoveBike::initOob() {
2665 KartMove::initOob();
2672 constexpr s16 COOLDOWN_FRAMES = 20;
2673 bool dpadUp = inputs()->currentState().trickUp();
2674 auto &status = KartObjectProxy::status();
2679 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 activate(Type type, s16 frames)
Starts/restarts a boost of the given type.
bool calc()
Computes the current frame's boost multiplier, acceleration, and speed limit.
void applyWrenchScaled(const EGG::Vector3f &p, const EGG::Vector3f &f, f32 scale)
Applies a force linearly and rotationally to the kart.
Handles the physics and boosts associated with zippers.
f32 m_leanRot
Z-axis rotation of the bike from leaning.
f32 m_leanRotCap
The maximum leaning rotation.
void calcWheelie() override
STAGE 1+ - Every frame, checks player input for wheelies and computes wheelie rotation.
void calcMtCharge() override
Every frame during a drift, calculates MT charge based on player input.
virtual void startWheelie()
STAGE 1+ - Sets the wheelie bit flag and some wheelie-related variables.
f32 m_wheelieRotDec
The wheelie rotation decrementor, used after a wheelie has ended.
u32 m_wheelieFrames
Tracks wheelie duration and cancels the wheelie after 180 frames.
f32 m_wheelieRot
X-axis rotation from wheeling.
const TurningParameters * m_turningParams
Inside/outside drifting bike turn info.
void setTurnParams() override
On init, sets the bike's lean rotation cap and increment.In addition to setting the lean rotation cap...
f32 m_maxWheelieRot
The maximum wheelie rotation.
void tryStartWheelie()
STAGE 1+ - Every frame, checks player input to see if we should start or stop a wheelie.
void onWallCollision() override
Called when you collide with a wall. All it does for bikes is cancel wheelies.
void onHop() override
Virtual function that just cancels wheelies when you hop.
bool canWheelie() const override
Checks if the kart is going fast enough to wheelie.
void calcVehicleRotation(f32) override
Every frame, calculates rotation, EV, and angular velocity for the bike.
s16 m_wheelieCooldown
The number of frames before another wheelie can start.
f32 m_leanRotInc
The incrementor for leaning rotation.
virtual void cancelWheelie()
Clears the wheelie bit flag and resets the rotation decrement.
f32 m_baseSpeed
The speed associated with the current character/vehicle stats.
void calcRotation()
Every frame, calculates kart rotation based on player input.
s16 m_ssmtLeewayTimer
Frames to forgive letting go of A before clearing SSMT charge.
s32 m_hopFrame
A timer that can prevent subsequent hops until reset.
void calcDisableBackwardsAccel()
Computes the current cooldown duration between braking and reversing.
EGG::Vector3f m_hopUp
The up vector when hopping.
u16 m_mushroomBoostTimer
Number of frames until the mushroom boost runs out.
void calcSpecialFloor()
Every frame, calculates any boost resulting from a boost panel.
void calcWallCollisionStart(f32 param_2)
If we started to collide with a wall this frame, applies rotation.
KartHalfPipe * m_halfPipe
Pertains to zipper physics.
f32 m_kclRotFactor
Float between 0-1 that scales the player's turning radius on offroad.
f32 m_outsideDriftBonus
Added to angular velocity when outside drifting.
void tryStartBoostRamp()
Sets offroad invincibility and and enables the ramp boost bitfield flag.
u16 m_smtCharge
A value between 0 and 300 representing current SMT charge.
f32 m_speedRatio
The ratio between current speed and the player's base speed stat.
@ DriftReset
Set when a wall bonk should cancel your drift.
@ SsmtCharged
Set after holding a stand-still mini-turbo for 75 frames.
@ TrickableSurface
Set when driving on a trickable surface.
@ SsmtLeeway
If set, activates SSMT when not pressing A or B.
@ WallBounce
Set when our speed loss from wall collision is > 30.0f.
@ Respawned
Set when Lakitu lets go of the player, cleared when landing.
void tryStartJumpPad()
Applies calculations to start interacting with a jump pad.
f32 m_jumpPadMinSpeed
Snaps the player to a minimum speed when first touching a jump pad.
f32 m_hopPosY
Relative position as the result of a hop. Starts at 0.
DrivingDirection m_drivingDirection
Current state of driver's direction.
u16 m_crushTimer
Number of frames until player will be uncrushed.
bool calcPreDrift()
Each frame, checks for hop or slipdrift. Computes drift direction based on player input.
f32 m_speed
Current speed, restricted to the soft speed limit.
s16 m_offroadInvincibility
How many frames until the player is affected by offroad.
void calcAirtimeTop()
Calculates rotation of the bike due to excessive airtime.
void startManualDrift()
Called when the player lands from a drift hop, or to start a slipdrift.
void controlOutsideDriftAngle()
Every frame, handles mini-turbo charging and outside drifting bike rotation.
f32 m_softSpeedLimit
Base speed + boosts + wheelies, restricted to the hard speed limit.
virtual void hop()
Initializes hop information, resets upwards EV and clears upwards force.
void calcStandstillBoostRot()
STAGE Computes the x-component of angular velocity based on the kart's speed.
EGG::Vector3f m_outsideDriftLastDir
Used to compute the next m_outsideDriftAngle.
void calcManualDrift()
Each frame, handles hopping, drifting, and mini-turbos.
virtual void calcMtCharge()
Every frame during a drift, calculates MT/SMT charge based on player input.
void calcSsmt()
Calculates standstill mini-turbo components, if applicable.
void calcAcceleration()
Every frame, applies acceleration to the kart's internal velocity.
f32 m_processedSpeed
Offset 0x28. It's only ever just a copy of m_speed.
void releaseMt()
Stops charging a mini-turbo, and applies boost if charged.
f32 m_kclSpeedFactor
Float between 0-1 that scales the player's speed on offroad.
f32 m_weightedTurn
Magnitude+direction of stick input, factoring in the kart's stats.
void calcVehicleSpeed()
Every frame, computes speed based on acceleration and any active boosts.
f32 m_lastSpeed
Last frame's speed, cached to calculate angular velocity.
s16 m_ssmtDisableAccelTimer
Counter that tracks delay before starting to reverse.
void calcOffroadInvincibility()
Checks a timer to see if we are still ignoring offroad slowdown.
KartBurnout m_burnout
Manages the state of start boost burnout.
f32 calcVehicleAcceleration() const
Every frame, computes acceleration based off the character/vehicle stats.
void calcAutoDrift()
Each frame, handles automatic transmission drifting.
f32 m_realTurn
The "true" turn magnitude. Equal to m_weightedTurn unless drifting.
const DriftingParameters * m_driftingParams
Drift-type-specific parameters.
void calc()
Each frame, calculates the kart's movement.
EGG::Vector3f m_up
Vector perpendicular to the floor, pointing upwards.
s16 m_ssmtCharge
Increments every frame up to 75 when charging stand-still MT.
f32 m_speedDragMultiplier
After 5 frames of airtime, this causes speed to slowly decay.
u16 m_mtCharge
A value between 0 and 270 representing current MT charge.
void calcSsmtStart()
Calculates whether we are starting a standstill mini-turbo.
s16 m_respawnPostLandTimer
Counts up to 4 if not accelerating after respawn landing.
virtual f32 getWheelieSoftSpeedLimitBonus() const
Returns the % speed boost from wheelies. For karts, this is always 0.
f32 m_kclWheelRotFactor
The slowest rotation multiplier of each wheel's floor collision.
void resetDriftManual()
Clears drift state. Called when touching ground and drift is canceled.
f32 m_totalScale
[Unused] Always 1.0f
f32 m_acceleration
Captures the acceleration from player input and boosts.
virtual void calcVehicleRotation(f32 turn)
Every frame, calculates rotation, EV, and angular velocity for the kart.
s16 m_respawnPreLandTimer
Counts down from 4 when pressing A before landing from respawn.
virtual void calcTurn()
Each frame, looks at player input and kart stats. Saves turn-related info.
f32 m_divingRot
Induces x-axis angular velocity based on up/down stick input.
f32 m_outsideDriftAngle
The facing angle of an outward-drifting vehicle.
EGG::Vector3f m_lastDir
m_speed from the previous frame but with signed magnitude.
EGG::Vector3f m_smoothedUp
A smoothed up vector, mostly used after significant airtime.
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.
f32 driftManualTightness
Affects turn radius when manual drifting.
std::array< f32, 32 > kclRot
Rotation scalars, indexed using KCL attributes.
f32 driftAutomaticTightness
Affects turn radius when automatic drifting.
f32 driftReactivity
A weight applied to turn radius when drifting.
f32 speed
Base full speed of the character/vehicle combo.
f32 handlingReactivity
A weight applied to turn radius when not drifting.
u32 miniTurbo
The framecount duration of a charged mini-turbo.