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 Kinoko::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_sumHitboxBottomHeightFloorOnly = 0.0f;
37 m_sumHitboxBottomHeightSoftWall = 0.0f;
38 m_numFloorOnlyCollisions = 0;
39 m_numSoftWallCollisions = 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
56 bool wasHalfPipe = status().onBit(eStatus::EndHalfPipe, eStatus::ActionMidZipper);
57 const EGG::Quatf &rot = wasHalfPipe ? mainRot() : fullRot();
58 calcBodyCollision(move()->totalScale(), body()->sinkDepth(), rot, scale());
59
60 auto &colData = collisionData();
61 bool existingWallCollision = colData.bWall || colData.bWall3;
62 bool newWallCollision =
63 m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
64 if (existingWallCollision || newWallCollision) {
65 if (!existingWallCollision) {
66 colData.wallNrm = m_totalReactionWallNrm;
67 if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall)) {
68 colData.bWall = true;
69 } else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
70 colData.bWall3 = true;
71 }
72 } else if (newWallCollision) {
73 colData.wallNrm += m_totalReactionWallNrm;
74 if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall)) {
75 colData.bWall = true;
76 } else if (m_surfaceFlags.onBit(eSurfaceFlags::ObjectWall3)) {
77 colData.bWall3 = true;
78 }
79 }
80
81 colData.wallNrm.normalise();
82 }
83
85}
86
91 f32 fVar1;
92
93 auto &status = KartObjectProxy::status();
94
95 if (isInRespawn() ||
97 eStatus::NoSparkInvisibleWall, eStatus::HalfPipeRamp)) {
98 fVar1 = 0.0f;
99 } else {
100 fVar1 = 0.05f;
101 }
102
103 bool resetXZ = fVar1 > 0.0f && status.onBit(eStatus::AirtimeOver20) &&
104 dynamics()->velocity().y < -50.0f;
105
106 FUN_805B72B8(status.onBit(eStatus::InAction) ? 0.3f : 0.01f, fVar1, resetXZ,
107 status.offBit(eStatus::JumpPadDisableYsusForce));
108}
109
117void KartCollide::FUN_805B72B8(f32 param_1, f32 param_2, bool lockXZ, bool addExtVelY) {
118 const auto &colData = collisionData();
119
120 if (!colData.bFloor && !colData.bWall && !colData.bWall3) {
121 return;
122 }
123
124 EGG::Vector3f collisionDir = colData.floorNrm + colData.wallNrm;
125 collisionDir.normalise();
126
127 f32 directionalVelocity = colData.vel.dot(collisionDir);
128 if (directionalVelocity >= 0.0f) {
129 return;
130 }
131
132 EGG::Matrix34f rotMat;
133 EGG::Matrix34f rotMatTrans;
134
135 rotMat.makeQ(dynamics()->mainRot());
136 rotMatTrans = rotMat.transpose();
137 rotMat = rotMat.multiplyTo(dynamics()->invInertiaTensor()).multiplyTo(rotMatTrans);
138
139 EGG::Vector3f relPos = colData.relPos;
140 if (lockXZ) {
141 relPos.x = 0.0f;
142 relPos.z = 0.0f;
143 }
144
145 EGG::Vector3f step1 = relPos.cross(collisionDir);
146 EGG::Vector3f step2 = rotMat.multVector33(step1);
147 EGG::Vector3f step3 = step2.cross(relPos);
148 f32 val = (-directionalVelocity * (1.0f + param_2)) / (1.0f + collisionDir.dot(step3));
149 EGG::Vector3f step4 = collisionDir.cross(-colData.vel);
150 EGG::Vector3f step5 = step4.cross(collisionDir);
151 step5.normalise();
152
153 f32 fVar1 = param_1 * EGG::Mathf::abs(val);
154 f32 otherVal = (val * colData.vel.dot(step5)) / directionalVelocity;
155
156 f32 fVar3 = otherVal;
157 if (fVar1 < EGG::Mathf::abs(otherVal)) {
158 fVar3 = fVar1;
159 if (otherVal < 0.0f) {
160 fVar3 = -param_1 * EGG::Mathf::abs(val);
161 }
162 }
163
164 EGG::Vector3f step6 = val * collisionDir + fVar3 * step5;
165
166 f32 local_1d0 = step6.y;
167 if (!addExtVelY) {
168 local_1d0 = 0.0f;
169 } else if (colData.bFloor) {
170 f32 velY = intVel().y;
171 if (velY > 0.0f) {
172 velY += extVel().y;
173 if (velY < 0.0f) {
174 EGG::Vector3f newExtVel = extVel();
175 newExtVel.y = velY;
176 dynamics()->setExtVel(newExtVel);
177 }
178 }
179 }
180
181 f32 prevExtVelY = extVel().y;
182 EGG::Vector3f extVelAdd = step6;
183 extVelAdd.y = local_1d0;
184 dynamics()->setExtVel(extVel() + extVelAdd);
185
186 if (prevExtVelY < 0.0f && extVel().y > 0.0f && extVel().y < 10.0f) {
187 EGG::Vector3f extVelNoY = extVel();
188 extVelNoY.y = 0.0f;
189 dynamics()->setExtVel(extVelNoY);
190 }
191
192 EGG::Vector3f step7 = relPos.cross(step6);
193 EGG::Vector3f step8 = rotMat.multVector33(step7);
194 EGG::Vector3f step9 = mainRot().rotateVectorInv(step8);
195 step9.y = 0.0f;
196 dynamics()->setAngVel0(dynamics()->angVel0() + step9);
197}
198
205void KartCollide::calcBodyCollision(f32 totalScale, f32 sinkDepth, const EGG::Quatf &rot,
206 const EGG::Vector3f &scale) {
207 CollisionGroup *hitboxGroup = physics()->hitboxGroup();
208 CollisionData &collisionData = hitboxGroup->collisionData();
209 collisionData.reset();
210
211 EGG::Vector3f posRel = EGG::Vector3f::zero;
212 s32 count = 0;
213 Field::CollisionInfo colInfo;
214 colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero);
215 Field::KCLTypeMask maskOut;
217 EGG::BoundBox3f minMax;
218 minMax.setZero();
219 bool bVar1 = false;
220
221 for (u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) {
222 Field::KCLTypeMask flags = KCL_TYPE_DRIVER_SOLID_SURFACE;
223 Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx);
224
225 if (hitbox.bspHitbox()->wallsOnly != 0) {
226 flags = 0x4A109000;
227 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
228 }
229
230 hitbox.calc(totalScale, sinkDepth, scale, rot, pos());
231
232 if (Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.radius(),
233 hitbox.worldPos(), hitbox.lastPos(), flags, &colInfo, &maskOut, 0)) {
234 if (!!(maskOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) {
235 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&maskOut,
237 }
238
239 if (!FUN_805B6A9C(collisionData, hitbox, minMax, posRel, count, maskOut, colInfo)) {
240 bVar1 = true;
241
242 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
243 collisionData.bHasRoadVel = true;
244 collisionData.roadVelocity = colInfo.roadVelocity;
245 }
246
247 processBody(collisionData, hitbox, &colInfo, &maskOut);
248 }
249 }
250 }
251
252 if (bVar1) {
253 EGG::Vector3f movement = minMax.min + minMax.max;
254 applyBodyCollision(collisionData, movement, posRel, count);
255 } else {
256 collisionData.speedFactor = 1.0f;
257 collisionData.rotFactor = 1.0f;
258 }
259}
260
262void KartCollide::calcFloorEffect() {
263 if (status().onBit(eStatus::TouchingGround)) {
264 m_surfaceFlags.setBit(eSurfaceFlags::Offroad, eSurfaceFlags::GroundBoostPanelOrRamp);
265 }
266
267 m_sumHitboxBottomHeightFloorOnly = 0.0f;
268 m_surfaceFlags.resetBit(eSurfaceFlags::Wall, eSurfaceFlags::SolidOOB, eSurfaceFlags::BoostRamp,
269 eSurfaceFlags::Offroad, eSurfaceFlags::Trickable, eSurfaceFlags::NotTrickable,
270 eSurfaceFlags::StopHalfPipeState);
271 m_sumHitboxBottomHeightSoftWall = 0.0f;
272 m_numFloorOnlyCollisions = 0;
273 m_numSoftWallCollisions = 0;
274
275 Field::KCLTypeMask mask = KCL_NONE;
276 calcTriggers(&mask, pos(), false);
277
278 auto *colDir = Field::CollisionDirector::Instance();
279
280 if (m_solidOobTimer >= 3 && m_surfaceFlags.onBit(eSurfaceFlags::SolidOOB) &&
281 m_surfaceFlags.offBit(eSurfaceFlags::Wall)) {
282 if (mask & KCL_TYPE_BIT(COL_TYPE_SOLID_OOB)) {
283 colDir->findClosestCollisionEntry(&mask, KCL_TYPE_BIT(COL_TYPE_SOLID_OOB));
284 }
285
286 activateOob(true, &mask, false, false);
287 }
288
289 mask = KCL_NONE;
290 calcTriggers(&mask, pos(), true);
291
292 m_solidOobTimer =
293 m_surfaceFlags.onBit(eSurfaceFlags::SolidOOB) ? std::min(3, m_solidOobTimer + 1) : 0;
294
295 if (status().onBit(eStatus::Wall3Collision, eStatus::WallCollision)) {
296 Field::KCLTypeMask maskOut = KCL_NONE;
297
298 if (colDir->checkSphereCachedPartialPush(m_boundingRadius, pos(), EGG::Vector3f::inf,
299 KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY), nullptr, &maskOut, 0)) {
300 calcFallBoundary(&maskOut, true);
301 }
302 }
303}
304
306void KartCollide::calcTriggers(Field::KCLTypeMask *mask, const EGG::Vector3f &pos, bool twoPoint) {
307 EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf;
308 Field::KCLTypeMask typeMask = twoPoint ? KCL_TYPE_DIRECTIONAL : KCL_TYPE_NON_DIRECTIONAL;
309 f32 radius = twoPoint ? 80.0f : 100.0f * move()->totalScale();
310 f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f;
311 EGG::Vector3f scaledPos = pos + scalar * componentYAxis();
312 EGG::Vector3f back = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
313
314 m_smoothedBack += (back.dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f;
315
316 scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale();
317 scaledPos += scalar * back;
318
319 bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(radius,
320 scaledPos, v1, typeMask, nullptr, mask, 0);
321
322 if (!collide) {
323 return;
324 }
325
326 if (twoPoint) {
327 handleTriggers(mask);
328 } else {
329 if (*mask & KCL_TYPE_FLOOR) {
330 Field::CollisionDirector::Instance()->findClosestCollisionEntry(mask, KCL_TYPE_FLOOR);
331 }
332
333 if (*mask & KCL_TYPE_WALL) {
334 m_surfaceFlags.setBit(eSurfaceFlags::Wall);
335 }
336
337 if (*mask & KCL_TYPE_BIT(COL_TYPE_SOLID_OOB)) {
338 m_surfaceFlags.setBit(eSurfaceFlags::SolidOOB);
339 }
340 }
341}
342
344void KartCollide::handleTriggers(Field::KCLTypeMask *mask) {
345 calcFallBoundary(mask, false);
346 processCannon(mask);
347
349 auto *colDir = Field::CollisionDirector::Instance();
350 if (colDir->findClosestCollisionEntry(mask, KCL_TYPE_BIT(COL_TYPE_EFFECT_TRIGGER))) {
351 if (colDir->closestCollisionEntry()->variant() == 4) {
352 halfPipe()->end(true);
353 status().setBit(eStatus::EndHalfPipe);
354 m_surfaceFlags.setBit(eSurfaceFlags::StopHalfPipeState);
355 }
356 }
357 }
358}
359
361void KartCollide::calcFallBoundary(Field::KCLTypeMask *mask, bool shortBoundary) {
362 if (!(*mask & KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY))) {
363 return;
364 }
365
366 auto *colDir = Field::CollisionDirector::Instance();
367 if (!colDir->findClosestCollisionEntry(mask, KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY))) {
368 return;
369 }
370
371 bool safe = false;
372 const auto *entry = colDir->closestCollisionEntry();
373
374 if (shortBoundary) {
375 if (entry->variant() != 7) {
376 safe = true;
377 }
378 }
379
380 if (!safe) {
381 activateOob(false, mask, false, false);
382 }
383}
384
386void KartCollide::calcBeforeRespawn() {
387 if (pos().y < 0.0f) {
388 activateOob(true, nullptr, false, false);
389 }
390
391 auto &status = KartObjectProxy::status();
392
393 if (status.onBit(eStatus::BeforeRespawn)) {
394 if (--m_respawnTimer > 0) {
395 return;
396 }
397
398 status.resetBit(eStatus::BeforeRespawn);
399 m_respawnTimer = 0;
400 move()->triggerRespawn();
401 }
402
403 m_shrinkTimer = std::max(0, m_shrinkTimer - 1);
404}
405
407void KartCollide::activateOob(bool /*detachCamera*/, Field::KCLTypeMask * /*mask*/,
408 bool /*somethingCPU*/, bool /*somethingBullet*/) {
409 constexpr s16 RESPAWN_TIME = 130;
410
411 auto &status = KartObjectProxy::status();
412
413 if (status.onBit(eStatus::BeforeRespawn)) {
414 return;
415 }
416
417 move()->initOob();
418
419 m_respawnTimer = RESPAWN_TIME;
420 status.setBit(eStatus::BeforeRespawn);
421}
422
431void KartCollide::calcWheelCollision(u16 /*wheelIdx*/, CollisionGroup *hitboxGroup,
432 const EGG::Vector3f &colVel, const EGG::Vector3f &center, f32 radius) {
433 Hitbox &firstHitbox = hitboxGroup->hitbox(0);
434 BSP::Hitbox *bspHitbox = const_cast<BSP::Hitbox *>(firstHitbox.bspHitbox());
435 bspHitbox->radius = radius;
436 hitboxGroup->resetCollision();
437 firstHitbox.setWorldPos(center);
438
439 Field::CollisionInfo colInfo;
440 colInfo.bbox.setZero();
441 Field::KCLTypeMask kclOut;
443 Field::CourseColMgr::Instance()->setNoBounceWallInfo(&noBounceWallInfo);
444
445 bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush(
446 firstHitbox.radius(), firstHitbox.worldPos(), firstHitbox.lastPos(),
447 KCL_TYPE_VEHICLE_COLLIDEABLE, &colInfo, &kclOut, 0);
448
449 CollisionData &collisionData = hitboxGroup->collisionData();
450
451 if (!collided) {
452 collisionData.speedFactor = 1.0f;
453 collisionData.rotFactor = 1.0f;
454 return;
455 }
456
457 collisionData.tangentOff = colInfo.tangentOff;
458
459 if (noBounceWallInfo.dist > std::numeric_limits<f32>::min()) {
460 collisionData.tangentOff += noBounceWallInfo.tangentOff;
461 collisionData.noBounceWallNrm = noBounceWallInfo.fnrm;
462 collisionData.bSoftWall = true;
463 }
464
465 if (kclOut & KCL_TYPE_FLOOR) {
466 collisionData.bFloor = true;
467 collisionData.floorNrm = colInfo.floorNrm;
468 }
469
470 collisionData.relPos = firstHitbox.worldPos() - pos();
471 collisionData.vel = colVel;
472
473 if (colInfo.movingFloorDist > -std::numeric_limits<f32>::min()) {
474 collisionData.bHasRoadVel = true;
475 collisionData.roadVelocity = colInfo.roadVelocity;
476 }
477
478 processWheel(collisionData, firstHitbox, &colInfo, &kclOut);
479
480 if (!(kclOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) {
481 return;
482 }
483
484 Field::CollisionDirector::Instance()->findClosestCollisionEntry(&kclOut,
486}
487
491 Field::CollisionInfo *colInfo) {
492 if (colInfo->perpendicularity <= 0.0f) {
493 return;
494 }
495
496 m_colPerpendicularity = std::max(m_colPerpendicularity, colInfo->perpendicularity);
497
498 if (collisionData.bWallAtLeftCloser || collisionData.bWallAtRightCloser) {
499 return;
500 }
501
502 f32 bspPosX = hitbox.bspHitbox()->position.x;
503 if (EGG::Mathf::abs(bspPosX) > 10.0f) {
504 if (bspPosX > 0.0f) {
505 collisionData.bWallAtLeftCloser = true;
506 } else {
507 collisionData.bWallAtRightCloser = true;
508 }
509
510 collisionData.colPerpendicularity = colInfo->perpendicularity;
511
512 return;
513 }
514
515 EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex);
516 std::array<f32, 2> tangents = {0.0f, 0.0f};
517
518 // The loop is just to do left/right wall
519 for (size_t i = 0; i < tangents.size(); ++i) {
520 f32 sign = i == 1 ? -1.0f : 1.0f;
521 f32 effectiveRadius = sign * hitbox.radius();
522 EGG::Vector3f effectivePos = hitbox.worldPos() + effectiveRadius * right;
523 Field::CollisionInfoPartial tempColInfo;
524
525 if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(hitbox.radius(),
526 effectivePos, hitbox.lastPos(), KCL_TYPE_DRIVER_WALL, &tempColInfo, nullptr,
527 0)) {
528 tangents[i] = colInfo->tangentOff.squaredLength();
529 }
530 }
531
532 if (tangents[0] > tangents[1]) {
533 collisionData.bWallAtLeftCloser = true;
534 collisionData.colPerpendicularity = colInfo->perpendicularity;
535 } else if (tangents[1] > tangents[0]) {
536 collisionData.bWallAtRightCloser = true;
537 collisionData.colPerpendicularity = colInfo->perpendicularity;
538 }
539}
540
542void KartCollide::calcBoundingRadius() {
543 m_boundingRadius = collisionGroup()->boundingRadius() * move()->hitboxScale();
544}
545
547void KartCollide::calcObjectCollision() {
548 constexpr f32 COS_PI_OVER_4 = 0.707f;
549 constexpr s32 DUMMY_POLE_ANG_VEL_TIME = 3;
550 constexpr f32 DUMMY_POLE_ANG_VEL = 0.005f;
551 constexpr s32 SHRINK_TIME = 60;
552
553 m_totalReactionWallNrm = EGG::Vector3f::zero;
554 m_surfaceFlags.resetBit(eSurfaceFlags::ObjectWall, eSurfaceFlags::ObjectWall3);
555
556 auto *objColKart = objectCollisionKart();
557 size_t collisionCount = objColKart->checkCollision(pose(), velocity());
558
559 const auto *objectDirector = Field::ObjectDirector::Instance();
560
561 for (size_t i = 0; i < collisionCount; ++i) {
562 Reaction reaction = objectDirector->reaction(i);
563 if (reaction != Reaction::None && reaction != Reaction::UNK_7) {
564 size_t handlerIdx = static_cast<std::underlying_type_t<Reaction>>(reaction);
565 Action newAction = (this->*s_objectCollisionHandlers[handlerIdx])(i);
566
567 if (reaction == Reaction::SpinShrink && (m_shrinkTimer == 0)) {
568 m_shrinkTimer = SHRINK_TIME;
569 move()->activateShrink();
570 move()->applyForce(30.0f, objColKart->GetHitDirection(i), false);
571 } else if (reaction != Reaction::SmallBump && reaction != Reaction::BigBump) {
572 const EGG::Vector3f &hitDepth = objectDirector->hitDepth(i);
573 m_tangentOff += hitDepth;
574 m_movement += hitDepth;
575
576 if (newAction != Action::None) {
577 action()->setHitDepth(objectDirector->hitDepth(i));
578 action()->start(newAction);
579 }
580 }
581 }
582
583 if (objectDirector->collidingObject(i)->id() == Field::ObjectId::DummyPole) {
584 EGG::Vector3f hitDirection = objectCollisionKart()->GetHitDirection(i);
585 const EGG::Vector3f &lastDir = move()->lastDir();
586
587 if (lastDir.dot(hitDirection) < -COS_PI_OVER_4) {
588 EGG::Vector3f angVel = hitDirection.cross(lastDir);
589 f32 sign = angVel.y > 0.0f ? -1.0f : 1.0f;
590
591 m_poleAngVelTimer = DUMMY_POLE_ANG_VEL_TIME;
592 m_poleYaw = DUMMY_POLE_ANG_VEL * sign;
593 }
594 }
595 }
596
597 calcPoleTimer();
598}
599
601void KartCollide::calcPoleTimer() {
602 if (m_poleAngVelTimer > 0 && status().onBit(eStatus::Accelerate, eStatus::Brake)) {
603 EGG::Vector3f angVel2 = dynamics()->angVel2();
604 angVel2.y += m_poleYaw;
605 dynamics()->setAngVel2(angVel2);
606 }
607
608 m_poleAngVelTimer = std::max(0, m_poleAngVelTimer - 1);
609}
610
614void KartCollide::processWheel(CollisionData &collisionData, Hitbox &hitbox,
615 Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut) {
616 processMovingWater(collisionData, maskOut);
617 processFloor(collisionData, hitbox, colInfo, maskOut, true);
618}
619
621void KartCollide::processBody(CollisionData &collisionData, Hitbox &hitbox,
622 Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut) {
623 processMovingWater(collisionData, maskOut);
624
625 bool hasWallCollision = processWall(collisionData, maskOut);
626
627 processFloor(collisionData, hitbox, colInfo, maskOut, false);
628
629 if (hasWallCollision) {
630 calcSideCollision(collisionData, hitbox, colInfo);
631 }
632
633 processCannon(maskOut);
634}
635
637void KartCollide::processMovingWater(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
638 if (!(*maskOut & KCL_TYPE_BIT(COL_TYPE_MOVING_WATER))) {
639 return;
640 }
641
642 auto *colDir = Field::CollisionDirector::Instance();
643 if (!colDir->findClosestCollisionEntry(maskOut, KCL_TYPE_BIT(COL_TYPE_MOVING_WATER))) {
644 return;
645 }
646
647 state()->status().setBit(eStatus::StickyRoad);
648
649 auto *entry = colDir->closestCollisionEntry();
650 switch (entry->variant()) {
651 case 1:
652 collisionData.bMovingWaterMomentum = true;
653 collisionData.bMovingWaterStickyRoad = true;
654 collisionData.bMovingWaterDisableAccel = true;
655 break;
656 case 2:
657 collisionData.bMovingWaterDecaySpeed = true;
658 break;
659 case 3:
660 collisionData.bMovingWaterDecaySpeed = true;
661 collisionData.bMovingWaterDisableAccel = true;
662 collisionData.bMovingWaterVertical = true;
663 break;
664 default:
665 collisionData.bMovingWaterMomentum = true;
666 break;
667 }
668}
669
671bool KartCollide::processWall(CollisionData &collisionData, Field::KCLTypeMask *maskOut) {
672 if (!(*maskOut & KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2)) {
673 return false;
674 }
675
676 auto *colDirector = Field::CollisionDirector::Instance();
677 if (!colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_DRIVER_WALL_NO_INVISIBLE_WALL2)) {
678 return false;
679 }
680
682 colDirector->findClosestCollisionEntry(maskOut,
684 auto *entry = colDirector->closestCollisionEntry();
685
686 collisionData.closestWallFlags = entry->baseType();
687 collisionData.closestWallSettings = entry->variant();
688
689 if (entry->attribute.onBit(Field::CollisionDirector::eCollisionAttribute::Soft)) {
690 collisionData.bSoftWall = true;
691 }
692 }
693
694 return true;
695}
696
703void KartCollide::processFloor(CollisionData &collisionData, Hitbox &hitbox,
704 Field::CollisionInfo * /*colInfo*/, Field::KCLTypeMask *maskOut, bool wheel) {
705 constexpr Field::KCLTypeMask BOOST_RAMP_MASK = KCL_TYPE_BIT(COL_TYPE_BOOST_RAMP);
706
707 if (collisionData.bSoftWall) {
708 ++m_numSoftWallCollisions;
709 m_sumHitboxBottomHeightSoftWall += hitbox.worldPos().y - hitbox.radius();
710 }
711
712 if (!(*maskOut & KCL_TYPE_FLOOR)) {
713 return;
714 }
715
716 auto *colDirector = Field::CollisionDirector::Instance();
717
718 if (!colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_FLOOR)) {
719 return;
720 }
721
722 const auto *closestColEntry = colDirector->closestCollisionEntry();
723
724 if (closestColEntry->attribute.offBit(
725 Field::CollisionDirector::eCollisionAttribute::Trickable)) {
726 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
727 } else {
728 collisionData.bTrickable = true;
729 m_surfaceFlags.setBit(eSurfaceFlags::Trickable);
730 }
731
732 collisionData.speedFactor = std::min(collisionData.speedFactor,
733 param()->stats().kclSpeed[closestColEntry->baseType()]);
734
735 collisionData.intensity = closestColEntry->intensity();
736 collisionData.rotFactor += param()->stats().kclRot[closestColEntry->baseType()];
737
738 auto &status = KartObjectProxy::status();
739
740 if (closestColEntry->attribute.onBit(
741 Field::CollisionDirector::eCollisionAttribute::RejectRoad)) {
742 status.setBit(eStatus::RejectRoad);
743 }
744
745 collisionData.closestFloorFlags = closestColEntry->typeMask;
746 collisionData.closestFloorSettings = closestColEntry->variant();
747
748 if (wheel && !!(*maskOut & KCL_TYPE_BIT(COL_TYPE_BOOST_PAD))) {
749 move()->padType().setBit(KartMove::ePadType::BoostPanel);
750 }
751
752 if (!!(*maskOut & BOOST_RAMP_MASK) &&
753 colDirector->findClosestCollisionEntry(maskOut, BOOST_RAMP_MASK)) {
754 closestColEntry = colDirector->closestCollisionEntry();
755 move()->padType().setBit(KartMove::ePadType::BoostRamp);
756 state()->setBoostRampType(closestColEntry->variant());
757 m_surfaceFlags.setBit(eSurfaceFlags::BoostRamp, eSurfaceFlags::Trickable);
758 } else {
759 state()->setBoostRampType(-1);
760 m_surfaceFlags.setBit(eSurfaceFlags::NotTrickable);
761 }
762
763 if (!collisionData.bSoftWall) {
764 ++m_numFloorOnlyCollisions;
765 m_sumHitboxBottomHeightFloorOnly += hitbox.worldPos().y - hitbox.radius();
766 }
767
768 if (*maskOut & KCL_TYPE_BIT(COL_TYPE_STICKY_ROAD)) {
769 status.setBit(eStatus::StickyRoad);
770 }
771
772 Field::KCLTypeMask halfPipeRampMask = KCL_TYPE_BIT(COL_TYPE_HALFPIPE_RAMP);
773 if ((*maskOut & halfPipeRampMask) &&
774 colDirector->findClosestCollisionEntry(maskOut, halfPipeRampMask)) {
775 status.setBit(eStatus::HalfPipeRamp);
776 state()->setHalfPipeInvisibilityTimer(2);
777 if (colDirector->closestCollisionEntry()->variant() == 1) {
778 move()->padType().setBit(KartMove::ePadType::BoostPanel);
779 }
780 }
781
782 Field::KCLTypeMask jumpPadMask = KCL_TYPE_BIT(COL_TYPE_JUMP_PAD);
783 if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
784 if (status.offAnyBit(eStatus::TouchingGround, eStatus::JumpPad) &&
785 status.offBit(eStatus::JumpPadMushroomVelYInc)) {
786 move()->padType().setBit(KartMove::ePadType::JumpPad);
787 closestColEntry = colDirector->closestCollisionEntry();
788 state()->setJumpPadVariant(closestColEntry->variant());
789 }
790 collisionData.bTrickable = true;
791 }
792}
793
796void KartCollide::processCannon(Field::KCLTypeMask *maskOut) {
797 auto *colDirector = Field::CollisionDirector::Instance();
798 if (colDirector->findClosestCollisionEntry(maskOut, KCL_TYPE_BIT(COL_TYPE_CANNON_TRIGGER))) {
799 state()->setCannonPointId(colDirector->closestCollisionEntry()->variant());
800 status().setBit(eStatus::CannonStart);
801 }
802}
803
813void KartCollide::applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup,
814 const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed,
815 bool b1, bool b2, bool b3) {
816 CollisionData &colData = hitboxGroup->collisionData();
817 if (!colData.bFloor) {
818 return;
819 }
820
821 f32 velDotFloorNrm = colData.vel.dot(colData.floorNrm);
822
823 if (velDotFloorNrm >= 0.0f) {
824 return;
825 }
826
827 EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm);
828 crossVec = m_worldInertiaTensor.multVector(crossVec);
829 crossVec = crossVec.cross(colData.relPos);
830
831 f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.dot(crossVec));
832 EGG::Vector3f negSpeed = -speed;
833 crossVec = colData.floorNrm.cross(negSpeed);
834 crossVec = crossVec.cross(colData.floorNrm);
835
836 if (std::numeric_limits<f32>::epsilon() >= crossVec.squaredLength()) {
837 return;
838 }
839
840 crossVec.normalise();
841 f32 speedDot = std::min(0.0f, speed.dot(crossVec));
842 crossVec *= ((scalar * speedDot) / velDotFloorNrm);
843
844 auto [proj, rej] = crossVec.projAndRej(forward);
845
846 f32 projNorm = proj.length();
847 f32 rejNorm = rej.length();
848 f32 projNorm_ = projNorm;
849 f32 rejNorm_ = rejNorm;
850
851 f32 dVar7 = down * EGG::Mathf::abs(scalar);
852 if (dVar7 < EGG::Mathf::abs(projNorm)) {
853 projNorm_ = dVar7;
854 if (projNorm < 0.0f) {
855 projNorm_ = -down * EGG::Mathf::abs(scalar);
856 }
857 }
858
859 f32 dVar5 = rate * EGG::Mathf::abs(scalar);
860 if (EGG::Mathf::abs(rejNorm) > dVar5) {
861 rejNorm_ = dVar5;
862 if (rejNorm < 0.0f) {
863 rejNorm_ = -rate * EGG::Mathf::abs(scalar);
864 }
865 }
866
867 proj.normalise();
868 rej.normalise();
869
870 proj *= projNorm_;
871 rej *= rejNorm_;
872
873 EGG::Vector3f projRejSum = proj + rej;
874 EGG::Vector3f projRejSumOrig = projRejSum;
875
876 if (!b1) {
877 projRejSum.x = 0.0f;
878 projRejSum.z = 0.0f;
879 }
880 if (!b2) {
881 projRejSum.y = 0.0f;
882 }
883
884 projRejSum = projRejSum.rej(nextDir);
885
886 dynamics()->setExtVel(dynamics()->extVel() + projRejSum);
887
888 if (b3) {
889 EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig);
890 EGG::Vector3f rotation2 =
891 mainRot().rotateVectorInv(m_worldInertiaTensor.multVector(rotation));
892
893 EGG::Vector3f angVel = rotation2;
894 angVel.y = 0.0f;
895 if (!b1) {
896 angVel.x = 0.0f;
897 }
898 dynamics()->setAngVel0(dynamics()->angVel0() + angVel);
899 }
900}
901
906bool KartCollide::FUN_805B6A9C(CollisionData &collisionData, const Hitbox &hitbox,
907 EGG::BoundBox3f &minMax, EGG::Vector3f &relPos, s32 &count,
908 const Field::KCLTypeMask &maskOut, const Field::CollisionInfo &colInfo) {
909 if (maskOut & KCL_TYPE_WALL) {
910 if (!(maskOut & KCL_TYPE_FLOOR) && status().onBit(eStatus::HWG) &&
911 state()->softWallSpeed().dot(colInfo.wallNrm) < 0.3f) {
912 return true;
913 }
914
915 bool skipWalls = false;
916
917 collisionData.wallNrm += colInfo.wallNrm;
918
919 if (maskOut & KCL_TYPE_ANY_INVISIBLE_WALL) {
920 collisionData.bInvisibleWall = true;
921
922 if (!(maskOut & KCL_TYPE_4010D000)) {
923 collisionData.bInvisibleWallOnly = true;
924
926 skipWalls = true;
927 }
928 }
929 }
930
931 if (!skipWalls) {
932 if (maskOut & KCL_TYPE_BIT(COL_TYPE_WALL_2)) {
933 collisionData.bWall3 = true;
934 } else {
935 collisionData.bWall = true;
936 }
937 }
938 }
939
940 if (maskOut & KCL_TYPE_FLOOR) {
941 collisionData.floorNrm += colInfo.floorNrm;
942 collisionData.bFloor = true;
943 }
944
945 EGG::Vector3f tangentOff = colInfo.tangentOff;
946 minMax.min = minMax.min.minimize(tangentOff);
947 minMax.max = minMax.max.maximize(tangentOff);
948 tangentOff.normalise();
949
950 relPos += hitbox.relPos();
951 relPos += -hitbox.radius() * tangentOff;
952 ++count;
953
954 return false;
955}
956
962 const EGG::Vector3f &posRel, s32 count) {
963 setPos(pos() + movement);
964
965 if (!collisionData.bFloor && (collisionData.bWall || collisionData.bWall3)) {
966 collisionData.movement = movement;
967 }
968
969 f32 rotFactor = 1.0f / static_cast<f32>(count);
970 EGG::Vector3f scaledRelPos = rotFactor * posRel;
971 collisionData.rotFactor *= rotFactor;
972
973 EGG::Vector3f scaledAngVel0 = dynamics()->angVel0Factor() * dynamics()->angVel0();
974 EGG::Vector3f local_48 = mainRot().rotateVectorInv(scaledRelPos);
975 EGG::Vector3f local_30 = scaledAngVel0.cross(local_48);
976 local_30 = mainRot().rotateVector(local_30);
977 local_30 += extVel();
978
979 collisionData.vel = local_30;
980 collisionData.relPos = scaledRelPos;
981
982 if (collisionData.bFloor) {
983 f32 intVelY = dynamics()->intVel().y;
984 if (intVelY > 0.0f) {
985 collisionData.vel.y += intVelY;
986 }
987 collisionData.floorNrm.normalise();
988 }
989}
990
992void KartCollide::startFloorMomentRate() {
993 m_floorMomentRate = 0.01f;
994}
995
997void KartCollide::calcFloorMomentRate() {
998 m_floorMomentRate = status().onBit(eStatus::InAction) &&
999 action()->flags().onBit(KartAction::eFlags::Rotating) ?
1000 0.01f :
1001 std::min(m_floorMomentRate + 0.01f, 0.8f);
1002}
1003
1005Action KartCollide::handleReactNone(size_t /*idx*/) {
1006 return Action::None;
1007}
1008
1010Action KartCollide::handleReactWallAllSpeed(size_t idx) {
1011 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1012 m_surfaceFlags.setBit(eSurfaceFlags::ObjectWall);
1013
1014 return Action::None;
1015}
1016
1018Action KartCollide::handleReactSpinAllSpeed(size_t /*idx*/) {
1019 return Action::UNK_0;
1020}
1021
1023Action KartCollide::handleReactSpinSomeSpeed(size_t /*idx*/) {
1024 return Action::UNK_1;
1025}
1026
1028Action KartCollide::handleReactFireSpin(size_t /*idx*/) {
1029 return Action::UNK_9;
1030}
1031
1033Action KartCollide::handleReactSmallLaunch(size_t /*idx*/) {
1034 return Action::UNK_2;
1035}
1036
1038Action KartCollide::handleReactKnockbackSomeSpeedLoseItem(size_t /*idx*/) {
1039 return Action::UNK_3;
1040}
1041
1043Action KartCollide::handleReactLaunchSpinLoseItem(size_t /*idx*/) {
1044 return Action::UNK_6;
1045}
1046
1048Action KartCollide::handleReactKnockbackBumpLoseItem(size_t /*idx*/) {
1049 return Action::UNK_4;
1050}
1051
1053Action KartCollide::handleReactLongCrushLoseItem(size_t /*idx*/) {
1054 return Action::UNK_12;
1055}
1056
1058Action KartCollide::handleReactSmallBump(size_t idx) {
1059 move()->applyForce(30.0f, objectCollisionKart()->GetHitDirection(idx), false);
1060 return Action::None;
1061}
1062
1064Action KartCollide::handleReactSpinShrink(size_t /*idx*/) {
1065 return m_shrinkTimer <= 0 ? Action::UNK_15 : Action::None;
1066}
1067
1069Action KartCollide::handleReactHighLaunchLoseItem(size_t /*idx*/) {
1070 return Action::UNK_8;
1071}
1072
1074Action KartCollide::handleReactWeakWall(size_t /*idx*/) {
1075 move()->setSpeed(move()->speed() * 0.82f);
1076 return Action::None;
1077}
1078
1080Action KartCollide::handleReactOffroad(size_t /*idx*/) {
1081 status().setBit(eStatus::CollidingOffroad);
1082 m_surfaceFlags.setBit(eSurfaceFlags::Offroad);
1083 return Action::None;
1084}
1085
1087Action KartCollide::handleReactLaunchSpin(size_t idx) {
1088 action()->setTranslation(objectCollisionKart()->translation(idx));
1089 return Action::UNK_5;
1090}
1091
1093Action KartCollide::handleReactWallSpark(size_t idx) {
1094 m_totalReactionWallNrm += Field::ObjectCollisionKart::GetHitDirection(idx);
1095 m_surfaceFlags.setBit(eSurfaceFlags::ObjectWall3);
1096
1097 return Action::None;
1098}
1099
1101Action KartCollide::handleReactRubberWall(size_t idx) {
1102 constexpr f32 BASE_DIR_FORCE_SCALAR = 0.95f;
1103 constexpr f32 DIR_FORCE_SCALAR = 0.050000012f;
1104 constexpr f32 MAX_FORCE = 70.0f;
1105
1106 const EGG::Vector3f &hitDir = Field::ObjectCollisionKart::GetHitDirection(idx);
1107 const EGG::Vector3f &zAxis = componentZAxis();
1108
1109 f32 force = BASE_DIR_FORCE_SCALAR + DIR_FORCE_SCALAR * EGG::Mathf::abs(hitDir.dot(zAxis));
1110 move()->applyForce(MAX_FORCE * force, hitDir, true);
1111
1112 return Action::None;
1113}
1114
1116Action KartCollide::handleReactUntrickableJumpPad(size_t /*idx*/) {
1117 move()->setPadType(KartMove::PadType(KartMove::ePadType::JumpPad));
1118 state()->setJumpPadVariant(0);
1119
1120 return Action::None;
1121}
1122
1124Action KartCollide::handleReactShortCrushLoseItem(size_t /*idx*/) {
1125 return Action::UNK_14;
1126}
1127
1129Action KartCollide::handleReactCrushRespawn(size_t /*idx*/) {
1130 return Action::UNK_16;
1131}
1132
1134Action KartCollide::handleReactExplosionLoseItem(size_t /*idx*/) {
1135 return Action::UNK_7;
1136}
1137
1138std::array<KartCollide::ObjectCollisionHandler, 33> KartCollide::s_objectCollisionHandlers = {{
1147 &KartCollide::handleReactWallAllSpeed,
1148 &KartCollide::handleReactSpinAllSpeed,
1149 &KartCollide::handleReactSpinSomeSpeed,
1150 &KartCollide::handleReactFireSpin,
1152 &KartCollide::handleReactSmallLaunch,
1153 &KartCollide::handleReactKnockbackSomeSpeedLoseItem,
1154 &KartCollide::handleReactLaunchSpinLoseItem,
1155 &KartCollide::handleReactKnockbackBumpLoseItem,
1156 &KartCollide::handleReactLongCrushLoseItem,
1157 &KartCollide::handleReactSmallBump,
1159 &KartCollide::handleReactSpinShrink,
1160 &KartCollide::handleReactHighLaunchLoseItem,
1162 &KartCollide::handleReactWeakWall,
1163 &KartCollide::handleReactOffroad,
1164 &KartCollide::handleReactLaunchSpin,
1165 &KartCollide::handleReactWallSpark,
1166 &KartCollide::handleReactRubberWall,
1168 &KartCollide::handleReactUntrickableJumpPad,
1169 &KartCollide::handleReactShortCrushLoseItem,
1170 &KartCollide::handleReactCrushRespawn,
1171 &KartCollide::handleReactExplosionLoseItem,
1172}};
1173
1174} // namespace Kinoko::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:10
constexpr void makeQ(const Quatf &q)
Sets rotation matrix from quaternion.
Definition Matrix.hh:72
constexpr Matrix34f transpose() const
Transposes the 3x3 portion of the matrix.
Definition Matrix.hh:389
constexpr Vector3f multVector33(const Vector3f &vec) const
Multiplies a 3x3 matrix by a vector.
Definition Matrix.hh:285
constexpr Matrix34f multiplyTo(const Matrix34f &rhs) const
Multiplies two matrices.
Definition Matrix.hh:238
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
Definition BitFlag.hh:381
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 FUN_805B72B8(f32 param_1, f32 param_2, bool lockXZ, bool addExtVelY)
Affects velocity when landing from airtime.
void processFloor(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut, bool wheel)
Processes the floor triangles' attributes.
void calcSideCollision(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo)
EGG::Matrix34f m_worldInertiaTensor
The world-space inertia tensor of the kart.
void calcWheelCollision(u16 wheelIdx, CollisionGroup *hitboxGroup, const EGG::Vector3f &colVel, const EGG::Vector3f &center, f32 radius)
Checks wheel hitbox collision and stores position/velocity info.
Action handleReactNone(size_t idx)
Object collision functions.
void applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup, const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed, bool b1, bool b2, bool b3)
Applies external and angular velocity based on the collision with the floor.
bool FUN_805B6A9C(CollisionData &collisionData, const Hitbox &hitbox, EGG::BoundBox3f &minMax, EGG::Vector3f &relPos, s32 &count, const Field::KCLTypeMask &maskOut, const Field::CollisionInfo &colInfo)
Called on collision of a new KCL type??? This only happens after airtime so far.
void calcBodyCollision(f32 totalScale, f32 sinkDepth, const EGG::Quatf &rot, const EGG::Vector3f &scale)
Checks and acts on collision for each kart hitbox.
void applyBodyCollision(CollisionData &collisionData, const EGG::Vector3f &movement, const EGG::Vector3f &posRel, s32 count)
Saves collision info when vehicle body collision occurs.
void processWheel(CollisionData &collisionData, Hitbox &hitbox, Field::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut)
Processes moving water and floor collision effects.
void processCannon(Field::KCLTypeMask *maskOut)
Checks if we are colliding with a cannon trigger and sets the state flag if so.
Pertains to kart-related functionality.
@ RejectRoad
Collision which causes a change in the player's pos and rot.
Definition Status.hh:64
@ HalfPipeRamp
Set while colliding with zipper KCL.
Definition Status.hh:51
@ Boost
Set while in a boost.
Definition Status.hh:32
@ HWG
Set when "Horizontal Wall Glitch" is active.
Definition Status.hh:86
@ StickyRoad
Like the rBC stairs.
Definition Status.hh:29
@ Accelerate
Accel button is pressed.
Definition Status.hh:9
@ ActionMidZipper
Set when we enter an action while mid-air from a zipper.
Definition Status.hh:88
@ AirtimeOver20
Set after 20 frames of airtime, resets on landing.
Definition Status.hh:28
@ OverZipper
Set while mid-air from a zipper.
Definition Status.hh:52
@ Wall3Collision
Set when colliding with wall KCL COL_TYPE_WALL_2.
Definition Status.hh:18
@ TouchingGround
Set when any part of the vehicle is colliding with floor KCL.
Definition Status.hh:30
@ ZipperInvisibleWall
Set when colliding with invisible wall above a zipper.
Definition Status.hh:54
@ BeforeRespawn
Set on respawn collision, cleared on position snap.
Definition Status.hh:17
@ WallCollision
Set if we are colliding with a wall.
Definition Status.hh:19
A representation of a bounding cuboid.
Definition BoundBox.hh:37
A quaternion, used to represent 3D rotation.
Definition Quat.hh:12
constexpr TBitFlag< T, E > & resetBit(Es... es)
Resets the corresponding bits for the provided enum values.
Definition BitFlag.hh:75
constexpr bool offBit(Es... es) const
Checks if all of the corresponding bits for the provided enum values are off.
Definition BitFlag.hh:143
constexpr TBitFlag< T, E > & setBit(Es... es)
Sets the corresponding bits for the provided enum values.
Definition BitFlag.hh:64
constexpr bool onBit(Es... es) const
Checks if any of the corresponding bits for the provided enum values are on.
Definition BitFlag.hh:110
A 3D float vector.
Definition Vector.hh:107
constexpr Vector3f rej(const Vector3f &rhs) const
The rejection of this vector onto rhs.
Definition Vector.hh:229
constexpr f32 normalise()
Normalizes the vector and returns the original length.
Definition Vector.hh:278
constexpr f32 squaredLength() const
The dot product between the vector and itself.
Definition Vector.hh:201
constexpr Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
Definition Vector.hh:327
constexpr Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Definition Vector.hh:315
constexpr f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
Definition Vector.hh:206
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.
bool bFloor
Set if colliding with KCL which satisfies KCL_TYPE_FLOOR.