Skip to content

Commit

Permalink
Sync zipper mid-air physics
Browse files Browse the repository at this point in the history
  • Loading branch information
malleoz committed Nov 1, 2024
1 parent 0429ecd commit aff6ca9
Show file tree
Hide file tree
Showing 13 changed files with 390 additions and 71 deletions.
8 changes: 4 additions & 4 deletions STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Test Case | Frames | |
[`mc-ng-rta-1-20-357`](https://youtu.be/kG8PvG8K1ZA) | 1222 / 4228 | ❌ | KMP
[`cm-rta-0-25-145`](https://youtu.be/F_RUQVghmuA) | 1919 / 1919 | ✔️ |
[`cm-ng-rta-1-55-264`](https://youtu.be/XxKG3IYWduE) | 651 / 7320 | ❌ | KMP
[`dks-rta-1-44-568`](https://youtu.be/b9hacHlifcw) | 1049 / 6679 | ❌ | Zipper
[`dks-rta-1-44-568`](https://youtu.be/b9hacHlifcw) | 1147 / 6679 | ❌ | Zipper landing boost
[`wgm-rta-0-31-678`](https://youtu.be/VVFXP639DRY) | 2310 / 2310 | ✔️ |
[`wgm-ng-rta-1-49-934`](https://youtu.be/NbhzA2rtZ2A) | 7001 / 7001 | ✔️ |
[`dc-rta-1-28-321`](https://youtu.be/Rs5AK3iHVno) | 1058 / 5705 | ❌ | KMP
Expand All @@ -23,7 +23,7 @@ Test Case | Frames | |
[`gv-rta-0-15-425`](https://youtu.be/bB0oUzdCHTA) | 487 / 1336 | ❌ | KMP?
[`gv-ng-rta-1-32-914`](https://youtu.be/J55Fo2ZMz9M) | 570 / 5981 | ❌ | KMP?
[`gv-nosc-rta-1-50-927`](https://youtu.be/R7oK3U7iZrk) | 567 / 7060 | ❌ | KMP?
[`ddr-rta-1-46-400`](https://youtu.be/nVcVbd4n3yM) | 1559 / 6789 | ❌ | Zipper
[`ddr-rta-1-46-400`](https://youtu.be/nVcVbd4n3yM) | 1578 / 6789 | ❌ | Zipper trick
[`mh-rta-1-42-872`](https://youtu.be/CellUlOYgnc) | 6578 / 6578 | ✔️ |
[`bc-rta-2-08-697`](https://youtu.be/1DEReKemoeI) | 808 / 8126 | ❌ | KMP
[`bc-ng-rta-2-20-001`](https://youtu.be/028nClzy7B4) | 808 / 8803 | ❌ | KMP
Expand All @@ -40,8 +40,8 @@ Test Case | Frames | |
[`rsgb-rta-1-14-513`](https://youtu.be/lgfw-zswqIM) | 4286 / 4878 | ❌ | Respawn position
[`rsgb-ng-rta-1-21-363`](https://youtu.be/SjXUPXT8n8g) | 5288 / 5288 | ✔️ |
[`rds-rta-2-03-525`](https://youtu.be/a9Mnd2W7JXI) | 2306 / 7816 | ❌ | Drawbridge
[`rws-rta-1-31-987`](https://youtu.be/2rDSx5pgQ9A) | 3077 / 5925 | ❌ | Zipper
[`rws-ng-rta-1-48-193`](https://youtu.be/4PU4zpCU_q4) | 1430 / 6897 | ❌ | Zipper
[`rws-rta-1-31-987`](https://youtu.be/2rDSx5pgQ9A) | 3129 / 5925 | ❌ | Zipper landing
[`rws-ng-rta-1-48-193`](https://youtu.be/4PU4zpCU_q4) | 1473 / 6897 | ❌ | Zipper landing
[`rdh-rta-1-30-425`](https://youtu.be/v5Qj0DnqVo0) | 5832 / 5832 | ✔️ |
[`rdh-ng-rta-1-34-237`](https://youtu.be/4Lp-ehOOiGo) | 6060 / 6060 | ✔️ |
[`rbc3-rta-1-55-715`](https://youtu.be/vSbSADDEzEs) | 7347 / 7347 | ✔️ |
Expand Down
3 changes: 2 additions & 1 deletion source/egg/math/Math.hh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

static constexpr f32 F_PI = 3.1415927f; ///< Floating point representation of pi
static constexpr f32 DEG2RAD = 0.017453292f; ///< F_PI / 180.0f. Double precision and casted down.
static constexpr f32 RAD2DEG = 57.2957795f; ///< 180.0f / F_PI. Double precision and casted down.
static constexpr f32 DEG2RAD360 = 0.034906585f; ///< F_PI / 360.0f. Double precision, casted down.
static constexpr f32 RAD2DEG = 57.2957795f; ///< 180.0f / F_PI. Double precision and casted down.
static constexpr f32 DEG2FIDX = 256.0f / 360.0f; ///< Degrees to fixed index
static constexpr f32 RAD2FIDX = 128.0f / F_PI; ///< Radians to fixed index
static constexpr f32 FIDX2RAD = F_PI / 128.0f; ///< Fixed index to radians
Expand Down
5 changes: 5 additions & 0 deletions source/game/field/KCollisionTypes.hh
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ typedef enum {
KCL_TYPE_BIT(COL_TYPE_INVISIBLE_WALL) | \
KCL_TYPE_BIT(COL_TYPE_INVISIBLE_WALL2) & ~KCL_TYPE_BIT(COL_TYPE_ITEM_ROAD))

/// 0x90002000
#define KCL_TYPE_ANY_INVISIBLE_WALL \
(KCL_TYPE_BIT(COL_TYPE_INVISIBLE_WALL2) | KCL_TYPE_BIT(COL_TYPE_HALFPIPE_INVISIBLE_WALL) | \
KCL_TYPE_BIT(COL_TYPE_INVISIBLE_WALL))

namespace Field {
/// @brief The header of the KCL file format. It is 0x3C bytes long (for Mario %Kart Wii).
struct KColHeader {
Expand Down
21 changes: 8 additions & 13 deletions source/game/kart/KartCollide.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,7 @@ void KartCollide::findCollision() {
colData.wallNrm.normalise();
}

f32 fVar1;
if (state()->isBoost()) {
fVar1 = 0.0f;
} else {
fVar1 = 0.05f;
}

f32 dVar14 = fVar1;
fVar1 = 0.01f;
bool resetXZ = dVar14 > 0.0f && state()->isAirtimeOver20() && dynamics()->velocity().y < -50.0f;

FUN_805B72B8(fVar1, dVar14, resetXZ, true);
FUN_80572F4C();
}

/// @stage 2
Expand All @@ -80,7 +69,7 @@ void KartCollide::findCollision() {
void KartCollide::FUN_80572F4C() {
f32 fVar1;

if (state()->isBoost()) {
if (state()->isBoost() || state()->isOverZipper() || state()->isHalfpipeRamp()) {
fVar1 = 0.0f;
} else {
fVar1 = 0.05f;
Expand Down Expand Up @@ -553,6 +542,12 @@ void KartCollide::processFloor(CollisionData &collisionData, Hitbox &hitbox,
state()->setStickyRoad(true);
}

Field::KCLTypeMask halfpipeRampMask = KCL_TYPE_BIT(COL_TYPE_HALFPIPE_RAMP);
if (*maskOut & halfpipeRampMask &&
colDirector->findClosestCollisionEntry(maskOut, halfpipeRampMask)) {
state()->setHalfpipeRamp(true);
}

Field::KCLTypeMask jumpPadMask = KCL_TYPE_BIT(COL_TYPE_JUMP_PAD);
if (*maskOut & jumpPadMask && colDirector->findClosestCollisionEntry(maskOut, jumpPadMask)) {
if (!state()->isTouchingGround() || !state()->isJumpPad()) {
Expand Down
150 changes: 150 additions & 0 deletions source/game/kart/KartHalfPipe.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include "KartHalfPipe.hh"

#include "game/kart/KartDynamics.hh"
#include "game/kart/KartMove.hh"
#include "game/kart/KartParam.hh"
#include "game/kart/KartState.hh"

#include "game/field/CourseColMgr.hh"

#include <egg/math/Math.hh>

namespace Kart {

/// @addr{0x80574114}
KartHalfPipe::KartHalfPipe() = default;

/// @addr{0x80574170}
KartHalfPipe::~KartHalfPipe() = default;

/// @addr{0x805741B0}
void KartHalfPipe::reset() {
m_touchingZipper = false;
m_timer = 0;
}

/// @addr{0x80574340}
void KartHalfPipe::calc() {
constexpr s16 LANDING_BOOST_DELAY = 3;

if (state()->airtime() > 0xF && state()->isOverZipper()) {
m_timer = LANDING_BOOST_DELAY;
}

bool isLanding = state()->isHalfpipeRamp() && m_timer == 0;

if (m_touchingZipper && state()->isAirStart() && velocity().y > 0.0f) {
dynamics()->setExtVel(EGG::Vector3f::zero);
state()->setOverZipper(true);

EGG::Vector3f velNorm = velocity();
velNorm.normalise();
EGG::Vector3f rot = dynamics()->mainRot().rotateVectorInv(velNorm);
m_rot.makeVectorRotation(rot, EGG::Vector3f::ez);
m_prevPos = prevPos();

calcLanding(false);
} else {
if (!state()->isOverZipper()) {
if (state()->isHalfpipeRamp()) {
calcLanding(true);
}
} else {
dynamics()->setGravity(-1.3f);

EGG::Vector3f side = mainRot().rotateVector(EGG::Vector3f::ez);
EGG::Vector3f velNorm = velocity();
velNorm.normalise();

EGG::Quatf sideRot;
sideRot.makeVectorRotation(side, velNorm);
sideRot = sideRot.multSwap(mainRot()).multSwap(m_rot);

f32 t = move()->calcSlerpRate(DEG2RAD360, mainRot(), sideRot);
EGG::Quatf slerp = mainRot().slerpTo(sideRot, t);
dynamics()->setFullRot(slerp);
dynamics()->setMainRot(slerp);

calcLanding(false);
}
}

m_timer = std::max(0, m_timer - 1);

m_touchingZipper = isLanding;
}

/// @addr{0x805752E8}
void KartHalfPipe::calcLanding(bool) {
constexpr f32 LANDING_RADIUS = 150.0f;
constexpr f32 PREVIOUS_RADIUS = 200.0f;
constexpr f32 MIDAIR_RADIUS = 50.0f;
constexpr f32 WALL_RADIUS = 100.0f;

constexpr f32 COS_PI_OVER_4 = 0.707f;

Field::CourseColMgr::CollisionInfo colInfo;
Field::CourseColMgr::CollisionInfo colInfo2;
Field::KCLTypeMask maskOut;
EGG::Vector3f pos;
EGG::Vector3f upLocal;

Field::KCLTypeMask mask = state()->isOverZipper() ?
KCL_TYPE_ANY_INVISIBLE_WALL :
KCL_TYPE_BIT(COL_TYPE_HALFPIPE_INVISIBLE_WALL);
EGG::Vector3f prevPos = m_prevPos + EGG::Vector3f::ey * PREVIOUS_RADIUS;

bool hasDriverFloorCollision = move()->calcZipperCollision(LANDING_RADIUS, bsp().initialYPos,
pos, upLocal, prevPos, &colInfo, &maskOut, KCL_TYPE_DRIVER_FLOOR);

if (state()->isOverZipper()) {
prevPos = hasDriverFloorCollision ? EGG::Vector3f::inf : prevPos;

if (!move()->calcZipperCollision(MIDAIR_RADIUS, bsp().initialYPos, pos, upLocal, prevPos,
&colInfo2, &maskOut, mask)) {
mask |= KCL_TYPE_DRIVER_WALL;
}
}

if (move()->calcZipperCollision(WALL_RADIUS, bsp().initialYPos, pos, upLocal, prevPos,
&colInfo2, &maskOut, mask)) {
if (!(maskOut & ~KCL_TYPE_BIT(COL_TYPE_SPECIAL_WALL))) {}

EGG::Vector3f up = move()->up();
move()->setUp(up + (colInfo2.wallNrm - up) * 0.2f);
move()->setSmoothedUp(move()->up());

f32 yScale = bsp().initialYPos * scale().y;
EGG::Vector3f newPos =
pos + colInfo2.tangentOff + (-WALL_RADIUS * colInfo2.wallNrm) + yScale * upLocal;
newPos.y += move()->hopPosY();

dynamics()->setPos(newPos);
move()->setDir(move()->dir().perpInPlane(move()->up(), true));
move()->setVel1Dir(move()->dir());

if (state()->isOverZipper()) {
state()->setZipperStick(true);
}

m_prevPos = newPos;
} else {
if (state()->isOverZipper()) {
state()->setZipperStick(false);
}
}

if (!hasDriverFloorCollision || state()->airtime() <= 5) {
return;
}

if (colInfo.floorNrm.dot(EGG::Vector3f::ey) <= COS_PI_OVER_4) {
return;
}

if (state()->isOverZipper()) {
state()->setZipperStick(false);
}
}

} // namespace Kart
30 changes: 30 additions & 0 deletions source/game/kart/KartHalfPipe.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include "game/kart/KartObjectProxy.hh"

namespace Kart {

/// @brief Handles the physics and boosts associated with zippers.
/// @nosubgrouping
class KartHalfPipe : public KartObjectProxy {
public:
KartHalfPipe();
~KartHalfPipe();

void reset();
void calc();
void calcLanding(bool);

/// @addr{0x80574108}
static consteval f32 TerminalVelocity() {
return 65.0f;
}

private:
bool m_touchingZipper;
s16 m_timer;
EGG::Quatf m_rot;
EGG::Vector3f m_prevPos;
};

} // namespace Kart
Loading

0 comments on commit aff6ca9

Please sign in to comment.