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;
33void KartSub::createSubsystems(
bool isBike,
const KartParam::Stats &stats) {
34 m_move = isBike ?
new KartMoveBike :
new KartMove;
35 m_action =
new KartAction;
36 m_move->createSubsystems(stats);
37 m_state =
new KartState;
38 m_collide =
new KartCollide;
44 pointers.collide = m_collide;
45 pointers.state = m_state;
46 pointers.move = m_move;
47 pointers.action = m_action;
55 move()->setTurnParams();
61void KartSub::initAABB(KartAccessor &accessor, KartObject *
object) {
62 f32 radius = 25.0f + collide()->boundingRadius();
63 f32 hardSpeedLimit = move()->hardSpeedLimit();
65 accessor.boxColUnit = Field::BoxColManager::Instance()->insertDriver(radius, hardSpeedLimit,
66 &pos(),
true,
object);
70void KartSub::initPhysicsValues() {
72 collide()->resetHitboxes();
76void KartSub::resetPhysics() {
79 collide()->resetHitboxes();
81 for (
u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
82 suspensionPhysics(wheelIdx)->reset();
84 for (
u16 tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
85 tirePhysics(tireIdx)->reset();
87 m_move->setKartSpeedLimit();
93 m_maxSuspOvertravel.setZero();
94 m_minSuspOvertravel.setZero();
103 auto &status = KartObjectProxy::status();
105 if (status.
onBit(eStatus::CannonStart)) {
106 physics()->hitboxGroup()->reset();
107 for (
size_t i = 0; i < tireCount(); ++i) {
108 tirePhysics(i)->hitboxGroup()->reset();
110 move()->enterCannon();
115 if (status.
onBit(eStatus::TriggerRespawn)) {
119 move()->setTurnParams();
120 move()->calcRespawnStart();
123 physics()->setPos(dynamics()->pos());
124 physics()->setVelocity(dynamics()->velocity());
125 dynamics()->setGravity(-1.3f);
126 dynamics()->setAngVel0YFactor(0.9f);
131 collide()->pullPath().calc();
133 if (status.
onBit(eStatus::SkipWheelCalc)) {
134 for (
size_t tireIdx = 0; tireIdx < tireCount(); ++tireIdx) {
135 tirePhysics(tireIdx)->setLastPos(pos());
142 dynamics()->setTop(move()->up());
145 const auto *raceManager = System::RaceManager::Instance();
146 if (!raceManager->isStageReached(System::RaceManager::Stage::Race)) {
147 dynamics()->setIntVel(EGG::Vector3f::zero);
151 killExtVel = killExtVel.
rej(move()->smoothedUp());
157 dynamics()->setExtVel(killExtVel);
160 f32 maxSpeed = move()->hardSpeedLimit();
163 move()->calcRejectRoad();
165 if (status.
offBit(eStatus::InCannon)) {
167 collisionGroup()->setHitboxScale(move()->totalScale());
177 constexpr s16 SIDE_COLLISION_TIME = 5;
181 m_movingWaterCollisionCount = 0;
182 m_movingObjCollisionCount = 0;
183 m_floorCollisionCount = 0;
185 m_maxSuspOvertravel.setZero();
186 m_minSuspOvertravel.setZero();
190 flags.
setBit(Field::eBoxColFlag::Drivable, Field::eBoxColFlag::Object);
191 boxColUnit()->search(flags);
193 collide()->calcObjectCollision();
194 dynamics()->setPos(pos() + collide()->tangentOff());
196 auto &status = KartObjectProxy::status();
198 if (status.
onBit(eStatus::SomethingWallCollision)) {
199 const EGG::Vector3f &softWallSpeed = state()->softWallSpeed();
200 f32 speedFactor = 5.0f;
205 effectiveSpeed = softWallSpeed;
207 effectiveSpeed = softWallSpeed.
perpInPlane(move()->smoothedUp(),
true);
208 f32 speedDotUp = softWallSpeed.
dot(move()->smoothedUp());
209 if (speedDotUp < 0.0f) {
210 speedFactor += -speedDotUp * 10.0f;
214 effectiveSpeed *= speedFactor * scale().y;
215 setPos(pos() + effectiveSpeed);
216 collide()->setMovement(collide()->movement() + effectiveSpeed);
219 auto &colData = collisionData();
223 if (colData.bWallAtLeftCloser || colData.bWallAtRightCloser) {
224 f32 sign = colData.bWallAtRightCloser ? 1.0f : -1.0f;
230 colPerpBounceDir.y = 0.0f;
231 setPos(pos() + colPerpBounceDir);
232 collide()->setMovement(collide()->movement() + colPerpBounceDir);
237 body()->calcSinkDepth();
239 Field::CollisionDirector::Instance()->checkCourseColNarrScLocal(250.0f, pos(),
242 if (status.
offBit(eStatus::InCannon)) {
245 body()->calcTargetSinkDepth();
247 if (colData.bWall || colData.bWall3) {
248 collide()->setMovement(collide()->movement() + colData.movement);
254 collide()->calcFloorEffect();
255 collide()->calcFloorMomentRate();
257 if (colData.bFloor) {
259 addFloor(colData,
false);
264 m_someScale = std::max(scale().y, param()->stats().shrinkScale);
267 f32 speedFactor = 1.0f;
268 f32 handlingFactor = 0.0f;
269 for (
u16 i = 0; i < suspCount(); ++i) {
271 suspensionPhysics(i)->calcCollision(
DT, gravity, wheelMatrix);
273 const CollisionData &colData = tirePhysics(i)->hitboxGroup()->collisionData();
275 speedFactor = std::min(speedFactor, colData.speedFactor);
278 handlingFactor += colData.rotFactor;
279 addFloor(colData,
false);
283 if (status.
offBit(eStatus::SkipWheelCalc)) {
284 EGG::Vector3f vehicleCompensation = m_maxSuspOvertravel + m_minSuspOvertravel;
285 dynamics()->setPos(dynamics()->pos() + vehicleCompensation);
287 if (!collisionData().bFloor) {
293 for (
u16 wheelIdx = 0; wheelIdx < tireCount(); ++wheelIdx) {
294 const WheelPhysics *wheelPhysics = tirePhysics(wheelIdx);
295 if (wheelPhysics->_74() == 0.0f) {
299 const CollisionData &colData = wheelPhysics->hitboxGroup()->collisionData();
300 relPos += colData.relPos;
302 floorNrm += colData.floorNrm;
307 f32 scalar = (1.0f /
static_cast<f32
>(count));
310 collide()->setFloorColInfo(collisionData(), relPos * scalar, vel * scalar,
317 for (
u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) {
318 suspensionPhysics(wheelIdx)->
calcSuspension(forward, vehicleCompensation);
321 move()->calcHopPhysics();
324 if (status.
onBit(eStatus::CollidingOffroad)) {
325 const auto &stats = param()->stats();
326 speedFactor = stats.kclSpeed[3];
327 handlingFactor = stats.kclRot[3];
330 move()->setKCLWheelSpeedFactor(speedFactor);
331 move()->setKCLWheelRotFactor(handlingFactor);
333 move()->setFloorCollisionCount(m_floorCollisionCount);
340 collide()->resetHitboxes();
346void KartSub::resizeAABB(f32 radiusScale) {
347 f32 radius = radiusScale * collisionGroup()->boundingRadius();
348 boxColUnit()->resize(radius + 25.0f, move()->hardSpeedLimit());
352void KartSub::addFloor(
const CollisionData &colData,
bool) {
353 ++m_floorCollisionCount;
355 if (colData.bHasRoadVel) {
356 ++m_movingObjCollisionCount;
357 m_objVel += colData.roadVelocity;
360 auto &status = KartObjectProxy::status();
361 if (colData.bMovingWaterMomentum || colData.bMovingWaterDecaySpeed) {
362 ++m_movingWaterCollisionCount;
364 status.
changeBit(colData.bMovingWaterDecaySpeed, eStatus::MovingWaterDecaySpeed);
365 status.
changeBit(colData.bMovingWaterDisableAccel, eStatus::DisableAcceleration);
366 status.
changeBit(colData.bMovingWaterVertical, eStatus::MovingWaterVertical);
368 status.
resetBit(eStatus::MovingWaterDecaySpeed, eStatus::DisableAcceleration,
369 eStatus::MovingWaterVertical);
372 status.
changeBit(colData.bMovingWaterStickyRoad, eStatus::MovingWaterStickyRoad);
376void KartSub::updateSuspOvertravel(
const EGG::Vector3f &suspOvertravel) {
377 m_maxSuspOvertravel = m_maxSuspOvertravel.
minimize(suspOvertravel);
378 m_minSuspOvertravel = m_minSuspOvertravel.
maximize(suspOvertravel);
382void KartSub::tryEndHWG() {
383 auto &status = KartObjectProxy::status();
385 if (status.
onBit(eStatus::SoftWallDrift)) {
386 if (EGG::Mathf::abs(move()->speed()) > 15.0f ||
388 status.
resetBit(eStatus::SoftWallDrift);
390 if (EGG::Mathf::abs(componentXAxis().dot(EGG::Vector3f::ey)) > 0.8f) {
391 status.
resetBit(eStatus::SoftWallDrift);
403 if (status.
offBit(eStatus::InAction)) {
404 dynamics()->setForceUpright(status.
offBit(eStatus::SoftWallDrift));
409void KartSub::calcMovingObj() {
410 if (m_movingObjCollisionCount == 0) {
411 f32 scalar = state()->airtime() < 20 ? 1.0f : 0.9f;
412 physics()->composeDecayingMovingObjVel(0.7f, scalar, m_floorCollisionCount != 0);
414 m_objVel *= 1.0f /
static_cast<f32
>(m_floorCollisionCount);
415 physics()->composeMovingObjVel(m_objVel, 0.2f);
420void KartSub::calcMovingWater() {
421 constexpr f32 DECAY_FLOOR_SCALAR = 0.7f;
422 constexpr f32 DECAY_AIR_SCALAR = 0.5f;
423 constexpr f32 DECAY_KC_AIR_SCALAR = 0.3f;
425 auto &status = KartObjectProxy::status();
426 const auto &pullPath = collide()->pullPath();
428 if (m_movingWaterCollisionCount > 0) {
429 f32 ratio =
static_cast<f32
>(m_movingWaterCollisionCount) /
430 static_cast<f32
>(m_floorCollisionCount);
433 if (status.
offBit(eStatus::MovingWaterDecaySpeed)) {
435 physics()->composeMovingRoadVel(vel, 0.2f);
438 physics()->shiftDecayMovingRoadVel(vel, pullPath.maxPullSpeed());
441 f32 airScalar = status.
onBit(eStatus::MovingWaterStickyRoad) ? DECAY_AIR_SCALAR : 1.0f;
442 if (System::RaceConfig::Instance()->raceScenario().course == Course::Koopa_Cape &&
443 status.
onBit(eStatus::MovingWaterDecaySpeed)) {
444 airScalar = DECAY_KC_AIR_SCALAR;
447 physics()->decayMovingRoadVel(DECAY_FLOOR_SCALAR, airScalar, m_floorCollisionCount > 0);
450 if (status.
onBit(eStatus::MovingWaterStickyRoad)) {
454 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.
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.
virtual EGG::Matrix34f wheelMatrix(u16)
Computes a matrix to represent wheel rotation. For Karts, this is wheel-agnostic.
void calcHitboxes()
On each frame, calculates the positions for each hitbox.
void calc()
Each frame, calculates the kart's movement.
void updatePose()
Constructs a transformation matrix from rotation and position.
void calc(f32 dt, f32 maxSpeed, const EGG::Vector3f &scale, bool air)
Computes trick rotation and calls to KartDynamics::calc().
void calc()
Every frame, resets the input state and saves collision-related bit flags.
void calcInput()
Each frame, read input and save related bit flags. Also handles start boosts.
void resetEjection()
Resets certain bitfields pertaining to ejections (reject road, half pipe zippers, etc....
void calcPass0()
The first phase of physics computations on each frame.
static constexpr f32 DT
Delta time.
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...
s16 m_sideCollisionTimer
Number of frames to apply movement from wall collision.
void calcSuspension(const EGG::Vector3f &forward, const EGG::Vector3f &vehicleMovement)
Calculates linear force and rotation from the kart's suspension.
Manages wheel physics and collision checks.
Pertains to kart-related functionality.
@ HWG
Set when "Horizontal Wall Glitch" is active.
@ 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.
Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
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.
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.
Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
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.