A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
KartCollide.cc
1#include "KartCollide.hh"
2
3#include "game/kart/KartBody.hh"
4#include "game/kart/KartMove.hh"
5#include "game/kart/KartPhysics.hh"
6#include "game/kart/KartState.hh"
7
8#include "game/field/CollisionDirector.hh"
9#include "game/field/ObjectCollisionKart.hh"
10#include "game/field/ObjectDirector.hh"
11
12#include <egg/math/BoundBox.hh>
13#include <egg/math/Math.hh>
14
15namespace Kart {
16
18KartCollide::KartCollide() {
19 m_boundingRadius = 100.0f;
20 m_surfaceFlags.makeAllZero();
21}
22
24KartCollide::~KartCollide() = default;
25
27void KartCollide::init() {
28 m_pullPath.init();
29 calcBoundingRadius();
30 m_floorMomentRate = 0.8f;
31 m_surfaceFlags.makeAllZero();
32 m_respawnTimer = 0;
33 m_solidOobTimer = 0;
34 m_shrinkTimer = 0;
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;
41 m_poleYaw = 0.0f;
42 m_colPerpendicularity = 0.0f;
43}
44
46void KartCollide::resetHitboxes() {
47 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
48 for (u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
49 hitboxGroup->hitbox(idx).setLastPos(scale(), pose());
50 }
51}
52
57 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
58 for (u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
59 hitboxGroup->hitbox(idx).calc(move()->totalScale(), body()->sinkDepth(), scale(), fullRot(),
60 pos());
61 }
62}
63
67 bool wasHalfPipe = status().onBit(eStatus::EndHalfPipe, eStatus::ActionMidZipper);
68 const EGG::Quatf &rot = wasHalfPipe ? mainRot() : fullRot();
69 calcBodyCollision(move()->totalScale(), body()->sinkDepth(), rot, scale());
70
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)) {
79 colData.bWall = true;
80 } else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
81 colData.bWall3 = true;
82 }
83 } else if (newWallCollision) {
84 colData.wallNrm += m_totalReactionWallNrm;
85 if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall)) {
86 colData.bWall = true;
87 } else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
88 colData.bWall3 = true;
89 }
90 }
91
92 colData.wallNrm.normalise();
93 }
94
96}
97
102 f32 fVar1;
103
104 auto &status = KartObjectProxy::status();
105
106 if (isInRespawn() ||
108 eStatus::NoSparkInvisibleWall, eStatus::HalfPipeRamp)) {
109 fVar1 = 0.0f;
110 } else {
111 fVar1 = 0.05f;
112 }
113
114 bool resetXZ = fVar1 > 0.0f && status.onBit(eStatus::AirtimeOver20) &&
115 dynamics()->velocity().y < -50.0f;
116
117 FUN_805B72B8(status.onBit(eStatus::InAction) ? 0.3f : 0.01f, fVar1, resetXZ,
118 status.offBit(eStatus::JumpPadDisableYsusForce));
119}
120
128void KartCollide::FUN_805B72B8(f32 param_1, f32 param_2, bool lockXZ, bool addExtVelY) {
129 const auto &colData = collisionData();
130
131 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
132 return;
133 }
134
135 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
136 collisionDir.normalise();
137
138 f32 directionalVelocity = colData.vel.dot(collisionDir);
139 if (directionalVelocity >= 0.0f) {
140 return;
141 }
142
143 EGG::Matrix34f rotMat;
144 EGG::Matrix34f rotMatTrans;
145
146 rotMat.makeQ(dynamics()->mainRot());
147 rotMatTrans = rotMat.transpose();
148 rotMat = rotMat.multiplyTo(dynamics()->invInertiaTensor()).multiplyTo(rotMatTrans);
149
150 EGG::Vector3f relPos = colData.relPos;
151 if (lockXZ) {
152 relPos.x = 0.0f;
153 relPos.z = 0.0f;
154 }
155
156 EGG::Vector3f step1 = relPos.cross(collisionDir);
157 EGG::Vector3f step2 = rotMat.multVector33(step1);
158 EGG::Vector3f step3 = step2.cross(relPos);
159 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.dot(step3));
160 EGG::Vector3f step4 = collisionDir.cross(-colData.vel);
161 EGG::Vector3f step5 = step4.cross(collisionDir);
162 step5.normalise();
163
164 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
165 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
166
167 f32 fVar3 = otherVal;
168 if (fVar1 < EGG::Mathf::abs(otherVal)) {
169 fVar3 = fVar1;
170 if (otherVal < 0.0f) {
171 fVar3 = -param_1 * EGG::Mathf::abs(val);
172 }
173 }
174
175 EGG::Vector3f step6 = val * collisionDir + fVar3 * step5;
176
177 f32 local_1d0 = step6.y;
178 if (!addExtVelY) {
179 local_1d0 = 0.0f;
180 } else if (colData.bFloor) {
181 f32 velY = intVel().y;
182 if (velY > 0.0f) {
183 velY += extVel().y;
184 if (velY < 0.0f) {
185 EGG::Vector3f newExtVel = extVel();
186 newExtVel.y = velY;
187 dynamics()->setExtVel(newExtVel);
188 }
189 }
190 }
191
192 f32 prevExtVelY = extVel().y;
193 EGG::Vector3f extVelAdd = step6;
194 extVelAdd.y = local_1d0;
195 dynamics()->setExtVel(extVel() + extVelAdd);
196
197 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
198 EGG::Vector3f extVelNoY = extVel();
199 extVelNoY.y = 0.0f;
200 dynamics()->setExtVel(extVelNoY);
201 }
202
203 EGG::Vector3f step7 = relPos.cross(step6);
204 EGG::Vector3f step8 = rotMat.multVector33(step7);
205 EGG::Vector3f step9 = mainRot().rotateVectorInv(step8);
206 step9.y = 0.0f;
207 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
208}
209
216void KartCollide::calcBodyCollision(f32 totalScale, f32 sinkDepth, const EGG::Quatf &rot,
217 const EGG::Vector3f &scale) {
218 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
219 CollisionData &collisionData = hitboxGroup->collisionData();
220 collisionData.reset();
221
222 EGG::Vector3f posRel = EGG::Vector3f::zero;
223 s32 count = 0;
224 Field::CollisionInfo colInfo;
225 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
226 Field::KCLTypeMask maskOut;
228 EGG::BoundBox3f minMax;
229 minMax.setZero();
230 bool bVar1 = false;
231
232 for (u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
233 Field::KCLTypeMask flags = KCL_TYPE_DRIVER_SOLID_SURFACE;
234 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
235
236 if (hitbox.bspHitbox()->wallsOnly != 0) {
237 flags = 0x4A109000;
238 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
239 }
240
241 hitbox.calc(totalScale, sinkDepth, scale, rot, pos());
242
243 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
244 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
245 if (!!(maskOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) {
246 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&maskOut,
248 }
249
250 if (!FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
251 bVar1 = true;
252
253 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
254 collisionData.bHasRoadVel = true;
255 collisionData.roadVelocity = colInfo.roadVelocity;
256 }
257
258 processBody(collisionData, hitbox, &colInfo, &maskOut);
259 }
260 }
261 }
262
263 if (bVar1) {
264 EGG::Vector3f movement = minMax.min + minMax.max;
265 applyBodyCollision(collisionData, movement, posRel, count);
266 } else {
267 collisionData.speedFactor = 1.0f;
268 collisionData.rotFactor = 1.0f;
269 }
270}
271
273void KartCollide::calcFloorEffect() {
274 if (status().onBit(eStatus::TouchingGround)) {
275 m_surfaceFlags.setBit(eSurfaceFlags::Offroad, eSurfaceFlags::GroundBoostPanelOrRamp);
276 }
277
278 m_suspBottomHeightNonSoftWall = 0.0f;
279 m_surfaceFlags.resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
280 eSurfaceFlags::Offroad, eSurfaceFlags::Trickable, eSurfaceFlags::NotTrickable,
281 eSurfaceFlags::StopHalfPipeState);
282 m_suspBottomHeightSoftWall = 0.0f;
283 m_someNonSoftWallTimer = 0;
284 m_someSoftWallTimer = 0;
285
286 Field::KCLTypeMask mask = KCL_NONE;
287 calcTriggers(&mask, pos(), false);
288
289 auto *colDir = Field::CollisionDirector::Instance();
290
291 if (m_solidOobTimer >= 3 && m_surfaceFlags.onBit(eSurfaceFlags::SolidOOB) &&
292 m_surfaceFlags.offBit(eSurfaceFlags::Wall)) {
293 if (mask & KCL_TYPE_BIT(COL_TYPE_SOLID_OOB)) {
294 colDir->findClosestCollisionEntry(&mask, KCL_TYPE_BIT(COL_TYPE_SOLID_OOB));
295 }
296
297 activateOob(true, &mask, false, false);
298 }
299
300 mask = KCL_NONE;
301 calcTriggers(&mask, pos(), true);
302
303 m_solidOobTimer =
304 m_surfaceFlags.onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
305
306 if (status().onBit(eStatus::Wall3Collision, eStatus::WallCollision)) {
307 Field::KCLTypeMask maskOut = KCL_NONE;
308
309 if (colDir->checkSphereCachedPartialPush(m_boundingRadius, pos(), EGG::Vector3f::inf,
310 KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY), nullptr, &maskOut, 0)) {
311 calcFallBoundary(&maskOut, true);
312 }
313 }
314}
315
317void KartCollide::calcTriggers(Field::KCLTypeMask *mask, const EGG::Vector3f &pos, bool twoPoint) {
318 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
319 Field::KCLTypeMask typeMask = twoPoint ? KCL_TYPE_DIRECTIONAL : KCL_TYPE_NON_DIRECTIONAL;
320 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
321 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
322 EGG::Vector3f scaledPos = pos + scalar * componentYAxis();
323 EGG::Vector3f back = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
324
325 m_smoothedBack += (back.dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
326
327 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
328 scaledPos += scalar * back;
329
330 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
331 scaledPos, v1, typeMask, nullptr, mask, 0);
332
333 if (!collide) {
334 return;
335 }
336
337 if (twoPoint) {
338 handleTriggers(mask);
339 } else {
340 if (*mask & KCL_TYPE_FLOOR) {
341 Field::CollisionDirector::Instance()->findClosestCollisionEntry(mask, KCL_TYPE_FLOOR);
342 }
343
344 if (*mask & KCL_TYPE_WALL) {
345 m_surfaceFlags.setBit(eSurfaceFlags::Wall);
346 }
347
348 if (*mask & KCL_TYPE_BIT(COL_TYPE_SOLID_OOB)) {
349 m_surfaceFlags.setBit(eSurfaceFlags::SolidOOB);
350 }
351 }
352}
353
355void KartCollide::handleTriggers(Field::KCLTypeMask *mask) {
356 calcFallBoundary(mask, false);
357 processCannon(mask);
358
360 auto *colDir = Field::CollisionDirector::Instance();
361 if (colDir->findClosestCollisionEntry(mask, KCL_TYPE_BIT(COL_TYPE_EFFECT_TRIGGER))) {
362 if (colDir->closestCollisionEntry()->variant() == 4) {
363 halfPipe()->end(true);
364 status().setBit(eStatus::EndHalfPipe);
365 m_surfaceFlags.setBit(eSurfaceFlags::StopHalfPipeState);
366 }
367 }
368 }
369}
370
372void KartCollide::calcFallBoundary(Field::KCLTypeMask *mask, bool shortBoundary) {
373 if (!(*mask & KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY))) {
374 return;
375 }
376
377 auto *colDir = Field::CollisionDirector::Instance();
378 if (!colDir->findClosestCollisionEntry(mask, KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY))) {
379 return;
380 }
381
382 bool safe = false;
383 const auto *entry = colDir->closestCollisionEntry();
384
385 if (shortBoundary) {
386 if (entry->variant() != 7) {
387 safe = true;
388 }
389 }
390
391 if (!safe) {
392 activateOob(false, mask, false, false);
393 }
394}
395
397void KartCollide::calcBeforeRespawn() {
398 if (pos().y < 0.0f) {
399 activateOob(true, nullptr, false, false);
400 }
401
402 auto &status = KartObjectProxy::status();
403
404 if (status.onBit(eStatus::BeforeRespawn)) {
405 if (--m_respawnTimer > 0) {
406 return;
407 }
408
410 m_respawnTimer = 0;
411 move()->triggerRespawn();
412 }
413
414 m_shrinkTimer = std::max(0, m_shrinkTimer - 1);
415}
416
418void KartCollide::activateOob(bool /*detachCamera*/, Field::KCLTypeMask * /*mask*/,
419 bool /*somethingCPU*/, bool /*somethingBullet*/) {
420 constexpr s16 RESPAWN_TIME = 130;
421
422 auto &status = KartObjectProxy::status();
423
424 if (status.onBit(eStatus::BeforeRespawn)) {
425 return;
426 }
427
428 move()->initOob();
429
430 m_respawnTimer = RESPAWN_TIME;
432}
433
442void KartCollide::calcWheelCollision(u16 /*wheelIdx*/, CollisionGroup *hitboxGroup,
443 const EGG::Vector3f &colVel, const EGG::Vector3f &center, f32 radius) {
444 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
445 BSP::Hitbox *bspHitbox = const_cast<BSP::Hitbox *>(firstHitbox.bspHitbox());
446 bspHitbox->radius = radius;
447 hitboxGroup->resetCollision();
448 firstHitbox.setWorldPos(center);
449
450 Field::CollisionInfo colInfo;
451 colInfo.bbox.setZero();
452 Field::KCLTypeMask kclOut;
454 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
455
456 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
457 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
458 KCL_TYPE_VEHICLE_COLLIDEABLE, &colInfo, &kclOut, 0);
459
460 CollisionData &collisionData = hitboxGroup->collisionData();
461
462 if (!collided) {
463 collisionData.speedFactor = 1.0f;
464 collisionData.rotFactor = 1.0f;
465 return;
466 }
467
468 collisionData.tangentOff = colInfo.tangentOff;
469
470 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
471 collisionData.tangentOff += noBounceWallInfo.tangentOff;
472 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
473 collisionData.bSoftWall = true;
474 }
475
476 if (kclOut & KCL_TYPE_FLOOR) {
477 collisionData.bFloor = true;
478 collisionData.floorNrm = colInfo.floorNrm;
479 }
480
481 collisionData.relPos = firstHitbox.worldPos() - pos();
482 collisionData.vel = colVel;
483
484 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
485 collisionData.bHasRoadVel = true;
486 collisionData.roadVelocity = colInfo.roadVelocity;
487 }
488
489 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
490
491 if (!(kclOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) {
492 return;
493 }
494
495 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&kclOut,
497}
498
502 Field::CollisionInfo *colInfo) {
503 if (colInfo->perpendicularity <= 0.0f) {
504 return;
505 }
506
507 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
508
509 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
510 return;
511 }
512
513 f32 bspPosX = hitbox.bspHitbox()->position.x;
514 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
515 if (bspPosX > 0.0f) {
516 collisionData.bWallAtLeftCloser = true;
517 } else {
518 collisionData.bWallAtRightCloser = true;
519 }
520
521 collisionData.colPerpendicularity = colInfo->perpendicularity;
522
523 return;
524 }
525
526 EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex);
527 std::array<f32, 2> tangents = {0.0f, 0.0f};
528
529 // The loop is just to do left/right wall
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;
534 Field::CollisionInfoPartial tempColInfo;
535
536 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
537 effectivePos, hitbox.lastPos(), KCL_TYPE_DRIVER_WALL, &tempColInfo, nullptr,
538 0)) {
539 tangents[i] = colInfo->tangentOff.squaredLength();
540 }
541 }
542
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;
549 }
550}
551
553void KartCollide::calcBoundingRadius() {
554 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
555}
556
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;
563
564 m_totalReactionWallNrm = EGG::Vector3f::zero;
565 m_surfaceFlags.resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
566
567 auto *objColKart = objectCollisionKart();
568 size_t collisionCount = objColKart->checkCollision(pose(), velocity());
569
570 const auto *objectDirector = Field::ObjectDirector::Instance();
571
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);
577
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) {
583 const EGG::Vector3f &hitDepth = objectDirector->hitDepth(i);
584 m_tangentOff += hitDepth;
585 m_movement += hitDepth;
586
587 if (newAction != Action::None) {
588 action()->setHitDepth(objectDirector->hitDepth(i));
589 action()->start(newAction);
590 }
591 }
592 }
593
594 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
595 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
596 const EGG::Vector3f &lastDir = move()->lastDir();
597
598 if (lastDir.dot(hitDirection) < -COS_PI_OVER_4) {
599 EGG::Vector3f angVel = hitDirection.cross(lastDir);
600 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
601
602 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
603 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
604 }
605 }
606 }
607
608 calcPoleTimer();
609}
610
612void KartCollide::calcPoleTimer() {
613 if (m_poleAngVelTimer > 0 && status().onBit(eStatus::Accelerate, eStatus::Brake)) {
614 EGG::Vector3f angVel2 = dynamics()->angVel2();
615 angVel2.y += m_poleYaw;
616 dynamics()->setAngVel2(angVel2);
617 }
618
619 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
620}
621
625void KartCollide::processWheel(CollisionData &collisionData, Hitbox &hitbox,
626 Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut) {
627 processMovingWater(collisionData, maskOut);
628 processFloor(collisionData, hitbox, colInfo, maskOut, true);
629}
630
632void KartCollide::processBody(CollisionData &collisionData, Hitbox &hitbox,
633 Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut) {
634 processMovingWater(collisionData, maskOut);
635
636 bool hasWallCollision = processWall(collisionData, maskOut);
637
638 processFloor(collisionData, hitbox, colInfo, maskOut, false);
639
640 if (hasWallCollision) {
641 calcSideCollision(collisionData, hitbox, colInfo);
642 }
643
644 processCannon(maskOut);
645}
646
648void KartCollide::processMovingWater(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
649 if (!(*maskOut & KCL_TYPE_BIT(COL_TYPE_MOVING_WATER))) {
650 return;
651 }
652
653 auto *colDir = Field::CollisionDirector::Instance();
654 if (!colDir->findClosestCollisionEntry(maskOut, KCL_TYPE_BIT(COL_TYPE_MOVING_WATER))) {
655 return;
656 }
657
658 state()->status().setBit(eStatus::StickyRoad);
659
660 auto *entry = colDir->closestCollisionEntry();
661 switch (entry->variant()) {
662 case 1:
663 collisionData.bMovingWaterMomentum = true;
664 collisionData.bMovingWaterStickyRoad = true;
665 collisionData.bMovingWaterDisableAccel = true;
666 break;
667 case 2:
668 collisionData.bMovingWaterDecaySpeed = true;
669 break;
670 case 3:
671 collisionData.bMovingWaterDecaySpeed = true;
672 collisionData.bMovingWaterDisableAccel = true;
673 collisionData.bMovingWaterVertical = true;
674 break;
675 default:
676 collisionData.bMovingWaterMomentum = true;
677 break;
678 }
679}
680
682bool KartCollide::processWall(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
683 if (!(*maskOut & KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2)) {
684 return false;
685 }
686
687 auto *colDirector = Field::CollisionDirector::Instance();
688 if (!colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2)) {
689 return false;
690 }
691
693 colDirector->findClosestCollisionEntry(maskOut,
695 auto *entry = colDirector->closestCollisionEntry();
696
697 collisionData.closestWallFlags = entry->baseType();
698 collisionData.closestWallSettings = entry->variant();
699
700 if (entry->attribute.onBit(Field::CollisionDirector::eCollisionAttribute::Soft)) {
701 collisionData.bSoftWall = true;
702 }
703 }
704
705 return true;
706}
707
714void KartCollide::processFloor(CollisionData &collisionData, Hitbox &hitbox,
715 Field::CollisionInfo * /*colInfo*/, Field::KCLTypeMask *maskOut, bool wheel) {
716 constexpr Field::KCLTypeMask BOOST_RAMP_MASK = KCL_TYPE_BIT(COL_TYPE_BOOST_RAMP);
717
718 if (collisionData.bSoftWall) {
719 ++m_someSoftWallTimer;
720 m_suspBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
721 }
722
723 if (!(*maskOut & KCL_TYPE_FLOOR)) {
724 return;
725 }
726
727 auto *colDirector = Field::CollisionDirector::Instance();
728
729 if (!colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_FLOOR)) {
730 return;
731 }
732
733 const auto *closestColEntry = colDirector->closestCollisionEntry();
734
735 if (closestColEntry->attribute.offBit(
736 Field::CollisionDirector::eCollisionAttribute::Trickable)) {
737 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
738 } else {
739 collisionData.bTrickable = true;
740 m_surfaceFlags.setBit(eSurfaceFlags::Trickable);
741 }
742
743 collisionData.speedFactor = std::min(collisionData.speedFactor,
744 param()->stats().kclSpeed[closestColEntry->baseType()]);
745
746 collisionData.intensity = closestColEntry->intensity();
747 collisionData.rotFactor += param()->stats().kclRot[closestColEntry->baseType()];
748
749 auto &status = KartObjectProxy::status();
750
751 if (closestColEntry->attribute.onBit(
752 Field::CollisionDirector::eCollisionAttribute::RejectRoad)) {
754 }
755
756 collisionData.closestFloorFlags = closestColEntry->typeMask;
757 collisionData.closestFloorSettings = closestColEntry->variant();
758
759 if (wheel && !!(*maskOut & KCL_TYPE_BIT(COL_TYPE_BOOST_PAD))) {
760 move()->padType().setBit(KartMove::ePadType::BoostPanel);
761 }
762
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);
769 } else {
770 state()->setBoostRampType(-1);
771 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
772 }
773
774 if (!collisionData.bSoftWall) {
775 ++m_someNonSoftWallTimer;
776 m_suspBottomHeightNonSoftWall += hitbox.worldPos().y - hitbox.radius();
777 }
778
779 if (*maskOut & KCL_TYPE_BIT(COL_TYPE_STICKY_ROAD)) {
781 }
782
783 Field::KCLTypeMask halfPipeRampMask = KCL_TYPE_BIT(COL_TYPE_HALFPIPE_RAMP);
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);
790 }
791 }
792
793 Field::KCLTypeMask jumpPadMask = KCL_TYPE_BIT(COL_TYPE_JUMP_PAD);
794 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
795 if (status.offAnyBit(eStatus::TouchingGround, eStatus::JumpPad) &&
796 status.offBit(eStatus::JumpPadMushroomVelYInc)) {
797 move()->padType().setBit(KartMove::ePadType::JumpPad);
798 closestColEntry = colDirector->closestCollisionEntry();
799 state()->setJumpPadVariant(closestColEntry->variant());
800 }
801 collisionData.bTrickable = true;
802 }
803}
804
807void KartCollide::processCannon(Field::KCLTypeMask *maskOut) {
808 auto *colDirector = Field::CollisionDirector::Instance();
809 if (colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_BIT(COL_TYPE_CANNON_TRIGGER))) {
810 state()->setCannonPointId(colDirector->closestCollisionEntry()->variant());
811 status().setBit(eStatus::CannonStart);
812 }
813}
814
824void KartCollide::applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup,
825 const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed,
826 bool b1, bool b2, bool b3) {
827 CollisionData &colData = hitboxGroup->collisionData();
828 if (!colData.bFloor) {
829 return;
830 }
831
832 f32 velDotFloorNrm = colData.vel.dot(colData.floorNrm);
833
834 if (velDotFloorNrm >= 0.0f) {
835 return;
836 }
837
838 EGG::Matrix34f rotMat;
839 rotMat.makeQ(dynamics()->mainRot());
840 EGG::Matrix34f tmp = rotMat.multiplyTo(dynamics()->invInertiaTensor());
841 EGG::Matrix34f rotMatTrans = rotMat.transpose();
842 tmp = tmp.multiplyTo(rotMatTrans);
843
844 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
845 crossVec = tmp.multVector(crossVec);
846 crossVec = crossVec.cross(colData.relPos);
847
848 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.dot(crossVec));
849 EGG::Vector3f negSpeed = -speed;
850 crossVec = colData.floorNrm.cross(negSpeed);
851 crossVec = crossVec.cross(colData.floorNrm);
852
853 if (std::numeric_limits<f32>::epsilon() >= crossVec.squaredLength()) {
854 return;
855 }
856
857 crossVec.normalise();
858 f32 speedDot = std::min(0.0f, speed.dot(crossVec));
859 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
860
861 auto [proj, rej] = crossVec.projAndRej(forward);
862
863 f32 projNorm = proj.length();
864 f32 rejNorm = rej.length();
865 f32 projNorm_ = projNorm;
866 f32 rejNorm_ = rejNorm;
867
868 f32 dVar7 = down * EGG::Mathf::abs(scalar);
869 if (dVar7 < EGG::Mathf::abs(projNorm)) {
870 projNorm_ = dVar7;
871 if (projNorm < 0.0f) {
872 projNorm_ = -down * EGG::Mathf::abs(scalar);
873 }
874 }
875
876 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
877 if (EGG::Mathf::abs(rejNorm) > dVar5) {
878 rejNorm_ = dVar5;
879 if (rejNorm < 0.0f) {
880 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
881 }
882 }
883
884 proj.normalise();
885 rej.normalise();
886
887 proj *= projNorm_;
888 rej *= rejNorm_;
889
890 EGG::Vector3f projRejSum = proj + rej;
891 EGG::Vector3f projRejSumOrig = projRejSum;
892
893 if (!b1) {
894 projRejSum.x = 0.0f;
895 projRejSum.z = 0.0f;
896 }
897 if (!b2) {
898 projRejSum.y = 0.0f;
899 }
900
901 projRejSum = projRejSum.rej(nextDir);
902
903 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
904
905 if (b3) {
906 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
907 EGG::Vector3f rotation2 = dynamics()->mainRot().rotateVectorInv(tmp.multVector(rotation));
908
909 EGG::Vector3f angVel = rotation2;
910 angVel.y = 0.0f;
911 if (!b1) {
912 angVel.x = 0.0f;
913 }
914 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
915 }
916}
917
922bool KartCollide::FUN_805B6A9C(CollisionData &collisionData, const Hitbox &hitbox,
923 EGG::BoundBox3f &minMax, EGG::Vector3f &relPos, s32 &count,
924 const Field::KCLTypeMask &maskOut, const Field::CollisionInfo &colInfo) {
925 if (maskOut & KCL_TYPE_WALL) {
926 if (!(maskOut & KCL_TYPE_FLOOR) && status().onBit(eStatus::HWG) &&
927 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
928 return true;
929 }
930
931 bool skipWalls = false;
932
933 collisionData.wallNrm += colInfo.wallNrm;
934
935 if (maskOut & KCL_TYPE_ANY_INVISIBLE_WALL) {
936 collisionData.bInvisibleWall = true;
937
938 if (!(maskOut & KCL_TYPE_4010D000)) {
939 collisionData.bInvisibleWallOnly = true;
940
942 skipWalls = true;
943 }
944 }
945 }
946
947 if (!skipWalls) {
948 if (maskOut & KCL_TYPE_BIT(COL_TYPE_WALL_2)) {
949 collisionData.bWall3 = true;
950 } else {
951 collisionData.bWall = true;
952 }
953 }
954 }
955
956 if (maskOut & KCL_TYPE_FLOOR) {
957 collisionData.floorNrm += colInfo.floorNrm;
958 collisionData.bFloor = true;
959 }
960
961 EGG::Vector3f tangentOff = colInfo.tangentOff;
962 minMax.min = minMax.min.minimize(tangentOff);
963 minMax.max = minMax.max.maximize(tangentOff);
964 tangentOff.normalise();
965
966 relPos += hitbox.relPos();
967 relPos += -hitbox.radius() * tangentOff;
968 ++count;
969
970 return false;
971}
972
978 const EGG::Vector3f &posRel, s32 count) {
979 setPos(pos() + movement);
980
981 if (!collisionData.bFloor && (collisionData.bWall || collisionData.bWall3)) {
982 collisionData.movement = movement;
983 }
984
985 f32 rotFactor = 1.0f / static_cast<f32>(count);
986 EGG::Vector3f scaledRelPos = rotFactor * posRel;
987 collisionData.rotFactor *= rotFactor;
988
989 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
990 EGG::Vector3f local_48 = mainRot().rotateVectorInv(scaledRelPos);
991 EGG::Vector3f local_30 = scaledAngVel0.cross(local_48);
992 local_30 = mainRot().rotateVector(local_30);
993 local_30 += extVel();
994
995 collisionData.vel = local_30;
996 collisionData.relPos = scaledRelPos;
997
998 if (collisionData.bFloor) {
999 f32 intVelY = dynamics()->intVel().y;
1000 if (intVelY > 0.0f) {
1001 collisionData.vel.y += intVelY;
1002 }
1003 collisionData.floorNrm.normalise();
1004 }
1005}
1006
1008void KartCollide::startFloorMomentRate() {
1009 m_floorMomentRate = 0.01f;
1010}
1011
1013void KartCollide::calcFloorMomentRate() {
1014 m_floorMomentRate = status().onBit(eStatus::InAction) &&
1015 action()->flags().onBit(KartAction::eFlags::Rotating) ?
1016 0.01f :
1017 std::min(m_floorMomentRate + 0.01f, 0.8f);
1018}
1019
1021Action KartCollide::handleReactNone(size_t /*idx*/) {
1022 return Action::None;
1023}
1024
1026Action KartCollide::handleReactWallAllSpeed(size_t idx) {
1027 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1028 m_surfaceFlags.setBit(eSurfaceFlags::ObjectWall);
1029
1030 return Action::None;
1031}
1032
1034Action KartCollide::handleReactSpinAllSpeed(size_t /*idx*/) {
1035 return Action::UNK_0;
1036}
1037
1039Action KartCollide::handleReactSpinSomeSpeed(size_t /*idx*/) {
1040 return Action::UNK_1;
1041}
1042
1044Action KartCollide::handleReactFireSpin(size_t /*idx*/) {
1045 return Action::UNK_9;
1046}
1047
1049Action KartCollide::handleReactSmallLaunch(size_t /*idx*/) {
1050 return Action::UNK_2;
1051}
1052
1054Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(size_t /*idx*/) {
1055 return Action::UNK_3;
1056}
1057
1059Action KartCollide::handleReactLaunchSpinLoseItem(size_t /*idx*/) {
1060 return Action::UNK_6;
1061}
1062
1064Action KartCollide::handleReactKnockbackBumpLoseItem(size_t /*idx*/) {
1065 return Action::UNK_4;
1066}
1067
1069Action KartCollide::handleReactLongCrushLoseItem(size_t /*idx*/) {
1070 return Action::UNK_12;
1071}
1072
1074Action KartCollide::handleReactSmallBump(size_t idx) {
1075 move()->applyForce(30.0f, objectCollisionKart()->GetHitDirection(idx), false);
1076 return Action::None;
1077}
1078
1080Action KartCollide::handleReactSpinShrink(size_t /*idx*/) {
1081 return m_shrinkTimer <= 0 ? Action::UNK_15 : Action::None;
1082}
1083
1085Action KartCollide::handleReactHighLaunchLoseItem(size_t /*idx*/) {
1086 return Action::UNK_8;
1087}
1088
1090Action KartCollide::handleReactWeakWall(size_t /*idx*/) {
1091 move()->setSpeed(move()->speed() * 0.82f);
1092 return Action::None;
1093}
1094
1096Action KartCollide::handleReactOffroad(size_t /*idx*/) {
1097 status().setBit(eStatus::CollidingOffroad);
1098 m_surfaceFlags.setBit(eSurfaceFlags::Offroad);
1099 return Action::None;
1100}
1101
1103Action KartCollide::handleReactLaunchSpin(size_t idx) {
1104 action()->setTranslation(objectCollisionKart()->translation(idx));
1105 return Action::UNK_5;
1106}
1107
1109Action KartCollide::handleReactWallSpark(size_t idx) {
1110 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1111 m_surfaceFlags.setBit(eSurfaceFlags::ObjectWall3);
1112
1113 return Action::None;
1114}
1115
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;
1121
1122 const EGG::Vector3f &hitDir = Field::ObjectCollisionKart::GetHitDirection(idx);
1123 const EGG::Vector3f &zAxis = componentZAxis();
1124
1125 f32 force = BASE_DIR_FORCE_SCALAR + DIR_FORCE_SCALAR * EGG::Mathf::abs(hitDir.dot(zAxis));
1126 move()->applyForce(MAX_FORCE * force, hitDir, true);
1127
1128 return Action::None;
1129}
1130
1132Action KartCollide::handleReactUntrickableJumpPad(size_t /*idx*/) {
1133 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1134 state()->setJumpPadVariant(0);
1135
1136 return Action::None;
1137}
1138
1140Action KartCollide::handleReactShortCrushLoseItem(size_t /*idx*/) {
1141 return Action::UNK_14;
1142}
1143
1145Action KartCollide::handleReactCrushRespawn(size_t /*idx*/) {
1146 return Action::UNK_16;
1147}
1148
1150Action KartCollide::handleReactExplosionLoseItem(size_t /*idx*/) {
1151 return Action::UNK_7;
1152}
1153
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,
1188}};
1189
1190} // namespace Kart
#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_BIT(x)
#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
A 3 x 4 matrix.
Definition Matrix.hh:8
Matrix34f multiplyTo(const Matrix34f &rhs) const
Multiplies two matrices.
Definition Matrix.cc:202
void makeQ(const Quatf &q)
Sets rotation matrix from quaternion.
Definition Matrix.cc:41
Vector3f multVector33(const Vector3f &vec) const
Multiplies a 3x3 matrix by a vector.
Definition Matrix.cc:249
Vector3f multVector(const Vector3f &vec) const
Multiplies a vector by a matrix.
Definition Matrix.cc:225
Matrix34f transpose() const
Transposes the 3x3 portion of the matrix.
Definition Matrix.cc:353
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
Definition BitFlag.hh:412
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
Definition BitFlag.hh:379
constexpr TBitFlagExt< N, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
Definition BitFlag.hh:355
constexpr bool offAnyBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are off.
Definition BitFlag.hh:434
constexpr TBitFlagExt< N, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
Definition BitFlag.hh:344
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.
Definition KartAction.cc:49
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 &center, 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.
Definition BoundBox.hh:30
A quaternion, used to represent 3D rotation.
Definition Quat.hh:12
Vector3f rotateVector(const Vector3f &vec) const
Rotates a vector based on the quat.
Definition Quat.cc:55
Vector3f rotateVectorInv(const Vector3f &vec) const
Rotates a vector on the inverse quat.
Definition Quat.cc:69
constexpr TBitFlag< T, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
Definition BitFlag.hh:73
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
Definition BitFlag.hh:108
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
Definition BitFlag.hh:141
constexpr void makeAllZero()
Resets all the bits to zero.
Definition BitFlag.hh:234
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
Definition BitFlag.hh:62
A 3D float vector.
Definition Vector.hh:88
f32 normalise()
Normalizes the vector and returns the original length.
Definition Vector.cc:52
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
Definition Vector.hh:187
f32 squaredLength() const
The dot product between the vector and itself.
Definition Vector.hh:182
Vector3f rej(const Vector3f &rhs) const
The rejection of this vector onto rhs.
Definition Vector.hh:210
Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Definition Vector.cc:83
Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
Definition Vector.cc:95
Represents one of the many hitboxes that make up a vehicle.
Definition KartParam.hh:10
EGG::Vector3f position
The relative position of the hitbox.
Definition KartParam.hh:12
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.
Definition KartParam.hh:113