1#include "KartCollide.hh"
3#include "game/kart/KartBody.hh"
4#include "game/kart/KartMove.hh"
5#include "game/kart/KartPhysics.hh"
6#include "game/kart/KartState.hh"
8#include "game/field/CollisionDirector.hh"
9#include "game/field/ObjectCollisionKart.hh"
10#include "game/field/ObjectDirector.hh"
12#include <egg/math/BoundBox.hh>
13#include <egg/math/Math.hh>
18KartCollide::KartCollide() {
19 m_boundingRadius = 100.0f;
24KartCollide::~KartCollide() =
default;
27void KartCollide::init() {
29 m_floorMomentRate = 0.8f;
33 m_smoothedBack = 0.0f;
34 m_suspBottomHeightNonSoftWall = 0.0f;
35 m_suspBottomHeightSoftWall = 0.0f;
36 m_someNonSoftWallTimer = 0;
37 m_someSoftWallTimer = 0;
38 m_poleAngVelTimer = 0;
40 m_colPerpendicularity = 0.0f;
44void KartCollide::resetHitboxes() {
45 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
46 for (
u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
47 hitboxGroup->hitbox(idx).setLastPos(scale(), pose());
56 for (
u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
57 hitboxGroup->hitbox(idx).
calc(move()->totalScale(), body()->sinkDepth(), scale(), fullRot(),
65 bool wasHalfPipe = state()->isEndHalfPipe() || state()->isActionMidZipper();
66 const EGG::Quatf &rot = wasHalfPipe ? mainRot() : fullRot();
69 auto &colData = collisionData();
70 bool existingWallCollision = colData.bWall || colData.bWall3;
71 bool newWallCollision =
72 m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
73 if (existingWallCollision || newWallCollision) {
74 if (!existingWallCollision) {
75 colData.wallNrm = m_totalReactionWallNrm;
76 if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall)) {
78 }
else if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall3)) {
79 colData.bWall3 =
true;
81 }
else if (newWallCollision) {
82 colData.wallNrm += m_totalReactionWallNrm;
83 if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall)) {
85 }
else if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall3)) {
86 colData.bWall3 =
true;
90 colData.wallNrm.normalise();
102 if (isInRespawn() || state()->isBoost() || state()->isOverZipper() ||
103 state()->isZipperInvisibleWall() || state()->isNoSparkInvisibleWall() ||
104 state()->isHalfPipeRamp()) {
110 bool resetXZ = fVar1 > 0.0f && state()->isAirtimeOver20() && dynamics()->velocity().y < -50.0f;
112 FUN_805B72B8(state()->isInAction() ? 0.3f : 0.01f, fVar1, resetXZ,
113 !state()->isJumpPadDisableYsusForce());
124 const auto &colData = collisionData();
126 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
130 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
133 f32 directionalVelocity = colData.vel.dot(collisionDir);
134 if (directionalVelocity >= 0.0f) {
141 rotMat.
makeQ(dynamics()->mainRot());
154 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.
dot(step3));
159 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
160 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
162 f32 fVar3 = otherVal;
163 if (fVar1 < EGG::Mathf::abs(otherVal)) {
165 if (otherVal < 0.0f) {
166 fVar3 = -param_1 * EGG::Mathf::abs(val);
172 f32 local_1d0 = step6.y;
175 }
else if (colData.bFloor) {
176 f32 velY = intVel().y;
182 dynamics()->setExtVel(newExtVel);
187 f32 prevExtVelY = extVel().y;
189 extVelAdd.y = local_1d0;
190 dynamics()->setExtVel(extVel() + extVelAdd);
192 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
195 dynamics()->setExtVel(extVelNoY);
202 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
215 collisionData.reset();
220 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
227 for (
u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
229 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
231 if (hitbox.bspHitbox()->wallsOnly != 0) {
233 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
236 hitbox.
calc(totalScale, sinkDepth, scale, rot, pos());
238 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
239 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
245 if (!
FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
248 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
249 collisionData.bHasRoadVel =
true;
250 collisionData.roadVelocity = colInfo.roadVelocity;
253 processBody(collisionData, hitbox, &colInfo, &maskOut);
262 collisionData.speedFactor = 1.0f;
263 collisionData.rotFactor = 1.0f;
268void KartCollide::calcFloorEffect() {
269 if (state()->isTouchingGround()) {
273 m_suspBottomHeightNonSoftWall = 0.0f;
274 m_surfaceFlags.
resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
276 eSurfaceFlags::StopHalfPipeState);
277 m_suspBottomHeightSoftWall = 0.0f;
278 m_someNonSoftWallTimer = 0;
279 m_someSoftWallTimer = 0;
282 calcTriggers(&mask, pos(),
false);
284 if (m_solidOobTimer >= 3 && m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) &&
285 m_surfaceFlags.
offBit(eSurfaceFlags::Wall)) {
291 activateOob(
true, &mask,
false,
false);
295 calcTriggers(&mask, pos(),
true);
298 m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
303 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
305 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
306 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
310 m_smoothedBack += (back.
dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
312 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
313 scaledPos += scalar * back;
315 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
316 scaledPos, v1, typeMask,
nullptr, mask, 0);
323 handleTriggers(mask);
330 m_surfaceFlags.
setBit(eSurfaceFlags::Wall);
334 m_surfaceFlags.
setBit(eSurfaceFlags::SolidOOB);
341 calcFallBoundary(mask,
false);
345 auto *colDir = Field::CollisionDirector::Instance();
348 halfPipe()->end(
true);
349 state()->setEndHalfPipe(
true);
350 m_surfaceFlags.
setBit(eSurfaceFlags::StopHalfPipeState);
362 auto *colDir = Field::CollisionDirector::Instance();
367 activateOob(
false, mask,
false,
false);
371void KartCollide::calcBeforeRespawn() {
372 if (pos().y < 0.0f) {
373 activateOob(
true,
nullptr,
false,
false);
376 if (!state()->isBeforeRespawn()) {
380 if (--m_respawnTimer > 0) {
384 state()->setBeforeRespawn(
false);
386 move()->triggerRespawn();
392 constexpr s16 RESPAWN_TIME = 130;
394 if (state()->isBeforeRespawn()) {
400 m_respawnTimer = RESPAWN_TIME;
401 state()->setBeforeRespawn(
true);
414 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
416 bspHitbox->radius = radius;
417 hitboxGroup->resetCollision();
418 firstHitbox.setWorldPos(center);
421 colInfo.bbox.setZero();
424 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
426 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
427 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
433 collisionData.speedFactor = 1.0f;
434 collisionData.rotFactor = 1.0f;
438 collisionData.tangentOff = colInfo.tangentOff;
440 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
441 collisionData.tangentOff += noBounceWallInfo.tangentOff;
442 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
443 collisionData.bSoftWall =
true;
447 collisionData.
bFloor =
true;
448 collisionData.floorNrm = colInfo.floorNrm;
451 collisionData.relPos = firstHitbox.worldPos() - pos();
452 collisionData.vel = colVel;
454 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
455 collisionData.bHasRoadVel =
true;
456 collisionData.roadVelocity = colInfo.roadVelocity;
459 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
473 if (colInfo->perpendicularity <= 0.0f) {
477 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
479 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
483 f32 bspPosX = hitbox.bspHitbox()->
position.x;
484 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
485 if (bspPosX > 0.0f) {
486 collisionData.bWallAtLeftCloser =
true;
488 collisionData.bWallAtRightCloser =
true;
491 collisionData.colPerpendicularity = colInfo->perpendicularity;
497 std::array<f32, 2> tangents = {0.0f, 0.0f};
500 for (
size_t i = 0; i < tangents.size(); ++i) {
501 f32 sign = i == 1 ? -1.0f : 1.0f;
502 f32 effectiveRadius = sign * hitbox.radius();
503 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
506 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
513 if (tangents[0] > tangents[1]) {
514 collisionData.bWallAtLeftCloser =
true;
515 collisionData.colPerpendicularity = colInfo->perpendicularity;
516 }
else if (tangents[1] > tangents[0]) {
517 collisionData.bWallAtRightCloser =
true;
518 collisionData.colPerpendicularity = colInfo->perpendicularity;
523void KartCollide::calcBoundingRadius() {
524 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
528void KartCollide::calcObjectCollision() {
529 constexpr f32 COS_PI_OVER_4 = 0.707f;
530 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
531 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
533 m_totalReactionWallNrm = EGG::Vector3f::zero;
534 m_surfaceFlags.
resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
536 size_t collisionCount = objectCollisionKart()->checkCollision(pose(), velocity());
538 const auto *objectDirector = Field::ObjectDirector::Instance();
540 for (
size_t i = 0; i < collisionCount; ++i) {
541 Reaction reaction = objectDirector->reaction(i);
542 if (reaction != Reaction::None && reaction != Reaction::UNK_7) {
543 size_t handlerIdx =
static_cast<std::underlying_type_t<Reaction>
>(reaction);
544 Action newAction = (this->*s_objectCollisionHandlers[handlerIdx])(i);
545 if (newAction != Action::None) {
546 action()->setHitDepth(objectDirector->hitDepth(i));
547 action()->
start(newAction);
550 if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
552 m_tangentOff += hitDepth;
553 m_movement += hitDepth;
557 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
558 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
561 if (lastDir.
dot(hitDirection) < -COS_PI_OVER_4) {
563 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
565 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
566 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
575void KartCollide::calcPoleTimer() {
576 if (m_poleAngVelTimer > 0 && (state()->isAccelerate() || state()->isBrake())) {
578 angVel2.y += m_poleYaw;
579 dynamics()->setAngVel2(angVel2);
582 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
590 processFloor(collisionData, hitbox, colInfo, maskOut,
true);
596 bool hasWallCollision = processWall(collisionData, maskOut);
598 processFloor(collisionData, hitbox, colInfo, maskOut,
false);
600 if (hasWallCollision) {
608bool KartCollide::processWall(CollisionData &collisionData,
Field::KCLTypeMask *maskOut) {
613 auto *colDirector = Field::CollisionDirector::Instance();
619 colDirector->findClosestCollisionEntry(maskOut,
621 auto *entry = colDirector->closestCollisionEntry();
623 collisionData.bSoftWall =
true;
640 if (collisionData.bSoftWall) {
641 ++m_someSoftWallTimer;
642 m_suspBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
649 auto *colDirector = Field::CollisionDirector::Instance();
651 if (!colDirector->findClosestCollisionEntry(maskOut,
KCL_TYPE_FLOOR)) {
655 const auto *closestColEntry = colDirector->closestCollisionEntry();
657 u16 attribute = closestColEntry->attribute;
658 if (!(attribute & 0x2000)) {
659 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
661 collisionData.bTrickable =
true;
662 m_surfaceFlags.
setBit(eSurfaceFlags::Trickable);
665 collisionData.speedFactor = std::min(collisionData.speedFactor,
668 collisionData.
intensity = (attribute >> 0xb) & 3;
671 if (attribute & 0x4000) {
672 state()->setRejectRoad(
true);
679 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
682 if (!!(*maskOut & BOOST_RAMP_MASK) &&
683 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
684 closestColEntry = colDirector->closestCollisionEntry();
685 move()->padType().
setBit(KartMove::ePadType::BoostRamp);
687 m_surfaceFlags.
setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
689 state()->setBoostRampType(-1);
690 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
693 if (!collisionData.bSoftWall) {
694 ++m_someNonSoftWallTimer;
695 m_suspBottomHeightNonSoftWall += hitbox.worldPos().y - hitbox.radius();
699 state()->setStickyRoad(
true);
703 if ((*maskOut & halfPipeRampMask) &&
704 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
705 state()->setHalfPipeRamp(
true);
706 state()->setHalfPipeInvisibilityTimer(2);
707 if (
KCL_VARIANT_TYPE(colDirector->closestCollisionEntry()->attribute) == 1) {
708 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
713 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
714 if ((!state()->isTouchingGround() || !state()->isJumpPad()) &&
715 !state()->isJumpPadMushroomVelYInc()) {
716 move()->padType().
setBit(KartMove::ePadType::JumpPad);
717 closestColEntry = colDirector->closestCollisionEntry();
720 collisionData.bTrickable =
true;
727 auto *colDirector = Field::CollisionDirector::Instance();
729 state()->setCannonPointId(
731 state()->setCannonStart(
true);
746 bool b1,
bool b2,
bool b3) {
752 f32 velDotFloorNrm = colData.vel.
dot(colData.floorNrm);
754 if (velDotFloorNrm >= 0.0f) {
759 rotMat.
makeQ(dynamics()->mainRot());
764 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
766 crossVec = crossVec.cross(colData.relPos);
768 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.
dot(crossVec));
770 crossVec = colData.floorNrm.cross(negSpeed);
771 crossVec = crossVec.cross(colData.floorNrm);
773 if (std::numeric_limits<f32>::epsilon() >= crossVec.
squaredLength()) {
778 f32 speedDot = std::min(0.0f, speed.
dot(crossVec));
779 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
781 auto [proj, rej] = crossVec.projAndRej(forward);
783 f32 projNorm = proj.length();
784 f32 rejNorm = rej.length();
785 f32 projNorm_ = projNorm;
786 f32 rejNorm_ = rejNorm;
788 f32 dVar7 = down * EGG::Mathf::abs(scalar);
789 if (dVar7 < EGG::Mathf::abs(projNorm)) {
791 if (projNorm < 0.0f) {
792 projNorm_ = -down * EGG::Mathf::abs(scalar);
796 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
797 if (dVar5 < EGG::Mathf::abs(rejNorm)) {
799 if (rejNorm < 0.0f) {
800 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
821 projRejSum = projRejSum.
rej(nextDir);
823 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
826 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
834 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
847 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
851 bool skipWalls =
false;
853 collisionData.wallNrm += colInfo.wallNrm;
856 collisionData.bInvisibleWall =
true;
859 collisionData.bInvisibleWallOnly =
true;
869 collisionData.
bWall3 =
true;
871 collisionData.
bWall =
true;
877 collisionData.floorNrm += colInfo.floorNrm;
878 collisionData.
bFloor =
true;
882 minMax.min = minMax.min.
minimize(tangentOff);
883 minMax.max = minMax.max.
maximize(tangentOff);
886 relPos += hitbox.relPos();
887 relPos += -hitbox.radius() * tangentOff;
899 setPos(pos() + movement);
902 collisionData.movement = movement;
905 f32 rotFactor = 1.0f /
static_cast<f32
>(count);
907 collisionData.rotFactor *= rotFactor;
909 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
913 local_30 += extVel();
915 collisionData.vel = local_30;
916 collisionData.relPos = scaledRelPos;
918 if (collisionData.
bFloor) {
919 f32 intVelY = dynamics()->intVel().y;
920 if (intVelY > 0.0f) {
921 collisionData.vel.y += intVelY;
928void KartCollide::startFloorMomentRate() {
929 m_floorMomentRate = 0.01f;
933void KartCollide::calcFloorMomentRate() {
934 m_floorMomentRate = state()->isInAction() ? 0.01f : std::min(m_floorMomentRate + 0.01f, 0.8f);
943Action KartCollide::handleReactWallAllSpeed(
size_t idx) {
944 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
945 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall);
951Action KartCollide::handleReactSpinAllSpeed(
size_t ) {
952 return Action::UNK_0;
956Action KartCollide::handleReactSpinSomeSpeed(
size_t ) {
957 return Action::UNK_1;
961Action KartCollide::handleReactFireSpin(
size_t ) {
962 return Action::UNK_9;
966Action KartCollide::handleReactSmallLaunch(
size_t ) {
967 return Action::UNK_2;
971Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(
size_t ) {
972 return Action::UNK_3;
976Action KartCollide::handleReactLaunchSpinLoseItem(
size_t ) {
977 return Action::UNK_6;
981Action KartCollide::handleReactKnockbackBumpLoseItem(
size_t ) {
982 return Action::UNK_4;
986Action KartCollide::handleReactLongCrushLoseItem(
size_t ) {
987 return Action::UNK_12;
991Action KartCollide::handleReactHighLaunchLoseItem(
size_t ) {
992 return Action::UNK_8;
996Action KartCollide::handleReactWeakWall(
size_t ) {
997 move()->setSpeed(move()->speed() * 0.82f);
1002Action KartCollide::handleReactLaunchSpin(
size_t ) {
1003 return Action::UNK_5;
1007Action KartCollide::handleReactWallSpark(
size_t idx) {
1008 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1009 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall3);
1011 return Action::None;
1015Action KartCollide::handleReactShortCrushLoseItem(
size_t ) {
1016 return Action::UNK_14;
1020Action KartCollide::handleReactCrushRespawn(
size_t ) {
1021 return Action::UNK_16;
1025Action KartCollide::handleReactExplosionLoseItem(
size_t ) {
1026 return Action::UNK_7;
1029std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1038 &KartCollide::handleReactWallAllSpeed,
1039 &KartCollide::handleReactSpinAllSpeed,
1040 &KartCollide::handleReactSpinSomeSpeed,
1041 &KartCollide::handleReactFireSpin,
1043 &KartCollide::handleReactSmallLaunch,
1044 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1045 &KartCollide::handleReactLaunchSpinLoseItem,
1046 &KartCollide::handleReactKnockbackBumpLoseItem,
1047 &KartCollide::handleReactLongCrushLoseItem,
1051 &KartCollide::handleReactHighLaunchLoseItem,
1053 &KartCollide::handleReactWeakWall,
1055 &KartCollide::handleReactLaunchSpin,
1056 &KartCollide::handleReactWallSpark,
1060 &KartCollide::handleReactShortCrushLoseItem,
1061 &KartCollide::handleReactCrushRespawn,
1062 &KartCollide::handleReactExplosionLoseItem,
#define KCL_TYPE_DRIVER_SOLID_SURFACE
0xEAFABDFF
#define KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2
0x4010B000
#define KCL_TYPE_DRIVER_WALL
0xC010B000
@ COL_TYPE_STICKY_ROAD
Player sticks if within 200 units (rBC stairs).
@ COL_TYPE_BOOST_RAMP
Trickable. Variant affects boost duration.
@ COL_TYPE_WALL_2
Difference to COL_TYPE_WALL is unknown.
@ COL_TYPE_HALFPIPE_RAMP
Bowser's Castle half-pipe ramps.
@ COL_TYPE_HALFPIPE_INVISIBLE_WALL
Invisible wall after a half-pipe jump, like in BC.
@ COL_TYPE_FALL_BOUNDARY
Non-solid out-of-bounds trigger.
@ COL_TYPE_BOOST_PAD
Boost panel.
@ COL_TYPE_CANNON_TRIGGER
Launches player to a destination "cannon point".
@ COL_TYPE_SOLID_OOB
Solid out-of-bounds trigger.
@ COL_TYPE_JUMP_PAD
Like GBA Shy Guy Beach.
@ COL_TYPE_EFFECT_TRIGGER
Activates various effects based on variant.
#define KCL_ATTRIBUTE_TYPE(x)
Computes the "Base Type" portion of the KCL flags. It's the lower 5 bits of the flag.
#define KCL_VARIANT_TYPE(x)
Extracts the "Variant" portion of the 2 byte KCL flag. It's the 3 bits before the "Bast Type".
#define KCL_TYPE_NON_DIRECTIONAL
0xE0F8BDFF
#define KCL_TYPE_4010D000
0x4010D000
#define KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL
0xC0109000
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
#define KCL_TYPE_ANY_INVISIBLE_WALL
0x90002000
#define KCL_TYPE_VEHICLE_COLLIDEABLE
0xEAF8BDFF
#define KCL_TYPE_DIRECTIONAL
0x05070000
#define KCL_TYPE_WALL
0xD010F000
Matrix34f multiplyTo(const Matrix34f &rhs) const
Multiplies two matrices.
void makeQ(const Quatf &q)
Sets rotation matrix from quaternion.
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.
Matrix34f transpose() const
Transposes the 3x3 portion of the matrix.
bool findClosestCollisionEntry(KCLTypeMask *typeMask, KCLTypeMask type)
Finds the closest KCL triangle out of the list of tris we are colliding with.
Houses hitbox and collision info for an object (body or wheel).
Represents a hitbox for the kart body or a wheel.
void calc(f32 totalScale, f32 sinkDepth, const EGG::Vector3f &scale, const EGG::Quatf &rot, const EGG::Vector3f &pos)
Calculates the position of a given hitbox, both relative to the player and world.
bool start(Action action)
Starts an action.
void applyBodyCollision(CollisionData &collisionData, const EGG::Vector3f &movement, const EGG::Vector3f &posRel, s32 count)
Saves collision info when vehicle body collision occurs.
void calcWheelCollision(u16 wheelIdx, CollisionGroup *hitboxGroup, const EGG::Vector3f &colVel, const EGG::Vector3f ¢er, f32 radius)
Checks wheel hitbox collision and stores position/velocity info.
void applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup, const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed, bool b1, bool b2, bool b3)
Applies external and angular velocity based on the collision with the floor.
void processCannon(Field::KCLTypeMask *maskOut)
Checks if we are colliding with a cannon trigger and sets the state flag if so.
Action handleReactNone(size_t idx)
Object collision functions.
bool FUN_805B6A9C(CollisionData &collisionData, const Hitbox &hitbox, EGG::BoundBox3f &minMax, EGG::Vector3f &relPos, s32 &count, const Field::KCLTypeMask &maskOut, const Field::CollisionInfo &colInfo)
Called on collision of a new KCL type??? This only happens after airtime so far.
void processWheel(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut)
Processes moving water and floor collision effects.
void calcHitboxes()
On each frame, calculates the positions for each hitbox.
void calcSideCollision(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo)
void processFloor(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut, bool wheel)
Processes the floor triangles' attributes.
void FUN_805B72B8(f32 param_1, f32 param_2, bool lockXZ, bool addExtVelY)
Affects velocity when landing from airtime.
void calcBodyCollision(f32 totalScale, f32 sinkDepth, const EGG::Quatf &rot, const EGG::Vector3f &scale)
Checks and acts on collision for each kart hitbox.
Pertains to kart-related functionality.
A representation of a bounding cuboid.
A quaternion, used to represent 3D rotation.
Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
Vector3f rotateVectorInv(const Vector3f &vec) const
Rotates a vector on the inverse quat.
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 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 squaredLength() const
The dot product between the vector and itself.
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.
Represents one of the many hitboxes that make up a vehicle.
EGG::Vector3f position
The relative position of the hitbox.
Information about the current collision and its properties.
u32 closestFloorSettings
The KCL flag's "variant".
bool bFloor
Set if colliding with KCL which satisfies KCL_TYPE_FLOOR.
bool bWall3
Set if colliding with COL_TYPE_WALL_2.
bool bWall
Set if colliding with KCL which satisfies KCL_TYPE_WALL.
Field::KCLTypeMask closestFloorFlags
The KCL flag's KColType.
s32 intensity
The KCL flag's "wheel depth".
std::array< f32, 32 > kclRot
Rotation scalars, indexed using KCL attributes.