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 calcBoundingRadius();
29 m_floorMomentRate = 0.8f;
30 m_surfaceFlags.makeAllZero();
31 m_respawnTimer = 0;
32 m_solidOobTimer = 0;
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;
39 m_poleYaw = 0.0f;
40 m_colPerpendicularity = 0.0f;
41}
42
44void KartCollide::resetHitboxes() {
45 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
46 for (u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
47 hitboxGroup->hitbox(idx).setLastPos(scale(), pose());
48 }
49}
50
55 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
56 for (u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) {
57 hitboxGroup->hitbox(idx).calc(move()->totalScale(), body()->sinkDepth(), scale(), fullRot(),
58 pos());
59 }
60}
61
65 bool wasHalfPipe = status().onBit(eStatus::EndHalfPipe, eStatus::ActionMidZipper);
66 const EGG::Quatf &rot = wasHalfPipe ? mainRot() : fullRot();
67 calcBodyCollision(move()->totalScale(), body()->sinkDepth(), rot, scale());
68
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)) {
77 colData.bWall = true;
78 } else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
79 colData.bWall3 = true;
80 }
81 } else if (newWallCollision) {
82 colData.wallNrm += m_totalReactionWallNrm;
83 if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall)) {
84 colData.bWall = true;
85 } else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
86 colData.bWall3 = true;
87 }
88 }
89
90 colData.wallNrm.normalise();
91 }
92
94}
95
100 f32 fVar1;
101
102 auto &status = KartObjectProxy::status();
103
104 if (isInRespawn() ||
106 eStatus::NoSparkInvisibleWall, eStatus::HalfPipeRamp)) {
107 fVar1 = 0.0f;
108 } else {
109 fVar1 = 0.05f;
110 }
111
112 bool resetXZ = fVar1 > 0.0f && status.onBit(eStatus::AirtimeOver20) &&
113 dynamics()->velocity().y < -50.0f;
114
115 FUN_805B72B8(status.onBit(eStatus::InAction) ? 0.3f : 0.01f, fVar1, resetXZ,
116 status.offBit(eStatus::JumpPadDisableYsusForce));
117}
118
126void KartCollide::FUN_805B72B8(f32 param_1, f32 param_2, bool lockXZ, bool addExtVelY) {
127 const auto &colData = collisionData();
128
129 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
130 return;
131 }
132
133 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
134 collisionDir.normalise();
135
136 f32 directionalVelocity = colData.vel.dot(collisionDir);
137 if (directionalVelocity >= 0.0f) {
138 return;
139 }
140
141 EGG::Matrix34f rotMat;
142 EGG::Matrix34f rotMatTrans;
143
144 rotMat.makeQ(dynamics()->mainRot());
145 rotMatTrans = rotMat.transpose();
146 rotMat = rotMat.multiplyTo(dynamics()->invInertiaTensor()).multiplyTo(rotMatTrans);
147
148 EGG::Vector3f relPos = colData.relPos;
149 if (lockXZ) {
150 relPos.x = 0.0f;
151 relPos.z = 0.0f;
152 }
153
154 EGG::Vector3f step1 = relPos.cross(collisionDir);
155 EGG::Vector3f step2 = rotMat.multVector33(step1);
156 EGG::Vector3f step3 = step2.cross(relPos);
157 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.dot(step3));
158 EGG::Vector3f step4 = collisionDir.cross(-colData.vel);
159 EGG::Vector3f step5 = step4.cross(collisionDir);
160 step5.normalise();
161
162 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
163 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
164
165 f32 fVar3 = otherVal;
166 if (fVar1 < EGG::Mathf::abs(otherVal)) {
167 fVar3 = fVar1;
168 if (otherVal < 0.0f) {
169 fVar3 = -param_1 * EGG::Mathf::abs(val);
170 }
171 }
172
173 EGG::Vector3f step6 = val * collisionDir + fVar3 * step5;
174
175 f32 local_1d0 = step6.y;
176 if (!addExtVelY) {
177 local_1d0 = 0.0f;
178 } else if (colData.bFloor) {
179 f32 velY = intVel().y;
180 if (velY > 0.0f) {
181 velY += extVel().y;
182 if (velY < 0.0f) {
183 EGG::Vector3f newExtVel = extVel();
184 newExtVel.y = velY;
185 dynamics()->setExtVel(newExtVel);
186 }
187 }
188 }
189
190 f32 prevExtVelY = extVel().y;
191 EGG::Vector3f extVelAdd = step6;
192 extVelAdd.y = local_1d0;
193 dynamics()->setExtVel(extVel() + extVelAdd);
194
195 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
196 EGG::Vector3f extVelNoY = extVel();
197 extVelNoY.y = 0.0f;
198 dynamics()->setExtVel(extVelNoY);
199 }
200
201 EGG::Vector3f step7 = relPos.cross(step6);
202 EGG::Vector3f step8 = rotMat.multVector33(step7);
203 EGG::Vector3f step9 = mainRot().rotateVectorInv(step8);
204 step9.y = 0.0f;
205 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
206}
207
214void KartCollide::calcBodyCollision(f32 totalScale, f32 sinkDepth, const EGG::Quatf &rot,
215 const EGG::Vector3f &scale) {
216 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
217 CollisionData &collisionData = hitboxGroup->collisionData();
218 collisionData.reset();
219
220 EGG::Vector3f posRel = EGG::Vector3f::zero;
221 s32 count = 0;
222 Field::CollisionInfo colInfo;
223 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
224 Field::KCLTypeMask maskOut;
226 EGG::BoundBox3f minMax;
227 minMax.setZero();
228 bool bVar1 = false;
229
230 for (u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
231 Field::KCLTypeMask flags = KCL_TYPE_DRIVER_SOLID_SURFACE;
232 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
233
234 if (hitbox.bspHitbox()->wallsOnly != 0) {
235 flags = 0x4A109000;
236 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
237 }
238
239 hitbox.calc(totalScale, sinkDepth, scale, rot, pos());
240
241 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
242 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
243 if (!!(maskOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) {
244 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&maskOut,
246 }
247
248 if (!FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
249 bVar1 = true;
250
251 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
252 collisionData.bHasRoadVel = true;
253 collisionData.roadVelocity = colInfo.roadVelocity;
254 }
255
256 processBody(collisionData, hitbox, &colInfo, &maskOut);
257 }
258 }
259 }
260
261 if (bVar1) {
262 EGG::Vector3f movement = minMax.min + minMax.max;
263 applyBodyCollision(collisionData, movement, posRel, count);
264 } else {
265 collisionData.speedFactor = 1.0f;
266 collisionData.rotFactor = 1.0f;
267 }
268}
269
271void KartCollide::calcFloorEffect() {
272 if (status().onBit(eStatus::TouchingGround)) {
273 m_surfaceFlags.setBit(eSurfaceFlags::Offroad, eSurfaceFlags::GroundBoostPanelOrRamp);
274 }
275
276 m_suspBottomHeightNonSoftWall = 0.0f;
277 m_surfaceFlags.resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
278 eSurfaceFlags::Offroad, eSurfaceFlags::Trickable, eSurfaceFlags::NotTrickable,
279 eSurfaceFlags::StopHalfPipeState);
280 m_suspBottomHeightSoftWall = 0.0f;
281 m_someNonSoftWallTimer = 0;
282 m_someSoftWallTimer = 0;
283
284 Field::KCLTypeMask mask = KCL_NONE;
285 calcTriggers(&mask, pos(), false);
286
287 auto *colDir = Field::CollisionDirector::Instance();
288
289 if (m_solidOobTimer >= 3 && m_surfaceFlags.onBit(eSurfaceFlags::SolidOOB) &&
290 m_surfaceFlags.offBit(eSurfaceFlags::Wall)) {
291 if (mask & KCL_TYPE_BIT(COL_TYPE_SOLID_OOB)) {
292 colDir->findClosestCollisionEntry(&mask, KCL_TYPE_BIT(COL_TYPE_SOLID_OOB));
293 }
294
295 activateOob(true, &mask, false, false);
296 }
297
298 mask = KCL_NONE;
299 calcTriggers(&mask, pos(), true);
300
301 m_solidOobTimer =
302 m_surfaceFlags.onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
303
304 if (status().onBit(eStatus::Wall3Collision, eStatus::WallCollision)) {
305 Field::KCLTypeMask maskOut = KCL_NONE;
306
307 if (colDir->checkSphereCachedPartialPush(m_boundingRadius, pos(), EGG::Vector3f::inf,
308 KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY), nullptr, &maskOut, 0)) {
309 calcFallBoundary(&maskOut, true);
310 }
311 }
312}
313
315void KartCollide::calcTriggers(Field::KCLTypeMask *mask, const EGG::Vector3f &pos, bool twoPoint) {
316 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
317 Field::KCLTypeMask typeMask = twoPoint ? KCL_TYPE_DIRECTIONAL : KCL_TYPE_NON_DIRECTIONAL;
318 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
319 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
320 EGG::Vector3f scaledPos = pos + scalar * componentYAxis();
321 EGG::Vector3f back = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
322
323 m_smoothedBack += (back.dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
324
325 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
326 scaledPos += scalar * back;
327
328 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
329 scaledPos, v1, typeMask, nullptr, mask, 0);
330
331 if (!collide) {
332 return;
333 }
334
335 if (twoPoint) {
336 handleTriggers(mask);
337 } else {
338 if (*mask & KCL_TYPE_FLOOR) {
339 Field::CollisionDirector::Instance()->findClosestCollisionEntry(mask, KCL_TYPE_FLOOR);
340 }
341
342 if (*mask & KCL_TYPE_WALL) {
343 m_surfaceFlags.setBit(eSurfaceFlags::Wall);
344 }
345
346 if (*mask & KCL_TYPE_BIT(COL_TYPE_SOLID_OOB)) {
347 m_surfaceFlags.setBit(eSurfaceFlags::SolidOOB);
348 }
349 }
350}
351
353void KartCollide::handleTriggers(Field::KCLTypeMask *mask) {
354 calcFallBoundary(mask, false);
355 processCannon(mask);
356
358 auto *colDir = Field::CollisionDirector::Instance();
359 if (colDir->findClosestCollisionEntry(mask, KCL_TYPE_BIT(COL_TYPE_EFFECT_TRIGGER))) {
360 if (colDir->closestCollisionEntry()->variant() == 4) {
361 halfPipe()->end(true);
362 status().setBit(eStatus::EndHalfPipe);
363 m_surfaceFlags.setBit(eSurfaceFlags::StopHalfPipeState);
364 }
365 }
366 }
367}
368
370void KartCollide::calcFallBoundary(Field::KCLTypeMask *mask, bool shortBoundary) {
371 if (!(*mask & KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY))) {
372 return;
373 }
374
375 auto *colDir = Field::CollisionDirector::Instance();
376 if (!colDir->findClosestCollisionEntry(mask, KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY))) {
377 return;
378 }
379
380 bool safe = false;
381 const auto *entry = colDir->closestCollisionEntry();
382
383 if (shortBoundary) {
384 if (entry->variant() != 7) {
385 safe = true;
386 }
387 }
388
389 if (!safe) {
390 activateOob(false, mask, false, false);
391 }
392}
393
395void KartCollide::calcBeforeRespawn() {
396 if (pos().y < 0.0f) {
397 activateOob(true, nullptr, false, false);
398 }
399
400 auto &status = KartObjectProxy::status();
401
402 if (status.offBit(eStatus::BeforeRespawn)) {
403 return;
404 }
405
406 if (--m_respawnTimer > 0) {
407 return;
408 }
409
411 m_respawnTimer = 0;
412 move()->triggerRespawn();
413}
414
416void KartCollide::activateOob(bool /*detachCamera*/, Field::KCLTypeMask * /*mask*/,
417 bool /*somethingCPU*/, bool /*somethingBullet*/) {
418 constexpr s16 RESPAWN_TIME = 130;
419
420 auto &status = KartObjectProxy::status();
421
422 if (status.onBit(eStatus::BeforeRespawn)) {
423 return;
424 }
425
426 move()->initOob();
427
428 m_respawnTimer = RESPAWN_TIME;
430}
431
440void KartCollide::calcWheelCollision(u16 /*wheelIdx*/, CollisionGroup *hitboxGroup,
441 const EGG::Vector3f &colVel, const EGG::Vector3f &center, f32 radius) {
442 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
443 BSP::Hitbox *bspHitbox = const_cast<BSP::Hitbox *>(firstHitbox.bspHitbox());
444 bspHitbox->radius = radius;
445 hitboxGroup->resetCollision();
446 firstHitbox.setWorldPos(center);
447
448 Field::CollisionInfo colInfo;
449 colInfo.bbox.setZero();
450 Field::KCLTypeMask kclOut;
452 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
453
454 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
455 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
456 KCL_TYPE_VEHICLE_COLLIDEABLE, &colInfo, &kclOut, 0);
457
458 CollisionData &collisionData = hitboxGroup->collisionData();
459
460 if (!collided) {
461 collisionData.speedFactor = 1.0f;
462 collisionData.rotFactor = 1.0f;
463 return;
464 }
465
466 collisionData.tangentOff = colInfo.tangentOff;
467
468 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
469 collisionData.tangentOff += noBounceWallInfo.tangentOff;
470 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
471 collisionData.bSoftWall = true;
472 }
473
474 if (kclOut & KCL_TYPE_FLOOR) {
475 collisionData.bFloor = true;
476 collisionData.floorNrm = colInfo.floorNrm;
477 }
478
479 collisionData.relPos = firstHitbox.worldPos() - pos();
480 collisionData.vel = colVel;
481
482 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
483 collisionData.bHasRoadVel = true;
484 collisionData.roadVelocity = colInfo.roadVelocity;
485 }
486
487 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
488
489 if (!(kclOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) {
490 return;
491 }
492
493 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&kclOut,
495}
496
500 Field::CollisionInfo *colInfo) {
501 if (colInfo->perpendicularity <= 0.0f) {
502 return;
503 }
504
505 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
506
507 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
508 return;
509 }
510
511 f32 bspPosX = hitbox.bspHitbox()->position.x;
512 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
513 if (bspPosX > 0.0f) {
514 collisionData.bWallAtLeftCloser = true;
515 } else {
516 collisionData.bWallAtRightCloser = true;
517 }
518
519 collisionData.colPerpendicularity = colInfo->perpendicularity;
520
521 return;
522 }
523
524 EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex);
525 std::array<f32, 2> tangents = {0.0f, 0.0f};
526
527 // The loop is just to do left/right wall
528 for (size_t i = 0; i < tangents.size(); ++i) {
529 f32 sign = i == 1 ? -1.0f : 1.0f;
530 f32 effectiveRadius = sign * hitbox.radius();
531 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
532 Field::CollisionInfoPartial tempColInfo;
533
534 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
535 effectivePos, hitbox.lastPos(), KCL_TYPE_DRIVER_WALL, &tempColInfo, nullptr,
536 0)) {
537 tangents[i] = colInfo->tangentOff.squaredLength();
538 }
539 }
540
541 if (tangents[0] > tangents[1]) {
542 collisionData.bWallAtLeftCloser = true;
543 collisionData.colPerpendicularity = colInfo->perpendicularity;
544 } else if (tangents[1] > tangents[0]) {
545 collisionData.bWallAtRightCloser = true;
546 collisionData.colPerpendicularity = colInfo->perpendicularity;
547 }
548}
549
551void KartCollide::calcBoundingRadius() {
552 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
553}
554
556void KartCollide::calcObjectCollision() {
557 constexpr f32 COS_PI_OVER_4 = 0.707f;
558 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
559 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
560
561 m_totalReactionWallNrm = EGG::Vector3f::zero;
562 m_surfaceFlags.resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
563
564 size_t collisionCount = objectCollisionKart()->checkCollision(pose(), velocity());
565
566 const auto *objectDirector = Field::ObjectDirector::Instance();
567
568 for (size_t i = 0; i < collisionCount; ++i) {
569 Reaction reaction = objectDirector->reaction(i);
570 if (reaction != Reaction::None && reaction != Reaction::UNK_7) {
571 size_t handlerIdx = static_cast<std::underlying_type_t<Reaction>>(reaction);
572 Action newAction = (this->*s_objectCollisionHandlers[handlerIdx])(i);
573 if (newAction != Action::None) {
574 action()->setHitDepth(objectDirector->hitDepth(i));
575 action()->start(newAction);
576 }
577
578 if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
579 const EGG::Vector3f &hitDepth = objectDirector->hitDepth(i);
580 m_tangentOff += hitDepth;
581 m_movement += hitDepth;
582 }
583 }
584
585 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
586 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
587 const EGG::Vector3f &lastDir = move()->lastDir();
588
589 if (lastDir.dot(hitDirection) < -COS_PI_OVER_4) {
590 EGG::Vector3f angVel = hitDirection.cross(lastDir);
591 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
592
593 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
594 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
595 }
596 }
597 }
598
599 calcPoleTimer();
600}
601
603void KartCollide::calcPoleTimer() {
604 if (m_poleAngVelTimer > 0 && status().onBit(eStatus::Accelerate, eStatus::Brake)) {
605 EGG::Vector3f angVel2 = dynamics()->angVel2();
606 angVel2.y += m_poleYaw;
607 dynamics()->setAngVel2(angVel2);
608 }
609
610 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
611}
612
616void KartCollide::processWheel(CollisionData &collisionData, Hitbox &hitbox,
617 Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut) {
618 processFloor(collisionData, hitbox, colInfo, maskOut, true);
619}
620
622void KartCollide::processBody(CollisionData &collisionData, Hitbox &hitbox,
623 Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut) {
624 bool hasWallCollision = processWall(collisionData, maskOut);
625
626 processFloor(collisionData, hitbox, colInfo, maskOut, false);
627
628 if (hasWallCollision) {
629 calcSideCollision(collisionData, hitbox, colInfo);
630 }
631
632 processCannon(maskOut);
633}
634
636bool KartCollide::processWall(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
637 if (!(*maskOut & KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2)) {
638 return false;
639 }
640
641 auto *colDirector = Field::CollisionDirector::Instance();
642 if (!colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2)) {
643 return false;
644 }
645
647 colDirector->findClosestCollisionEntry(maskOut,
649 auto *entry = colDirector->closestCollisionEntry();
650
651 collisionData.closestWallFlags = entry->baseType();
652 collisionData.closestWallSettings = entry->variant();
653
654 if (entry->attribute.onBit(Field::CollisionDirector::eCollisionAttribute::Soft)) {
655 collisionData.bSoftWall = true;
656 }
657 }
658
659 return true;
660}
661
668void KartCollide::processFloor(CollisionData &collisionData, Hitbox &hitbox,
669 Field::CollisionInfo * /*colInfo*/, Field::KCLTypeMask *maskOut, bool wheel) {
670 constexpr Field::KCLTypeMask BOOST_RAMP_MASK = KCL_TYPE_BIT(COL_TYPE_BOOST_RAMP);
671
672 if (collisionData.bSoftWall) {
673 ++m_someSoftWallTimer;
674 m_suspBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
675 }
676
677 if (!(*maskOut & KCL_TYPE_FLOOR)) {
678 return;
679 }
680
681 auto *colDirector = Field::CollisionDirector::Instance();
682
683 if (!colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_FLOOR)) {
684 return;
685 }
686
687 const auto *closestColEntry = colDirector->closestCollisionEntry();
688
689 if (closestColEntry->attribute.offBit(
690 Field::CollisionDirector::eCollisionAttribute::Trickable)) {
691 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
692 } else {
693 collisionData.bTrickable = true;
694 m_surfaceFlags.setBit(eSurfaceFlags::Trickable);
695 }
696
697 collisionData.speedFactor = std::min(collisionData.speedFactor,
698 param()->stats().kclSpeed[closestColEntry->baseType()]);
699
700 collisionData.intensity = closestColEntry->intensity();
701 collisionData.rotFactor += param()->stats().kclRot[closestColEntry->baseType()];
702
703 auto &status = KartObjectProxy::status();
704
705 if (closestColEntry->attribute.onBit(
706 Field::CollisionDirector::eCollisionAttribute::RejectRoad)) {
708 }
709
710 collisionData.closestFloorFlags = closestColEntry->typeMask;
711 collisionData.closestFloorSettings = closestColEntry->variant();
712
713 if (wheel && !!(*maskOut & KCL_TYPE_BIT(COL_TYPE_BOOST_PAD))) {
714 move()->padType().setBit(KartMove::ePadType::BoostPanel);
715 }
716
717 if (!!(*maskOut & BOOST_RAMP_MASK) &&
718 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
719 closestColEntry = colDirector->closestCollisionEntry();
720 move()->padType().setBit(KartMove::ePadType::BoostRamp);
721 state()->setBoostRampType(closestColEntry->variant());
722 m_surfaceFlags.setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
723 } else {
724 state()->setBoostRampType(-1);
725 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
726 }
727
728 if (!collisionData.bSoftWall) {
729 ++m_someNonSoftWallTimer;
730 m_suspBottomHeightNonSoftWall += hitbox.worldPos().y - hitbox.radius();
731 }
732
733 if (*maskOut & KCL_TYPE_BIT(COL_TYPE_STICKY_ROAD)) {
735 }
736
737 Field::KCLTypeMask halfPipeRampMask = KCL_TYPE_BIT(COL_TYPE_HALFPIPE_RAMP);
738 if ((*maskOut & halfPipeRampMask) &&
739 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
741 state()->setHalfPipeInvisibilityTimer(2);
742 if (colDirector->closestCollisionEntry()->variant() == 1) {
743 move()->padType().setBit(KartMove::ePadType::BoostPanel);
744 }
745 }
746
747 Field::KCLTypeMask jumpPadMask = KCL_TYPE_BIT(COL_TYPE_JUMP_PAD);
748 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
749 if (status.offAnyBit(eStatus::TouchingGround, eStatus::JumpPad) &&
750 status.offBit(eStatus::JumpPadMushroomVelYInc)) {
751 move()->padType().setBit(KartMove::ePadType::JumpPad);
752 closestColEntry = colDirector->closestCollisionEntry();
753 state()->setJumpPadVariant(closestColEntry->variant());
754 }
755 collisionData.bTrickable = true;
756 }
757}
758
761void KartCollide::processCannon(Field::KCLTypeMask *maskOut) {
762 auto *colDirector = Field::CollisionDirector::Instance();
763 if (colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_BIT(COL_TYPE_CANNON_TRIGGER))) {
764 state()->setCannonPointId(colDirector->closestCollisionEntry()->variant());
765 status().setBit(eStatus::CannonStart);
766 }
767}
768
778void KartCollide::applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup,
779 const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed,
780 bool b1, bool b2, bool b3) {
781 CollisionData &colData = hitboxGroup->collisionData();
782 if (!colData.bFloor) {
783 return;
784 }
785
786 f32 velDotFloorNrm = colData.vel.dot(colData.floorNrm);
787
788 if (velDotFloorNrm >= 0.0f) {
789 return;
790 }
791
792 EGG::Matrix34f rotMat;
793 rotMat.makeQ(dynamics()->mainRot());
794 EGG::Matrix34f tmp = rotMat.multiplyTo(dynamics()->invInertiaTensor());
795 EGG::Matrix34f rotMatTrans = rotMat.transpose();
796 tmp = tmp.multiplyTo(rotMatTrans);
797
798 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
799 crossVec = tmp.multVector(crossVec);
800 crossVec = crossVec.cross(colData.relPos);
801
802 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.dot(crossVec));
803 EGG::Vector3f negSpeed = -speed;
804 crossVec = colData.floorNrm.cross(negSpeed);
805 crossVec = crossVec.cross(colData.floorNrm);
806
807 if (std::numeric_limits<f32>::epsilon() >= crossVec.squaredLength()) {
808 return;
809 }
810
811 crossVec.normalise();
812 f32 speedDot = std::min(0.0f, speed.dot(crossVec));
813 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
814
815 auto [proj, rej] = crossVec.projAndRej(forward);
816
817 f32 projNorm = proj.length();
818 f32 rejNorm = rej.length();
819 f32 projNorm_ = projNorm;
820 f32 rejNorm_ = rejNorm;
821
822 f32 dVar7 = down * EGG::Mathf::abs(scalar);
823 if (dVar7 < EGG::Mathf::abs(projNorm)) {
824 projNorm_ = dVar7;
825 if (projNorm < 0.0f) {
826 projNorm_ = -down * EGG::Mathf::abs(scalar);
827 }
828 }
829
830 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
831 if (dVar5 < EGG::Mathf::abs(rejNorm)) {
832 rejNorm_ = dVar5;
833 if (rejNorm < 0.0f) {
834 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
835 }
836 }
837
838 proj.normalise();
839 rej.normalise();
840
841 proj *= projNorm_;
842 rej *= rejNorm_;
843
844 EGG::Vector3f projRejSum = proj + rej;
845 EGG::Vector3f projRejSumOrig = projRejSum;
846
847 if (!b1) {
848 projRejSum.x = 0.0f;
849 projRejSum.z = 0.0f;
850 }
851 if (!b2) {
852 projRejSum.y = 0.0f;
853 }
854
855 projRejSum = projRejSum.rej(nextDir);
856
857 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
858
859 if (b3) {
860 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
861 EGG::Vector3f rotation2 = dynamics()->mainRot().rotateVectorInv(tmp.multVector(rotation));
862
863 EGG::Vector3f angVel = rotation2;
864 angVel.y = 0.0f;
865 if (!b1) {
866 angVel.x = 0.0f;
867 }
868 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
869 }
870}
871
876bool KartCollide::FUN_805B6A9C(CollisionData &collisionData, const Hitbox &hitbox,
877 EGG::BoundBox3f &minMax, EGG::Vector3f &relPos, s32 &count,
878 const Field::KCLTypeMask &maskOut, const Field::CollisionInfo &colInfo) {
879 if (maskOut & KCL_TYPE_WALL) {
880 if (!(maskOut & KCL_TYPE_FLOOR) && status().onBit(eStatus::HWG) &&
881 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
882 return true;
883 }
884
885 bool skipWalls = false;
886
887 collisionData.wallNrm += colInfo.wallNrm;
888
889 if (maskOut & KCL_TYPE_ANY_INVISIBLE_WALL) {
890 collisionData.bInvisibleWall = true;
891
892 if (!(maskOut & KCL_TYPE_4010D000)) {
893 collisionData.bInvisibleWallOnly = true;
894
896 skipWalls = true;
897 }
898 }
899 }
900
901 if (!skipWalls) {
902 if (maskOut & KCL_TYPE_BIT(COL_TYPE_WALL_2)) {
903 collisionData.bWall3 = true;
904 } else {
905 collisionData.bWall = true;
906 }
907 }
908 }
909
910 if (maskOut & KCL_TYPE_FLOOR) {
911 collisionData.floorNrm += colInfo.floorNrm;
912 collisionData.bFloor = true;
913 }
914
915 EGG::Vector3f tangentOff = colInfo.tangentOff;
916 minMax.min = minMax.min.minimize(tangentOff);
917 minMax.max = minMax.max.maximize(tangentOff);
918 tangentOff.normalise();
919
920 relPos += hitbox.relPos();
921 relPos += -hitbox.radius() * tangentOff;
922 ++count;
923
924 return false;
925}
926
932 const EGG::Vector3f &posRel, s32 count) {
933 setPos(pos() + movement);
934
935 if (!collisionData.bFloor && (collisionData.bWall || collisionData.bWall3)) {
936 collisionData.movement = movement;
937 }
938
939 f32 rotFactor = 1.0f / static_cast<f32>(count);
940 EGG::Vector3f scaledRelPos = rotFactor * posRel;
941 collisionData.rotFactor *= rotFactor;
942
943 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
944 EGG::Vector3f local_48 = mainRot().rotateVectorInv(scaledRelPos);
945 EGG::Vector3f local_30 = scaledAngVel0.cross(local_48);
946 local_30 = mainRot().rotateVector(local_30);
947 local_30 += extVel();
948
949 collisionData.vel = local_30;
950 collisionData.relPos = scaledRelPos;
951
952 if (collisionData.bFloor) {
953 f32 intVelY = dynamics()->intVel().y;
954 if (intVelY > 0.0f) {
955 collisionData.vel.y += intVelY;
956 }
957 collisionData.floorNrm.normalise();
958 }
959}
960
962void KartCollide::startFloorMomentRate() {
963 m_floorMomentRate = 0.01f;
964}
965
967void KartCollide::calcFloorMomentRate() {
968 m_floorMomentRate = status().onBit(eStatus::InAction) &&
969 action()->flags().onBit(KartAction::eFlags::Rotating) ?
970 0.01f :
971 std::min(m_floorMomentRate + 0.01f, 0.8f);
972}
973
975Action KartCollide::handleReactNone(size_t /*idx*/) {
976 return Action::None;
977}
978
980Action KartCollide::handleReactWallAllSpeed(size_t idx) {
981 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
982 m_surfaceFlags.setBit(eSurfaceFlags::ObjectWall);
983
984 return Action::None;
985}
986
988Action KartCollide::handleReactSpinAllSpeed(size_t /*idx*/) {
989 return Action::UNK_0;
990}
991
993Action KartCollide::handleReactSpinSomeSpeed(size_t /*idx*/) {
994 return Action::UNK_1;
995}
996
998Action KartCollide::handleReactFireSpin(size_t /*idx*/) {
999 return Action::UNK_9;
1000}
1001
1003Action KartCollide::handleReactSmallLaunch(size_t /*idx*/) {
1004 return Action::UNK_2;
1005}
1006
1008Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(size_t /*idx*/) {
1009 return Action::UNK_3;
1010}
1011
1013Action KartCollide::handleReactLaunchSpinLoseItem(size_t /*idx*/) {
1014 return Action::UNK_6;
1015}
1016
1018Action KartCollide::handleReactKnockbackBumpLoseItem(size_t /*idx*/) {
1019 return Action::UNK_4;
1020}
1021
1023Action KartCollide::handleReactLongCrushLoseItem(size_t /*idx*/) {
1024 return Action::UNK_12;
1025}
1026
1028Action KartCollide::handleReactSmallBump(size_t idx) {
1029 move()->applyBumpForce(30.0f, objectCollisionKart()->GetHitDirection(idx), false);
1030 return Action::None;
1031}
1032
1034Action KartCollide::handleReactHighLaunchLoseItem(size_t /*idx*/) {
1035 return Action::UNK_8;
1036}
1037
1039Action KartCollide::handleReactWeakWall(size_t /*idx*/) {
1040 move()->setSpeed(move()->speed() * 0.82f);
1041 return Action::None;
1042}
1043
1045Action KartCollide::handleReactOffroad(size_t /*idx*/) {
1046 status().setBit(eStatus::CollidingOffroad);
1047 m_surfaceFlags.setBit(eSurfaceFlags::Offroad);
1048 return Action::None;
1049}
1050
1052Action KartCollide::handleReactLaunchSpin(size_t idx) {
1053 action()->setTranslation(objectCollisionKart()->translation(idx));
1054 return Action::UNK_5;
1055}
1056
1058Action KartCollide::handleReactWallSpark(size_t idx) {
1059 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1060 m_surfaceFlags.setBit(eSurfaceFlags::ObjectWall3);
1061
1062 return Action::None;
1063}
1064
1066Action KartCollide::handleReactUntrickableJumpPad(size_t /*idx*/) {
1067 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1068 state()->setJumpPadVariant(0);
1069
1070 return Action::None;
1071}
1072
1074Action KartCollide::handleReactShortCrushLoseItem(size_t /*idx*/) {
1075 return Action::UNK_14;
1076}
1077
1079Action KartCollide::handleReactCrushRespawn(size_t /*idx*/) {
1080 return Action::UNK_16;
1081}
1082
1084Action KartCollide::handleReactExplosionLoseItem(size_t /*idx*/) {
1085 return Action::UNK_7;
1086}
1087
1088std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1097 &KartCollide::handleReactWallAllSpeed,
1098 &KartCollide::handleReactSpinAllSpeed,
1099 &KartCollide::handleReactSpinSomeSpeed,
1100 &KartCollide::handleReactFireSpin,
1102 &KartCollide::handleReactSmallLaunch,
1103 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1104 &KartCollide::handleReactLaunchSpinLoseItem,
1105 &KartCollide::handleReactKnockbackBumpLoseItem,
1106 &KartCollide::handleReactLongCrushLoseItem,
1107 &KartCollide::handleReactSmallBump,
1110 &KartCollide::handleReactHighLaunchLoseItem,
1112 &KartCollide::handleReactWeakWall,
1113 &KartCollide::handleReactOffroad,
1114 &KartCollide::handleReactLaunchSpin,
1115 &KartCollide::handleReactWallSpark,
1118 &KartCollide::handleReactUntrickableJumpPad,
1119 &KartCollide::handleReactShortCrushLoseItem,
1120 &KartCollide::handleReactCrushRespawn,
1121 &KartCollide::handleReactExplosionLoseItem,
1122}};
1123
1124} // 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_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:189
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:236
Vector3f multVector(const Vector3f &vec) const
Multiplies a vector by a matrix.
Definition Matrix.cc:212
Matrix34f transpose() const
Transposes the 3x3 portion of the matrix.
Definition Matrix.cc:340
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