1#include "KartCollide.hh"
3#include "game/kart/KartBody.hh"
4#include "game/kart/KartMove.hh"
5#include "game/kart/KartPhysics.hh"
6#include "game/kart/KartState.hh"
8#include "game/field/CollisionDirector.hh"
9#include "game/field/ObjectCollisionKart.hh"
10#include "game/field/ObjectDirector.hh"
12#include <egg/math/BoundBox.hh>
13#include <egg/math/Math.hh>
18KartCollide::KartCollide() {
19 m_boundingRadius = 100.0f;
24KartCollide::~KartCollide() =
default;
27void KartCollide::init() {
29 m_floorMomentRate = 0.8f;
33 m_smoothedBack = 0.0f;
34 m_suspBottomHeightNonSoftWall = 0.0f;
35 m_suspBottomHeightSoftWall = 0.0f;
36 m_someNonSoftWallTimer = 0;
37 m_someSoftWallTimer = 0;
38 m_poleAngVelTimer = 0;
40 m_colPerpendicularity = 0.0f;
44void KartCollide::resetHitboxes() {
45 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
46 for (
u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
47 hitboxGroup->hitbox(idx).setLastPos(scale(), pose());
56 for (
u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
57 hitboxGroup->hitbox(idx).
calc(move()->totalScale(), body()->sinkDepth(), scale(), fullRot(),
65 bool wasHalfPipe = state()->isEndHalfPipe() || state()->isActionMidZipper();
66 const EGG::Quatf &rot = wasHalfPipe ? mainRot() : fullRot();
69 auto &colData = collisionData();
70 bool existingWallCollision = colData.bWall || colData.bWall3;
71 bool newWallCollision =
72 m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
73 if (existingWallCollision || newWallCollision) {
74 if (!existingWallCollision) {
75 colData.wallNrm = m_totalReactionWallNrm;
76 if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall)) {
78 }
else if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall3)) {
79 colData.bWall3 =
true;
81 }
else if (newWallCollision) {
82 colData.wallNrm += m_totalReactionWallNrm;
83 if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall)) {
85 }
else if (m_surfaceFlags.
onBit(eSurfaceFlags::ObjectWall3)) {
86 colData.bWall3 =
true;
90 colData.wallNrm.normalise();
102 if (isInRespawn() || state()->isBoost() || state()->isOverZipper() ||
103 state()->isZipperInvisibleWall() || state()->isNoSparkInvisibleWall() ||
104 state()->isHalfPipeRamp()) {
110 bool resetXZ = fVar1 > 0.0f && state()->isAirtimeOver20() && dynamics()->velocity().y < -50.0f;
112 FUN_805B72B8(state()->isInAction() ? 0.3f : 0.01f, fVar1, resetXZ,
113 !state()->isJumpPadDisableYsusForce());
124 const auto &colData = collisionData();
126 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
130 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
133 f32 directionalVelocity = colData.vel.dot(collisionDir);
134 if (directionalVelocity >= 0.0f) {
141 rotMat.
makeQ(dynamics()->mainRot());
154 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.
dot(step3));
159 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
160 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
162 f32 fVar3 = otherVal;
163 if (fVar1 < EGG::Mathf::abs(otherVal)) {
165 if (otherVal < 0.0f) {
166 fVar3 = -param_1 * EGG::Mathf::abs(val);
172 f32 local_1d0 = step6.y;
175 }
else if (colData.bFloor) {
176 f32 velY = intVel().y;
182 dynamics()->setExtVel(newExtVel);
187 f32 prevExtVelY = extVel().y;
189 extVelAdd.y = local_1d0;
190 dynamics()->setExtVel(extVel() + extVelAdd);
192 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
195 dynamics()->setExtVel(extVelNoY);
202 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
215 collisionData.reset();
220 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
227 for (
u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
229 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
231 if (hitbox.bspHitbox()->wallsOnly != 0) {
233 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
236 hitbox.
calc(totalScale, sinkDepth, scale, rot, pos());
238 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
239 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
245 if (!
FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
248 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
249 collisionData.bHasRoadVel =
true;
250 collisionData.roadVelocity = colInfo.roadVelocity;
253 processBody(collisionData, hitbox, &colInfo, &maskOut);
262 collisionData.speedFactor = 1.0f;
263 collisionData.rotFactor = 1.0f;
268void KartCollide::calcFloorEffect() {
269 if (state()->isTouchingGround()) {
273 m_suspBottomHeightNonSoftWall = 0.0f;
274 m_surfaceFlags.
resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
276 eSurfaceFlags::StopHalfPipeState);
277 m_suspBottomHeightSoftWall = 0.0f;
278 m_someNonSoftWallTimer = 0;
279 m_someSoftWallTimer = 0;
282 calcTriggers(&mask, pos(),
false);
284 if (m_solidOobTimer >= 3 && m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) &&
285 m_surfaceFlags.
offBit(eSurfaceFlags::Wall)) {
291 activateOob(
true, &mask,
false,
false);
295 calcTriggers(&mask, pos(),
true);
298 m_surfaceFlags.
onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
303 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
305 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
306 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
310 m_smoothedBack += (back.
dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
312 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
313 scaledPos += scalar * back;
315 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
316 scaledPos, v1, typeMask,
nullptr, mask, 0);
323 handleTriggers(mask);
330 m_surfaceFlags.
setBit(eSurfaceFlags::Wall);
334 m_surfaceFlags.
setBit(eSurfaceFlags::SolidOOB);
341 calcFallBoundary(mask,
false);
345 auto *colDir = Field::CollisionDirector::Instance();
348 halfPipe()->end(
true);
349 state()->setEndHalfPipe(
true);
350 m_surfaceFlags.
setBit(eSurfaceFlags::StopHalfPipeState);
362 auto *colDir = Field::CollisionDirector::Instance();
367 activateOob(
false, mask,
false,
false);
371void KartCollide::calcBeforeRespawn() {
372 if (pos().y < 0.0f) {
373 activateOob(
true,
nullptr,
false,
false);
376 if (!state()->isBeforeRespawn()) {
380 if (--m_respawnTimer > 0) {
384 state()->setBeforeRespawn(
false);
386 move()->triggerRespawn();
392 constexpr s16 RESPAWN_TIME = 130;
394 if (state()->isBeforeRespawn()) {
400 m_respawnTimer = RESPAWN_TIME;
401 state()->setBeforeRespawn(
true);
414 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
416 bspHitbox->radius = radius;
417 hitboxGroup->resetCollision();
418 firstHitbox.setWorldPos(center);
421 colInfo.bbox.setZero();
424 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
426 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
427 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
433 collisionData.speedFactor = 1.0f;
434 collisionData.rotFactor = 1.0f;
438 collisionData.tangentOff = colInfo.tangentOff;
440 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
441 collisionData.tangentOff += noBounceWallInfo.tangentOff;
442 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
443 collisionData.bSoftWall =
true;
447 collisionData.
bFloor =
true;
448 collisionData.floorNrm = colInfo.floorNrm;
451 collisionData.relPos = firstHitbox.worldPos() - pos();
452 collisionData.vel = colVel;
454 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
455 collisionData.bHasRoadVel =
true;
456 collisionData.roadVelocity = colInfo.roadVelocity;
459 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
473 if (colInfo->perpendicularity <= 0.0f) {
477 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
479 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
483 f32 bspPosX = hitbox.bspHitbox()->
position.x;
484 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
485 if (bspPosX > 0.0f) {
486 collisionData.bWallAtLeftCloser =
true;
488 collisionData.bWallAtRightCloser =
true;
491 collisionData.colPerpendicularity = colInfo->perpendicularity;
497 std::array<f32, 2> tangents = {0.0f, 0.0f};
500 for (
size_t i = 0; i < tangents.size(); ++i) {
501 f32 sign = i == 1 ? -1.0f : 1.0f;
502 f32 effectiveRadius = sign * hitbox.radius();
503 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
506 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
513 if (tangents[0] > tangents[1]) {
514 collisionData.bWallAtLeftCloser =
true;
515 collisionData.colPerpendicularity = colInfo->perpendicularity;
516 }
else if (tangents[1] > tangents[0]) {
517 collisionData.bWallAtRightCloser =
true;
518 collisionData.colPerpendicularity = colInfo->perpendicularity;
523void KartCollide::calcBoundingRadius() {
524 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
528void KartCollide::calcObjectCollision() {
529 constexpr f32 COS_PI_OVER_4 = 0.707f;
530 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
531 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
533 m_totalReactionWallNrm = EGG::Vector3f::zero;
534 m_surfaceFlags.
resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
536 size_t collisionCount = objectCollisionKart()->checkCollision(pose(), velocity());
538 const auto *objectDirector = Field::ObjectDirector::Instance();
540 for (
size_t i = 0; i < collisionCount; ++i) {
541 Reaction reaction = objectDirector->reaction(i);
542 if (reaction != Reaction::None && reaction != Reaction::UNK_7) {
543 size_t handlerIdx =
static_cast<std::underlying_type_t<Reaction>
>(reaction);
544 Action newAction = (this->*s_objectCollisionHandlers[handlerIdx])(i);
545 if (newAction != Action::None) {
546 action()->setHitDepth(objectDirector->hitDepth(i));
547 action()->
start(newAction);
550 if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
552 m_tangentOff += hitDepth;
553 m_movement += hitDepth;
557 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
558 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
561 if (lastDir.
dot(hitDirection) < -COS_PI_OVER_4) {
563 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
565 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
566 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
575void KartCollide::calcPoleTimer() {
576 if (m_poleAngVelTimer > 0 && (state()->isAccelerate() || state()->isBrake())) {
578 angVel2.y += m_poleYaw;
579 dynamics()->setAngVel2(angVel2);
582 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
590 processFloor(collisionData, hitbox, colInfo, maskOut,
true);
596 bool hasWallCollision = processWall(collisionData, maskOut);
598 processFloor(collisionData, hitbox, colInfo, maskOut,
false);
600 if (hasWallCollision) {
608bool KartCollide::processWall(CollisionData &collisionData,
Field::KCLTypeMask *maskOut) {
613 auto *colDirector = Field::CollisionDirector::Instance();
619 colDirector->findClosestCollisionEntry(maskOut,
621 auto *entry = colDirector->closestCollisionEntry();
627 collisionData.bSoftWall =
true;
644 if (collisionData.bSoftWall) {
645 ++m_someSoftWallTimer;
646 m_suspBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
653 auto *colDirector = Field::CollisionDirector::Instance();
655 if (!colDirector->findClosestCollisionEntry(maskOut,
KCL_TYPE_FLOOR)) {
659 const auto *closestColEntry = colDirector->closestCollisionEntry();
661 u16 attribute = closestColEntry->attribute;
662 if (!(attribute & 0x2000)) {
663 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
665 collisionData.bTrickable =
true;
666 m_surfaceFlags.
setBit(eSurfaceFlags::Trickable);
669 collisionData.speedFactor = std::min(collisionData.speedFactor,
672 collisionData.
intensity = (attribute >> 0xb) & 3;
675 if (attribute & 0x4000) {
676 state()->setRejectRoad(
true);
683 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
686 if (!!(*maskOut & BOOST_RAMP_MASK) &&
687 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
688 closestColEntry = colDirector->closestCollisionEntry();
689 move()->padType().
setBit(KartMove::ePadType::BoostRamp);
691 m_surfaceFlags.
setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
693 state()->setBoostRampType(-1);
694 m_surfaceFlags.
setBit(eSurfaceFlags::NotTrickable);
697 if (!collisionData.bSoftWall) {
698 ++m_someNonSoftWallTimer;
699 m_suspBottomHeightNonSoftWall += hitbox.worldPos().y - hitbox.radius();
703 state()->setStickyRoad(
true);
707 if ((*maskOut & halfPipeRampMask) &&
708 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
709 state()->setHalfPipeRamp(
true);
710 state()->setHalfPipeInvisibilityTimer(2);
711 if (
KCL_VARIANT_TYPE(colDirector->closestCollisionEntry()->attribute) == 1) {
712 move()->padType().
setBit(KartMove::ePadType::BoostPanel);
717 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
718 if ((!state()->isTouchingGround() || !state()->isJumpPad()) &&
719 !state()->isJumpPadMushroomVelYInc()) {
720 move()->padType().
setBit(KartMove::ePadType::JumpPad);
721 closestColEntry = colDirector->closestCollisionEntry();
724 collisionData.bTrickable =
true;
731 auto *colDirector = Field::CollisionDirector::Instance();
733 state()->setCannonPointId(
735 state()->setCannonStart(
true);
750 bool b1,
bool b2,
bool b3) {
756 f32 velDotFloorNrm = colData.vel.
dot(colData.floorNrm);
758 if (velDotFloorNrm >= 0.0f) {
763 rotMat.
makeQ(dynamics()->mainRot());
768 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
770 crossVec = crossVec.cross(colData.relPos);
772 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.
dot(crossVec));
774 crossVec = colData.floorNrm.cross(negSpeed);
775 crossVec = crossVec.cross(colData.floorNrm);
777 if (std::numeric_limits<f32>::epsilon() >= crossVec.
squaredLength()) {
782 f32 speedDot = std::min(0.0f, speed.
dot(crossVec));
783 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
785 auto [proj, rej] = crossVec.projAndRej(forward);
787 f32 projNorm = proj.length();
788 f32 rejNorm = rej.length();
789 f32 projNorm_ = projNorm;
790 f32 rejNorm_ = rejNorm;
792 f32 dVar7 = down * EGG::Mathf::abs(scalar);
793 if (dVar7 < EGG::Mathf::abs(projNorm)) {
795 if (projNorm < 0.0f) {
796 projNorm_ = -down * EGG::Mathf::abs(scalar);
800 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
801 if (dVar5 < EGG::Mathf::abs(rejNorm)) {
803 if (rejNorm < 0.0f) {
804 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
825 projRejSum = projRejSum.
rej(nextDir);
827 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
830 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
838 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
851 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
855 bool skipWalls =
false;
857 collisionData.wallNrm += colInfo.wallNrm;
860 collisionData.bInvisibleWall =
true;
863 collisionData.bInvisibleWallOnly =
true;
873 collisionData.
bWall3 =
true;
875 collisionData.
bWall =
true;
881 collisionData.floorNrm += colInfo.floorNrm;
882 collisionData.
bFloor =
true;
886 minMax.min = minMax.min.
minimize(tangentOff);
887 minMax.max = minMax.max.
maximize(tangentOff);
890 relPos += hitbox.relPos();
891 relPos += -hitbox.radius() * tangentOff;
903 setPos(pos() + movement);
906 collisionData.movement = movement;
909 f32 rotFactor = 1.0f /
static_cast<f32
>(count);
911 collisionData.rotFactor *= rotFactor;
913 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
917 local_30 += extVel();
919 collisionData.vel = local_30;
920 collisionData.relPos = scaledRelPos;
922 if (collisionData.
bFloor) {
923 f32 intVelY = dynamics()->intVel().y;
924 if (intVelY > 0.0f) {
925 collisionData.vel.y += intVelY;
932void KartCollide::startFloorMomentRate() {
933 m_floorMomentRate = 0.01f;
937void KartCollide::calcFloorMomentRate() {
938 if (state()->isInAction() && action()->flags().onBit(KartAction::eFlags::Rotating)) {
939 m_floorMomentRate = 0.01f;
941 m_floorMomentRate = std::min(m_floorMomentRate + 0.01f, 0.8f);
951Action KartCollide::handleReactWallAllSpeed(
size_t idx) {
952 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
953 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall);
959Action KartCollide::handleReactSpinAllSpeed(
size_t ) {
960 return Action::UNK_0;
964Action KartCollide::handleReactSpinSomeSpeed(
size_t ) {
965 return Action::UNK_1;
969Action KartCollide::handleReactFireSpin(
size_t ) {
970 return Action::UNK_9;
974Action KartCollide::handleReactSmallLaunch(
size_t ) {
975 return Action::UNK_2;
979Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(
size_t ) {
980 return Action::UNK_3;
984Action KartCollide::handleReactLaunchSpinLoseItem(
size_t ) {
985 return Action::UNK_6;
989Action KartCollide::handleReactKnockbackBumpLoseItem(
size_t ) {
990 return Action::UNK_4;
994Action KartCollide::handleReactLongCrushLoseItem(
size_t ) {
995 return Action::UNK_12;
999Action KartCollide::handleReactSmallBump(
size_t idx) {
1000 move()->applyBumpForce(30.0f, objectCollisionKart()->GetHitDirection(idx),
false);
1001 return Action::None;
1005Action KartCollide::handleReactHighLaunchLoseItem(
size_t ) {
1006 return Action::UNK_8;
1010Action KartCollide::handleReactWeakWall(
size_t ) {
1011 move()->setSpeed(move()->speed() * 0.82f);
1012 return Action::None;
1016Action KartCollide::handleReactOffroad(
size_t ) {
1017 state()->setCollidingOffroad(
true);
1019 return Action::None;
1023Action KartCollide::handleReactLaunchSpin(
size_t idx) {
1024 action()->setTranslation(objectCollisionKart()->translation(idx));
1025 return Action::UNK_5;
1029Action KartCollide::handleReactWallSpark(
size_t idx) {
1030 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1031 m_surfaceFlags.
setBit(eSurfaceFlags::ObjectWall3);
1033 return Action::None;
1037Action KartCollide::handleReactUntrickableJumpPad(
size_t ) {
1038 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1039 state()->setJumpPadVariant(0);
1041 return Action::None;
1045Action KartCollide::handleReactShortCrushLoseItem(
size_t ) {
1046 return Action::UNK_14;
1050Action KartCollide::handleReactCrushRespawn(
size_t ) {
1051 return Action::UNK_16;
1055Action KartCollide::handleReactExplosionLoseItem(
size_t ) {
1056 return Action::UNK_7;
1059std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1068 &KartCollide::handleReactWallAllSpeed,
1069 &KartCollide::handleReactSpinAllSpeed,
1070 &KartCollide::handleReactSpinSomeSpeed,
1071 &KartCollide::handleReactFireSpin,
1073 &KartCollide::handleReactSmallLaunch,
1074 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1075 &KartCollide::handleReactLaunchSpinLoseItem,
1076 &KartCollide::handleReactKnockbackBumpLoseItem,
1077 &KartCollide::handleReactLongCrushLoseItem,
1078 &KartCollide::handleReactSmallBump,
1081 &KartCollide::handleReactHighLaunchLoseItem,
1083 &KartCollide::handleReactWeakWall,
1084 &KartCollide::handleReactOffroad,
1085 &KartCollide::handleReactLaunchSpin,
1086 &KartCollide::handleReactWallSpark,
1089 &KartCollide::handleReactUntrickableJumpPad,
1090 &KartCollide::handleReactShortCrushLoseItem,
1091 &KartCollide::handleReactCrushRespawn,
1092 &KartCollide::handleReactExplosionLoseItem,
#define KCL_TYPE_DRIVER_SOLID_SURFACE
0xEAFABDFF
#define KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2
0x4010B000
#define KCL_TYPE_DRIVER_WALL
0xC010B000
@ COL_TYPE_STICKY_ROAD
Player sticks if within 200 units (rBC stairs).
@ COL_TYPE_BOOST_RAMP
Trickable. Variant affects boost duration.
@ COL_TYPE_WALL_2
Difference to COL_TYPE_WALL is unknown.
@ COL_TYPE_HALFPIPE_RAMP
Bowser's Castle half-pipe ramps.
@ COL_TYPE_HALFPIPE_INVISIBLE_WALL
Invisible wall after a half-pipe jump, like in BC.
@ COL_TYPE_FALL_BOUNDARY
Non-solid out-of-bounds trigger.
@ COL_TYPE_BOOST_PAD
Boost panel.
@ COL_TYPE_CANNON_TRIGGER
Launches player to a destination "cannon point".
@ COL_TYPE_SOLID_OOB
Solid out-of-bounds trigger.
@ COL_TYPE_JUMP_PAD
Like GBA Shy Guy Beach.
@ COL_TYPE_EFFECT_TRIGGER
Activates various effects based on variant.
#define KCL_ATTRIBUTE_TYPE(x)
Computes the "Base Type" portion of the KCL flags. It's the lower 5 bits of the flag.
#define KCL_VARIANT_TYPE(x)
Extracts the "Variant" portion of the 2 byte KCL flag. It's the 3 bits before the "Bast Type".
#define KCL_TYPE_NON_DIRECTIONAL
0xE0F8BDFF
#define KCL_TYPE_4010D000
0x4010D000
#define KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL
0xC0109000
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
#define KCL_TYPE_ANY_INVISIBLE_WALL
0x90002000
#define KCL_TYPE_VEHICLE_COLLIDEABLE
0xEAF8BDFF
#define KCL_TYPE_DIRECTIONAL
0x05070000
#define KCL_TYPE_WALL
0xD010F000
Matrix34f multiplyTo(const Matrix34f &rhs) const
Multiplies two matrices.
void makeQ(const Quatf &q)
Sets rotation matrix from quaternion.
Vector3f multVector33(const Vector3f &vec) const
Multiplies a 3x3 matrix by a vector.
Vector3f multVector(const Vector3f &vec) const
Multiplies a vector by a matrix.
Matrix34f transpose() const
Transposes the 3x3 portion of the matrix.
bool findClosestCollisionEntry(KCLTypeMask *typeMask, KCLTypeMask type)
Finds the closest KCL triangle out of the list of tris we are colliding with.
Houses hitbox and collision info for an object (body or wheel).
Represents a hitbox for the kart body or a wheel.
void calc(f32 totalScale, f32 sinkDepth, const EGG::Vector3f &scale, const EGG::Quatf &rot, const EGG::Vector3f &pos)
Calculates the position of a given hitbox, both relative to the player and world.
bool start(Action action)
Starts an action.
void applyBodyCollision(CollisionData &collisionData, const EGG::Vector3f &movement, const EGG::Vector3f &posRel, s32 count)
Saves collision info when vehicle body collision occurs.
void calcWheelCollision(u16 wheelIdx, CollisionGroup *hitboxGroup, const EGG::Vector3f &colVel, const EGG::Vector3f ¢er, f32 radius)
Checks wheel hitbox collision and stores position/velocity info.
void applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup, const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed, bool b1, bool b2, bool b3)
Applies external and angular velocity based on the collision with the floor.
void processCannon(Field::KCLTypeMask *maskOut)
Checks if we are colliding with a cannon trigger and sets the state flag if so.
Action handleReactNone(size_t idx)
Object collision functions.
bool FUN_805B6A9C(CollisionData &collisionData, const Hitbox &hitbox, EGG::BoundBox3f &minMax, EGG::Vector3f &relPos, s32 &count, const Field::KCLTypeMask &maskOut, const Field::CollisionInfo &colInfo)
Called on collision of a new KCL type??? This only happens after airtime so far.
void processWheel(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut)
Processes moving water and floor collision effects.
void calcHitboxes()
On each frame, calculates the positions for each hitbox.
void calcSideCollision(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo)
void processFloor(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut, bool wheel)
Processes the floor triangles' attributes.
void FUN_805B72B8(f32 param_1, f32 param_2, bool lockXZ, bool addExtVelY)
Affects velocity when landing from airtime.
void calcBodyCollision(f32 totalScale, f32 sinkDepth, const EGG::Quatf &rot, const EGG::Vector3f &scale)
Checks and acts on collision for each kart hitbox.
Pertains to kart-related functionality.
A representation of a bounding cuboid.
A quaternion, used to represent 3D rotation.
Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
Vector3f rotateVectorInv(const Vector3f &vec) const
Rotates a vector on the inverse quat.
constexpr TBitFlag< T, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
constexpr void makeAllZero()
Resets all the bits to zero.
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
f32 normalise()
Normalizes the vector and returns the original length.
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
f32 squaredLength() const
The dot product between the vector and itself.
Vector3f rej(const Vector3f &rhs) const
The rejection of this vector onto rhs.
Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
Represents one of the many hitboxes that make up a vehicle.
EGG::Vector3f position
The relative position of the hitbox.
Information about the current collision and its properties.
u32 closestFloorSettings
The 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.