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;
20 m_surfaceFlags.makeAllZero();
24KartCollide::~KartCollide() =
default;
27void KartCollide::init() {
30 m_floorMomentRate = 0.8f;
31 m_surfaceFlags.makeAllZero();
35 m_smoothedBack = 0.0f;
36 m_sumHitboxBottomHeightFloorOnly = 0.0f;
37 m_sumHitboxBottomHeightSoftWall = 0.0f;
38 m_numFloorOnlyCollisions = 0;
39 m_numSoftWallCollisions = 0;
40 m_poleAngVelTimer = 0;
42 m_colPerpendicularity = 0.0f;
46void KartCollide::resetHitboxes() {
47 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
48 for (u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
49 hitboxGroup->hitbox(idx).setLastPos(scale(), pose());
57 const EGG::Quatf &rot = wasHalfPipe ? mainRot() : fullRot();
60 auto &colData = collisionData();
61 bool existingWallCollision = colData.bWall || colData.bWall3;
62 bool newWallCollision =
63 m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
64 if (existingWallCollision || newWallCollision) {
65 if (!existingWallCollision) {
66 colData.wallNrm = m_totalReactionWallNrm;
67 if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall)) {
69 }
else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
70 colData.bWall3 =
true;
72 }
else if (newWallCollision) {
73 colData.wallNrm += m_totalReactionWallNrm;
74 if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall)) {
76 }
else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
77 colData.bWall3 =
true;
81 colData.wallNrm.normalise();
93 auto &status = KartObjectProxy::status();
104 dynamics()->velocity().y < -50.0f;
106 FUN_805B72B8(status.onBit(eStatus::InAction) ? 0.3f : 0.01f, fVar1, resetXZ,
107 status.offBit(eStatus::JumpPadDisableYsusForce));
118 const auto &colData = collisionData();
120 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
124 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
127 f32 directionalVelocity = colData.vel.dot(collisionDir);
128 if (directionalVelocity >= 0.0f) {
135 rotMat.
makeQ(dynamics()->mainRot());
148 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.
dot(step3));
153 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
154 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
156 f32 fVar3 = otherVal;
157 if (fVar1 < EGG::Mathf::abs(otherVal)) {
159 if (otherVal < 0.0f) {
160 fVar3 = -param_1 * EGG::Mathf::abs(val);
166 f32 local_1d0 = step6.y;
169 }
else if (colData.bFloor) {
170 f32 velY = intVel().y;
176 dynamics()->setExtVel(newExtVel);
181 f32 prevExtVelY = extVel().y;
183 extVelAdd.y = local_1d0;
184 dynamics()->setExtVel(extVel() + extVelAdd);
186 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
189 dynamics()->setExtVel(extVelNoY);
196 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
209 collisionData.reset();
214 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
215 Field::KCLTypeMask maskOut;
221 for (u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
223 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
225 if (hitbox.bspHitbox()->wallsOnly != 0) {
227 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
230 hitbox.
calc(totalScale, sinkDepth, scale, rot, pos());
232 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
233 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
235 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&maskOut,
239 if (!
FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
242 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
243 collisionData.bHasRoadVel =
true;
244 collisionData.roadVelocity = colInfo.roadVelocity;
247 processBody(collisionData, hitbox, &colInfo, &maskOut);
256 collisionData.speedFactor = 1.0f;
257 collisionData.rotFactor = 1.0f;
262void KartCollide::calcFloorEffect() {
267 m_sumHitboxBottomHeightFloorOnly = 0.0f;
268 m_surfaceFlags.
resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
270 eSurfaceFlags::StopHalfPipeState);
271 m_sumHitboxBottomHeightSoftWall = 0.0f;
272 m_numFloorOnlyCollisions = 0;
273 m_numSoftWallCollisions = 0;
275 Field::KCLTypeMask mask = KCL_NONE;
276 calcTriggers(&mask, pos(),
false);
278 auto *colDir = Field::CollisionDirector::Instance();
280 if (m_solidOobTimer >= 3 && m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) &&
281 m_surfaceFlags.
offBit(eSurfaceFlags::Wall)) {
286 activateOob(
true, &mask,
false,
false);
290 calcTriggers(&mask, pos(),
true);
293 m_surfaceFlags.onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
296 Field::KCLTypeMask maskOut = KCL_NONE;
298 if (colDir->checkSphereCachedPartialPush(m_boundingRadius, pos(), EGG::Vector3f::inf,
300 calcFallBoundary(&maskOut,
true);
306void KartCollide::calcTriggers(Field::KCLTypeMask *mask,
const EGG::Vector3f &pos,
bool twoPoint) {
307 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
309 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
310 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
311 EGG::Vector3f scaledPos = pos + scalar * componentYAxis();
312 EGG::Vector3f back = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
314 m_smoothedBack += (back.dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
316 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
317 scaledPos += scalar * back;
319 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
320 scaledPos, v1, typeMask,
nullptr, mask, 0);
327 handleTriggers(mask);
330 Field::CollisionDirector::Instance()->findClosestCollisionEntry(mask,
KCL_TYPE_FLOOR);
334 m_surfaceFlags.setBit(eSurfaceFlags::Wall);
338 m_surfaceFlags.setBit(eSurfaceFlags::SolidOOB);
344void KartCollide::handleTriggers(Field::KCLTypeMask *mask) {
345 calcFallBoundary(mask,
false);
349 auto *colDir = Field::CollisionDirector::Instance();
351 if (colDir->closestCollisionEntry()->variant() == 4) {
352 halfPipe()->end(
true);
353 status().setBit(eStatus::EndHalfPipe);
354 m_surfaceFlags.setBit(eSurfaceFlags::StopHalfPipeState);
361void KartCollide::calcFallBoundary(Field::KCLTypeMask *mask,
bool shortBoundary) {
366 auto *colDir = Field::CollisionDirector::Instance();
372 const auto *entry = colDir->closestCollisionEntry();
375 if (entry->variant() != 7) {
381 activateOob(
false, mask,
false,
false);
386void KartCollide::calcBeforeRespawn() {
387 if (pos().y < 0.0f) {
388 activateOob(
true,
nullptr,
false,
false);
391 auto &status = KartObjectProxy::status();
394 if (--m_respawnTimer > 0) {
400 move()->triggerRespawn();
403 m_shrinkTimer = std::max(0, m_shrinkTimer - 1);
407void KartCollide::activateOob(
bool , Field::KCLTypeMask * ,
409 constexpr s16 RESPAWN_TIME = 130;
411 auto &status = KartObjectProxy::status();
419 m_respawnTimer = RESPAWN_TIME;
433 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
435 bspHitbox->radius = radius;
436 hitboxGroup->resetCollision();
437 firstHitbox.setWorldPos(center);
440 colInfo.bbox.setZero();
441 Field::KCLTypeMask kclOut;
443 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
445 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
446 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
452 collisionData.speedFactor = 1.0f;
453 collisionData.rotFactor = 1.0f;
457 collisionData.tangentOff = colInfo.tangentOff;
459 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
460 collisionData.tangentOff += noBounceWallInfo.tangentOff;
461 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
462 collisionData.bSoftWall =
true;
466 collisionData.bFloor =
true;
467 collisionData.floorNrm = colInfo.floorNrm;
470 collisionData.relPos = firstHitbox.worldPos() - pos();
471 collisionData.vel = colVel;
473 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
474 collisionData.bHasRoadVel =
true;
475 collisionData.roadVelocity = colInfo.roadVelocity;
478 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
484 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&kclOut,
492 if (colInfo->perpendicularity <= 0.0f) {
496 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
498 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
502 f32 bspPosX = hitbox.bspHitbox()->
position.x;
503 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
504 if (bspPosX > 0.0f) {
505 collisionData.bWallAtLeftCloser =
true;
507 collisionData.bWallAtRightCloser =
true;
510 collisionData.colPerpendicularity = colInfo->perpendicularity;
515 EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex);
516 std::array<f32, 2> tangents = {0.0f, 0.0f};
519 for (
size_t i = 0; i < tangents.size(); ++i) {
520 f32 sign = i == 1 ? -1.0f : 1.0f;
521 f32 effectiveRadius = sign * hitbox.radius();
522 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
525 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
532 if (tangents[0] > tangents[1]) {
533 collisionData.bWallAtLeftCloser =
true;
534 collisionData.colPerpendicularity = colInfo->perpendicularity;
535 }
else if (tangents[1] > tangents[0]) {
536 collisionData.bWallAtRightCloser =
true;
537 collisionData.colPerpendicularity = colInfo->perpendicularity;
542void KartCollide::calcBoundingRadius() {
543 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
547void KartCollide::calcObjectCollision() {
548 constexpr f32 COS_PI_OVER_4 = 0.707f;
549 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
550 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
551 constexpr s32 SHRINK_TIME = 60;
553 m_totalReactionWallNrm = EGG::Vector3f::zero;
554 m_surfaceFlags.
resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
556 auto *objColKart = objectCollisionKart();
557 size_t collisionCount = objColKart->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);
567 if (reaction == Reaction::SpinShrink && (m_shrinkTimer == 0)) {
568 m_shrinkTimer = SHRINK_TIME;
569 move()->activateShrink();
570 move()->applyForce(30.0f, objColKart->GetHitDirection(i),
false);
571 }
else if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
572 const EGG::Vector3f &hitDepth = objectDirector->hitDepth(i);
573 m_tangentOff += hitDepth;
574 m_movement += hitDepth;
576 if (newAction != Action::None) {
577 action()->setHitDepth(objectDirector->hitDepth(i));
578 action()->
start(newAction);
583 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
584 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
585 const EGG::Vector3f &lastDir = move()->lastDir();
587 if (lastDir.dot(hitDirection) < -COS_PI_OVER_4) {
588 EGG::Vector3f angVel = hitDirection.cross(lastDir);
589 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
591 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
592 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
601void KartCollide::calcPoleTimer() {
603 EGG::Vector3f angVel2 = dynamics()->angVel2();
604 angVel2.y += m_poleYaw;
605 dynamics()->setAngVel2(angVel2);
608 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
616 processMovingWater(collisionData, maskOut);
617 processFloor(collisionData, hitbox, colInfo, maskOut,
true);
623 processMovingWater(collisionData, maskOut);
625 bool hasWallCollision = processWall(collisionData, maskOut);
627 processFloor(collisionData, hitbox, colInfo, maskOut,
false);
629 if (hasWallCollision) {
637void KartCollide::processMovingWater(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
642 auto *colDir = Field::CollisionDirector::Instance();
649 auto *entry = colDir->closestCollisionEntry();
650 switch (entry->variant()) {
652 collisionData.bMovingWaterMomentum =
true;
653 collisionData.bMovingWaterStickyRoad =
true;
654 collisionData.bMovingWaterDisableAccel =
true;
657 collisionData.bMovingWaterDecaySpeed =
true;
660 collisionData.bMovingWaterDecaySpeed =
true;
661 collisionData.bMovingWaterDisableAccel =
true;
662 collisionData.bMovingWaterVertical =
true;
665 collisionData.bMovingWaterMomentum =
true;
671bool KartCollide::processWall(
CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
676 auto *colDirector = Field::CollisionDirector::Instance();
682 colDirector->findClosestCollisionEntry(maskOut,
684 auto *entry = colDirector->closestCollisionEntry();
686 collisionData.closestWallFlags = entry->baseType();
687 collisionData.closestWallSettings = entry->variant();
689 if (entry->attribute.onBit(Field::CollisionDirector::eCollisionAttribute::Soft)) {
690 collisionData.bSoftWall =
true;
707 if (collisionData.bSoftWall) {
708 ++m_numSoftWallCollisions;
709 m_sumHitboxBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
716 auto *colDirector = Field::CollisionDirector::Instance();
718 if (!colDirector->findClosestCollisionEntry(maskOut,
KCL_TYPE_FLOOR)) {
722 const auto *closestColEntry = colDirector->closestCollisionEntry();
724 if (closestColEntry->attribute.offBit(
725 Field::CollisionDirector::eCollisionAttribute::Trickable)) {
726 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
728 collisionData.bTrickable =
true;
729 m_surfaceFlags.setBit(eSurfaceFlags::Trickable);
732 collisionData.speedFactor = std::min(collisionData.speedFactor,
733 param()->stats().kclSpeed[closestColEntry->baseType()]);
735 collisionData.intensity = closestColEntry->intensity();
736 collisionData.rotFactor += param()->stats().kclRot[closestColEntry->baseType()];
738 auto &status = KartObjectProxy::status();
740 if (closestColEntry->attribute.onBit(
741 Field::CollisionDirector::eCollisionAttribute::RejectRoad)) {
745 collisionData.closestFloorFlags = closestColEntry->typeMask;
746 collisionData.closestFloorSettings = closestColEntry->variant();
749 move()->padType().setBit(KartMove::ePadType::BoostPanel);
752 if (!!(*maskOut & BOOST_RAMP_MASK) &&
753 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
754 closestColEntry = colDirector->closestCollisionEntry();
755 move()->padType().setBit(KartMove::ePadType::BoostRamp);
756 state()->setBoostRampType(closestColEntry->variant());
757 m_surfaceFlags.setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
759 state()->setBoostRampType(-1);
760 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
763 if (!collisionData.bSoftWall) {
764 ++m_numFloorOnlyCollisions;
765 m_sumHitboxBottomHeightFloorOnly += hitbox.worldPos().y - hitbox.radius();
773 if ((*maskOut & halfPipeRampMask) &&
774 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
776 state()->setHalfPipeInvisibilityTimer(2);
777 if (colDirector->closestCollisionEntry()->variant() == 1) {
778 move()->padType().setBit(KartMove::ePadType::BoostPanel);
783 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
785 status.offBit(eStatus::JumpPadMushroomVelYInc)) {
786 move()->padType().setBit(KartMove::ePadType::JumpPad);
787 closestColEntry = colDirector->closestCollisionEntry();
788 state()->setJumpPadVariant(closestColEntry->variant());
790 collisionData.bTrickable =
true;
797 auto *colDirector = Field::CollisionDirector::Instance();
799 state()->setCannonPointId(colDirector->closestCollisionEntry()->variant());
800 status().setBit(eStatus::CannonStart);
815 bool b1,
bool b2,
bool b3) {
821 f32 velDotFloorNrm = colData.vel.
dot(colData.floorNrm);
823 if (velDotFloorNrm >= 0.0f) {
827 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
829 crossVec = crossVec.cross(colData.relPos);
831 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.
dot(crossVec));
833 crossVec = colData.floorNrm.cross(negSpeed);
834 crossVec = crossVec.cross(colData.floorNrm);
836 if (std::numeric_limits<f32>::epsilon() >= crossVec.
squaredLength()) {
841 f32 speedDot = std::min(0.0f, speed.dot(crossVec));
842 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
844 auto [proj, rej] = crossVec.projAndRej(forward);
846 f32 projNorm = proj.length();
847 f32 rejNorm = rej.length();
848 f32 projNorm_ = projNorm;
849 f32 rejNorm_ = rejNorm;
851 f32 dVar7 = down * EGG::Mathf::abs(scalar);
852 if (dVar7 < EGG::Mathf::abs(projNorm)) {
854 if (projNorm < 0.0f) {
855 projNorm_ = -down * EGG::Mathf::abs(scalar);
859 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
860 if (EGG::Mathf::abs(rejNorm) > dVar5) {
862 if (rejNorm < 0.0f) {
863 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
884 projRejSum = projRejSum.
rej(nextDir);
886 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
889 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
898 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
911 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
915 bool skipWalls =
false;
917 collisionData.wallNrm += colInfo.wallNrm;
920 collisionData.bInvisibleWall =
true;
923 collisionData.bInvisibleWallOnly =
true;
933 collisionData.bWall3 =
true;
935 collisionData.bWall =
true;
941 collisionData.floorNrm += colInfo.floorNrm;
942 collisionData.bFloor =
true;
946 minMax.min = minMax.min.
minimize(tangentOff);
947 minMax.max = minMax.max.
maximize(tangentOff);
950 relPos += hitbox.relPos();
951 relPos += -hitbox.radius() * tangentOff;
963 setPos(pos() + movement);
965 if (!collisionData.bFloor && (collisionData.bWall || collisionData.bWall3)) {
966 collisionData.movement = movement;
969 f32 rotFactor = 1.0f /
static_cast<f32
>(count);
971 collisionData.rotFactor *= rotFactor;
973 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
974 EGG::Vector3f local_48 = mainRot().rotateVectorInv(scaledRelPos);
976 local_30 = mainRot().rotateVector(local_30);
977 local_30 += extVel();
979 collisionData.vel = local_30;
980 collisionData.relPos = scaledRelPos;
982 if (collisionData.bFloor) {
983 f32 intVelY = dynamics()->intVel().y;
984 if (intVelY > 0.0f) {
985 collisionData.vel.y += intVelY;
987 collisionData.floorNrm.normalise();
992void KartCollide::startFloorMomentRate() {
993 m_floorMomentRate = 0.01f;
997void KartCollide::calcFloorMomentRate() {
998 m_floorMomentRate = status().
onBit(eStatus::InAction) &&
999 action()->flags().
onBit(KartAction::eFlags::Rotating) ?
1001 std::min(m_floorMomentRate + 0.01f, 0.8f);
1006 return Action::None;
1010Action KartCollide::handleReactWallAllSpeed(
size_t idx) {
1011 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1012 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall);
1014 return Action::None;
1018Action KartCollide::handleReactSpinAllSpeed(
size_t ) {
1019 return Action::UNK_0;
1023Action KartCollide::handleReactSpinSomeSpeed(
size_t ) {
1024 return Action::UNK_1;
1028Action KartCollide::handleReactFireSpin(
size_t ) {
1029 return Action::UNK_9;
1033Action KartCollide::handleReactSmallLaunch(
size_t ) {
1034 return Action::UNK_2;
1038Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(
size_t ) {
1039 return Action::UNK_3;
1043Action KartCollide::handleReactLaunchSpinLoseItem(
size_t ) {
1044 return Action::UNK_6;
1048Action KartCollide::handleReactKnockbackBumpLoseItem(
size_t ) {
1049 return Action::UNK_4;
1053Action KartCollide::handleReactLongCrushLoseItem(
size_t ) {
1054 return Action::UNK_12;
1058Action KartCollide::handleReactSmallBump(
size_t idx) {
1059 move()->applyForce(30.0f, objectCollisionKart()->GetHitDirection(idx),
false);
1060 return Action::None;
1064Action KartCollide::handleReactSpinShrink(
size_t ) {
1065 return m_shrinkTimer <= 0 ? Action::UNK_15 : Action::None;
1069Action KartCollide::handleReactHighLaunchLoseItem(
size_t ) {
1070 return Action::UNK_8;
1074Action KartCollide::handleReactWeakWall(
size_t ) {
1075 move()->setSpeed(move()->speed() * 0.82f);
1076 return Action::None;
1080Action KartCollide::handleReactOffroad(
size_t ) {
1081 status().
setBit(eStatus::CollidingOffroad);
1083 return Action::None;
1087Action KartCollide::handleReactLaunchSpin(
size_t idx) {
1088 action()->setTranslation(objectCollisionKart()->translation(idx));
1089 return Action::UNK_5;
1093Action KartCollide::handleReactWallSpark(
size_t idx) {
1094 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1095 m_surfaceFlags.setBit(eSurfaceFlags::ObjectWall3);
1097 return Action::None;
1101Action KartCollide::handleReactRubberWall(
size_t idx) {
1102 constexpr f32 BASE_DIR_FORCE_SCALAR = 0.95f;
1103 constexpr f32 DIR_FORCE_SCALAR = 0.050000012f;
1104 constexpr f32 MAX_FORCE = 70.0f;
1106 const EGG::Vector3f &hitDir = Field::ObjectCollisionKart::GetHitDirection(idx);
1107 const EGG::Vector3f &zAxis = componentZAxis();
1109 f32 force = BASE_DIR_FORCE_SCALAR + DIR_FORCE_SCALAR * EGG::Mathf::abs(hitDir.dot(zAxis));
1110 move()->applyForce(MAX_FORCE * force, hitDir,
true);
1112 return Action::None;
1116Action KartCollide::handleReactUntrickableJumpPad(
size_t ) {
1117 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1118 state()->setJumpPadVariant(0);
1120 return Action::None;
1124Action KartCollide::handleReactShortCrushLoseItem(
size_t ) {
1125 return Action::UNK_14;
1129Action KartCollide::handleReactCrushRespawn(
size_t ) {
1130 return Action::UNK_16;
1134Action KartCollide::handleReactExplosionLoseItem(
size_t ) {
1135 return Action::UNK_7;
1138std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1147 &KartCollide::handleReactWallAllSpeed,
1148 &KartCollide::handleReactSpinAllSpeed,
1149 &KartCollide::handleReactSpinSomeSpeed,
1150 &KartCollide::handleReactFireSpin,
1152 &KartCollide::handleReactSmallLaunch,
1153 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1154 &KartCollide::handleReactLaunchSpinLoseItem,
1155 &KartCollide::handleReactKnockbackBumpLoseItem,
1156 &KartCollide::handleReactLongCrushLoseItem,
1157 &KartCollide::handleReactSmallBump,
1159 &KartCollide::handleReactSpinShrink,
1160 &KartCollide::handleReactHighLaunchLoseItem,
1162 &KartCollide::handleReactWeakWall,
1163 &KartCollide::handleReactOffroad,
1164 &KartCollide::handleReactLaunchSpin,
1165 &KartCollide::handleReactWallSpark,
1166 &KartCollide::handleReactRubberWall,
1168 &KartCollide::handleReactUntrickableJumpPad,
1169 &KartCollide::handleReactShortCrushLoseItem,
1170 &KartCollide::handleReactCrushRespawn,
1171 &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_MOVING_WATER
Koopa Cape and DS Yoshi Falls.
@ 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
constexpr void makeQ(const Quatf &q)
Sets rotation matrix from quaternion.
constexpr Matrix34f transpose() const
Transposes the 3x3 portion of the matrix.
constexpr Vector3f multVector33(const Vector3f &vec) const
Multiplies a 3x3 matrix by a vector.
constexpr Matrix34f multiplyTo(const Matrix34f &rhs) const
Multiplies two matrices.
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
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 FUN_805B72B8(f32 param_1, f32 param_2, bool lockXZ, bool addExtVelY)
Affects velocity when landing from airtime.
void processFloor(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut, bool wheel)
Processes the floor triangles' attributes.
void calcSideCollision(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo)
EGG::Matrix34f m_worldInertiaTensor
The world-space inertia tensor of the kart.
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.
Action handleReactNone(size_t idx)
Object collision functions.
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.
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 calcBodyCollision(f32 totalScale, f32 sinkDepth, const EGG::Quatf &rot, const EGG::Vector3f &scale)
Checks and acts on collision for each kart hitbox.
void applyBodyCollision(CollisionData &collisionData, const EGG::Vector3f &movement, const EGG::Vector3f &posRel, s32 count)
Saves collision info when vehicle body collision occurs.
void processWheel(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut)
Processes moving water and floor collision effects.
void processCannon(Field::KCLTypeMask *maskOut)
Checks if we are colliding with a cannon trigger and sets the state flag if so.
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.
constexpr TBitFlag< T, E > & resetBit(Es... es)
Resets 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 TBitFlag< T, E > & setBit(Es... es)
Sets 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 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 squaredLength() const
The dot product between the vector and itself.
constexpr Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
constexpr Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
constexpr f32 dot(const Vector3f &rhs) const
The dot product between two 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.
bool bFloor
Set if colliding with KCL which satisfies KCL_TYPE_FLOOR.