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() {
30 m_floorMomentRate = 0.8f;
35 m_smoothedBack = 0.0f;
36 m_suspBottomHeightNonSoftWall = 0.0f;
37 m_suspBottomHeightSoftWall = 0.0f;
38 m_someNonSoftWallTimer = 0;
39 m_someSoftWallTimer = 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());
58 for (
u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
59 hitboxGroup->hitbox(idx).
calc(move()->totalScale(), body()->sinkDepth(), scale(), fullRot(),
68 const EGG::Quatf &rot = wasHalfPipe ? mainRot() : fullRot();
71 auto &colData = collisionData();
72 bool existingWallCollision = colData.bWall || colData.bWall3;
73 bool newWallCollision =
74 m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
75 if (existingWallCollision || newWallCollision) {
76 if (!existingWallCollision) {
77 colData.wallNrm = m_totalReactionWallNrm;
78 if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall)) {
80 }
else if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall3)) {
81 colData.bWall3 =
true;
83 }
else if (newWallCollision) {
84 colData.wallNrm += m_totalReactionWallNrm;
85 if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall)) {
87 }
else if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall3)) {
88 colData.bWall3 =
true;
92 colData.wallNrm.normalise();
104 auto &status = KartObjectProxy::status();
115 dynamics()->velocity().y < -50.0f;
118 status.
offBit(eStatus::JumpPadDisableYsusForce));
129 const auto &colData = collisionData();
131 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
135 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
138 f32 directionalVelocity = colData.vel.dot(collisionDir);
139 if (directionalVelocity >= 0.0f) {
146 rotMat.
makeQ(dynamics()->mainRot());
159 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.
dot(step3));
164 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
165 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
167 f32 fVar3 = otherVal;
168 if (fVar1 < EGG::Mathf::abs(otherVal)) {
170 if (otherVal < 0.0f) {
171 fVar3 = -param_1 * EGG::Mathf::abs(val);
177 f32 local_1d0 = step6.y;
180 }
else if (colData.bFloor) {
181 f32 velY = intVel().y;
187 dynamics()->setExtVel(newExtVel);
192 f32 prevExtVelY = extVel().y;
194 extVelAdd.y = local_1d0;
195 dynamics()->setExtVel(extVel() + extVelAdd);
197 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
200 dynamics()->setExtVel(extVelNoY);
207 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
220 collisionData.reset();
225 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
226 Field::KCLTypeMask maskOut;
232 for (
u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
234 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
236 if (hitbox.bspHitbox()->wallsOnly != 0) {
238 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
241 hitbox.
calc(totalScale, sinkDepth, scale, rot, pos());
243 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
244 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
250 if (!
FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
253 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
254 collisionData.bHasRoadVel =
true;
255 collisionData.roadVelocity = colInfo.roadVelocity;
258 processBody(collisionData, hitbox, &colInfo, &maskOut);
267 collisionData.speedFactor = 1.0f;
268 collisionData.rotFactor = 1.0f;
273void KartCollide::calcFloorEffect() {
278 m_suspBottomHeightNonSoftWall = 0.0f;
279 m_surfaceFlags.
resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
281 eSurfaceFlags::StopHalfPipeState);
282 m_suspBottomHeightSoftWall = 0.0f;
283 m_someNonSoftWallTimer = 0;
284 m_someSoftWallTimer = 0;
286 Field::KCLTypeMask mask = KCL_NONE;
287 calcTriggers(&mask, pos(),
false);
289 auto *colDir = Field::CollisionDirector::Instance();
291 if (m_solidOobTimer >= 3 && m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) &&
292 m_surfaceFlags.
offBit(eSurfaceFlags::Wall)) {
297 activateOob(
true, &mask,
false,
false);
301 calcTriggers(&mask, pos(),
true);
304 m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
307 Field::KCLTypeMask maskOut = KCL_NONE;
309 if (colDir->checkSphereCachedPartialPush(m_boundingRadius, pos(), EGG::Vector3f::inf,
311 calcFallBoundary(&maskOut,
true);
317void KartCollide::calcTriggers(Field::KCLTypeMask *mask,
const EGG::Vector3f &pos,
bool twoPoint) {
318 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
320 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
321 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
325 m_smoothedBack += (back.
dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
327 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
328 scaledPos += scalar * back;
330 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
331 scaledPos, v1, typeMask,
nullptr, mask, 0);
338 handleTriggers(mask);
345 m_surfaceFlags.
setBit(eSurfaceFlags::Wall);
349 m_surfaceFlags.
setBit(eSurfaceFlags::SolidOOB);
355void KartCollide::handleTriggers(Field::KCLTypeMask *mask) {
356 calcFallBoundary(mask,
false);
360 auto *colDir = Field::CollisionDirector::Instance();
362 if (colDir->closestCollisionEntry()->variant() == 4) {
363 halfPipe()->end(
true);
364 status().
setBit(eStatus::EndHalfPipe);
365 m_surfaceFlags.
setBit(eSurfaceFlags::StopHalfPipeState);
372void KartCollide::calcFallBoundary(Field::KCLTypeMask *mask,
bool shortBoundary) {
377 auto *colDir = Field::CollisionDirector::Instance();
383 const auto *entry = colDir->closestCollisionEntry();
386 if (entry->variant() != 7) {
392 activateOob(
false, mask,
false,
false);
397void KartCollide::calcBeforeRespawn() {
398 if (pos().y < 0.0f) {
399 activateOob(
true,
nullptr,
false,
false);
402 auto &status = KartObjectProxy::status();
405 if (--m_respawnTimer > 0) {
411 move()->triggerRespawn();
414 m_shrinkTimer = std::max(0, m_shrinkTimer - 1);
418void KartCollide::activateOob(
bool , Field::KCLTypeMask * ,
420 constexpr s16 RESPAWN_TIME = 130;
422 auto &status = KartObjectProxy::status();
430 m_respawnTimer = RESPAWN_TIME;
444 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
446 bspHitbox->radius = radius;
447 hitboxGroup->resetCollision();
448 firstHitbox.setWorldPos(center);
451 colInfo.bbox.setZero();
452 Field::KCLTypeMask kclOut;
454 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
456 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
457 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
463 collisionData.speedFactor = 1.0f;
464 collisionData.rotFactor = 1.0f;
468 collisionData.tangentOff = colInfo.tangentOff;
470 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
471 collisionData.tangentOff += noBounceWallInfo.tangentOff;
472 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
473 collisionData.bSoftWall =
true;
477 collisionData.
bFloor =
true;
478 collisionData.floorNrm = colInfo.floorNrm;
481 collisionData.relPos = firstHitbox.worldPos() - pos();
482 collisionData.vel = colVel;
484 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
485 collisionData.bHasRoadVel =
true;
486 collisionData.roadVelocity = colInfo.roadVelocity;
489 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
503 if (colInfo->perpendicularity <= 0.0f) {
507 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
509 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
513 f32 bspPosX = hitbox.bspHitbox()->
position.x;
514 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
515 if (bspPosX > 0.0f) {
516 collisionData.bWallAtLeftCloser =
true;
518 collisionData.bWallAtRightCloser =
true;
521 collisionData.colPerpendicularity = colInfo->perpendicularity;
527 std::array<f32, 2> tangents = {0.0f, 0.0f};
530 for (
size_t i = 0; i < tangents.size(); ++i) {
531 f32 sign = i == 1 ? -1.0f : 1.0f;
532 f32 effectiveRadius = sign * hitbox.radius();
533 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
536 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
543 if (tangents[0] > tangents[1]) {
544 collisionData.bWallAtLeftCloser =
true;
545 collisionData.colPerpendicularity = colInfo->perpendicularity;
546 }
else if (tangents[1] > tangents[0]) {
547 collisionData.bWallAtRightCloser =
true;
548 collisionData.colPerpendicularity = colInfo->perpendicularity;
553void KartCollide::calcBoundingRadius() {
554 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
558void KartCollide::calcObjectCollision() {
559 constexpr f32 COS_PI_OVER_4 = 0.707f;
560 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
561 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
562 constexpr s32 SHRINK_TIME = 60;
564 m_totalReactionWallNrm = EGG::Vector3f::zero;
565 m_surfaceFlags.
resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
567 auto *objColKart = objectCollisionKart();
568 size_t collisionCount = objColKart->checkCollision(pose(), velocity());
570 const auto *objectDirector = Field::ObjectDirector::Instance();
572 for (
size_t i = 0; i < collisionCount; ++i) {
573 Reaction reaction = objectDirector->reaction(i);
574 if (reaction != Reaction::None && reaction != Reaction::UNK_7) {
575 size_t handlerIdx =
static_cast<std::underlying_type_t<Reaction>
>(reaction);
576 Action newAction = (this->*s_objectCollisionHandlers[handlerIdx])(i);
578 if (reaction == Reaction::SpinShrink && (m_shrinkTimer == 0)) {
579 m_shrinkTimer = SHRINK_TIME;
580 move()->activateShrink();
581 move()->applyForce(30.0f, objColKart->GetHitDirection(i),
false);
582 }
else if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
584 m_tangentOff += hitDepth;
585 m_movement += hitDepth;
587 if (newAction != Action::None) {
588 action()->setHitDepth(objectDirector->hitDepth(i));
589 action()->
start(newAction);
594 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
595 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
598 if (lastDir.
dot(hitDirection) < -COS_PI_OVER_4) {
600 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
602 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
603 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
612void KartCollide::calcPoleTimer() {
615 angVel2.y += m_poleYaw;
616 dynamics()->setAngVel2(angVel2);
619 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
627 processMovingWater(collisionData, maskOut);
628 processFloor(collisionData, hitbox, colInfo, maskOut,
true);
634 processMovingWater(collisionData, maskOut);
636 bool hasWallCollision = processWall(collisionData, maskOut);
638 processFloor(collisionData, hitbox, colInfo, maskOut,
false);
640 if (hasWallCollision) {
648void KartCollide::processMovingWater(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
653 auto *colDir = Field::CollisionDirector::Instance();
660 auto *entry = colDir->closestCollisionEntry();
661 switch (entry->variant()) {
663 collisionData.bMovingWaterMomentum =
true;
664 collisionData.bMovingWaterStickyRoad =
true;
665 collisionData.bMovingWaterDisableAccel =
true;
668 collisionData.bMovingWaterDecaySpeed =
true;
671 collisionData.bMovingWaterDecaySpeed =
true;
672 collisionData.bMovingWaterDisableAccel =
true;
673 collisionData.bMovingWaterVertical =
true;
676 collisionData.bMovingWaterMomentum =
true;
682bool KartCollide::processWall(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
687 auto *colDirector = Field::CollisionDirector::Instance();
693 colDirector->findClosestCollisionEntry(maskOut,
695 auto *entry = colDirector->closestCollisionEntry();
697 collisionData.closestWallFlags = entry->baseType();
698 collisionData.closestWallSettings = entry->variant();
700 if (entry->attribute.onBit(Field::CollisionDirector::eCollisionAttribute::Soft)) {
701 collisionData.bSoftWall =
true;
718 if (collisionData.bSoftWall) {
719 ++m_someSoftWallTimer;
720 m_suspBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
727 auto *colDirector = Field::CollisionDirector::Instance();
729 if (!colDirector->findClosestCollisionEntry(maskOut,
KCL_TYPE_FLOOR)) {
733 const auto *closestColEntry = colDirector->closestCollisionEntry();
735 if (closestColEntry->attribute.offBit(
736 Field::CollisionDirector::eCollisionAttribute::Trickable)) {
737 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
739 collisionData.bTrickable =
true;
740 m_surfaceFlags.
setBit(eSurfaceFlags::Trickable);
743 collisionData.speedFactor = std::min(collisionData.speedFactor,
744 param()->stats().kclSpeed[closestColEntry->baseType()]);
746 collisionData.
intensity = closestColEntry->intensity();
747 collisionData.rotFactor += param()->stats().
kclRot[closestColEntry->baseType()];
749 auto &status = KartObjectProxy::status();
751 if (closestColEntry->attribute.onBit(
752 Field::CollisionDirector::eCollisionAttribute::RejectRoad)) {
760 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
763 if (!!(*maskOut & BOOST_RAMP_MASK) &&
764 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
765 closestColEntry = colDirector->closestCollisionEntry();
766 move()->padType().
setBit(KartMove::ePadType::BoostRamp);
767 state()->setBoostRampType(closestColEntry->variant());
768 m_surfaceFlags.
setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
770 state()->setBoostRampType(-1);
771 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
774 if (!collisionData.bSoftWall) {
775 ++m_someNonSoftWallTimer;
776 m_suspBottomHeightNonSoftWall += hitbox.worldPos().y - hitbox.radius();
784 if ((*maskOut & halfPipeRampMask) &&
785 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
787 state()->setHalfPipeInvisibilityTimer(2);
788 if (colDirector->closestCollisionEntry()->variant() == 1) {
789 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
794 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
796 status.
offBit(eStatus::JumpPadMushroomVelYInc)) {
797 move()->padType().
setBit(KartMove::ePadType::JumpPad);
798 closestColEntry = colDirector->closestCollisionEntry();
799 state()->setJumpPadVariant(closestColEntry->variant());
801 collisionData.bTrickable =
true;
808 auto *colDirector = Field::CollisionDirector::Instance();
810 state()->setCannonPointId(colDirector->closestCollisionEntry()->variant());
811 status().
setBit(eStatus::CannonStart);
826 bool b1,
bool b2,
bool b3) {
832 f32 velDotFloorNrm = colData.vel.
dot(colData.floorNrm);
834 if (velDotFloorNrm >= 0.0f) {
839 rotMat.
makeQ(dynamics()->mainRot());
844 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
846 crossVec = crossVec.cross(colData.relPos);
848 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.
dot(crossVec));
850 crossVec = colData.floorNrm.cross(negSpeed);
851 crossVec = crossVec.cross(colData.floorNrm);
853 if (std::numeric_limits<f32>::epsilon() >= crossVec.
squaredLength()) {
858 f32 speedDot = std::min(0.0f, speed.
dot(crossVec));
859 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
861 auto [proj, rej] = crossVec.projAndRej(forward);
863 f32 projNorm = proj.length();
864 f32 rejNorm = rej.length();
865 f32 projNorm_ = projNorm;
866 f32 rejNorm_ = rejNorm;
868 f32 dVar7 = down * EGG::Mathf::abs(scalar);
869 if (dVar7 < EGG::Mathf::abs(projNorm)) {
871 if (projNorm < 0.0f) {
872 projNorm_ = -down * EGG::Mathf::abs(scalar);
876 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
877 if (EGG::Mathf::abs(rejNorm) > dVar5) {
879 if (rejNorm < 0.0f) {
880 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
901 projRejSum = projRejSum.
rej(nextDir);
903 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
906 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
914 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
927 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
931 bool skipWalls =
false;
933 collisionData.wallNrm += colInfo.wallNrm;
936 collisionData.bInvisibleWall =
true;
939 collisionData.bInvisibleWallOnly =
true;
949 collisionData.
bWall3 =
true;
951 collisionData.
bWall =
true;
957 collisionData.floorNrm += colInfo.floorNrm;
958 collisionData.
bFloor =
true;
962 minMax.min = minMax.min.
minimize(tangentOff);
963 minMax.max = minMax.max.
maximize(tangentOff);
966 relPos += hitbox.relPos();
967 relPos += -hitbox.radius() * tangentOff;
979 setPos(pos() + movement);
982 collisionData.movement = movement;
985 f32 rotFactor = 1.0f /
static_cast<f32
>(count);
987 collisionData.rotFactor *= rotFactor;
989 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
993 local_30 += extVel();
995 collisionData.vel = local_30;
996 collisionData.relPos = scaledRelPos;
998 if (collisionData.
bFloor) {
999 f32 intVelY = dynamics()->intVel().y;
1000 if (intVelY > 0.0f) {
1001 collisionData.vel.y += intVelY;
1008void KartCollide::startFloorMomentRate() {
1009 m_floorMomentRate = 0.01f;
1013void KartCollide::calcFloorMomentRate() {
1014 m_floorMomentRate = status().
onBit(eStatus::InAction) &&
1015 action()->flags().
onBit(KartAction::eFlags::Rotating) ?
1017 std::min(m_floorMomentRate + 0.01f, 0.8f);
1022 return Action::None;
1026Action KartCollide::handleReactWallAllSpeed(
size_t idx) {
1027 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1028 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall);
1030 return Action::None;
1034Action KartCollide::handleReactSpinAllSpeed(
size_t ) {
1035 return Action::UNK_0;
1039Action KartCollide::handleReactSpinSomeSpeed(
size_t ) {
1040 return Action::UNK_1;
1044Action KartCollide::handleReactFireSpin(
size_t ) {
1045 return Action::UNK_9;
1049Action KartCollide::handleReactSmallLaunch(
size_t ) {
1050 return Action::UNK_2;
1054Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(
size_t ) {
1055 return Action::UNK_3;
1059Action KartCollide::handleReactLaunchSpinLoseItem(
size_t ) {
1060 return Action::UNK_6;
1064Action KartCollide::handleReactKnockbackBumpLoseItem(
size_t ) {
1065 return Action::UNK_4;
1069Action KartCollide::handleReactLongCrushLoseItem(
size_t ) {
1070 return Action::UNK_12;
1074Action KartCollide::handleReactSmallBump(
size_t idx) {
1075 move()->applyForce(30.0f, objectCollisionKart()->GetHitDirection(idx),
false);
1076 return Action::None;
1080Action KartCollide::handleReactSpinShrink(
size_t ) {
1081 return m_shrinkTimer <= 0 ? Action::UNK_15 : Action::None;
1085Action KartCollide::handleReactHighLaunchLoseItem(
size_t ) {
1086 return Action::UNK_8;
1090Action KartCollide::handleReactWeakWall(
size_t ) {
1091 move()->setSpeed(move()->speed() * 0.82f);
1092 return Action::None;
1096Action KartCollide::handleReactOffroad(
size_t ) {
1097 status().
setBit(eStatus::CollidingOffroad);
1099 return Action::None;
1103Action KartCollide::handleReactLaunchSpin(
size_t idx) {
1104 action()->setTranslation(objectCollisionKart()->translation(idx));
1105 return Action::UNK_5;
1109Action KartCollide::handleReactWallSpark(
size_t idx) {
1110 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1111 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall3);
1113 return Action::None;
1117Action KartCollide::handleReactRubberWall(
size_t idx) {
1118 constexpr f32 BASE_DIR_FORCE_SCALAR = 0.95f;
1119 constexpr f32 DIR_FORCE_SCALAR = 0.050000012f;
1120 constexpr f32 MAX_FORCE = 70.0f;
1122 const EGG::Vector3f &hitDir = Field::ObjectCollisionKart::GetHitDirection(idx);
1125 f32 force = BASE_DIR_FORCE_SCALAR + DIR_FORCE_SCALAR * EGG::Mathf::abs(hitDir.
dot(zAxis));
1126 move()->applyForce(MAX_FORCE * force, hitDir,
true);
1128 return Action::None;
1132Action KartCollide::handleReactUntrickableJumpPad(
size_t ) {
1133 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1134 state()->setJumpPadVariant(0);
1136 return Action::None;
1140Action KartCollide::handleReactShortCrushLoseItem(
size_t ) {
1141 return Action::UNK_14;
1145Action KartCollide::handleReactCrushRespawn(
size_t ) {
1146 return Action::UNK_16;
1150Action KartCollide::handleReactExplosionLoseItem(
size_t ) {
1151 return Action::UNK_7;
1154std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1163 &KartCollide::handleReactWallAllSpeed,
1164 &KartCollide::handleReactSpinAllSpeed,
1165 &KartCollide::handleReactSpinSomeSpeed,
1166 &KartCollide::handleReactFireSpin,
1168 &KartCollide::handleReactSmallLaunch,
1169 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1170 &KartCollide::handleReactLaunchSpinLoseItem,
1171 &KartCollide::handleReactKnockbackBumpLoseItem,
1172 &KartCollide::handleReactLongCrushLoseItem,
1173 &KartCollide::handleReactSmallBump,
1175 &KartCollide::handleReactSpinShrink,
1176 &KartCollide::handleReactHighLaunchLoseItem,
1178 &KartCollide::handleReactWeakWall,
1179 &KartCollide::handleReactOffroad,
1180 &KartCollide::handleReactLaunchSpin,
1181 &KartCollide::handleReactWallSpark,
1182 &KartCollide::handleReactRubberWall,
1184 &KartCollide::handleReactUntrickableJumpPad,
1185 &KartCollide::handleReactShortCrushLoseItem,
1186 &KartCollide::handleReactCrushRespawn,
1187 &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
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.