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();
56 m_hardSpeedLimit = 0.0f;
60KartMove::~KartMove() {
61 EGG::egg_delete(m_jump);
68 m_jump = EGG::egg_new<KartJump>(
this);
80 auto &status = KartObjectProxy::status();
82 if (status.onBit(eStatus::InAction, eStatus::CannonStart, eStatus::InCannon,
93 if (status.onBit(eStatus::JumpPadMushroomCollision)) {
103 if (state()->isDrifting()) {
104 reactivity = param()->stats().driftReactivity;
106 reactivity = param()->stats().handlingReactivity;
114 if (!state()->isDrifting()) {
124void KartMove::setTurnParams() {
125 static constexpr std::array<DriftingParameters, 3> DRIFTING_PARAMS_ARRAY = {{
126 {10.0f, 0.5f, 0.5f, 1.0f},
127 {10.0f, 0.5f, 0.5f, 0.2f},
128 {10.0f, 0.22f, 0.5f, 0.2f},
135 m_landingDir = m_dir;
136 m_smoothedForward = m_dir;
138 m_driftingParams = &DRIFTING_PARAMS_ARRAY[
static_cast<u32
>(param()->stats().driftType)];
143void KartMove::init(
bool b1,
bool b2) {
151 m_up = EGG::Vector3f::ey;
153 m_smoothedForward = EGG::Vector3f::ez;
154 m_vel1Dir = EGG::Vector3f::ez;
156 m_dir = EGG::Vector3f::ez;
157 m_landingDir = EGG::Vector3f::ez;
158 m_dirDiff = EGG::Vector3f::zero;
159 m_hasLandingDir =
false;
161 m_landingAngle = 0.0f;
179 m_standStillBoostRot = 0.0f;
180 m_driftState = DriftState::NotDrifting;
185 m_zipperBoostTimer = 0;
186 m_zipperBoostMax = 0;
192 m_nonZipperAirtime = 0;
199 m_hitboxScale = 1.0f;
200 m_shockSpeedMultiplier = 1.0f;
208 m_jumpPadMaxSpeed = 0.0f;
209 m_jumpPadBoostMultiplier = 0.0f;
210 m_jumpPadProperties =
nullptr;
212 m_autoDriftAngle = 0.0f;
213 m_autoDriftStartFrameCounter = 0;
215 m_cannonEntryOfsLength = 0.0f;
216 m_cannonEntryPos.setZero();
217 m_cannonEntryOfs.setZero();
218 m_cannonOrthog.setZero();
219 m_cannonProgress.setZero();
230 m_padType.makeAllZero();
231 m_flags.makeAllZero();
238void KartMove::clear() {
239 auto &status = KartObjectProxy::status();
250 clearOffroadInvincibility();
259 EGG::Quatf quaternion = EGG::Quatf::FromRPY(angles * DEG2RAD);
262 Field::KCLTypeMask kcl_flags = KCL_NONE;
264 bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos,
265 EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0);
268 newPos = newPos + info.tangentOff + (info.floorNrm * -100.0f);
269 newPos += info.floorNrm * bsp().initialYPos;
275 sub()->initPhysicsValues();
277 physics()->setPos(pos());
278 physics()->setVelocity(dynamics()->velocity());
284 dynamics()->setTop(
m_up);
286 for (u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) {
287 suspension(tireIdx)->setInitialState();
297 auto &status = KartObjectProxy::status();
299 if (status.onBit(eStatus::InRespawn)) {
304 dynamics()->resetInternalVelocity();
335 if (status.onBit(eStatus::InCannon)) {
346void KartMove::calcRespawnStart() {
347 constexpr float RESPAWN_HEIGHT = 700.0f;
349 const auto *jugemPoint = System::RaceManager::Instance()->jugemPoint();
354 respawnPos.y += RESPAWN_HEIGHT;
359 Item::ItemDirector::Instance()->kartItem(0).clear();
361 status().
resetBit(eStatus::TriggerRespawn).setBit(eStatus::InRespawn);
365void KartMove::calcInRespawn() {
366 constexpr f32 LAKITU_VELOCITY = 1.5f;
367 constexpr u16 RESPAWN_DURATION = 110;
369 auto &status = KartObjectProxy::status();
371 if (status.
offBit(eStatus::InRespawn)) {
375 EGG::Vector3f newPos = pos();
376 newPos.y -= LAKITU_VELOCITY;
377 dynamics()->setPos(newPos);
378 dynamics()->setNoGravity(
true);
384 dynamics()->setNoGravity(
false);
389void KartMove::calcRespawnBoost() {
390 constexpr s16 RESPAWN_BOOST_DURATION = 30;
391 constexpr s16 RESPAWN_BOOST_INPUT_LENIENCY = 4;
393 auto &status = KartObjectProxy::status();
395 if (status.onBit(eStatus::AfterRespawn)) {
399 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
400 m_respawnTimer = RESPAWN_BOOST_DURATION;
406 status.resetBit(eStatus::AfterRespawn);
420 activateBoost(KartBoost::Type::AllMt, RESPAWN_BOOST_DURATION);
421 m_respawnTimer = RESPAWN_BOOST_DURATION;
433 m_respawnTimer = std::max(0, m_respawnTimer - 1);
437void KartMove::calcTop() {
438 f32 stabilizationFactor = 0.1f;
439 m_hasLandingDir =
false;
440 EGG::Vector3f inputTop = state()->top();
441 auto &status = KartObjectProxy::status();
447 m_dirDiff = m_landingDir.proj(m_landingDir);
448 m_hasLandingDir =
true;
454 inputTop.dot(m_dir) > 0.0f &&
m_speed > 50.0f &&
455 collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::NotTrickable)) {
467 f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(inputTop.dot(componentZAxis())));
468 scalar = std::min(0.8f, std::max(0.3f, topDotZ));
476 if (bodyDotFront < -0.1f) {
477 stabilizationFactor += std::min(0.2f, EGG::Mathf::abs(bodyDotFront) * 0.5f);
480 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
481 stabilizationFactor = 0.4f;
488 dynamics()->setStabilizationFactor(stabilizationFactor);
491 m_flags.changeBit(collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::Trickable),
498 auto &status = KartObjectProxy::status();
511 if (
m_up.y <= 0.99f) {
512 m_up += (EGG::Vector3f::ey -
m_up) * 0.03f;
515 m_up = EGG::Vector3f::ey;
523 const auto *raceMgr = System::RaceManager::Instance();
524 if (!raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
528 if (m_padType.onBit(ePadType::BoostPanel)) {
529 tryStartBoostPanel();
532 if (m_padType.onBit(ePadType::BoostRamp)) {
536 if (m_padType.onBit(ePadType::JumpPad)) {
540 m_padType.makeAllZero();
544void KartMove::calcDirs() {
548 m_flags.
setBit(eFlags::LaunchBoost);
549 auto &status = KartObjectProxy::status();
553 !m_jump->isBoostRampEnabled()) &&
554 status.
offBit(eStatus::JumpPad) && state()->airtime() <= 5) ||
555 status.
onBit(eStatus::JumpPadMushroomCollision,
556 eStatus::NoSparkInvisibleWall))) {
570 if (dirDiff.
squaredLength() <= std::numeric_limits<f32>::epsilon()) {
574 EGG::Vector3f origDirCross = m_dir.cross(local_b8);
579 EGG::Vector3f newDirCross = m_dir.cross(local_b8);
581 if (origDirCross.dot(newDirCross) < 0.0f) {
588 m_flags.resetBit(eFlags::LaunchBoost);
597 EGG::Vector3f nextDir =
m_up.cross(local_88);
598 m_smoothedForward = nextDir.cross(
m_up);
599 m_smoothedForward.normalise();
601 if (m_hasLandingDir) {
602 f32 dot = m_dir.dot(m_landingDir);
603 EGG::Vector3f cross = m_dir.cross(m_landingDir);
604 f32 crossDot = cross.length();
605 f32 angle = EGG::Mathf::atan2(crossDot, dot);
606 angle = EGG::Mathf::abs(angle);
613 m_landingAngle += (angle * RAD2DEG) * fVar4;
616 if (m_landingAngle <= 0.0f) {
617 if (m_landingAngle < 0.0f) {
618 m_landingAngle = std::min(0.0f, m_landingAngle + 2.0f);
621 m_landingAngle = std::max(0.0f, m_landingAngle - 2.0f);
626void KartMove::calcStickyRoad() {
627 constexpr f32 STICKY_RADIUS = 200.0f;
628 constexpr Field::KCLTypeMask STICKY_MASK =
631 auto &status = KartObjectProxy::status();
639 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::Trickable)) ||
640 EGG::Mathf::abs(
m_speed) <= 20.0f) {
644 EGG::Vector3f pos = dynamics()->pos();
645 EGG::Vector3f vel = dynamics()->movingObjVel() +
m_speed * m_vel1Dir;
646 Field::CollisionInfo colInfo;
647 colInfo.bbox.setZero();
648 Field::KCLTypeMask kcl_flags = KCL_NONE;
649 bool stickyRoad =
false;
651 for (
size_t i = 0; i < 3; ++i) {
652 EGG::Vector3f newPos = pos + vel;
653 if (Field::CollisionDirector::Instance()->checkSphereFull(STICKY_RADIUS, newPos,
654 EGG::Vector3f::inf, STICKY_MASK, &colInfo, &kcl_flags, 0)) {
655 m_vel1Dir = m_vel1Dir.perpInPlane(colInfo.floorNrm,
true);
656 dynamics()->setMovingObjVel(dynamics()->movingObjVel().rej(colInfo.floorNrm));
657 dynamics()->setMovingRoadVel(dynamics()->movingRoadVel().rej(colInfo.floorNrm));
659 if (status.onBit(eStatus::MovingWaterStickyRoad)) {
660 m_up = colInfo.floorNrm;
669 pos += -STICKY_RADIUS * componentYAxis();
681 auto &status = KartObjectProxy::status();
713void KartMove::calcRisingWater() {
714 auto *objDir = Field::ObjectDirector::Instance();
715 auto *psea = objDir->psea();
720 f32 pos = wheelPos(0).y;
721 u16 count = tireCount();
722 for (u16 wheelIdx = 0; wheelIdx < count; ++wheelIdx) {
723 f32 tmp = wheelEdgePos(wheelIdx).y;
724 if (wheelIdx == 0 || tmp < pos) {
729 if (objDir->risingWaterKillPlaneHeight() > pos) {
730 collide()->activateOob(
true,
nullptr,
false,
false);
733 f32 dist = -objDir->distAboveRisingWater(pos);
735 f32 speedScale = std::min(1.0f, dist / 100.0f);
736 m_kclSpeedFactor = 1.0f - (1.0f - param()->stats().kclSpeed[3]) * speedScale;
741void KartMove::calcBoost() {
742 auto &status = KartObjectProxy::status();
744 if (m_boost.calc()) {
754void KartMove::calcRampBoost() {
755 auto &status = KartObjectProxy::status();
757 if (status.offBit(eStatus::RampBoost)) {
762 if (--m_rampBoost < 1) {
764 status.resetBit(eStatus::RampBoost);
772 auto &status = KartObjectProxy::status();
789 constexpr s16 MAX_SSMT_CHARGE = 75;
790 constexpr s16 SSMT_BOOST_FRAMES = 30;
791 constexpr s16 LEEWAY_FRAMES = 1;
792 constexpr s16 DISABLE_ACCEL_FRAMES = 20;
796 auto &status = KartObjectProxy::status();
822 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
829 activateBoost(KartBoost::Type::AllMt, SSMT_BOOST_FRAMES);
846 auto &status = KartObjectProxy::status();
891 m_driftState = DriftState::NotDrifting;
903 m_driftState = DriftState::NotDrifting;
909 m_autoDriftAngle = 0.0f;
911 m_autoDriftStartFrameCounter = 0;
915void KartMove::clearJumpPad() {
917 status().
resetBit(eStatus::JumpPad);
921void KartMove::clearRampBoost() {
923 status().
resetBit(eStatus::RampBoost);
927void KartMove::clearZipperBoost() {
928 m_zipperBoostTimer = 0;
933void KartMove::clearBoost() {
934 m_boost.resetActive();
939void KartMove::clearSsmt() {
947void KartMove::clearOffroadInvincibility() {
952void KartMove::clearRejectRoad() {
960 constexpr s16 AUTO_DRIFT_START_DELAY = 12;
962 auto &status = KartObjectProxy::status();
970 EGG::Mathf::abs(state()->stickX()) > 0.85f) {
971 m_autoDriftStartFrameCounter =
972 std::min<s16>(AUTO_DRIFT_START_DELAY, m_autoDriftStartFrameCounter + 1);
974 m_autoDriftStartFrameCounter = 0;
977 if (m_autoDriftStartFrameCounter >= AUTO_DRIFT_START_DELAY) {
978 status.setBit(eStatus::DriftAuto);
981 if (state()->stickX() < 0.0f) {
983 m_autoDriftAngle -= 30.0f * param()->stats().driftAutomaticTightness;
987 m_autoDriftAngle += 30.0f * param()->stats().driftAutomaticTightness;
991 f32 halfTarget = 0.5f * param()->stats().driftOutsideTargetAngle;
992 m_autoDriftAngle = std::min(halfTarget, std::max(-halfTarget, m_autoDriftAngle));
994 status.resetBit(eStatus::DriftAuto);
997 if (m_autoDriftAngle > 0.0f) {
999 std::max(0.0f, m_autoDriftAngle - param()->stats().driftOutsideDecrement);
1002 std::min(0.0f, m_autoDriftAngle + param()->stats().driftOutsideDecrement);
1008 physics()->composeExtraRot(angleAxis);
1016 auto &status = KartObjectProxy::status();
1019 const EGG::Vector3f rotZ = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
1022 param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike &&
1023 status.offBit(eStatus::JumpPadMushroomCollision) &&
1025 m_flags.onBit(eFlags::LaunchBoost)) {
1026 const EGG::Vector3f up = dynamics()->mainRot().rotateVector(EGG::Vector3f::ey);
1030 f32 rejCrossDirMag = driftRej.cross(rotZ).
length();
1031 f32 angle = EGG::Mathf::atan2(rejCrossDirMag, driftRej.
dot(rotZ));
1033 if ((rotZ.z * (rotZ.x - driftRej.x)) - (rotZ.x * (rotZ.z - driftRej.z)) > 0.0f) {
1063 if (action()->flags().offBit(KartAction::eFlags::Rotating) ||
m_speed <= 20.0f) {
1064 f32 driftAngleDecr = param()->stats().driftOutsideDecrement;
1097 constexpr f32 OUTSIDE_DRIFT_BONUS = 0.5f;
1099 const auto &stats = param()->stats();
1100 auto &status = KartObjectProxy::status();
1102 if (stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1103 f32 driftAngle = 0.0f;
1106 const EGG::Vector3f rotZ = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
1112 EGG::Mathf::atan2(hopCrossRot.
length(),
m_hopDir.dot(rotRej)) * RAD2DEG;
1131 m_driftState = DriftState::ChargingMt;
1139 constexpr f32 SMT_LENGTH_FACTOR = 3.0f;
1141 auto &status = KartObjectProxy::status();
1143 if (m_driftState < DriftState::ChargedMt || status.onBit(
eStatus::Brake)) {
1144 m_driftState = DriftState::NotDrifting;
1148 u16 mtLength = param()->stats().miniTurbo;
1150 if (m_driftState == DriftState::ChargedSmt) {
1151 mtLength *= SMT_LENGTH_FACTOR;
1155 activateBoost(KartBoost::Type::AllMt, mtLength);
1158 m_driftState = DriftState::NotDrifting;
1165 if (state()->airtime() > 5) {
1169 if (param()->stats().driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1172 f32 targetAngle = param()->stats().driftOutsideTargetAngle;
1173 if (angle > targetAngle) {
1175 }
else if (angle < targetAngle) {
1181 f32 targetAngle = -param()->stats().driftOutsideTargetAngle;
1182 if (targetAngle > angle) {
1184 }
else if (targetAngle < angle) {
1199 auto &status = KartObjectProxy::status();
1200 bool drifting = state()->isDrifting() && status.offBit(eStatus::JumpPadMushroomCollision);
1202 const auto &stats = param()->stats();
1205 turn = autoDrift ? stats.driftAutomaticTightness : stats.driftManualTightness;
1207 turn = autoDrift ? stats.handlingAutomaticTightness : stats.handlingManualTightness;
1210 if (drifting && stats.driftType != KartParam::Stats::DriftType::Inside_Drift_Bike) {
1215 bool forwards =
true;
1229 bool noTurn =
false;
1231 EGG::Mathf::abs(
m_speed) < 1.0f) {
1237 if (forwards && !noTurn) {
1241 turn += (1.0f - (
m_speed - 20.0f) / 50.0f) * turn;
1244 turn = (turn * 0.4f) + (
m_speed / 20.0f) * (turn * 0.6f);
1257 f32 stickX = EGG::Mathf::abs(state()->stickX());
1258 if (autoDrift && stickX > 0.3f) {
1259 f32 stickScalar = (stickX - 0.3f) / 0.7f;
1260 stickX = drifting ? 0.2f : 0.5f;
1267 if (status.onBit(eStatus::RampBoost) && m_jump->isBoostRampEnabled()) {
1269 }
else if (status.offBit(eStatus::JumpPadMushroomCollision)) {
1270 u32 airtime = state()->airtime();
1271 if (airtime >= 70) {
1273 }
else if (airtime >= 30) {
1274 turn = std::max(0.0f, turn * (1.0f - (airtime - 30) * 0.025f));
1279 const EGG::Vector3f forward = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
1280 f32 angle = EGG::Mathf::atan2(forward.cross(m_dir).
length(), forward.
dot(m_dir));
1281 angle = EGG::Mathf::abs(angle) * RAD2DEG;
1283 if (angle > 60.0f) {
1284 turn *= std::max(0.0f, 1.0f - (angle - 60.0f) / 40.0f);
1295 const auto *raceMgr = System::RaceManager::Instance();
1296 auto &status = KartObjectProxy::status();
1298 if (raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1299 f32 speedFix = dynamics()->speedFix();
1300 if (status.onBit(eStatus::InAction) ||
1302 EGG::Mathf::abs(speedFix) >= 3.0f) &&
1314 if (status.onBit(eStatus::MovingWaterVertical) ||
1315 (status.onBit(eStatus::MovingWaterDecaySpeed) &&
1318 m_speed *= collide()->pullPath().roadSpeedDecay();
1324 if (status.onBit(eStatus::InAction)) {
1325 action()->calcVehicleSpeed();
1333 if (status.onBit(eStatus::RampBoost) && state()->airtime() < 4) {
1342 if (state()->airtime() > 5) {
1350 if (status.offBit(eStatus::JumpPad, eStatus::RampBoost)) {
1370 const auto &stats = param()->stats();
1373 m_speed *= stats.turningSpeed + (1.0f - stats.turningSpeed) * x;
1389 initialVel = std::min(initialVel * 2.0f, 2.0f);
1391 vel *= std::min(0.5f, std::max(-0.5f, -
bodyFront().y));
1405 std::span<const f32> as;
1406 std::span<const f32> ts;
1407 if (state()->isDrifting()) {
1408 as = param()->stats().accelerationDriftA;
1409 ts = param()->stats().accelerationDriftT;
1411 as = param()->stats().accelerationStandardA;
1412 ts = param()->stats().accelerationStandardT;
1416 f32 acceleration = 0.0f;
1418 for (; i < ts.size(); ++i) {
1419 if (ratio < ts[i]) {
1420 acceleration = as[i] + ((as[i + 1] - as[i]) / (ts[i] - t_curr)) * (ratio - t_curr);
1427 return i < ts.size() ? acceleration : as.back();
1434 constexpr f32 ROTATION_SCALAR_NORMAL = 0.5f;
1435 constexpr f32 ROTATION_SCALAR_MIDAIR = 0.2f;
1436 constexpr f32 ROTATION_SCALAR_BOOST_RAMP = 4.0f;
1437 constexpr f32 OOB_SLOWDOWN_RATE = 0.95f;
1438 constexpr f32 TERMINAL_VELOCITY = 90.0f;
1441 auto &status = KartObjectProxy::status();
1443 if (status.offBit(eStatus::InAction)) {
1477 f32 speedLimit = status.onBit(eStatus::JumpPad) ? m_jumpPadMaxSpeed :
m_baseSpeed;
1478 const f32 boostMultiplier = m_boost.multiplier();
1479 const f32 boostSpdLimit = m_boost.speedLimit();
1480 m_jumpPadBoostMultiplier = boostMultiplier;
1482 f32 scaleMultiplier = m_shockSpeedMultiplier;
1483 if (status.onBit(eStatus::Crushed)) {
1484 scaleMultiplier *= 0.7f;
1488 speedLimit *= status.onBit(eStatus::JumpPadFixedSpeed) ?
1494 f32 boostSpeed = ignoreScale ? 1.0f : scaleMultiplier;
1497 if (status.offBit(eStatus::JumpPad) && boostSpeed > 0.0f && boostSpeed > speedLimit) {
1498 speedLimit = boostSpeed;
1503 if (status.onBit(eStatus::RampBoost)) {
1504 speedLimit = std::max(speedLimit, 100.0f);
1509 f32 local_c8 = 1.0f;
1524 if (status.onBit(eStatus::JumpPad)) {
1535 crossVec = -crossVec;
1538 f32 rotationScalar = ROTATION_SCALAR_NORMAL;
1539 if (collide()->surfaceFlags().onBit(KartCollide::eSurfaceFlags::BoostRamp)) {
1540 rotationScalar = ROTATION_SCALAR_BOOST_RAMP;
1542 rotationScalar = ROTATION_SCALAR_MIDAIR;
1549 const auto *raceMgr = System::RaceManager::Instance();
1552 raceMgr->isStageReached(System::RaceManager::Stage::Race)) {
1561 nextSpeed.y = std::min(nextSpeed.y, maxSpeedY);
1563 dynamics()->setIntVel(dynamics()->intVel() + nextSpeed);
1570 DrivingDirection::Backwards;
1586 auto &status = KartObjectProxy::status();
1599 if (wallNrm.y > 0.0f) {
1607 f1 = std::max(0.0f, dot + 1.0f);
1621 auto &status = KartObjectProxy::status();
1628 if (status.offBit(eStatus::InAction)) {
1631 m_landingDir = m_dir;
1632 m_smoothedForward = m_dir;
1639 if (speedDiff > 30.0f) {
1642 f32 dot = -
bodyUp().
dot(colData.relPos) * 0.5f;
1646 speedDiff = std::min(60.0f, speedDiff);
1649 auto [proj, rej] = scaledWallNrm.projAndRej(m_vel1Dir);
1654 proj = EGG::Vector3f::zero;
1655 rej = EGG::Vector3f::zero;
1658 if (
bodyFront().dot(colData.wallNrm) > 0.0f) {
1659 proj = EGG::Vector3f::zero;
1664 f32 bumpDeviation = 0.0f;
1666 bumpDeviation = param()->stats().bumpDeviationLevel;
1669 dynamics()->applyWrenchScaled(newPos, projRejSum, bumpDeviation);
1671 dynamics()->addForce(colData.wallNrm * 15.0f);
1672 collide()->startFloorMomentRate();
1676 dynamics()->addForce(colData.wallNrm * 15.0f);
1677 collide()->startFloorMomentRate();
1689 auto &status = KartObjectProxy::status();
1692 if (System::RaceManager::Instance()->stage() == System::RaceManager::Stage::Countdown) {
1693 next = 0.015f * -state()->startBoostCharge();
1695 if (status.offBit(eStatus::JumpPad, eStatus::RampBoost,
1698 scalar = std::min(3.0f, std::max(speedDiff, -3.0f));
1701 next = (scalar * 0.15f) * 0.25f;
1706 next = (scalar * 0.15f) * 0.08f;
1711 constexpr s16 MAX_SSMT_CHARGE = 75;
1712 next = 0.015f * (-
static_cast<f32
>(
m_ssmtCharge) /
static_cast<f32
>(MAX_SSMT_CHARGE));
1717 m_standStillBoostRot = isBike() ? next * 3.0f : next * 10.0f;
1719 m_standStillBoostRot += scalar * (next * m_invScale - m_standStillBoostRot);
1727 constexpr f32 DIVE_LIMIT = 0.8f;
1731 auto &status = KartObjectProxy::status();
1738 f32 stickY = state()->stickY();
1741 stickY = std::min(1.0f, stickY + 0.4f);
1744 u32 airtime = state()->airtime();
1747 if (EGG::Mathf::abs(stickY) < 0.1f) {
1751 stickY *= (airtime / 50.0f);
1758 dynamics()->setAngVel2(angVel2);
1760 if (state()->airtime() < 50) {
1764 EGG::Vector3f topRotated = dynamics()->mainRot().rotateVector(EGG::Vector3f::ey);
1765 EGG::Vector3f forwardRotated = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
1766 f32 upDotTop =
m_up.dot(topRotated);
1768 f32 crossNorm = upCrossTop.
length();
1769 f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));
1771 f32 fVar1 = angle * RAD2DEG - 20.0f;
1772 if (fVar1 <= 0.0f) {
1776 f32 mult = std::min(1.0f, fVar1 / 20.0f);
1777 if (forwardRotated.y > 0.0f) {
1778 dynamics()->setGravity((1.0f - 0.2f * mult) * dynamics()->gravity());
1780 dynamics()->setGravity((0.2f * mult + 1.0f) * dynamics()->gravity());
1788 auto &status = KartObjectProxy::status();
1800void KartMove::calcHopPhysics() {
1811void KartMove::calcRejectRoad() {
1812 m_reject.calcRejectRoad();
1816bool KartMove::calcZipperCollision(f32 radius, f32 scale, EGG::Vector3f &pos,
1817 EGG::Vector3f &upLocal,
const EGG::Vector3f &prevPos, Field::CollisionInfo *colInfo,
1818 Field::KCLTypeMask *maskOut, Field::KCLTypeMask flags)
const {
1819 upLocal = mainRot().rotateVector(EGG::Vector3f::ey);
1820 pos = dynamics()->pos() + (-scale *
m_scale.y) * upLocal;
1822 auto *colDir = Field::CollisionDirector::Instance();
1823 return colDir->checkSphereFullPush(radius, pos, prevPos, flags, colInfo, maskOut, 0);
1827f32 KartMove::calcSlerpRate(f32 scale,
const EGG::Quatf &from,
const EGG::Quatf &to)
const {
1828 f32 dotNorm = std::max(-1.0f, std::min(1.0f, from.dot(to)));
1829 f32 acos = EGG::Mathf::acos(dotNorm);
1830 return acos > 0.0f ? std::min(0.1f, scale / acos) : 0.1f;
1834void KartMove::applyForce(f32 force,
const EGG::Vector3f &hitDir,
bool stop) {
1835 constexpr s16 BUMP_COOLDOWN = 5;
1841 dynamics()->addForce(force * hitDir.perpInPlane(
m_up,
true));
1842 collide()->startFloorMomentRate();
1854 f32 tiltMagnitude = 0.0f;
1855 auto &status = KartObjectProxy::status();
1862 f32 magnitude = tiltMagnitude;
1864 if (frontSpeed.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
1865 magnitude = frontSpeed.
length();
1867 if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
1868 magnitude = -magnitude;
1871 tiltMagnitude = -1.0f;
1872 if (-1.0f <= magnitude) {
1873 tiltMagnitude = std::min(1.0f, magnitude);
1879 dynamics()->setAngVel0(angVel0);
1883 m_invScale * (tiltMagnitude * param()->stats().tilt * EGG::Mathf::abs(
m_weightedTurn));
1888 angVel0.x += m_standStillBoostRot;
1890 dynamics()->setAngVel0(angVel0);
1894 dynamics()->setAngVel2(angVel2);
1904 constexpr u16 MAX_MT_CHARGE = 270;
1905 constexpr u16 MAX_SMT_CHARGE = 300;
1906 constexpr u16 BASE_MT_CHARGE = 2;
1907 constexpr u16 BASE_SMT_CHARGE = 2;
1908 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
1909 constexpr u16 EXTRA_MT_CHARGE = 3;
1911 if (m_driftState == DriftState::ChargedSmt) {
1915 f32 stickX = state()->stickX();
1917 if (m_driftState == DriftState::ChargingMt) {
1920 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1921 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1930 m_driftState = DriftState::ChargingSmt;
1934 if (m_driftState != DriftState::ChargingSmt) {
1940 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
1941 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
1950 m_driftState = DriftState::ChargedSmt;
1955void KartMove::initOob() {
1961 clearOffroadInvincibility();
1971 m_hopUp = dynamics()->mainRot().rotateVector(EGG::Vector3f::ey);
1972 m_hopDir = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
1973 m_driftState = DriftState::NotDrifting;
1985 dynamics()->setExtVel(extVel);
1988 totalForce.y = 0.0f;
1989 dynamics()->setTotalForce(totalForce);
1993void KartMove::tryStartBoostPanel() {
1994 constexpr s16 BOOST_PANEL_DURATION = 60;
2000 activateBoost(KartBoost::Type::MushroomAndBoostPanel, BOOST_PANEL_DURATION);
2008 constexpr s16 BOOST_RAMP_DURATION = 60;
2010 auto &status = KartObjectProxy::status();
2016 status.setBit(eStatus::RampBoost);
2017 m_rampBoost = BOOST_RAMP_DURATION;
2026 static constexpr std::array<JumpPadProperties, 8> JUMP_PAD_PROPERTIES = {{
2027 {50.0f, 50.0f, 35.0f},
2028 {50.0f, 50.0f, 47.0f},
2029 {59.0f, 59.0f, 30.0f},
2030 {73.0f, 73.0f, 45.0f},
2031 {73.0f, 73.0f, 53.0f},
2032 {56.0f, 56.0f, 50.0f},
2033 {55.0f, 55.0f, 35.0f},
2034 {56.0f, 56.0f, 50.0f},
2037 auto &status = KartObjectProxy::status();
2043 status.setBit(eStatus::JumpPad);
2044 s32 jumpPadVariant = state()->jumpPadVariant();
2045 m_jumpPadProperties = &JUMP_PAD_PROPERTIES[jumpPadVariant];
2047 if (jumpPadVariant == 3 || jumpPadVariant == 4) {
2048 if (m_jumpPadBoostMultiplier > 1.3f || m_jumpPadSoftSpeedLimit > 110.0f) {
2051 static constexpr std::array<JumpPadProperties, 2> JUMP_PAD_PROPERTIES_SHROOM_BOOST = {{
2052 {100.0f, 100.0f, 70.0f},
2053 {100.0f, 100.0f, 65.0f},
2055 m_jumpPadProperties = &JUMP_PAD_PROPERTIES_SHROOM_BOOST[jumpPadVariant != 3];
2058 status.setBit(eStatus::JumpPadFixedSpeed);
2061 if (jumpPadVariant == 4) {
2062 status.setBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadMushroomVelYInc,
2063 eStatus::JumpPadMushroomCollision);
2068 extVel.y = m_jumpPadProperties->velY;
2069 totalForce.y = 0.0f;
2071 dynamics()->setExtVel(extVel);
2072 dynamics()->setTotalForce(totalForce);
2074 if (jumpPadVariant != 3) {
2081 status.setBit(eStatus::JumpPadDisableYsusForce);
2086 m_jumpPadMaxSpeed = m_jumpPadProperties->maxSpeed;
2091void KartMove::tryEndJumpPad() {
2092 auto &status = KartObjectProxy::status();
2093 if (status.
onBit(eStatus::JumpPadMushroomTrigger)) {
2095 status.
resetBit(eStatus::JumpPadMushroomTrigger, eStatus::JumpPadFixedSpeed,
2096 eStatus::JumpPadMushroomVelYInc);
2099 if (status.
onBit(eStatus::JumpPadMushroomVelYInc)) {
2101 newExtVel.y += 20.0f;
2102 if (m_jumpPadProperties->velY < newExtVel.y) {
2103 newExtVel.y = m_jumpPadProperties->velY;
2104 status.
resetBit(eStatus::JumpPadMushroomVelYInc);
2106 dynamics()->setExtVel(newExtVel);
2116void KartMove::cancelJumpPad() {
2118 status().resetBit(eStatus::JumpPad);
2122void KartMove::activateBoost(KartBoost::Type type, s16 frames) {
2123 if (m_boost.activate(type, frames)) {
2129void KartMove::applyStartBoost(s16 frames) {
2130 activateBoost(KartBoost::Type::AllMt, frames);
2134void KartMove::activateMushroom() {
2135 constexpr s16 MUSHROOM_DURATION = 90;
2137 auto &status = KartObjectProxy::status();
2143 activateBoost(KartBoost::Type::MushroomAndBoostPanel, MUSHROOM_DURATION);
2151void KartMove::activateZipperBoost() {
2152 constexpr s16 BASE_DURATION = 50;
2153 constexpr s16 TRICK_DURATION = 100;
2155 auto &status = KartObjectProxy::status();
2162 activateBoost(KartBoost::Type::TrickAndZipper, boostDuration);
2165 m_zipperBoostTimer = 0;
2166 m_zipperBoostMax = boostDuration;
2186 auto &status = KartObjectProxy::status();
2202 auto &status = KartObjectProxy::status();
2216void KartMove::calcZipperBoost() {
2217 auto &status = KartObjectProxy::status();
2226 m_zipperBoostTimer = 0;
2230 if (m_zipperBoostTimer < 10) {
2231 EGG::Vector3f angVel = dynamics()->angVel0();
2233 dynamics()->setAngVel0(angVel);
2238void KartMove::landTrick() {
2239 static constexpr std::array<s16, 3> KART_TRICK_BOOST_DURATION = {{
2244 static constexpr std::array<s16, 3> BIKE_TRICK_BOOST_DURATION = {{
2256 duration = BIKE_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2258 duration = KART_TRICK_BOOST_DURATION[
static_cast<u32
>(m_jump->variant())];
2261 activateBoost(KartBoost::Type::TrickAndZipper, duration);
2265void KartMove::activateCrush(u16 timer) {
2266 status().setBit(eStatus::Crushed);
2272void KartMove::calcCrushed() {
2273 if (status().offBit(eStatus::Crushed)) {
2278 status().resetBit(eStatus::Crushed);
2284void KartMove::calcScale() {
2287 const EGG::Vector3f sizeScale =
m_kartScale->sizeScale();
2292 if (sizeScale.z != 1.0f) {
2300void KartMove::applyShrink(u16 timer) {
2301 auto &status = state()->status();
2303 if (status.onBit(eStatus::InRespawn, eStatus::AfterRespawn, eStatus::CannonStart)) {
2307 action()->start(Action::UNK_15);
2308 Item::ItemDirector::Instance()->kartItem(0).clear();
2309 status.setBit(eStatus::Shocked);
2311 if (timer > m_shockTimer) {
2312 m_shockTimer = timer;
2318void KartMove::calcShock() {
2319 auto &status = state()->status();
2321 if (status.onBit(eStatus::Shocked)) {
2322 if (--m_shockTimer == 0) {
2323 deactivateShock(
false);
2326 m_shockSpeedMultiplier = std::max(0.7f, m_shockSpeedMultiplier - 0.03f);
2328 m_shockSpeedMultiplier = std::min(1.0f, m_shockSpeedMultiplier + 0.05f);
2333void KartMove::deactivateShock(
bool resetSpeed) {
2334 status().resetBit(eStatus::Shocked);
2339 m_shockSpeedMultiplier = 1.0f;
2344void KartMove::enterCannon() {
2346 physics()->clearDecayingRot();
2347 m_boost.resetActive();
2349 auto &status = KartObjectProxy::status();
2357 clearOffroadInvincibility();
2359 dynamics()->reset();
2364 .setBit(eStatus::InCannon, eStatus::SkipWheelCalc);
2366 const auto [cannonPos, cannonDir] = getCannonPosRot();
2367 m_cannonEntryPos = pos();
2368 m_cannonEntryOfs = cannonPos - pos();
2369 m_cannonEntryOfsLength = m_cannonEntryOfs.normalise();
2370 m_cannonEntryOfs.normalise();
2371 m_dir = m_cannonEntryOfs;
2372 m_vel1Dir = m_cannonEntryOfs;
2373 m_cannonOrthog = EGG::Vector3f::ey.perpInPlane(m_cannonEntryOfs,
true);
2374 m_cannonProgress.setZero();
2378void KartMove::calcCannon() {
2379 auto [cannonPos, cannonDir] = getCannonPosRot();
2380 EGG::Vector3f forwardXZ = cannonPos - m_cannonEntryPos - m_cannonProgress;
2381 EGG::Vector3f forward = forwardXZ;
2382 f32 forwardLength = forward.normalise();
2384 forwardXZ.normalise();
2385 EGG::Vector3f local94 = m_cannonEntryOfs;
2387 local94.normalise();
2390 EGG::Matrix34f cannonOrientation;
2391 cannonOrientation.makeOrthonormalBasis(forward, EGG::Vector3f::ey);
2392 EGG::Vector3f up = cannonOrientation.multVector33(EGG::Vector3f::ey);
2396 if (forwardLength < 30.0f || local94.dot(forwardXZ) <= 0.0f) {
2401 m_smoothedForward = cannonOrientation.multVector33(EGG::Vector3f::ez);
2403 const auto *cannonPoint =
2404 System::CourseMap::Instance()->getCannonPoint(state()->cannonPointId());
2405 size_t cannonParameterIdx = std::max<s16>(0, cannonPoint->parameterIdx());
2406 ASSERT(cannonParameterIdx < CANNON_PARAMETERS.size());
2407 const auto &cannonParams = CANNON_PARAMETERS[cannonParameterIdx];
2408 f32 newSpeed = cannonParams.speed;
2409 if (forwardLength < cannonParams.decelFactor) {
2410 f32 factor = std::max(0.0f, forwardLength / cannonParams.decelFactor);
2412 newSpeed = cannonParams.endDecel;
2413 if (newSpeed <= 0.0f) {
2417 newSpeed += factor * (cannonParams.speed - newSpeed);
2418 if (cannonParams.endDecel > 0.0f) {
2423 m_cannonProgress += m_cannonEntryOfs * newSpeed;
2425 EGG::Vector3f newPos = EGG::Vector3f::zero;
2426 if (cannonParams.height > 0.0f) {
2427 f32 fVar9 = EGG::Mathf::SinFIdx(
2428 (1.0f - (forwardLength / m_cannonEntryOfsLength)) * 180.0f * DEG2FIDX);
2429 newPos = fVar9 * cannonParams.height * m_cannonOrthog;
2432 dynamics()->setPos(m_cannonEntryPos + m_cannonProgress + newPos);
2433 m_dir = m_cannonEntryOfs;
2434 m_vel1Dir = m_cannonEntryOfs;
2436 calcRotCannon(forward);
2438 dynamics()->setExtVel(EGG::Vector3f::zero);
2442void KartMove::calcRotCannon(
const EGG::Vector3f &forward) {
2443 EGG::Vector3f local48 = forward;
2444 local48.normalise();
2446 EGG::Vector3f local60 = local54 + ((local48 - local54) * 0.3f);
2447 local54.normalise();
2448 local60.normalise();
2451 local80.makeVectorRotation(local54, local60);
2452 local80 *= dynamics()->fullRot();
2453 local80.normalise();
2455 localB8.makeVectorRotation(local80.rotateVector(EGG::Vector3f::ey), smoothedUp());
2456 EGG::Quatf newRot = local80.slerpTo(localB8.multSwap(local80), 0.3f);
2457 dynamics()->setFullRot(newRot);
2458 dynamics()->setMainRot(newRot);
2462void KartMove::exitCannon() {
2463 auto &status = KartObjectProxy::status();
2465 if (status.offBit(eStatus::InCannon)) {
2469 status.resetBit(eStatus::InCannon, eStatus::SkipWheelCalc).setBit(eStatus::AfterCannon);
2470 dynamics()->setIntVel(m_cannonEntryOfs *
m_speed);
2474void KartMove::triggerRespawn() {
2476 status().setBit(eStatus::TriggerRespawn);
2480KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {}
2483KartMoveBike::~KartMoveBike() =
default;
2488 constexpr f32 MAX_WHEELIE_ROTATION = 0.07f;
2489 constexpr u16 WHEELIE_COOLDOWN = 20;
2496 m_autoHardStickXFrames = 0;
2505 m_autoHardStickXFrames = 0;
2510 m_jump = EGG::egg_new<KartJumpBike>(
this);
2521 const auto *raceManager = System::RaceManager::Instance();
2523 auto &status = KartObjectProxy::status();
2526 if (!raceManager->isStageReached(System::RaceManager::Stage::Race) ||
2527 EGG::Mathf::abs(
m_speed) < 5.0f) {
2539 f32 stickX = state()->stickX();
2540 f32 extVelXFactor = 0.0f;
2547 eStatus::CannonStart, eStatus::InCannon)) {
2549 }
else if (!state()->isDrifting()) {
2550 if (stickX <= 0.2f) {
2551 if (stickX >= -0.2f) {
2566 leanRotMin = -leanRotMax;
2570 if (stickX == 0.0f) {
2576 }
else if (stickX == 0.0f) {
2584 bool capped =
false;
2596 dynamics()->setExtVel(dynamics()->extVel() + componentXAxis() * extVelXFactor);
2599 f32 leanRotScalar = state()->isDrifting() ? 0.065f : 0.05f;
2603 dynamics()->setAngVel2(dynamics()->angVel2() +
2604 EGG::Vector3f(m_standStillBoostRot, turn * wheelieRotFactor(),
2613 scalar = std::min(1.0f, scalar);
2614 top = scalar *
m_up + (1.0f - scalar) * EGG::Vector3f::ey;
2616 if (std::numeric_limits<f32>::epsilon() < top.
squaredLength()) {
2621 dynamics()->setTop_(top);
2629 static constexpr std::array<TurningParameters, 2> TURNING_PARAMS_ARRAY = {{
2630 {0.8f, 0.08f, 1.0f, 0.1f, 1.2f, 0.8f, 0.08f, 0.6f, 0.15f, 1.6f, 0.9f, 180},
2631 {1.0f, 0.1f, 1.0f, 0.05f, 1.5f, 0.7f, 0.08f, 0.6f, 0.15f, 1.3f, 0.9f, 180},
2634 KartMove::setTurnParams();
2636 if (param()->stats().driftType == KartParam::Stats::DriftType::Outside_Drift_Bike) {
2638 }
else if (param()->stats().driftType == KartParam::Stats::DriftType::Inside_Drift_Bike) {
2642 if (System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
2652void KartMoveBike::init(
bool b1,
bool b2) {
2653 KartMove::init(b1, b2);
2662 m_autoHardStickXFrames = 0;
2666void KartMoveBike::clear() {
2674 constexpr u32 FAILED_WHEELIE_FRAMES = 15;
2675 constexpr f32 AUTO_WHEELIE_CANCEL_STICK_THRESHOLD = 0.85f;
2680 auto &status = KartObjectProxy::status();
2683 bool cancelAutoWheelie =
false;
2686 EGG::Mathf::abs(state()->stickX()) <= AUTO_WHEELIE_CANCEL_STICK_THRESHOLD) {
2687 m_autoHardStickXFrames = 0;
2689 if (++m_autoHardStickXFrames > 15) {
2690 cancelAutoWheelie =
true;
2702 dynamics()->setAngVel0(angVel0);
2712 f32 vel1DirUp = m_vel1Dir.dot(EGG::Vector3f::ey);
2717 angVel2.x -=
m_wheelieRot * (1.0f - EGG::Mathf::abs(vel1DirUp));
2718 dynamics()->setAngVel2(angVel2);
2723 status.setBit(eStatus::WheelieRot);
2725 status.resetBit(eStatus::WheelieRot);
2752 constexpr u16 MAX_MT_CHARGE = 270;
2753 constexpr u16 BASE_MT_CHARGE = 2;
2754 constexpr f32 BONUS_CHARGE_STICK_THRESHOLD = 0.4f;
2755 constexpr u16 EXTRA_MT_CHARGE = 3;
2757 if (m_driftState != DriftState::ChargingMt) {
2763 f32 stickX = state()->stickX();
2764 if (-BONUS_CHARGE_STICK_THRESHOLD <= stickX) {
2765 if (BONUS_CHARGE_STICK_THRESHOLD < stickX &&
m_hopStickX == -1) {
2774 m_driftState = DriftState::ChargedMt;
2779void KartMoveBike::initOob() {
2780 KartMove::initOob();
2787 constexpr s16 COOLDOWN_FRAMES = 20;
2788 bool dpadUp = inputs()->currentState().trickUp();
2789 auto &status = KartObjectProxy::status();
2794 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 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 TBitFlagExt< N, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
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.
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.
@ SoftWallPush
Tracks whether we are being pushed by a soft wall or HWG.
@ 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.
@ SoftWallUnlockRotation
Set when we are in "Barrel Roll" state.
@ 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 setAxisRotation(f32 angle, const EGG::Vector3f &axis)
Set the quat given angle and axis.
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
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 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.