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 auto *colDir = Field::CollisionDirector::Instance();
286 if (m_solidOobTimer >= 3 && m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) &&
287 m_surfaceFlags.
offBit(eSurfaceFlags::Wall)) {
292 activateOob(
true, &mask,
false,
false);
296 calcTriggers(&mask, pos(),
true);
299 m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
301 if (state()->isWall3Collision() || state()->isWallCollision()) {
304 if (colDir->checkSphereCachedPartialPush(m_boundingRadius, pos(), EGG::Vector3f::inf,
306 calcFallBoundary(&maskOut,
true);
313 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
315 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
316 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
320 m_smoothedBack += (back.
dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
322 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
323 scaledPos += scalar * back;
325 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
326 scaledPos, v1, typeMask,
nullptr, mask, 0);
333 handleTriggers(mask);
340 m_surfaceFlags.
setBit(eSurfaceFlags::Wall);
344 m_surfaceFlags.
setBit(eSurfaceFlags::SolidOOB);
351 calcFallBoundary(mask,
false);
355 auto *colDir = Field::CollisionDirector::Instance();
357 if (colDir->closestCollisionEntry()->variant() == 4) {
358 halfPipe()->end(
true);
359 state()->setEndHalfPipe(
true);
360 m_surfaceFlags.
setBit(eSurfaceFlags::StopHalfPipeState);
372 auto *colDir = Field::CollisionDirector::Instance();
378 const auto *entry = colDir->closestCollisionEntry();
381 if (entry->variant() != 7) {
387 activateOob(
false, mask,
false,
false);
392void KartCollide::calcBeforeRespawn() {
393 if (pos().y < 0.0f) {
394 activateOob(
true,
nullptr,
false,
false);
397 if (!state()->isBeforeRespawn()) {
401 if (--m_respawnTimer > 0) {
405 state()->setBeforeRespawn(
false);
407 move()->triggerRespawn();
413 constexpr s16 RESPAWN_TIME = 130;
415 if (state()->isBeforeRespawn()) {
421 m_respawnTimer = RESPAWN_TIME;
422 state()->setBeforeRespawn(
true);
435 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
437 bspHitbox->radius = radius;
438 hitboxGroup->resetCollision();
439 firstHitbox.setWorldPos(center);
442 colInfo.bbox.setZero();
445 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
447 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
448 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
454 collisionData.speedFactor = 1.0f;
455 collisionData.rotFactor = 1.0f;
459 collisionData.tangentOff = colInfo.tangentOff;
461 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
462 collisionData.tangentOff += noBounceWallInfo.tangentOff;
463 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
464 collisionData.bSoftWall =
true;
468 collisionData.
bFloor =
true;
469 collisionData.floorNrm = colInfo.floorNrm;
472 collisionData.relPos = firstHitbox.worldPos() - pos();
473 collisionData.vel = colVel;
475 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
476 collisionData.bHasRoadVel =
true;
477 collisionData.roadVelocity = colInfo.roadVelocity;
480 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
494 if (colInfo->perpendicularity <= 0.0f) {
498 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
500 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
504 f32 bspPosX = hitbox.bspHitbox()->
position.x;
505 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
506 if (bspPosX > 0.0f) {
507 collisionData.bWallAtLeftCloser =
true;
509 collisionData.bWallAtRightCloser =
true;
512 collisionData.colPerpendicularity = colInfo->perpendicularity;
518 std::array<f32, 2> tangents = {0.0f, 0.0f};
521 for (
size_t i = 0; i < tangents.size(); ++i) {
522 f32 sign = i == 1 ? -1.0f : 1.0f;
523 f32 effectiveRadius = sign * hitbox.radius();
524 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
527 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
534 if (tangents[0] > tangents[1]) {
535 collisionData.bWallAtLeftCloser =
true;
536 collisionData.colPerpendicularity = colInfo->perpendicularity;
537 }
else if (tangents[1] > tangents[0]) {
538 collisionData.bWallAtRightCloser =
true;
539 collisionData.colPerpendicularity = colInfo->perpendicularity;
544void KartCollide::calcBoundingRadius() {
545 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
549void KartCollide::calcObjectCollision() {
550 constexpr f32 COS_PI_OVER_4 = 0.707f;
551 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
552 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
554 m_totalReactionWallNrm = EGG::Vector3f::zero;
555 m_surfaceFlags.
resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
557 size_t collisionCount = objectCollisionKart()->checkCollision(pose(), velocity());
559 const auto *objectDirector = Field::ObjectDirector::Instance();
561 for (
size_t i = 0; i < collisionCount; ++i) {
562 Reaction reaction = objectDirector->reaction(i);
563 if (reaction != Reaction::None && reaction != Reaction::UNK_7) {
564 size_t handlerIdx =
static_cast<std::underlying_type_t<Reaction>
>(reaction);
565 Action newAction = (this->*s_objectCollisionHandlers[handlerIdx])(i);
566 if (newAction != Action::None) {
567 action()->setHitDepth(objectDirector->hitDepth(i));
568 action()->
start(newAction);
571 if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
573 m_tangentOff += hitDepth;
574 m_movement += hitDepth;
578 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
579 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
582 if (lastDir.
dot(hitDirection) < -COS_PI_OVER_4) {
584 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
586 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
587 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
596void KartCollide::calcPoleTimer() {
597 if (m_poleAngVelTimer > 0 && (state()->isAccelerate() || state()->isBrake())) {
599 angVel2.y += m_poleYaw;
600 dynamics()->setAngVel2(angVel2);
603 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
611 processFloor(collisionData, hitbox, colInfo, maskOut,
true);
617 bool hasWallCollision = processWall(collisionData, maskOut);
619 processFloor(collisionData, hitbox, colInfo, maskOut,
false);
621 if (hasWallCollision) {
629bool KartCollide::processWall(CollisionData &collisionData,
Field::KCLTypeMask *maskOut) {
634 auto *colDirector = Field::CollisionDirector::Instance();
640 colDirector->findClosestCollisionEntry(maskOut,
642 auto *entry = colDirector->closestCollisionEntry();
644 collisionData.closestWallFlags = entry->baseType();
645 collisionData.closestWallSettings = entry->variant();
647 if (entry->attribute.onBit(Field::CollisionDirector::eCollisionAttribute::Soft)) {
648 collisionData.bSoftWall =
true;
665 if (collisionData.bSoftWall) {
666 ++m_someSoftWallTimer;
667 m_suspBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
674 auto *colDirector = Field::CollisionDirector::Instance();
676 if (!colDirector->findClosestCollisionEntry(maskOut,
KCL_TYPE_FLOOR)) {
680 const auto *closestColEntry = colDirector->closestCollisionEntry();
682 if (closestColEntry->attribute.offBit(
683 Field::CollisionDirector::eCollisionAttribute::Trickable)) {
684 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
686 collisionData.bTrickable =
true;
687 m_surfaceFlags.
setBit(eSurfaceFlags::Trickable);
690 collisionData.speedFactor = std::min(collisionData.speedFactor,
691 param()->stats().kclSpeed[closestColEntry->baseType()]);
693 collisionData.
intensity = closestColEntry->intensity();
694 collisionData.rotFactor += param()->stats().
kclRot[closestColEntry->baseType()];
696 if (closestColEntry->attribute.onBit(
697 Field::CollisionDirector::eCollisionAttribute::RejectRoad)) {
698 state()->setRejectRoad(
true);
705 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
708 if (!!(*maskOut & BOOST_RAMP_MASK) &&
709 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
710 closestColEntry = colDirector->closestCollisionEntry();
711 move()->padType().
setBit(KartMove::ePadType::BoostRamp);
712 state()->setBoostRampType(closestColEntry->variant());
713 m_surfaceFlags.
setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
715 state()->setBoostRampType(-1);
716 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
719 if (!collisionData.bSoftWall) {
720 ++m_someNonSoftWallTimer;
721 m_suspBottomHeightNonSoftWall += hitbox.worldPos().y - hitbox.radius();
725 state()->setStickyRoad(
true);
729 if ((*maskOut & halfPipeRampMask) &&
730 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
731 state()->setHalfPipeRamp(
true);
732 state()->setHalfPipeInvisibilityTimer(2);
733 if (colDirector->closestCollisionEntry()->variant() == 1) {
734 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
739 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
740 if ((!state()->isTouchingGround() || !state()->isJumpPad()) &&
741 !state()->isJumpPadMushroomVelYInc()) {
742 move()->padType().
setBit(KartMove::ePadType::JumpPad);
743 closestColEntry = colDirector->closestCollisionEntry();
744 state()->setJumpPadVariant(closestColEntry->variant());
746 collisionData.bTrickable =
true;
753 auto *colDirector = Field::CollisionDirector::Instance();
755 state()->setCannonPointId(colDirector->closestCollisionEntry()->variant());
756 state()->setCannonStart(
true);
771 bool b1,
bool b2,
bool b3) {
777 f32 velDotFloorNrm = colData.vel.
dot(colData.floorNrm);
779 if (velDotFloorNrm >= 0.0f) {
784 rotMat.
makeQ(dynamics()->mainRot());
789 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
791 crossVec = crossVec.cross(colData.relPos);
793 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.
dot(crossVec));
795 crossVec = colData.floorNrm.cross(negSpeed);
796 crossVec = crossVec.cross(colData.floorNrm);
798 if (std::numeric_limits<f32>::epsilon() >= crossVec.
squaredLength()) {
803 f32 speedDot = std::min(0.0f, speed.
dot(crossVec));
804 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
806 auto [proj, rej] = crossVec.projAndRej(forward);
808 f32 projNorm = proj.length();
809 f32 rejNorm = rej.length();
810 f32 projNorm_ = projNorm;
811 f32 rejNorm_ = rejNorm;
813 f32 dVar7 = down * EGG::Mathf::abs(scalar);
814 if (dVar7 < EGG::Mathf::abs(projNorm)) {
816 if (projNorm < 0.0f) {
817 projNorm_ = -down * EGG::Mathf::abs(scalar);
821 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
822 if (dVar5 < EGG::Mathf::abs(rejNorm)) {
824 if (rejNorm < 0.0f) {
825 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
846 projRejSum = projRejSum.
rej(nextDir);
848 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
851 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
859 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
872 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
876 bool skipWalls =
false;
878 collisionData.wallNrm += colInfo.wallNrm;
881 collisionData.bInvisibleWall =
true;
884 collisionData.bInvisibleWallOnly =
true;
894 collisionData.
bWall3 =
true;
896 collisionData.
bWall =
true;
902 collisionData.floorNrm += colInfo.floorNrm;
903 collisionData.
bFloor =
true;
907 minMax.min = minMax.min.
minimize(tangentOff);
908 minMax.max = minMax.max.
maximize(tangentOff);
911 relPos += hitbox.relPos();
912 relPos += -hitbox.radius() * tangentOff;
924 setPos(pos() + movement);
927 collisionData.movement = movement;
930 f32 rotFactor = 1.0f /
static_cast<f32
>(count);
932 collisionData.rotFactor *= rotFactor;
934 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
938 local_30 += extVel();
940 collisionData.vel = local_30;
941 collisionData.relPos = scaledRelPos;
943 if (collisionData.
bFloor) {
944 f32 intVelY = dynamics()->intVel().y;
945 if (intVelY > 0.0f) {
946 collisionData.vel.y += intVelY;
953void KartCollide::startFloorMomentRate() {
954 m_floorMomentRate = 0.01f;
958void KartCollide::calcFloorMomentRate() {
959 if (state()->isInAction() && action()->flags().onBit(KartAction::eFlags::Rotating)) {
960 m_floorMomentRate = 0.01f;
962 m_floorMomentRate = std::min(m_floorMomentRate + 0.01f, 0.8f);
972Action KartCollide::handleReactWallAllSpeed(
size_t idx) {
973 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
974 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall);
980Action KartCollide::handleReactSpinAllSpeed(
size_t ) {
981 return Action::UNK_0;
985Action KartCollide::handleReactSpinSomeSpeed(
size_t ) {
986 return Action::UNK_1;
990Action KartCollide::handleReactFireSpin(
size_t ) {
991 return Action::UNK_9;
995Action KartCollide::handleReactSmallLaunch(
size_t ) {
996 return Action::UNK_2;
1000Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(
size_t ) {
1001 return Action::UNK_3;
1005Action KartCollide::handleReactLaunchSpinLoseItem(
size_t ) {
1006 return Action::UNK_6;
1010Action KartCollide::handleReactKnockbackBumpLoseItem(
size_t ) {
1011 return Action::UNK_4;
1015Action KartCollide::handleReactLongCrushLoseItem(
size_t ) {
1016 return Action::UNK_12;
1020Action KartCollide::handleReactSmallBump(
size_t idx) {
1021 move()->applyBumpForce(30.0f, objectCollisionKart()->GetHitDirection(idx),
false);
1022 return Action::None;
1026Action KartCollide::handleReactHighLaunchLoseItem(
size_t ) {
1027 return Action::UNK_8;
1031Action KartCollide::handleReactWeakWall(
size_t ) {
1032 move()->setSpeed(move()->speed() * 0.82f);
1033 return Action::None;
1037Action KartCollide::handleReactOffroad(
size_t ) {
1038 state()->setCollidingOffroad(
true);
1040 return Action::None;
1044Action KartCollide::handleReactLaunchSpin(
size_t idx) {
1045 action()->setTranslation(objectCollisionKart()->translation(idx));
1046 return Action::UNK_5;
1050Action KartCollide::handleReactWallSpark(
size_t idx) {
1051 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1052 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall3);
1054 return Action::None;
1058Action KartCollide::handleReactUntrickableJumpPad(
size_t ) {
1059 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1060 state()->setJumpPadVariant(0);
1062 return Action::None;
1066Action KartCollide::handleReactShortCrushLoseItem(
size_t ) {
1067 return Action::UNK_14;
1071Action KartCollide::handleReactCrushRespawn(
size_t ) {
1072 return Action::UNK_16;
1076Action KartCollide::handleReactExplosionLoseItem(
size_t ) {
1077 return Action::UNK_7;
1080std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1089 &KartCollide::handleReactWallAllSpeed,
1090 &KartCollide::handleReactSpinAllSpeed,
1091 &KartCollide::handleReactSpinSomeSpeed,
1092 &KartCollide::handleReactFireSpin,
1094 &KartCollide::handleReactSmallLaunch,
1095 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1096 &KartCollide::handleReactLaunchSpinLoseItem,
1097 &KartCollide::handleReactKnockbackBumpLoseItem,
1098 &KartCollide::handleReactLongCrushLoseItem,
1099 &KartCollide::handleReactSmallBump,
1102 &KartCollide::handleReactHighLaunchLoseItem,
1104 &KartCollide::handleReactWeakWall,
1105 &KartCollide::handleReactOffroad,
1106 &KartCollide::handleReactLaunchSpin,
1107 &KartCollide::handleReactWallSpark,
1110 &KartCollide::handleReactUntrickableJumpPad,
1111 &KartCollide::handleReactShortCrushLoseItem,
1112 &KartCollide::handleReactCrushRespawn,
1113 &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.
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 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.