3#include "game/kart/KartAction.hh"
4#include "game/kart/KartBody.hh"
5#include "game/kart/KartCollide.hh"
6#include "game/kart/KartMove.hh"
7#include "game/kart/KartObject.hh"
8#include "game/kart/KartPullPath.hh"
9#include "game/kart/KartState.hh"
10#include "game/kart/KartSuspensionPhysics.hh"
12#include "game/field/BoxColManager.hh"
13#include "game/field/CollisionDirector.hh"
15#include "game/system/RaceConfig.hh"
16#include "game/system/RaceManager.hh"
18#include <egg/math/Math.hh>
22KartSub::KartSub() =
default;
26 EGG::egg_delete(m_collide);
27 EGG::egg_delete(m_state);
28 EGG::egg_delete(m_move);
29 EGG::egg_delete(m_action);
34 m_move = isBike ?
static_cast<KartMove *
>(EGG::egg_new<KartMoveBike>()) :
35 EGG::egg_new<KartMove>();
36 m_action = EGG::egg_new<KartAction>();
37 m_move->createSubsystems(stats);
38 m_state = EGG::egg_new<KartState>();
39 m_collide = EGG::egg_new<KartCollide>();
45 pointers.collide = m_collide;
46 pointers.state = m_state;
47 pointers.move = m_move;
48 pointers.action = m_action;
56 move()->setTurnParams();
62void KartSub::initAABB(KartAccessor &accessor, KartObject *
object) {
63 f32 radius = 25.0f + collide()->boundingRadius();
64 f32 hardSpeedLimit = move()->hardSpeedLimit();
66 accessor.boxColUnit = Field::BoxColManager::Instance()->insertDriver(radius, hardSpeedLimit,
67 &pos(),
true,
object);
71void KartSub::initPhysicsValues() {
73 collide()->resetHitboxes();
77void KartSub::resetPhysics() {
79 physics()->updatePose();
80 collide()->resetHitboxes();
82 for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
83 suspensionPhysics(wheelIdx)->reset();
85 for (u16 tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
86 tirePhysics(tireIdx)->reset();
88 m_move->setKartSpeedLimit();
94 m_maxSuspOvertravel.setZero();
95 m_minSuspOvertravel.setZero();
104 auto &status = KartObjectProxy::status();
106 if (status.onBit(eStatus::CannonStart)) {
107 physics()->hitboxGroup()->reset();
108 for (
size_t i = 0; i < tireCount(); ++i) {
109 tirePhysics(i)->hitboxGroup()->reset();
111 move()->enterCannon();
116 if (status.onBit(eStatus::TriggerRespawn)) {
120 move()->setTurnParams();
121 move()->calcRespawnStart();
124 physics()->setPos(dynamics()->pos());
125 physics()->setVelocity(dynamics()->velocity());
126 dynamics()->setGravity(-1.3f);
127 dynamics()->setAngVel0YFactor(0.9f);
129 state()->calcInput();
132 collide()->pullPath().calc();
134 if (status.onBit(eStatus::SkipWheelCalc)) {
135 for (
size_t tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
136 tirePhysics(tireIdx)->setLastPos(pos());
143 dynamics()->setTop(move()->up());
146 const auto *raceManager = System::RaceManager::Instance();
147 if (!raceManager->isStageReached(System::RaceManager::Stage::Race)) {
148 dynamics()->setIntVel(EGG::Vector3f::zero);
152 killExtVel = killExtVel.
rej(move()->smoothedUp());
158 dynamics()->setExtVel(killExtVel);
161 f32 maxSpeed = move()->hardSpeedLimit();
164 move()->calcRejectRoad();
166 if (status.offBit(eStatus::InCannon)) {
167 collide()->calcHitboxes();
168 collisionGroup()->setHitboxScale(move()->totalScale());
178 constexpr s16 SIDE_COLLISION_TIME = 5;
180 state()->resetEjection();
182 m_movingWaterCollisionCount = 0;
183 m_movingObjCollisionCount = 0;
184 m_floorCollisionCount = 0;
186 m_maxSuspOvertravel.setZero();
187 m_minSuspOvertravel.setZero();
190 Field::BoxColFlag flags;
191 flags.
setBit(Field::eBoxColFlag::Drivable, Field::eBoxColFlag::Object);
192 boxColUnit()->search(flags);
194 collide()->calcObjectCollision();
195 dynamics()->setPos(pos() + collide()->tangentOff());
197 auto &status = KartObjectProxy::status();
200 const EGG::Vector3f &softWallSpeed = state()->softWallSpeed();
201 f32 speedFactor = 5.0f;
206 effectiveSpeed = softWallSpeed;
208 effectiveSpeed = softWallSpeed.
perpInPlane(move()->smoothedUp(),
true);
209 f32 speedDotUp = softWallSpeed.
dot(move()->smoothedUp());
210 if (speedDotUp < 0.0f) {
211 speedFactor += -speedDotUp * 10.0f;
215 effectiveSpeed *= speedFactor * scale().y;
216 setPos(pos() + effectiveSpeed);
217 collide()->setMovement(collide()->movement() + effectiveSpeed);
220 auto &colData = collisionData();
222 EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex);
224 if (colData.bWallAtLeftCloser || colData.bWallAtRightCloser) {
225 f32 sign = colData.bWallAtRightCloser ? 1.0f : -1.0f;
231 colPerpBounceDir.y = 0.0f;
232 setPos(pos() + colPerpBounceDir);
233 collide()->setMovement(collide()->movement() + colPerpBounceDir);
238 body()->calcSinkDepth();
240 Field::CollisionDirector::Instance()->checkCourseColNarrScLocal(250.0f, pos(),
243 if (status.offBit(eStatus::InCannon)) {
245 collide()->findCollision();
246 body()->calcTargetSinkDepth();
248 if (colData.bWall || colData.bWall3) {
249 collide()->setMovement(collide()->movement() + colData.movement);
255 collide()->calcFloorEffect();
256 collide()->calcFloorMomentRate();
258 if (colData.bFloor) {
260 addFloor(colData,
false);
264 EGG::Vector3f forward = fullRot().rotateVector(EGG::Vector3f::ez);
265 m_someScale = std::max(scale().y, param()->stats().shrinkScale);
268 f32 speedFactor = 1.0f;
269 f32 handlingFactor = 0.0f;
270 for (u16 i = 0; i < suspCount(); ++i) {
272 suspensionPhysics(i)->calcCollision(
DT, gravity, wheelMatrix);
274 const CollisionData &colData = tirePhysics(i)->hitboxGroup()->collisionData();
276 speedFactor = std::min(speedFactor, colData.speedFactor);
279 handlingFactor += colData.rotFactor;
280 addFloor(colData,
false);
284 if (status.offBit(eStatus::SkipWheelCalc)) {
285 EGG::Vector3f vehicleCompensation = m_maxSuspOvertravel + m_minSuspOvertravel;
286 dynamics()->setPos(dynamics()->pos() + vehicleCompensation);
288 if (!collisionData().bFloor) {
294 for (u16 wheelIdx = 0; wheelIdx < tireCount(); ++wheelIdx) {
295 const WheelPhysics *wheelPhysics = tirePhysics(wheelIdx);
296 if (wheelPhysics->_74() == 0.0f) {
300 const CollisionData &colData = wheelPhysics->hitboxGroup()->collisionData();
301 relPos += colData.relPos;
303 floorNrm += colData.floorNrm;
308 f32 scalar = (1.0f /
static_cast<f32
>(count));
311 collide()->setFloorColInfo(collisionData(), relPos * scalar, vel * scalar,
314 collide()->FUN_80572F4C();
318 for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
319 suspensionPhysics(wheelIdx)->calcSuspension(forward, vehicleCompensation);
322 move()->calcHopPhysics();
325 if (status.onBit(eStatus::CollidingOffroad)) {
326 const auto &stats = param()->stats();
327 speedFactor = stats.kclSpeed[3];
328 handlingFactor = stats.kclRot[3];
331 move()->setKCLWheelSpeedFactor(speedFactor);
332 move()->setKCLWheelRotFactor(handlingFactor);
334 move()->setFloorCollisionCount(m_floorCollisionCount);
339 physics()->updatePose();
341 collide()->resetHitboxes();
347void KartSub::resizeAABB(f32 radiusScale) {
348 f32 radius = radiusScale * collisionGroup()->boundingRadius();
349 boxColUnit()->resize(radius + 25.0f, move()->hardSpeedLimit());
353void KartSub::addFloor(
const CollisionData &colData,
bool) {
354 ++m_floorCollisionCount;
356 if (colData.bHasRoadVel) {
357 ++m_movingObjCollisionCount;
358 m_objVel += colData.roadVelocity;
361 auto &status = KartObjectProxy::status();
362 if (colData.bMovingWaterMomentum || colData.bMovingWaterDecaySpeed) {
363 ++m_movingWaterCollisionCount;
365 status.
changeBit(colData.bMovingWaterDecaySpeed, eStatus::MovingWaterDecaySpeed);
366 status.
changeBit(colData.bMovingWaterDisableAccel, eStatus::DisableAcceleration);
367 status.
changeBit(colData.bMovingWaterVertical, eStatus::MovingWaterVertical);
369 status.resetBit(eStatus::MovingWaterDecaySpeed, eStatus::DisableAcceleration,
370 eStatus::MovingWaterVertical);
373 status.changeBit(colData.bMovingWaterStickyRoad, eStatus::MovingWaterStickyRoad);
377void KartSub::updateSuspOvertravel(
const EGG::Vector3f &suspOvertravel) {
378 m_maxSuspOvertravel = m_maxSuspOvertravel.minimize(suspOvertravel);
379 m_minSuspOvertravel = m_minSuspOvertravel.maximize(suspOvertravel);
383void KartSub::tryEndHWG() {
384 auto &status = KartObjectProxy::status();
387 if (EGG::Mathf::abs(move()->speed()) > 15.0f ||
391 if (EGG::Mathf::abs(componentXAxis().dot(EGG::Vector3f::ey)) > 0.8f) {
404 if (status.offBit(eStatus::InAction)) {
410void KartSub::calcMovingObj() {
411 if (m_movingObjCollisionCount == 0) {
412 f32 scalar = state()->airtime() < 20 ? 1.0f : 0.9f;
413 physics()->composeDecayingMovingObjVel(0.7f, scalar, m_floorCollisionCount != 0);
415 m_objVel *= 1.0f /
static_cast<f32
>(m_floorCollisionCount);
416 physics()->composeMovingObjVel(m_objVel, 0.2f);
421void KartSub::calcMovingWater() {
422 constexpr f32 DECAY_FLOOR_SCALAR = 0.7f;
423 constexpr f32 DECAY_AIR_SCALAR = 0.5f;
424 constexpr f32 DECAY_KC_AIR_SCALAR = 0.3f;
426 auto &status = KartObjectProxy::status();
427 const auto &pullPath = collide()->pullPath();
429 if (m_movingWaterCollisionCount > 0) {
430 f32 ratio =
static_cast<f32
>(m_movingWaterCollisionCount) /
431 static_cast<f32
>(m_floorCollisionCount);
432 EGG::Vector3f dir = pullPath.pullDirection().perpInPlane(move()->smoothedUp(),
true);
434 if (status.offBit(eStatus::MovingWaterDecaySpeed)) {
435 EGG::Vector3f vel = pullPath.pullSpeed() * dir * ratio;
436 physics()->composeMovingRoadVel(vel, 0.2f);
438 EGG::Vector3f vel = pullPath.pullSpeed() * dir;
439 physics()->shiftDecayMovingRoadVel(vel, pullPath.maxPullSpeed());
442 f32 airScalar = status.onBit(eStatus::MovingWaterStickyRoad) ? DECAY_AIR_SCALAR : 1.0f;
443 if (System::RaceConfig::Instance()->raceScenario().course == Course::Koopa_Cape &&
444 status.onBit(eStatus::MovingWaterDecaySpeed)) {
445 airScalar = DECAY_KC_AIR_SCALAR;
448 physics()->decayMovingRoadVel(DECAY_FLOOR_SCALAR, airScalar, m_floorCollisionCount > 0);
451 if (status.onBit(eStatus::MovingWaterStickyRoad)) {
452 EGG::Vector3f dir = pullPath.pullDirection().perpInPlane(move()->smoothedUp(),
true);
454 const EGG::Vector3f &vel = dynamics()->movingRoadVel();
455 dynamics()->setMovingRoadVel(EGG::Vector3f(vel.x, dir.y, vel.z));
#define KCL_TYPE_VEHICLE_INTERACTABLE
0xEFFFBDFF
constexpr TBitFlagExt< N, E > & changeBit(bool on, Es... es)
Changes the state of the corresponding bits for the provided enum values.
void updatePose()
Constructs a transformation matrix from rotation and position.
void calcPass0()
The first phase of physics computations on each frame.
s16 m_sideCollisionTimer
Number of frames to apply movement from wall collision.
void copyPointers(KartAccessor &pointers)
Called during static construction of KartObject to synchronize the pointers.
f32 m_colPerpendicularity
Dot product between floor and colliding wall normals.
void calcPass1()
The second phase of physics computations on each frame.Handles the second-half of physics calculation...
static constexpr f32 DT
Delta time.
Manages wheel physics and collision checks.
Pertains to kart-related functionality.
@ SoftWallPush
Tracks whether we are being pushed by a soft wall or HWG.
@ HWG
Set when "Horizontal Wall Glitch" is active.
@ SoftWallUnlockRotation
Set when we are in "Barrel Roll" state.
@ ZipperStick
Set while mid-air and still influenced by the zipper.
@ AllWheelsCollision
Set when all wheels are touching floor collision.
@ AirtimeOver20
Set after 20 frames of airtime, resets on landing.
@ Wall3Collision
Set when colliding with wall KCL COL_TYPE_WALL_2.
@ TouchingGround
Set when any part of the vehicle is colliding with floor KCL.
@ WallCollision
Set if we are colliding with a wall.
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 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.
bool bFloor
Set if colliding with KCL which satisfies KCL_TYPE_FLOOR.
Shared between classes who inherit KartObjectProxy so they can access one another.
Various character/vehicle-related handling and speed stats.