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(),
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 auto &status = KartObjectProxy::status();
113 dynamics()->velocity().y < -50.0f;
116 status.
offBit(eStatus::JumpPadDisableYsusForce));
127 const auto &colData = collisionData();
129 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
133 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
136 f32 directionalVelocity = colData.vel.dot(collisionDir);
137 if (directionalVelocity >= 0.0f) {
144 rotMat.
makeQ(dynamics()->mainRot());
157 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.
dot(step3));
162 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
163 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
165 f32 fVar3 = otherVal;
166 if (fVar1 < EGG::Mathf::abs(otherVal)) {
168 if (otherVal < 0.0f) {
169 fVar3 = -param_1 * EGG::Mathf::abs(val);
175 f32 local_1d0 = step6.y;
178 }
else if (colData.bFloor) {
179 f32 velY = intVel().y;
185 dynamics()->setExtVel(newExtVel);
190 f32 prevExtVelY = extVel().y;
192 extVelAdd.y = local_1d0;
193 dynamics()->setExtVel(extVel() + extVelAdd);
195 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
198 dynamics()->setExtVel(extVelNoY);
205 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
218 collisionData.reset();
223 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
224 Field::KCLTypeMask maskOut;
230 for (
u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
232 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
234 if (hitbox.bspHitbox()->wallsOnly != 0) {
236 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
239 hitbox.
calc(totalScale, sinkDepth, scale, rot, pos());
241 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
242 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
248 if (!
FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
251 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
252 collisionData.bHasRoadVel =
true;
253 collisionData.roadVelocity = colInfo.roadVelocity;
256 processBody(collisionData, hitbox, &colInfo, &maskOut);
265 collisionData.speedFactor = 1.0f;
266 collisionData.rotFactor = 1.0f;
271void KartCollide::calcFloorEffect() {
276 m_suspBottomHeightNonSoftWall = 0.0f;
277 m_surfaceFlags.
resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
279 eSurfaceFlags::StopHalfPipeState);
280 m_suspBottomHeightSoftWall = 0.0f;
281 m_someNonSoftWallTimer = 0;
282 m_someSoftWallTimer = 0;
284 Field::KCLTypeMask mask = KCL_NONE;
285 calcTriggers(&mask, pos(),
false);
287 auto *colDir = Field::CollisionDirector::Instance();
289 if (m_solidOobTimer >= 3 && m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) &&
290 m_surfaceFlags.
offBit(eSurfaceFlags::Wall)) {
295 activateOob(
true, &mask,
false,
false);
299 calcTriggers(&mask, pos(),
true);
302 m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
305 Field::KCLTypeMask maskOut = KCL_NONE;
307 if (colDir->checkSphereCachedPartialPush(m_boundingRadius, pos(), EGG::Vector3f::inf,
309 calcFallBoundary(&maskOut,
true);
315void KartCollide::calcTriggers(Field::KCLTypeMask *mask,
const EGG::Vector3f &pos,
bool twoPoint) {
316 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
318 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
319 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
323 m_smoothedBack += (back.
dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
325 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
326 scaledPos += scalar * back;
328 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
329 scaledPos, v1, typeMask,
nullptr, mask, 0);
336 handleTriggers(mask);
343 m_surfaceFlags.
setBit(eSurfaceFlags::Wall);
347 m_surfaceFlags.
setBit(eSurfaceFlags::SolidOOB);
353void KartCollide::handleTriggers(Field::KCLTypeMask *mask) {
354 calcFallBoundary(mask,
false);
358 auto *colDir = Field::CollisionDirector::Instance();
360 if (colDir->closestCollisionEntry()->variant() == 4) {
361 halfPipe()->end(
true);
362 status().
setBit(eStatus::EndHalfPipe);
363 m_surfaceFlags.
setBit(eSurfaceFlags::StopHalfPipeState);
370void KartCollide::calcFallBoundary(Field::KCLTypeMask *mask,
bool shortBoundary) {
375 auto *colDir = Field::CollisionDirector::Instance();
381 const auto *entry = colDir->closestCollisionEntry();
384 if (entry->variant() != 7) {
390 activateOob(
false, mask,
false,
false);
395void KartCollide::calcBeforeRespawn() {
396 if (pos().y < 0.0f) {
397 activateOob(
true,
nullptr,
false,
false);
400 auto &status = KartObjectProxy::status();
406 if (--m_respawnTimer > 0) {
412 move()->triggerRespawn();
416void KartCollide::activateOob(
bool , Field::KCLTypeMask * ,
418 constexpr s16 RESPAWN_TIME = 130;
420 auto &status = KartObjectProxy::status();
428 m_respawnTimer = RESPAWN_TIME;
442 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
444 bspHitbox->radius = radius;
445 hitboxGroup->resetCollision();
446 firstHitbox.setWorldPos(center);
449 colInfo.bbox.setZero();
450 Field::KCLTypeMask kclOut;
452 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
454 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
455 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
461 collisionData.speedFactor = 1.0f;
462 collisionData.rotFactor = 1.0f;
466 collisionData.tangentOff = colInfo.tangentOff;
468 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
469 collisionData.tangentOff += noBounceWallInfo.tangentOff;
470 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
471 collisionData.bSoftWall =
true;
475 collisionData.
bFloor =
true;
476 collisionData.floorNrm = colInfo.floorNrm;
479 collisionData.relPos = firstHitbox.worldPos() - pos();
480 collisionData.vel = colVel;
482 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
483 collisionData.bHasRoadVel =
true;
484 collisionData.roadVelocity = colInfo.roadVelocity;
487 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
501 if (colInfo->perpendicularity <= 0.0f) {
505 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
507 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
511 f32 bspPosX = hitbox.bspHitbox()->
position.x;
512 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
513 if (bspPosX > 0.0f) {
514 collisionData.bWallAtLeftCloser =
true;
516 collisionData.bWallAtRightCloser =
true;
519 collisionData.colPerpendicularity = colInfo->perpendicularity;
525 std::array<f32, 2> tangents = {0.0f, 0.0f};
528 for (
size_t i = 0; i < tangents.size(); ++i) {
529 f32 sign = i == 1 ? -1.0f : 1.0f;
530 f32 effectiveRadius = sign * hitbox.radius();
531 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
534 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
541 if (tangents[0] > tangents[1]) {
542 collisionData.bWallAtLeftCloser =
true;
543 collisionData.colPerpendicularity = colInfo->perpendicularity;
544 }
else if (tangents[1] > tangents[0]) {
545 collisionData.bWallAtRightCloser =
true;
546 collisionData.colPerpendicularity = colInfo->perpendicularity;
551void KartCollide::calcBoundingRadius() {
552 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
556void KartCollide::calcObjectCollision() {
557 constexpr f32 COS_PI_OVER_4 = 0.707f;
558 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
559 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
561 m_totalReactionWallNrm = EGG::Vector3f::zero;
562 m_surfaceFlags.
resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
564 size_t collisionCount = objectCollisionKart()->checkCollision(pose(), velocity());
566 const auto *objectDirector = Field::ObjectDirector::Instance();
568 for (
size_t i = 0; i < collisionCount; ++i) {
569 Reaction reaction = objectDirector->reaction(i);
570 if (reaction != Reaction::None && reaction != Reaction::UNK_7) {
571 size_t handlerIdx =
static_cast<std::underlying_type_t<Reaction>
>(reaction);
572 Action newAction = (this->*s_objectCollisionHandlers[handlerIdx])(i);
573 if (newAction != Action::None) {
574 action()->setHitDepth(objectDirector->hitDepth(i));
575 action()->
start(newAction);
578 if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
580 m_tangentOff += hitDepth;
581 m_movement += hitDepth;
585 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
586 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
589 if (lastDir.
dot(hitDirection) < -COS_PI_OVER_4) {
591 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
593 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
594 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
603void KartCollide::calcPoleTimer() {
606 angVel2.y += m_poleYaw;
607 dynamics()->setAngVel2(angVel2);
610 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
618 processFloor(collisionData, hitbox, colInfo, maskOut,
true);
624 bool hasWallCollision = processWall(collisionData, maskOut);
626 processFloor(collisionData, hitbox, colInfo, maskOut,
false);
628 if (hasWallCollision) {
636bool KartCollide::processWall(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
641 auto *colDirector = Field::CollisionDirector::Instance();
647 colDirector->findClosestCollisionEntry(maskOut,
649 auto *entry = colDirector->closestCollisionEntry();
651 collisionData.closestWallFlags = entry->baseType();
652 collisionData.closestWallSettings = entry->variant();
654 if (entry->attribute.onBit(Field::CollisionDirector::eCollisionAttribute::Soft)) {
655 collisionData.bSoftWall =
true;
672 if (collisionData.bSoftWall) {
673 ++m_someSoftWallTimer;
674 m_suspBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
681 auto *colDirector = Field::CollisionDirector::Instance();
683 if (!colDirector->findClosestCollisionEntry(maskOut,
KCL_TYPE_FLOOR)) {
687 const auto *closestColEntry = colDirector->closestCollisionEntry();
689 if (closestColEntry->attribute.offBit(
690 Field::CollisionDirector::eCollisionAttribute::Trickable)) {
691 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
693 collisionData.bTrickable =
true;
694 m_surfaceFlags.
setBit(eSurfaceFlags::Trickable);
697 collisionData.speedFactor = std::min(collisionData.speedFactor,
698 param()->stats().kclSpeed[closestColEntry->baseType()]);
700 collisionData.
intensity = closestColEntry->intensity();
701 collisionData.rotFactor += param()->stats().
kclRot[closestColEntry->baseType()];
703 auto &status = KartObjectProxy::status();
705 if (closestColEntry->attribute.onBit(
706 Field::CollisionDirector::eCollisionAttribute::RejectRoad)) {
714 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
717 if (!!(*maskOut & BOOST_RAMP_MASK) &&
718 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
719 closestColEntry = colDirector->closestCollisionEntry();
720 move()->padType().
setBit(KartMove::ePadType::BoostRamp);
721 state()->setBoostRampType(closestColEntry->variant());
722 m_surfaceFlags.
setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
724 state()->setBoostRampType(-1);
725 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
728 if (!collisionData.bSoftWall) {
729 ++m_someNonSoftWallTimer;
730 m_suspBottomHeightNonSoftWall += hitbox.worldPos().y - hitbox.radius();
738 if ((*maskOut & halfPipeRampMask) &&
739 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
741 state()->setHalfPipeInvisibilityTimer(2);
742 if (colDirector->closestCollisionEntry()->variant() == 1) {
743 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
748 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
750 status.
offBit(eStatus::JumpPadMushroomVelYInc)) {
751 move()->padType().
setBit(KartMove::ePadType::JumpPad);
752 closestColEntry = colDirector->closestCollisionEntry();
753 state()->setJumpPadVariant(closestColEntry->variant());
755 collisionData.bTrickable =
true;
762 auto *colDirector = Field::CollisionDirector::Instance();
764 state()->setCannonPointId(colDirector->closestCollisionEntry()->variant());
765 status().
setBit(eStatus::CannonStart);
780 bool b1,
bool b2,
bool b3) {
786 f32 velDotFloorNrm = colData.vel.
dot(colData.floorNrm);
788 if (velDotFloorNrm >= 0.0f) {
793 rotMat.
makeQ(dynamics()->mainRot());
798 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
800 crossVec = crossVec.cross(colData.relPos);
802 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.
dot(crossVec));
804 crossVec = colData.floorNrm.cross(negSpeed);
805 crossVec = crossVec.cross(colData.floorNrm);
807 if (std::numeric_limits<f32>::epsilon() >= crossVec.
squaredLength()) {
812 f32 speedDot = std::min(0.0f, speed.
dot(crossVec));
813 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
815 auto [proj, rej] = crossVec.projAndRej(forward);
817 f32 projNorm = proj.length();
818 f32 rejNorm = rej.length();
819 f32 projNorm_ = projNorm;
820 f32 rejNorm_ = rejNorm;
822 f32 dVar7 = down * EGG::Mathf::abs(scalar);
823 if (dVar7 < EGG::Mathf::abs(projNorm)) {
825 if (projNorm < 0.0f) {
826 projNorm_ = -down * EGG::Mathf::abs(scalar);
830 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
831 if (dVar5 < EGG::Mathf::abs(rejNorm)) {
833 if (rejNorm < 0.0f) {
834 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
855 projRejSum = projRejSum.
rej(nextDir);
857 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
860 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
868 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
881 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
885 bool skipWalls =
false;
887 collisionData.wallNrm += colInfo.wallNrm;
890 collisionData.bInvisibleWall =
true;
893 collisionData.bInvisibleWallOnly =
true;
903 collisionData.
bWall3 =
true;
905 collisionData.
bWall =
true;
911 collisionData.floorNrm += colInfo.floorNrm;
912 collisionData.
bFloor =
true;
916 minMax.min = minMax.min.
minimize(tangentOff);
917 minMax.max = minMax.max.
maximize(tangentOff);
920 relPos += hitbox.relPos();
921 relPos += -hitbox.radius() * tangentOff;
933 setPos(pos() + movement);
936 collisionData.movement = movement;
939 f32 rotFactor = 1.0f /
static_cast<f32
>(count);
941 collisionData.rotFactor *= rotFactor;
943 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
947 local_30 += extVel();
949 collisionData.vel = local_30;
950 collisionData.relPos = scaledRelPos;
952 if (collisionData.
bFloor) {
953 f32 intVelY = dynamics()->intVel().y;
954 if (intVelY > 0.0f) {
955 collisionData.vel.y += intVelY;
962void KartCollide::startFloorMomentRate() {
963 m_floorMomentRate = 0.01f;
967void KartCollide::calcFloorMomentRate() {
968 m_floorMomentRate = status().
onBit(eStatus::InAction) &&
969 action()->flags().
onBit(KartAction::eFlags::Rotating) ?
971 std::min(m_floorMomentRate + 0.01f, 0.8f);
980Action KartCollide::handleReactWallAllSpeed(
size_t idx) {
981 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
982 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall);
988Action KartCollide::handleReactSpinAllSpeed(
size_t ) {
989 return Action::UNK_0;
993Action KartCollide::handleReactSpinSomeSpeed(
size_t ) {
994 return Action::UNK_1;
998Action KartCollide::handleReactFireSpin(
size_t ) {
999 return Action::UNK_9;
1003Action KartCollide::handleReactSmallLaunch(
size_t ) {
1004 return Action::UNK_2;
1008Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(
size_t ) {
1009 return Action::UNK_3;
1013Action KartCollide::handleReactLaunchSpinLoseItem(
size_t ) {
1014 return Action::UNK_6;
1018Action KartCollide::handleReactKnockbackBumpLoseItem(
size_t ) {
1019 return Action::UNK_4;
1023Action KartCollide::handleReactLongCrushLoseItem(
size_t ) {
1024 return Action::UNK_12;
1028Action KartCollide::handleReactSmallBump(
size_t idx) {
1029 move()->applyBumpForce(30.0f, objectCollisionKart()->GetHitDirection(idx),
false);
1030 return Action::None;
1034Action KartCollide::handleReactHighLaunchLoseItem(
size_t ) {
1035 return Action::UNK_8;
1039Action KartCollide::handleReactWeakWall(
size_t ) {
1040 move()->setSpeed(move()->speed() * 0.82f);
1041 return Action::None;
1045Action KartCollide::handleReactOffroad(
size_t ) {
1046 status().
setBit(eStatus::CollidingOffroad);
1048 return Action::None;
1052Action KartCollide::handleReactLaunchSpin(
size_t idx) {
1053 action()->setTranslation(objectCollisionKart()->translation(idx));
1054 return Action::UNK_5;
1058Action KartCollide::handleReactWallSpark(
size_t idx) {
1059 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1060 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall3);
1062 return Action::None;
1066Action KartCollide::handleReactUntrickableJumpPad(
size_t ) {
1067 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1068 state()->setJumpPadVariant(0);
1070 return Action::None;
1074Action KartCollide::handleReactShortCrushLoseItem(
size_t ) {
1075 return Action::UNK_14;
1079Action KartCollide::handleReactCrushRespawn(
size_t ) {
1080 return Action::UNK_16;
1084Action KartCollide::handleReactExplosionLoseItem(
size_t ) {
1085 return Action::UNK_7;
1088std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1097 &KartCollide::handleReactWallAllSpeed,
1098 &KartCollide::handleReactSpinAllSpeed,
1099 &KartCollide::handleReactSpinSomeSpeed,
1100 &KartCollide::handleReactFireSpin,
1102 &KartCollide::handleReactSmallLaunch,
1103 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1104 &KartCollide::handleReactLaunchSpinLoseItem,
1105 &KartCollide::handleReactKnockbackBumpLoseItem,
1106 &KartCollide::handleReactLongCrushLoseItem,
1107 &KartCollide::handleReactSmallBump,
1110 &KartCollide::handleReactHighLaunchLoseItem,
1112 &KartCollide::handleReactWeakWall,
1113 &KartCollide::handleReactOffroad,
1114 &KartCollide::handleReactLaunchSpin,
1115 &KartCollide::handleReactWallSpark,
1118 &KartCollide::handleReactUntrickableJumpPad,
1119 &KartCollide::handleReactShortCrushLoseItem,
1120 &KartCollide::handleReactCrushRespawn,
1121 &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_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.
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
constexpr TBitFlagExt< N, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
constexpr bool offAnyBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are off.
constexpr TBitFlagExt< N, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
bool 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.
@ RejectRoad
Collision which causes a change in the player's pos and rot.
@ HalfPipeRamp
Set while colliding with zipper KCL.
@ Boost
Set while in a boost.
@ HWG
Set when "Horizontal Wall Glitch" is active.
@ StickyRoad
Like the rBC stairs.
@ Accelerate
Accel button is pressed.
@ ActionMidZipper
Set when we enter an action while mid-air from a zipper.
@ AirtimeOver20
Set after 20 frames of airtime, resets on landing.
@ OverZipper
Set while mid-air from a zipper.
@ Wall3Collision
Set when colliding with wall KCL COL_TYPE_WALL_2.
@ TouchingGround
Set when any part of the vehicle is colliding with floor KCL.
@ ZipperInvisibleWall
Set when colliding with invisible wall above a zipper.
@ BeforeRespawn
Set on respawn collision, cleared on position snap.
@ WallCollision
Set if we are colliding with a wall.
A 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 colliding floor 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 colliding floor KCL flag's KColType.
s32 intensity
The KCL flag's "wheel depth".
std::array< f32, 32 > kclRot
Rotation scalars, indexed using KCL attributes.