From 57c80633d17fd83093b88e45eb483150b70381c3 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sun, 19 Apr 2026 02:04:32 -0400 Subject: [PATCH 1/2] rewrite nulldrifttilt into a more general system karttilt can now be used by other systems karttilt now also respects physics more --- src/d_player.h | 4 +- src/k_kart.c | 128 +++++++++++++++++++++++++++++++----------- src/k_kart.h | 3 + src/lua_baselib.c | 24 ++++++++ src/lua_playerlib.c | 30 +++++++++- src/p_saveg.c | 3 +- src/r_patchrotation.c | 2 +- 7 files changed, 157 insertions(+), 37 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 2b6f68603..a3d431976 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -624,9 +624,11 @@ struct player_t INT32 drift_wannaturn; // Turn values the game uses to determine the direction you want to drift. INT32 nulldrift; // When you drift without accelerating, this value ticks up/down depending on your drift's angle. - INT32 nulldrifttilt; // Sliptide-like kart tilting! (Can be toggled off) tic_t nulldrifttime; + fixed_t karttilt; // Generalized sliptide-like kart tilting as an angle_t (Can be toggled off by clients) + fixed_t karttiltmomentum; // angular velocity in rad/sec + // (Delay-drift) - Delay in tics before the final drift angle is determined. // Potentially influenced by player lag. tic_t driftdelay; diff --git a/src/k_kart.c b/src/k_kart.c index 178b76f5d..fcc6686ac 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1849,6 +1849,9 @@ void K_KartMoveAnimation(player_t *player) SINT8 drift = player->drift; UINT8 spr2, glanceofs; + INT16 reversejitter = 0; + INT16 nullrolloffset = 0; + player->mo->rollingxoffset = 0; player->mo->rollingyoffset = 0; @@ -1976,8 +1979,6 @@ void K_KartMoveAnimation(player_t *player) if (onground && drift) { - INT16 reversejitter = 0; - INT16 nullrolloffset = 0; if ((player->jitterlegacy) && (!skincompat)) { // Make RR characters imitate legacy jitters. @@ -1993,15 +1994,6 @@ void K_KartMoveAnimation(player_t *player) else if (turndir == 1) spr2 += 1; // Outwards drift } - - if (abs(player->nulldrifttilt) > 0) - { - //todo: a way for skins(?) to define the tyre offset - nullrolloffset = intsign(drift) * ((36 * FINESINE(abs(player->nulldrifttilt)>>ANGLETOFINESHIFT))/FRACUNIT); - player->mo->rollingyoffset = ((18 * FINESINE(abs(player->nulldrifttilt)>>ANGLETOFINESHIFT))/FRACUNIT); - } - - player->mo->rollingxoffset = reversejitter - nullrolloffset; } else if (glanceofs) spr2 += glanceofs+1; @@ -2037,6 +2029,15 @@ void K_KartMoveAnimation(player_t *player) if (!player->glanceDir) player->pflags &= ~PF_GAINAX; + if (abs(player->karttilt) > 0) + { + //todo: a way for skins(?) to define the tyre offset + nullrolloffset = FixedMul(intsign(player->karttilt) * player->mo->radius, abs(FINESINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))/FRACUNIT; + player->mo->rollingyoffset = FixedMul(player->mo->height/2, abs(FINESINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))/FRACUNIT; + } + + player->mo->rollingxoffset = reversejitter - nullrolloffset; + // Update lastspeed value -- we use to display slow driving frames instead of fast driving when slowing down. player->lastspeed = player->speed; } @@ -4244,7 +4245,7 @@ static void K_SpawnDriftSparks(player_t *player) for (i = 0; i < 2; i++) { fixed_t driftExtraScale = 0; - if (player->nulldrifttilt && + if (player->karttilt && ((player->drift < 0 && (i & 1)) || (player->drift > 0 && !(i & 1)))) { // when tilting during a null-drift don't spawn the sparks for the front tyre @@ -9431,6 +9432,78 @@ static void K_HandleAirDriftDrag(player_t *player, boolean onground) } } +// no float-to-fixed here because this is in the deterministic path +#define FIXEDRADTODEG (3754879) //57.2958 +#define PLAYERTILTCAP (60*FRACUNIT) + +/// @brief simulates applying a torque by applying a force at an offset from the player's centre, is a purely visual effect +/// @param player player to apply angular momentum to +/// @param distance distance from player centre to apply torque to ("radius") +/// @param force linear force to apply to the player to turn into angular velocity, + is CCW +void K_AddTiltImpulse(player_t *player, fixed_t distance, fixed_t force) +{ + fixed_t angularvelocity = FixedDiv(force, abs(distance)); + player->karttiltmomentum += angularvelocity; +} + +/// @brief adds angular velocity to the player's tilt, this is a purely visual effect +/// @param player player to apply angular momentum to +/// @param angularvelocity angular velocity in rad/sec to impart onto the player's sprite +void K_AddTiltMomentum(player_t *player, fixed_t angularvelocity) +{ + player->karttiltmomentum += angularvelocity; +} + +static void K_HandleKartTilt(player_t *player) +{ + SINT8 startsign = intsign(player->karttilt); + fixed_t angulargravity; + fixed_t angaccelgravity; + + if (player->karttilt == 0 && player->karttiltmomentum == 0) + { + return; + } + + // O = (linear velocity) / (distance from CoM) + if (abs(player->karttilt) >= PLAYERTILTCAP) + { + // let's quickly return to reasonable values + player->karttiltmomentum /= 8; + } + + if (player->mo->eflags & MFE_UNDERWATER) + { + angulargravity = abs(player->mo->gravity) * (P_IsObjectOnGround(player->mo) ? 2 : 1); + } + else + { + angulargravity = abs(player->mo->gravity) * (P_IsObjectOnGround(player->mo) ? 5 : 2); + } + angaccelgravity = FixedDiv(angulargravity, abs(FixedMul(player->mo->radius, FINECOSINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))); + + player->karttiltmomentum += angaccelgravity * -intsign(player->karttilt); + player->karttilt += FixedMul(player->karttiltmomentum, FIXEDRADTODEG)/TICRATE; + + // CONS_Printf("angaccelgravity: %4.3f\n", FixedToFloat(angaccelgravity)); + // CONS_Printf("tilt angle: %4.3f\n", FixedToFloat(player->karttilt)); + // CONS_Printf("tilt angle momentum (deg/s): %4.3f\n", FixedToFloat(player->karttiltmomentum) * (M_PI/180.0)); + + if ((player->karttilt < 0 && startsign > 0) || + (player->karttilt > 0 && startsign < 0)) + { + player->karttilt = 0; + player->karttiltmomentum /= 3; + if (abs(player->karttiltmomentum) < FRACUNIT/4) + { + player->karttiltmomentum = 0; + } + } + player->karttilt = CLAMP(player->karttilt, -PLAYERTILTCAP, PLAYERTILTCAP); +} + +#undef FIXEDRADTODEG + static void K_KartDrift(player_t *player, boolean onground) { fixed_t minspeed = (10 * player->mo->scale); @@ -9694,30 +9767,25 @@ static void K_KartDrift(player_t *player, boolean onground) } } + if (player->nulldrift) { if (player->nulldrifttime <= 3*TICRATE/4) { - angle_t tilt; fixed_t dot; + fixed_t angularvelocity; vector2_t fwd = {P_ReturnThrustX(player->mo, player->mo->angle, FRACUNIT), P_ReturnThrustY(player->mo, player->mo->angle, FRACUNIT)}; vector2_t mov = {player->mo->momx, player->mo->momy}; FV2_Normalize(&mov); - dot = FRACUNIT - abs(FV2_Dot(&fwd, &mov)); - tilt = FixedAngle(FixedMul(AngleFixed(ANG20), dot)); + dot = FixedMul(min(player->speed, 32*mapobjectscale), FRACUNIT - abs(FV2_Dot(&fwd, &mov))); - if ((angle_t)abs(player->nulldrifttilt) < tilt) + angularvelocity = FixedDiv(-dot * intsign(player->nulldrift), + abs(FixedMul(player->mo->height/2, abs(FINECOSINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))) + ); + if (abs(player->karttiltmomentum) < FixedMul(abs(angularvelocity), CLAMP(FRACUNIT - FixedDiv(abs(player->nulldrift), 25*FRACUNIT), 0, FRACUNIT))) { - player->nulldrifttilt = (abs(player->nulldrifttilt) + tilt/10) * -player->nulldrift; - } - } - else if (player->nulldrifttilt) - { - player->nulldrifttilt -= (CLAMP(ANG20 - abs(player->nulldrifttilt), ANG1, abs(player->nulldrifttilt))/3) * intsign(player->nulldrifttilt); - if (abs(player->nulldrifttilt) < (ANGLE_11hh / 4)) - { - player->nulldrifttilt = 0; + player->karttiltmomentum = angularvelocity; } } @@ -9727,14 +9795,6 @@ static void K_KartDrift(player_t *player, boolean onground) // Let's have some faith that the driftspark thinker will set this value again player->nulldrift = 0; } - else if (player->nulldrifttilt) - { - player->nulldrifttilt -= (CLAMP(ANG20 - abs(player->nulldrifttilt), ANG1, abs(player->nulldrifttilt))/3) * intsign(player->nulldrifttilt); - if (abs(player->nulldrifttilt) < (ANGLE_11hh / 4)) - { - player->nulldrifttilt = 0; - } - } if (!K_Sliptiding(player)) { @@ -11276,6 +11336,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_KartSlipdash(player, onground); + K_HandleKartTilt(player); + K_RecoveryDash(player); // Quick Turning diff --git a/src/k_kart.h b/src/k_kart.h index 7d7fede88..9e2c7bb08 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -431,6 +431,9 @@ void K_KillAirDrop(player_t *player, p_airdropflags_t airdropflags); boolean K_NullDriftTiltEnabled(void); +void K_AddTiltMomentum(player_t *player, fixed_t angularvelocity); +void K_AddTiltImpulse(player_t *player, fixed_t distance, fixed_t force); + #define RECOVERYDASHADD (TICRATE/2) //og val 2*TICRATE #define RECOVERYDASHCHARGETIME (3*TICRATE/2) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 49eed7cd1..123641900 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -4755,6 +4755,27 @@ static int lib_kMomentum3D(lua_State *L) return 1; } +static int lib_kAddTiltImpulse(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + fixed_t distance = luaL_checkinteger(L, 2); + fixed_t force = luaL_checkinteger(L, 3); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + K_AddTiltImpulse(player, distance, force); + return 0; +} + +static int lib_kAddTiltMomentum(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + fixed_t angularvelocity = luaL_checkinteger(L, 2); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + K_AddTiltMomentum(player, angularvelocity); + return 0; +} + static int lib_kMissileOrKartItem(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -5867,6 +5888,9 @@ static luaL_Reg lib[] = { {"K_GetKartRingCap", lib_kGetKartRingCap}, {"K_IsPlayerRingBurnt", lib_kIsPlayerRingBurnt}, {"K_GetKartRingPower", lib_kGetKartRingPower}, + + {"K_AddTiltImpulse", lib_kAddTiltImpulse}, + {"K_AddTiltMomentum", lib_kAddTiltMomentum}, // kart stuff that's in other places for some reason {"K_IsMissileOrKartItem", lib_kMissileOrKartItem}, diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 9436665dc..1f602d2bf 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -467,7 +467,11 @@ static int lib_lenLocalplayers(lua_State *L) X(recoverydash) \ X(tripwireunstuck) \ X(bumpunstuck) \ - X(public_key) + X(public_key) \ + X(nulldrift) \ + X(nulldrifttime) \ + X(karttilt) \ + X(karttiltmomentum) enum player_e { @@ -1226,6 +1230,18 @@ static int player_get(lua_State *L) case player_bumpunstuck: lua_pushinteger(L, plr->bumpUnstuck); break; + case player_nulldrift: + lua_pushinteger(L, plr->nulldrift); + break; + case player_nulldrifttime: + lua_pushinteger(L, plr->nulldrifttime); + break; + case player_karttilt: + lua_pushinteger(L, plr->karttilt); + break; + case player_karttiltmomentum: + lua_pushinteger(L, plr->karttiltmomentum); + break; #ifdef HWRENDER case player_fovadd: lua_pushfixed(L, plr->fovadd); @@ -2004,6 +2020,18 @@ static int player_set(lua_State *L) case player_bumpunstuck: plr->bumpUnstuck = lua_tointeger(L, 3); break; + case player_nulldrift: + plr->nulldrift = lua_tointeger(L, 3); + break; + case player_nulldrifttime: + plr->nulldrifttime = lua_tointeger(L, 3); + break; + case player_karttilt: + plr->karttilt = lua_tointeger(L, 3); + break; + case player_karttiltmomentum: + plr->karttiltmomentum = lua_tointeger(L, 3); + break; #ifdef HWRENDER case player_fovadd: plr->fovadd = luaL_checkfixed(L, 3); diff --git a/src/p_saveg.c b/src/p_saveg.c index 90f2c27ac..da7feec47 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -641,7 +641,8 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].driftboost); SYNC(players[i].airdriftspeed); SYNC(players[i].nulldrift); - SYNC(players[i].nulldrifttilt); + SYNC(players[i].karttilt); + SYNC(players[i].karttiltmomentum); SYNC(players[i].nulldrifttime); SYNC(players[i].recoverydashcharge); diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c index 8d7b08355..3e4716456 100644 --- a/src/r_patchrotation.c +++ b/src/r_patchrotation.c @@ -65,7 +65,7 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer, bo angle_t viewingAngle = R_PointToAnglePlayer(viewPlayer, player->mo->x, player->mo->y); angle_t angleDelta = (viewingAngle - player->mo->angle); - INT32 nulltilt = (K_NullDriftTiltEnabled()) ? player->nulldrifttilt : 0; + INT32 nulltilt = (K_NullDriftTiltEnabled()) ? FixedAngle(player->karttilt) : 0; INT32 aiztilt = (cv_sliptidetilt.value) ? player->aizdrifttilt : 0; boolean nullBeforeSlip = (abs(aiztilt) < abs(nulltilt)); From af527247e3f16b34ad822dbd8ab0cb1bd837db7c Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sun, 19 Apr 2026 19:07:05 -0400 Subject: [PATCH 2/2] angular dampening, fix issues with null drift tilt also fix the sprite offset not working --- src/k_kart.c | 82 +++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index fcc6686ac..62afa8255 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1849,11 +1849,9 @@ void K_KartMoveAnimation(player_t *player) SINT8 drift = player->drift; UINT8 spr2, glanceofs; - INT16 reversejitter = 0; - INT16 nullrolloffset = 0; - player->mo->rollingxoffset = 0; player->mo->rollingyoffset = 0; + player->mo->spritexoffset = 0; if (!lookback) player->pflags &= ~PF_GAINAX; @@ -1982,7 +1980,7 @@ void K_KartMoveAnimation(player_t *player) if ((player->jitterlegacy) && (!skincompat)) { // Make RR characters imitate legacy jitters. - reversejitter = ((player->driftelapsed & 1) * 2) * -intsign(drift); + player->mo->rollingxoffset = ((player->driftelapsed & 1) * 2) * -intsign(drift); } else { @@ -2029,15 +2027,12 @@ void K_KartMoveAnimation(player_t *player) if (!player->glanceDir) player->pflags &= ~PF_GAINAX; - if (abs(player->karttilt) > 0) + if (player->karttilt) { //todo: a way for skins(?) to define the tyre offset - nullrolloffset = FixedMul(intsign(player->karttilt) * player->mo->radius, abs(FINESINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))/FRACUNIT; - player->mo->rollingyoffset = FixedMul(player->mo->height/2, abs(FINESINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))/FRACUNIT; + player->mo->rollingyoffset = -abs(FixedMul(2 * player->mo->radius, FINESINE(FixedAngle(abs(player->karttilt))>>ANGLETOFINESHIFT))/FRACUNIT); } - player->mo->rollingxoffset = reversejitter - nullrolloffset; - // Update lastspeed value -- we use to display slow driving frames instead of fast driving when slowing down. player->lastspeed = player->speed; } @@ -7168,8 +7163,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->mo->sprxoff = 0; player->mo->spryoff = 0; player->mo->sprzoff = 0; - player->mo->spritexoffset = 0; - player->mo->spriteyoffset = 0; player->mo->bakexoff = 0; player->mo->bakeyoff = 0; @@ -9434,7 +9427,8 @@ static void K_HandleAirDriftDrag(player_t *player, boolean onground) // no float-to-fixed here because this is in the deterministic path #define FIXEDRADTODEG (3754879) //57.2958 -#define PLAYERTILTCAP (60*FRACUNIT) +#define PLAYERTILTCAP (90*FRACUNIT) +#define PLAYERTILTMOMENTUMCAP (4*M_PI_FIXED) /// @brief simulates applying a torque by applying a force at an offset from the player's centre, is a purely visual effect /// @param player player to apply angular momentum to @@ -9464,25 +9458,28 @@ static void K_HandleKartTilt(player_t *player) { return; } - - // O = (linear velocity) / (distance from CoM) - if (abs(player->karttilt) >= PLAYERTILTCAP) - { - // let's quickly return to reasonable values - player->karttiltmomentum /= 8; - } - + if (player->mo->eflags & MFE_UNDERWATER) { + // angular drag + player->karttiltmomentum = FixedMul(player->karttiltmomentum, 92*FRACUNIT/100); angulargravity = abs(player->mo->gravity) * (P_IsObjectOnGround(player->mo) ? 2 : 1); } else { - angulargravity = abs(player->mo->gravity) * (P_IsObjectOnGround(player->mo) ? 5 : 2); + // angular drag + player->karttiltmomentum = FixedMul(player->karttiltmomentum, 98*FRACUNIT/100); + angulargravity = abs(player->mo->gravity) * (P_IsObjectOnGround(player->mo) ? 8 : 3); } + // O = (linear velocity) / (distance from CoM) angaccelgravity = FixedDiv(angulargravity, abs(FixedMul(player->mo->radius, FINECOSINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))); player->karttiltmomentum += angaccelgravity * -intsign(player->karttilt); + if (abs(player->karttiltmomentum) >= PLAYERTILTMOMENTUMCAP) + { + player->karttiltmomentum = PLAYERTILTMOMENTUMCAP * intsign(player->karttiltmomentum); + } + player->karttilt += FixedMul(player->karttiltmomentum, FIXEDRADTODEG)/TICRATE; // CONS_Printf("angaccelgravity: %4.3f\n", FixedToFloat(angaccelgravity)); @@ -9493,16 +9490,22 @@ static void K_HandleKartTilt(player_t *player) (player->karttilt > 0 && startsign < 0)) { player->karttilt = 0; - player->karttiltmomentum /= 3; - if (abs(player->karttiltmomentum) < FRACUNIT/4) + player->karttiltmomentum /= 2; + if (abs(player->karttiltmomentum) < FRACUNIT/3) { player->karttiltmomentum = 0; } } - player->karttilt = CLAMP(player->karttilt, -PLAYERTILTCAP, PLAYERTILTCAP); + else if (abs(player->karttilt) >= PLAYERTILTCAP) + { + player->karttiltmomentum = 0; + player->karttilt = CLAMP(player->karttilt, -PLAYERTILTCAP + 1, PLAYERTILTCAP - 1); + } } #undef FIXEDRADTODEG +#undef PLAYERTILTCAP +#undef PLAYERTILTMOMENTUMCAP static void K_KartDrift(player_t *player, boolean onground) { @@ -9770,23 +9773,22 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->nulldrift) { - if (player->nulldrifttime <= 3*TICRATE/4) + fixed_t dot; + fixed_t angularvelocity; + vector2_t fwd = {P_ReturnThrustX(player->mo, player->mo->angle, FRACUNIT), P_ReturnThrustY(player->mo, player->mo->angle, FRACUNIT)}; + vector2_t mov = {player->mo->momx, player->mo->momy}; + + FV2_Normalize(&fwd); + FV2_Normalize(&mov); + dot = FixedMul(CLAMP(abs(player->speed), 0, 30*mapobjectscale), FRACUNIT - abs(FV2_Dot(&fwd, &mov))); + + angularvelocity = FixedDiv(-dot * intsign(player->nulldrift), + abs(FixedMul(player->mo->height/2, FINECOSINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT))) + ); + angularvelocity = FixedMul(CLAMP(angularvelocity, -M_PI_FIXED, M_PI_FIXED), FINECOSINE(FixedAngle(2 * player->karttilt)>>ANGLETOFINESHIFT)); + if (abs(player->karttiltmomentum) < abs(angularvelocity)) { - fixed_t dot; - fixed_t angularvelocity; - vector2_t fwd = {P_ReturnThrustX(player->mo, player->mo->angle, FRACUNIT), P_ReturnThrustY(player->mo, player->mo->angle, FRACUNIT)}; - vector2_t mov = {player->mo->momx, player->mo->momy}; - - FV2_Normalize(&mov); - dot = FixedMul(min(player->speed, 32*mapobjectscale), FRACUNIT - abs(FV2_Dot(&fwd, &mov))); - - angularvelocity = FixedDiv(-dot * intsign(player->nulldrift), - abs(FixedMul(player->mo->height/2, abs(FINECOSINE(FixedAngle(player->karttilt)>>ANGLETOFINESHIFT)))) - ); - if (abs(player->karttiltmomentum) < FixedMul(abs(angularvelocity), CLAMP(FRACUNIT - FixedDiv(abs(player->nulldrift), 25*FRACUNIT), 0, FRACUNIT))) - { - player->karttiltmomentum = angularvelocity; - } + player->karttiltmomentum = angularvelocity; } // Increment nulldrift timer.