From 1bb3df7435bd9af674617ca150b5af1c07f39d56 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Tue, 3 Feb 2026 23:39:05 -0500 Subject: [PATCH 01/27] two(!!) new modes for airdrop bouncy = a-la ring racers heavy = even faster and gives a small boost on landing --- src/d_netcmd.c | 24 +++------ src/d_player.h | 7 +-- src/k_hud.c | 3 +- src/k_kart.c | 143 +++++++++++++++++++++++++++++++++++-------------- src/k_kart.h | 8 +++ src/p_mobj.h | 2 +- src/p_saveg.c | 2 +- src/p_setup.c | 7 ++- 8 files changed, 131 insertions(+), 65 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3ee92edd0..86329f65c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -613,7 +613,12 @@ consvar_t cv_kartdrafting_closedraft = CVAR_INIT ("kartdrafting_closedraft", "Of consvar_t cv_kartdrafting_closedeadzone = CVAR_INIT ("kartdrafting_closedeadzone", "640", CV_NETVAR|CV_CHEAT, CV_Unsigned, NULL); consvar_t cv_kartdrafting_basedistance = CVAR_INIT ("kartdrafting_basedistance", "2560", CV_NETVAR|CV_CHEAT, CV_Unsigned, NULL); -consvar_t cv_kartairdrop = CVAR_INIT ("kartairdrop", "No", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, CV_YesNo, KartAirDrop_OnChange); +static CV_PossibleValue_t airdrop_cons_t[] = {{AIRDROP_NONE, "Off"}, + {AIRDROP_LIGHT, "Light"}, + {AIRDROP_BOUNCY, "Bouncy"}, + {AIRDROP_HEAVY, "Heavy"}, + {0, NULL}}; +consvar_t cv_kartairdrop = CVAR_INIT ("kartairdrop", "Off", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, airdrop_cons_t, KartAirDrop_OnChange); consvar_t cv_kartairthrust = CVAR_INIT ("kartairthrust", "No", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, CV_YesNo, KartAirThrust_OnChange); consvar_t cv_kartairthrust_reductionrate = CVAR_INIT ("kartairthrust_reductionrate", "0.5", CV_NETVAR|CV_FLOAT|CV_CHEAT|CV_GUARD, CV_Unsigned, NULL); @@ -8473,24 +8478,11 @@ static void KartAirDrop_OnChange(void) { if (leveltime < starttime) { - airdropactive = true; - CONS_Printf(M_GetText("Air Drop has been turned \"On\".\n")); + CONS_Printf(M_GetText("Air Drop has been set to \"%s\".\n"), cv_kartairdrop.string); } else { - CONS_Printf(M_GetText("Air Drop will be turned \"On\" Next Round.\n")); - } - } - else if (K_AirDropActive() && !cv_kartairdrop.value) - { - if (leveltime < starttime) - { - airdropactive = false; - CONS_Printf(M_GetText("Air Drop has been turned \"Off\".\n")); - } - else - { - CONS_Printf(M_GetText("Air Drop will be turned \"Off\" next round.\n")); + CONS_Printf(M_GetText("Air Drop will be set to \"%s\" Next Round.\n"), cv_kartairdrop.string); } } } diff --git a/src/d_player.h b/src/d_player.h index 1842884a5..a134bfff9 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -71,8 +71,9 @@ typedef enum // TODO: Is there a better way to track this? PF_GAINAX = 1<<3, PF_KICKSTARTACCEL = 1<<4, // Accessibility feature: Is accelerate in kickstart mode? - // 1<<5 free - // 1<<6 free + + PF_WANTSAIRDROP = 1<<5, // Wants air drop (active while holding down brake, used for bouncy air drop) + PF_AIRDROP = 1<<6, // Is in Air Drop (activation / deactivation criteria change if light or heavy) PF_WANTSTOJOIN = 1<<7, // Spectator that wants to join @@ -672,7 +673,7 @@ struct player_t UINT8 ringvolume; // When consuming lots of rings, lower the sound a little. UINT8 ringtransparency; // When consuming lots of rings, fade out the rings again. - UINT8 airdroptime; // Tracks how long airdrop has been active, used for delay before airdrop kicks in. + fixed_t airdroptime; // Tracks how long airdrop has been active, used for delay before airdrop kicks in. //boolean ringdrop; // Set when having ringdrop applied. mobj_t *shieldtracer; // Blankart: Shield mobj diff --git a/src/k_hud.c b/src/k_hud.c index 6e6247a2a..34ab6a275 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2542,7 +2542,8 @@ static void K_DrawServerMods(INT32 x, INT32 y) {"Chain Offroad", 0, &cv_kartchainingoffroad, -1, true}, {"Slope Boost", 0, NULL, K_PurpleDriftActive() > 0, true}, {"Drafting", 0, NULL, K_DraftingActive() > 0, true}, - {"Air Drop", 0, NULL, K_AirDropActive() > 0, true}, + {"Light Air Drop", 0, NULL, K_AirDropActive() == AIRDROP_LIGHT, true}, + {"Heavy Air Drop", 0, NULL, K_AirDropActive() >= AIRDROP_HEAVY, true}, {"Air Thrust", 0, NULL, K_AirThrustActive() > 0, true}, {"Recovery Dash", 0, NULL, K_RecoveryDashActive() > 0, true}, {"Bump Spark", 0, &cv_kartbumpspark, -1, true}, diff --git a/src/k_kart.c b/src/k_kart.c index 1da21f895..645088e5e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6978,65 +6978,130 @@ static void K_SpawnFallLines(player_t *player, boolean ringdrop) static void K_AirDrop(player_t *player, ticcmd_t *cmd) { + if (!(cmd->buttons & BT_BRAKE)) + { + player->pflags &= ~PF_WANTSAIRDROP; + } + if (!K_AirDropActive() || P_IsObjectOnGround(player->mo) || P_PlayerInPain(player) || player->loop.radius || (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT) || player->respawn ) { - // Conditions! + if ((player->pflags & PF_AIRDROP)) + { + if (airdropactive == AIRDROP_BOUNCY) + { + const fixed_t maxBounce = mapobjectscale * 10; + const fixed_t minBounce = mapobjectscale; + fixed_t bounce = 2 * abs(player->airdroptime) / 3; + + // Lose speed on bad bounce. + // Slow down more as horizontal momentum shrinks + // compared to vertical momentum. + angle_t a = R_PointToAngle2(0, 0, 4 * maxBounce, player->speed); + fixed_t f = FSIN(a); + player->mo->momx = FixedMul(player->mo->momx, f); + player->mo->momy = FixedMul(player->mo->momy, f); + if (bounce > maxBounce) + { + bounce = maxBounce; + } + else if (bounce < minBounce) + { + bounce = minBounce; + } + + if (player->mo->eflags & MFE_UNDERWATER) + bounce = (117 * bounce) / 200; + + // Nudge the player in their facing angle, and provide a little starting momentum if they need it. + // The bounce is already a strong tradeoff, so this allows it to be used for saves and get you back into flow. + angle_t momangle = K_MomentumAngle(player->mo); + fixed_t minspeed = K_GetKartSpeed(player, false, false)/2; + fixed_t returnspeed = max(FixedHypot(player->mo->momx, player->mo->momy), minspeed); + + // Initial momentum set uses real speed to avoid some weird twitchy behavior at low XY speed + P_InstaThrust(player->mo, momangle, FixedHypot(player->mo->momx, player->mo->momy)/2); + P_Thrust(player->mo, player->mo->angle, returnspeed/2); + + player->mo->z += P_MobjFlip(player->mo); + player->mo->momz = bounce * P_MobjFlip(player->mo); + + if (S_SoundPlaying(player->mo, sfx_s3kd9l)) + { + S_StopSoundByID(player->mo, sfx_s3kd9l); + } + } + else if (airdropactive == AIRDROP_HEAVY) + { + if (player->rings > 0) + { + P_PlayerRingBurst(player, min(2, player->rings)); + } + player->startboost = TICRATE/2; + } + } player->airdroptime = 0; - //player->ringdrop = false; + player->pflags &= ~PF_AIRDROP; return; } - if (cmd->buttons & BT_BRAKE) + if ((cmd->buttons & BT_BRAKE) && !(player->pflags & PF_WANTSAIRDROP)) { - SINT8 airbrakedelay = TICRATE/3; - if (player->airdroptime < airbrakedelay) + if (airdropactive == AIRDROP_HEAVY && !(player->pflags & PF_AIRDROP)) { - /*if (player->rings > 0) - { - player->ringlock = 6; - } + // TODO: heavy air drop should allow keeping current boost stack + S_StartSound(player->mo, sfx_cdfm01); + S_StartSound(player->mo, sfx_s3k51); - player->ringdrop = false;*/ + player->mo->momx = FixedMul(player->mo->momx, 90*FRACUNIT/100); + player->mo->momy = FixedMul(player->mo->momy, 90*FRACUNIT/100); + player->mo->momz -= 8*P_MobjFlip(player->mo)*mapobjectscale; + } + player->pflags |= PF_AIRDROP|PF_WANTSAIRDROP; + } + else + { + if (airdropactive == AIRDROP_LIGHT) + player->pflags &= ~PF_AIRDROP; + } + + if (player->pflags & PF_AIRDROP) + { + + K_SpawnFallLines(player, false); + + if (airdropactive == AIRDROP_BOUNCY) + { + player->airdroptime = player->mo->momz; + if (!S_SoundPlaying(player->mo, sfx_s3kd9l)) + { + S_StartSound(player->mo, sfx_s3kd9l); + } } else { - /*if (!player->ringdelay && player->rings > 0) - { - mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); - P_SetMobjState(ring, S_FASTRING1); - - ring->renderflags |= RF_ADD; - ring->renderflags |= RF_TRANS60; - ring->colorized = true; - ring->color = SKINCOLOR_SILVER; - - ring->extravalue1 = 17; // Ring use animation timer - ring->extravalue2 = 1; // Ring use animation flag - ring->extravalue3 = 1; // Ring airdrop use flag - ring->shadowscale = 0; - P_SetTarget(&ring->target, player->mo); // user - player->rings--; - player->ringdelay = 7; - player->ringlock = 6; - } - - if (player->rings <= 0) - { - player->ringdrop = false; - }*/ - - K_SpawnFallLines(player, /*player->ringdrop*/ false); + if (player->airdroptime < UINT8_MAX) + player->airdroptime++; + } + + if (airdropactive == AIRDROP_HEAVY && player->airdroptime <= TICRATE/4) + { + player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); + } + else if (airdropactive == AIRDROP_LIGHT) + { player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); + K_SpawnAirdropTrail(player); + } + else + { + player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); } } - - if (player->airdroptime < UINT8_MAX) - player->airdroptime++; } // Returns the bumpspark value as an enum. diff --git a/src/k_kart.h b/src/k_kart.h index 5b4d9830c..0d071ae16 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -419,6 +419,14 @@ typedef enum BUMPSPARK_ALL } bumpsparktype_t; +typedef enum +{ + AIRDROP_NONE = 0, + AIRDROP_LIGHT, + AIRDROP_HEAVY, + AIRDROP_BOUNCY, +} airdroptype_t; + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_mobj.h b/src/p_mobj.h index 80f0447ba..01bab7ad1 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -617,7 +617,7 @@ extern boolean slipdashactive; extern boolean purpledriftactive; extern boolean slopeboostactive; extern boolean draftingactive; -extern boolean airdropactive; +extern UINT8 airdropactive; extern boolean airthrustactive; extern boolean recoverydashactive; extern boolean waterskipbricks; diff --git a/src/p_saveg.c b/src/p_saveg.c index cd3bc4a5b..5c027e292 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4239,13 +4239,13 @@ static boolean P_NetSyncMisc(savebuffer_t *save, boolean resending) SYNCBOOLEAN(purpledriftactive); SYNCBOOLEAN(slopeboostactive); SYNCBOOLEAN(draftingactive); - SYNCBOOLEAN(airdropactive); SYNCBOOLEAN(airthrustactive); SYNCBOOLEAN(recoverydashactive); SYNCBOOLEAN(waterskipbricks); SYNCBOOLEAN(itemlittering); SYNCBOOLEAN(itempushing); SYNCBOOLEAN(itemlistactive); + SYNC(airdropactive); SYNC(bumpsparkactive); SYNC(antibumptime); diff --git a/src/p_setup.c b/src/p_setup.c index 35dc4b2f6..ab04a1aa3 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -160,7 +160,7 @@ boolean slipdashactive; boolean purpledriftactive; boolean slopeboostactive; boolean draftingactive; -boolean airdropactive; +UINT8 airdropactive; boolean airthrustactive; boolean recoverydashactive; boolean waterskipbricks; @@ -8183,7 +8183,7 @@ static void P_InitLevelSettings(boolean reloadinggamestate) purpledriftactive = false; slopeboostactive = false; draftingactive = false; - airdropactive = false; + airdropactive = 0; airthrustactive = false; recoverydashactive = false; waterskipbricks = false; @@ -8214,8 +8214,7 @@ static void P_InitLevelSettings(boolean reloadinggamestate) if (cv_kartdrafting.value) draftingactive = true; - if (cv_kartairdrop.value) - airdropactive = true; + airdropactive = (UINT8)cv_kartairdrop.value; if (cv_kartairthrust.value) airthrustactive = true; From 76f17ca97bfb725f717dbf481712ad20a6a508f3 Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 00:51:07 -0500 Subject: [PATCH 02/27] Bounce sound --- src/info/sounds.h | 3 +++ src/k_kart.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/info/sounds.h b/src/info/sounds.h index 2182f9703..2ef254abf 100644 --- a/src/info/sounds.h +++ b/src/info/sounds.h @@ -1020,3 +1020,6 @@ _(rainbr) // Horncode _(horn00) + +// Fastfall bounce +_(vclgna) diff --git a/src/k_kart.c b/src/k_kart.c index 645088e5e..c972594dc 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7033,6 +7033,9 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) { S_StopSoundByID(player->mo, sfx_s3kd9l); } + + // POOMP! + S_StartSound(player->mo, sfx_vclgna); } else if (airdropactive == AIRDROP_HEAVY) { From bdbf38807283502411b5a39c137f7d3748cd9e04 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 4 Feb 2026 01:06:51 -0500 Subject: [PATCH 03/27] disable bouncy fastfall via directive should check light fast fall again --- src/d_netcmd.c | 2 ++ src/k_kart.c | 12 +++++++++++- src/k_kart.h | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 86329f65c..13f2cb7d4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -615,7 +615,9 @@ consvar_t cv_kartdrafting_basedistance = CVAR_INIT ("kartdrafting_basedistance", static CV_PossibleValue_t airdrop_cons_t[] = {{AIRDROP_NONE, "Off"}, {AIRDROP_LIGHT, "Light"}, +#if 0 {AIRDROP_BOUNCY, "Bouncy"}, +#endif {AIRDROP_HEAVY, "Heavy"}, {0, NULL}}; consvar_t cv_kartairdrop = CVAR_INIT ("kartairdrop", "Off", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, airdrop_cons_t, KartAirDrop_OnChange); diff --git a/src/k_kart.c b/src/k_kart.c index c972594dc..8445d22f5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6991,6 +6991,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) { if ((player->pflags & PF_AIRDROP)) { +#if 0 if (airdropactive == AIRDROP_BOUNCY) { const fixed_t maxBounce = mapobjectscale * 10; @@ -7038,12 +7039,17 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) S_StartSound(player->mo, sfx_vclgna); } else if (airdropactive == AIRDROP_HEAVY) +#endif + if (airdropactive == AIRDROP_HEAVY) { if (player->rings > 0) { P_PlayerRingBurst(player, min(2, player->rings)); } player->startboost = TICRATE/2; + + // POOMP! + S_StartSound(player->mo, sfx_vclgna); } } player->airdroptime = 0; @@ -7067,7 +7073,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } else { - if (airdropactive == AIRDROP_LIGHT) + if (airdropactive == AIRDROP_LIGHT && !(cmd->buttons & BT_BRAKE)) player->pflags &= ~PF_AIRDROP; } @@ -7076,6 +7082,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) K_SpawnFallLines(player, false); +#if 0 if (airdropactive == AIRDROP_BOUNCY) { player->airdroptime = player->mo->momz; @@ -7085,6 +7092,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } } else +#endif { if (player->airdroptime < UINT8_MAX) player->airdroptime++; @@ -7095,11 +7103,13 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); } +#if 0 else if (airdropactive == AIRDROP_LIGHT) { player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); } +#endif else { player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); diff --git a/src/k_kart.h b/src/k_kart.h index 0d071ae16..43d041258 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -424,7 +424,7 @@ typedef enum AIRDROP_NONE = 0, AIRDROP_LIGHT, AIRDROP_HEAVY, - AIRDROP_BOUNCY, + // AIRDROP_BOUNCY, } airdroptype_t; #ifdef __cplusplus From 657bbf8521028d74bf475f77cbbf3147a2568168 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 4 Feb 2026 01:08:57 -0500 Subject: [PATCH 04/27] Revert "disable bouncy fastfall via directive" This reverts commit bdbf38807283502411b5a39c137f7d3748cd9e04. --- src/d_netcmd.c | 2 -- src/k_kart.c | 12 +----------- src/k_kart.h | 2 +- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 13f2cb7d4..86329f65c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -615,9 +615,7 @@ consvar_t cv_kartdrafting_basedistance = CVAR_INIT ("kartdrafting_basedistance", static CV_PossibleValue_t airdrop_cons_t[] = {{AIRDROP_NONE, "Off"}, {AIRDROP_LIGHT, "Light"}, -#if 0 {AIRDROP_BOUNCY, "Bouncy"}, -#endif {AIRDROP_HEAVY, "Heavy"}, {0, NULL}}; consvar_t cv_kartairdrop = CVAR_INIT ("kartairdrop", "Off", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, airdrop_cons_t, KartAirDrop_OnChange); diff --git a/src/k_kart.c b/src/k_kart.c index 8445d22f5..c972594dc 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6991,7 +6991,6 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) { if ((player->pflags & PF_AIRDROP)) { -#if 0 if (airdropactive == AIRDROP_BOUNCY) { const fixed_t maxBounce = mapobjectscale * 10; @@ -7039,17 +7038,12 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) S_StartSound(player->mo, sfx_vclgna); } else if (airdropactive == AIRDROP_HEAVY) -#endif - if (airdropactive == AIRDROP_HEAVY) { if (player->rings > 0) { P_PlayerRingBurst(player, min(2, player->rings)); } player->startboost = TICRATE/2; - - // POOMP! - S_StartSound(player->mo, sfx_vclgna); } } player->airdroptime = 0; @@ -7073,7 +7067,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } else { - if (airdropactive == AIRDROP_LIGHT && !(cmd->buttons & BT_BRAKE)) + if (airdropactive == AIRDROP_LIGHT) player->pflags &= ~PF_AIRDROP; } @@ -7082,7 +7076,6 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) K_SpawnFallLines(player, false); -#if 0 if (airdropactive == AIRDROP_BOUNCY) { player->airdroptime = player->mo->momz; @@ -7092,7 +7085,6 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } } else -#endif { if (player->airdroptime < UINT8_MAX) player->airdroptime++; @@ -7103,13 +7095,11 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); } -#if 0 else if (airdropactive == AIRDROP_LIGHT) { player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); } -#endif else { player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); diff --git a/src/k_kart.h b/src/k_kart.h index 43d041258..0d071ae16 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -424,7 +424,7 @@ typedef enum AIRDROP_NONE = 0, AIRDROP_LIGHT, AIRDROP_HEAVY, - // AIRDROP_BOUNCY, + AIRDROP_BOUNCY, } airdroptype_t; #ifdef __cplusplus From 45ae657dec23e73980aeb128f51f7009d850b3bc Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 4 Feb 2026 01:09:46 -0500 Subject: [PATCH 05/27] add poomf sound to heavy too --- src/k_kart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index c972594dc..0e9c0a8e3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7044,6 +7044,9 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) P_PlayerRingBurst(player, min(2, player->rings)); } player->startboost = TICRATE/2; + + // POOMP! + S_StartSound(player->mo, sfx_vclgna); } } player->airdroptime = 0; @@ -7067,7 +7070,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } else { - if (airdropactive == AIRDROP_LIGHT) + if (airdropactive == AIRDROP_LIGHT && !(cmd->buttons & BT_BRAKE)) player->pflags &= ~PF_AIRDROP; } From 0c2d8a8e36d1b5a2b24ab6371112e1042e9babb6 Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 01:45:06 -0500 Subject: [PATCH 06/27] Shove bouncy fastfalling into a pflag Reduce headaches about fastfall bounce by obfuscating its existence a decent bit --- src/d_netcmd.c | 1 - src/d_player.h | 3 +- src/deh_tables.c | 2 +- src/k_kart.c | 157 +++++++++++++++++++++++++---------------------- src/k_kart.h | 1 - 5 files changed, 87 insertions(+), 77 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 86329f65c..ec4f42434 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -615,7 +615,6 @@ consvar_t cv_kartdrafting_basedistance = CVAR_INIT ("kartdrafting_basedistance", static CV_PossibleValue_t airdrop_cons_t[] = {{AIRDROP_NONE, "Off"}, {AIRDROP_LIGHT, "Light"}, - {AIRDROP_BOUNCY, "Bouncy"}, {AIRDROP_HEAVY, "Heavy"}, {0, NULL}}; consvar_t cv_kartairdrop = CVAR_INIT ("kartairdrop", "Off", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, airdrop_cons_t, KartAirDrop_OnChange); diff --git a/src/d_player.h b/src/d_player.h index a134bfff9..0f008eea8 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -65,7 +65,8 @@ typedef enum { PF_GODMODE = 1<<0, // Immortal. - // free: 1<<1 and 1<<2 + // free: 1<<1 + PF_BOUNCYAIRDROP = 1<<2, // Enables bouncy (RR) air-drop for players. // Look back VFX has been spawned // TODO: Is there a better way to track this? diff --git a/src/deh_tables.c b/src/deh_tables.c index f44e346ba..8659db4c1 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -336,7 +336,7 @@ const char *const PLAYERFLAG_LIST[] = { "GODMODE", // Immortal. "\x01", - "\x01", + "BOUNCYAIRDROP", // Makes Heavy Air-Drop act like Ring Racers. // Look back VFX has been spawned // TODO: Is there a better way to track this? diff --git a/src/k_kart.c b/src/k_kart.c index 0e9c0a8e3..d223db309 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6991,62 +6991,65 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) { if ((player->pflags & PF_AIRDROP)) { - if (airdropactive == AIRDROP_BOUNCY) + if (airdropactive == AIRDROP_HEAVY) { - const fixed_t maxBounce = mapobjectscale * 10; - const fixed_t minBounce = mapobjectscale; - fixed_t bounce = 2 * abs(player->airdroptime) / 3; - - // Lose speed on bad bounce. - // Slow down more as horizontal momentum shrinks - // compared to vertical momentum. - angle_t a = R_PointToAngle2(0, 0, 4 * maxBounce, player->speed); - fixed_t f = FSIN(a); - player->mo->momx = FixedMul(player->mo->momx, f); - player->mo->momy = FixedMul(player->mo->momy, f); - if (bounce > maxBounce) + if (player->pflags & PF_BOUNCYAIRDROP) { - bounce = maxBounce; + const fixed_t maxBounce = mapobjectscale * 10; + const fixed_t minBounce = mapobjectscale; + fixed_t bounce = 2 * abs(player->airdroptime) / 3; + + // Lose speed on bad bounce. + // Slow down more as horizontal momentum shrinks + // compared to vertical momentum. + angle_t a = R_PointToAngle2(0, 0, 4 * maxBounce, player->speed); + fixed_t f = FSIN(a); + player->mo->momx = FixedMul(player->mo->momx, f); + player->mo->momy = FixedMul(player->mo->momy, f); + if (bounce > maxBounce) + { + bounce = maxBounce; + } + else if (bounce < minBounce) + { + bounce = minBounce; + } + + if (player->mo->eflags & MFE_UNDERWATER) + bounce = (117 * bounce) / 200; + + // Nudge the player in their facing angle, and provide a little starting momentum if they need it. + // The bounce is already a strong tradeoff, so this allows it to be used for saves and get you back into flow. + angle_t momangle = K_MomentumAngle(player->mo); + fixed_t minspeed = K_GetKartSpeed(player, false, false)/2; + fixed_t returnspeed = max(FixedHypot(player->mo->momx, player->mo->momy), minspeed); + + // Initial momentum set uses real speed to avoid some weird twitchy behavior at low XY speed + P_InstaThrust(player->mo, momangle, FixedHypot(player->mo->momx, player->mo->momy)/2); + P_Thrust(player->mo, player->mo->angle, returnspeed/2); + + player->mo->z += P_MobjFlip(player->mo); + player->mo->momz = bounce * P_MobjFlip(player->mo); + + if (S_SoundPlaying(player->mo, sfx_s3kd9l)) + { + S_StopSoundByID(player->mo, sfx_s3kd9l); + } + + // POOMP! + S_StartSound(player->mo, sfx_vclgna); } - else if (bounce < minBounce) + else { - bounce = minBounce; + if (player->rings > 0) + { + P_PlayerRingBurst(player, min(2, player->rings)); + } + player->startboost = TICRATE/2; + + // POOMP! + S_StartSound(player->mo, sfx_vclgna); } - - if (player->mo->eflags & MFE_UNDERWATER) - bounce = (117 * bounce) / 200; - - // Nudge the player in their facing angle, and provide a little starting momentum if they need it. - // The bounce is already a strong tradeoff, so this allows it to be used for saves and get you back into flow. - angle_t momangle = K_MomentumAngle(player->mo); - fixed_t minspeed = K_GetKartSpeed(player, false, false)/2; - fixed_t returnspeed = max(FixedHypot(player->mo->momx, player->mo->momy), minspeed); - - // Initial momentum set uses real speed to avoid some weird twitchy behavior at low XY speed - P_InstaThrust(player->mo, momangle, FixedHypot(player->mo->momx, player->mo->momy)/2); - P_Thrust(player->mo, player->mo->angle, returnspeed/2); - - player->mo->z += P_MobjFlip(player->mo); - player->mo->momz = bounce * P_MobjFlip(player->mo); - - if (S_SoundPlaying(player->mo, sfx_s3kd9l)) - { - S_StopSoundByID(player->mo, sfx_s3kd9l); - } - - // POOMP! - S_StartSound(player->mo, sfx_vclgna); - } - else if (airdropactive == AIRDROP_HEAVY) - { - if (player->rings > 0) - { - P_PlayerRingBurst(player, min(2, player->rings)); - } - player->startboost = TICRATE/2; - - // POOMP! - S_StartSound(player->mo, sfx_vclgna); } } player->airdroptime = 0; @@ -7056,7 +7059,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if ((cmd->buttons & BT_BRAKE) && !(player->pflags & PF_WANTSAIRDROP)) { - if (airdropactive == AIRDROP_HEAVY && !(player->pflags & PF_AIRDROP)) + if (airdropactive == AIRDROP_HEAVY && !(player->pflags & (PF_AIRDROP|PF_BOUNCYAIRDROP))) { // TODO: heavy air drop should allow keeping current boost stack S_StartSound(player->mo, sfx_cdfm01); @@ -7079,33 +7082,41 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) K_SpawnFallLines(player, false); - if (airdropactive == AIRDROP_BOUNCY) + if (airdropactive == AIRDROP_HEAVY) { - player->airdroptime = player->mo->momz; - if (!S_SoundPlaying(player->mo, sfx_s3kd9l)) + if (player->pflags & PF_BOUNCYAIRDROP) { - S_StartSound(player->mo, sfx_s3kd9l); + player->airdroptime = player->mo->momz; + if (!S_SoundPlaying(player->mo, sfx_s3kd9l)) + { + S_StartSound(player->mo, sfx_s3kd9l); + } + + player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); + } + else + { + if (player->airdroptime < UINT8_MAX) + player->airdroptime++; + + if (player->airdroptime <= TICRATE/4) + { + player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); + K_SpawnAirdropTrail(player); + } } - } - else - { - if (player->airdroptime < UINT8_MAX) - player->airdroptime++; - } - - if (airdropactive == AIRDROP_HEAVY && player->airdroptime <= TICRATE/4) - { - player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); - K_SpawnAirdropTrail(player); } else if (airdropactive == AIRDROP_LIGHT) { - player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); - K_SpawnAirdropTrail(player); - } - else - { - player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); + SINT8 airbrakedelay = TICRATE/3; + if (player->airdroptime >= airbrakedelay) + { + player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); + K_SpawnAirdropTrail(player); + } + + if (player->airdroptime < UINT8_MAX) + player->airdroptime++; } } } diff --git a/src/k_kart.h b/src/k_kart.h index 0d071ae16..b980d8945 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -424,7 +424,6 @@ typedef enum AIRDROP_NONE = 0, AIRDROP_LIGHT, AIRDROP_HEAVY, - AIRDROP_BOUNCY, } airdroptype_t; #ifdef __cplusplus From ac280f78f3cf1c41a549158b5f38d47adb394445 Mon Sep 17 00:00:00 2001 From: yamamama Date: Tue, 3 Feb 2026 21:41:47 -0500 Subject: [PATCH 07/27] Refactor v.getDrawInfo Pushes draw info as a Lua table instead of a set of integers, and also includes the new datafields --- src/lua_hudlib.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 0e15b536e..d4bd71435 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -1507,6 +1507,17 @@ static int libd_getDrawInfo(lua_State *L) drawinfo_t info; rouletteinfo_t rinfo; + // Initialize the extra values that might not get used. + info.hudScale = 0; + info.hudScaleFloat = 0.0f; + + rinfo.crop.x = 0; + rinfo.crop.y = 0; + rinfo.flags = 0; + rinfo.offset = 0; + rinfo.spacing = 0; + rinfo.intSpacing = 0; + switch(option) { case huddrawinfo_item: K_getItemBoxDrawinfo(&info, &rinfo);break; case huddrawinfo_gametypeinfo: K_getLapsDrawinfo(&info); break; @@ -1519,11 +1530,50 @@ static int libd_getDrawInfo(lua_State *L) return 0; // unreachable } + // There's no easy solution to pushing temporary metadata to Lua, + // so to avoid memory leaks, I'm making a massive table with all the data. Sue me. + + lua_createtable(L, 0, 5); + lua_pushinteger(L, info.x); + lua_setfield(L, -2, "x"); + lua_pushinteger(L, info.y); + lua_setfield(L, -2, "y"); + lua_pushinteger(L, info.flags); + lua_setfield(L, -2, "flags"); + lua_pushinteger(L, info.flipamount); - return 4; + lua_setfield(L, -2, "flipamount"); + + lua_pushinteger(L, FLOAT_TO_FIXED(info.hudScaleFloat)); // Can't use floats in BLUA + lua_setfield(L, -2, "hudscale"); + + lua_createtable(L, 0, 5); + + lua_createtable(L, 0, 2); + lua_pushinteger(L, rinfo.crop.x); + lua_setfield(L, -2, "x"); + + lua_pushinteger(L, rinfo.crop.y); + lua_setfield(L, -2, "y"); + lua_setfield(L, -2, "crop"); + + lua_pushinteger(L, rinfo.offset); + lua_setfield(L, -2, "offset"); + + lua_pushinteger(L, rinfo.spacing); + lua_setfield(L, -2, "spacing"); + + lua_pushinteger(L, rinfo.intSpacing); + lua_setfield(L, -2, "intspacing"); + + lua_pushinteger(L, rinfo.flags); + lua_setfield(L, -2, "flags"); + lua_setfield(L, -2, "roulette"); + + return 1; } // Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int From c87c5fd8cdd9b253924424036b5a30f5eedf841f Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 10:10:19 -0500 Subject: [PATCH 08/27] Allow (U)INT64 savebuffer read+write and netsync Sincerely hoping there's a valid reason this codebase is allergic to 64-bit integers like this --- src/byteptr.h | 3 +++ src/p_saveg.c | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/byteptr.h b/src/byteptr.h index 8141a2bd8..881e90a2f 100644 --- a/src/byteptr.h +++ b/src/byteptr.h @@ -32,6 +32,7 @@ extern "C" { #define WRITEUINT16(p,b) do { UINT16 *p_tmp = ( UINT16 *)p; const UINT16 tv = ( UINT16)(b); memcpy(p, &tv, sizeof( UINT16)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) #define WRITEINT32(p,b) do { INT32 *p_tmp = ( INT32 *)p; const INT32 tv = ( INT32)(b); memcpy(p, &tv, sizeof( INT32)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) #define WRITEUINT32(p,b) do { UINT32 *p_tmp = ( UINT32 *)p; const UINT32 tv = ( UINT32)(b); memcpy(p, &tv, sizeof( UINT32)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEINT64(p,b) do { INT64 *p_tmp = ( INT64 *)p; const INT64 tv = ( INT64)(b); memcpy(p, &tv, sizeof( INT64)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) #define WRITEUINT64(p,b) do { UINT64 *p_tmp = ( UINT64 *)p; const UINT64 tv = ( UINT64)(b); memcpy(p, &tv, sizeof( UINT64)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) #define WRITECHAR(p,b) do { char *p_tmp = ( char *)p; const char tv = ( char)(b); memcpy(p, &tv, sizeof( char)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) #define WRITEFIXED(p,b) do { fixed_t *p_tmp = (fixed_t *)p; const fixed_t tv = (fixed_t)(b); memcpy(p, &tv, sizeof(fixed_t)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) @@ -45,6 +46,7 @@ extern "C" { #define READUINT16(p) ({ const UINT16 *p_tmp = (const UINT16 *)p; UINT16 b; memcpy(&b, p, sizeof( UINT16)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) #define READINT32(p) ({ const INT32 *p_tmp = (const INT32 *)p; INT32 b; memcpy(&b, p, sizeof( INT32)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) #define READUINT32(p) ({ const UINT32 *p_tmp = (const UINT32 *)p; UINT32 b; memcpy(&b, p, sizeof( UINT32)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READINT64(p) ({ const INT64 *p_tmp = (const INT64 *)p; INT64 b; memcpy(&b, p, sizeof( INT64)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) #define READUINT64(p) ({ const UINT64 *p_tmp = (const UINT64 *)p; UINT64 b; memcpy(&b, p, sizeof( UINT64)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) #define READCHAR(p) ({ const char *p_tmp = (const char *)p; char b; memcpy(&b, p, sizeof( char)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) #define READFIXED(p) ({ const fixed_t *p_tmp = (const fixed_t *)p; fixed_t b; memcpy(&b, p, sizeof(fixed_t)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) @@ -56,6 +58,7 @@ extern "C" { #define READUINT16(p) ((const UINT16*) (*(const void**)(&(p)) = (const void*)&((const UINT16*) (p))[1]))[-1] #define READINT32(p) ((const INT32*) (*(const void**)(&(p)) = (const void*)&((const INT32*) (p))[1]))[-1] #define READUINT32(p) ((const UINT32*) (*(const void**)(&(p)) = (const void*)&((const UINT32*) (p))[1]))[-1] +#define READINT64(p) ((const INT64*) (*(const void**)(&(p)) = (const void*)&((const INT64*) (p))[1]))[-1] #define READUINT64(p) ((const UINT64*) (*(const void**)(&(p)) = (const void*)&((const UINT64*) (p))[1]))[-1] #define READCHAR(p) ((const char*) (*(const void**)(&(p)) = (const void*)&((const char*) (p))[1]))[-1] #define READFIXED(p) ((const fixed_t*)(*(const void**)(&(p)) = (const void*)&((const fixed_t*)(p))[1]))[-1] diff --git a/src/p_saveg.c b/src/p_saveg.c index 5c027e292..c0c158be8 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -117,6 +117,28 @@ static INT32 P_SyncINT32(savebuffer_t *save, INT32 v) return READINT32(save->p); } +static UINT64 P_SyncUINT64(savebuffer_t *save, UINT32 v) +{ + if (save->write) + { + WRITEUINT64(save->p, v); + return v; + } + else + return READUINT64(save->p); +} + +static INT64 P_SyncINT64(savebuffer_t *save, INT32 v) +{ + if (save->write) + { + WRITEINT64(save->p, v); + return v; + } + else + return READINT64(save->p); +} + /* static char P_SyncChar(savebuffer_t *save, char v) { @@ -362,9 +384,11 @@ static void P_RelinkWaypoint(savebuffer_t *save, waypoint_t **ptr) SINT8: P_SyncSINT8, \ INT16: P_SyncINT16, \ INT32: P_SyncINT32, \ + INT64: P_SyncINT64, \ UINT8: P_SyncUINT8, \ UINT16: P_SyncUINT16, \ UINT32: P_SyncUINT32, \ + UINT64: P_SyncUINT64, \ player_t *: P_SyncPlayer, \ state_t *: P_SyncState, \ ffloor_t *: P_SyncFFloor, \ From 2a84eda311430366f4ac0caa679f22fcc484c024 Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 09:52:04 -0500 Subject: [PATCH 09/27] Move airdrop-related flags to its own variable --- src/d_player.h | 18 +++++++++++++----- src/k_kart.c | 20 ++++++++++---------- src/m_cheat.c | 1 + src/p_saveg.c | 1 + 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 0f008eea8..d64edf356 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -65,16 +65,14 @@ typedef enum { PF_GODMODE = 1<<0, // Immortal. - // free: 1<<1 - PF_BOUNCYAIRDROP = 1<<2, // Enables bouncy (RR) air-drop for players. + // free: 1<<1 and 1<<2 // Look back VFX has been spawned // TODO: Is there a better way to track this? PF_GAINAX = 1<<3, PF_KICKSTARTACCEL = 1<<4, // Accessibility feature: Is accelerate in kickstart mode? - - PF_WANTSAIRDROP = 1<<5, // Wants air drop (active while holding down brake, used for bouncy air drop) - PF_AIRDROP = 1<<6, // Is in Air Drop (activation / deactivation criteria change if light or heavy) + + // free: 1<<5 and 1<<6 PF_WANTSTOJOIN = 1<<7, // Spectator that wants to join @@ -113,6 +111,13 @@ typedef enum PF_SLIDING = 1<<31, // For lua compat, don't use! } pflags_t; +typedef enum +{ + PAF_WANTSAIRDROP = 1<<0, // Wants air drop (active while holding down brake, used for bouncy air drop) + PAF_AIRDROP = 1<<1, // Is in Air Drop (activation / deactivation criteria change if light or heavy) + PAF_BOUNCYAIRDROP = 1<<2, // Enables bouncy (Ring Racers) air-drop for players. +} p_airdropflags_t; + typedef enum { // Are animation frames playing? @@ -508,6 +513,9 @@ struct player_t // See pflags_t, above. pflags_t pflags; + // Airdrop-exclusive bitflags. + p_airdropflags_t airdropflags; + // playing animation. panim_t panim; diff --git a/src/k_kart.c b/src/k_kart.c index d223db309..efad1547c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6980,7 +6980,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) { if (!(cmd->buttons & BT_BRAKE)) { - player->pflags &= ~PF_WANTSAIRDROP; + player->airdropflags &= ~PAF_WANTSAIRDROP; } if (!K_AirDropActive() || P_IsObjectOnGround(player->mo) @@ -6989,11 +6989,11 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) || player->respawn ) { - if ((player->pflags & PF_AIRDROP)) + if ((player->airdropflags & PAF_AIRDROP)) { if (airdropactive == AIRDROP_HEAVY) { - if (player->pflags & PF_BOUNCYAIRDROP) + if (player->airdropflags & PAF_BOUNCYAIRDROP) { const fixed_t maxBounce = mapobjectscale * 10; const fixed_t minBounce = mapobjectscale; @@ -7053,13 +7053,13 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } } player->airdroptime = 0; - player->pflags &= ~PF_AIRDROP; + player->airdropflags &= ~PAF_AIRDROP; return; } - if ((cmd->buttons & BT_BRAKE) && !(player->pflags & PF_WANTSAIRDROP)) + if ((cmd->buttons & BT_BRAKE) && !(player->airdropflags & PAF_WANTSAIRDROP)) { - if (airdropactive == AIRDROP_HEAVY && !(player->pflags & (PF_AIRDROP|PF_BOUNCYAIRDROP))) + if (airdropactive == AIRDROP_HEAVY && !(player->airdropflags & (PAF_AIRDROP|PAF_BOUNCYAIRDROP))) { // TODO: heavy air drop should allow keeping current boost stack S_StartSound(player->mo, sfx_cdfm01); @@ -7069,22 +7069,22 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->mo->momy = FixedMul(player->mo->momy, 90*FRACUNIT/100); player->mo->momz -= 8*P_MobjFlip(player->mo)*mapobjectscale; } - player->pflags |= PF_AIRDROP|PF_WANTSAIRDROP; + player->airdropflags |= PAF_AIRDROP|PAF_WANTSAIRDROP; } else { if (airdropactive == AIRDROP_LIGHT && !(cmd->buttons & BT_BRAKE)) - player->pflags &= ~PF_AIRDROP; + player->airdropflags &= ~PAF_AIRDROP; } - if (player->pflags & PF_AIRDROP) + if (player->airdropflags & PAF_AIRDROP) { K_SpawnFallLines(player, false); if (airdropactive == AIRDROP_HEAVY) { - if (player->pflags & PF_BOUNCYAIRDROP) + if (player->airdropflags & PAF_BOUNCYAIRDROP) { player->airdroptime = player->mo->momz; if (!S_SoundPlaying(player->mo, sfx_s3kd9l)) diff --git a/src/m_cheat.c b/src/m_cheat.c index 8dcd8d068..308356218 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1190,6 +1190,7 @@ void Command_ObjectPlace_f(void) // Remove ALL flags and motion. P_UnsetThingPosition(players[0].mo); players[0].pflags = 0; + players[0].airdropflags = 0; players[0].mo->flags2 = 0; players[0].mo->eflags = 0; players[0].mo->flags = (MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP); diff --git a/src/p_saveg.c b/src/p_saveg.c index c0c158be8..1ce77719c 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -501,6 +501,7 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].playerstate); SYNC(players[i].pflags); + SYNC(players[i].airdropflags); SYNC(players[i].panim); SYNCBOOLEAN(players[i].spectator); SYNC(players[i].spectatewait); From bf6f86a5fb78a0e66ccaf3c5b51ad183be7ead1c Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 11:21:25 -0500 Subject: [PATCH 10/27] Expose airdrop flags to Lua --- src/deh_lua.c | 12 ++++++++++++ src/deh_tables.c | 10 +++++++++- src/deh_tables.h | 1 + src/lua_playerlib.c | 7 +++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/deh_lua.c b/src/deh_lua.c index 56fc35502..023a08958 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -178,6 +178,18 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word) if (mathlib) return luaL_error(L, "playerflag '%s' could not be found.\n", word); return 0; } + else if (fastncmp("PAF_", word, 4)) { + p = word+4; + for (i = 0; AIRDROPFLAG_LIST[i]; i++) + { + if (fastcmp(p, AIRDROPFLAG_LIST[i])) { + CacheAndPushConstant(L, word, ((lua_Integer)1<airdroptime); break; + case player_airdropflags: + lua_pushinteger(L, plr->airdropflags); + break; /*case player_ringdrop: lua_pushinteger(L, plr->ringdrop); break;*/ @@ -1613,6 +1617,9 @@ static int player_set(lua_State *L) case player_airdroptime: plr->airdroptime = luaL_checkinteger(L, 3); break; + case player_airdropflags: + plr->airdropflags = luaL_checkinteger(L, 3); + break; /*case player_ringdrop: plr->ringdrop = luaL_checkinteger(L, 3); break;*/ From f2f9ad98f20b8a5a6499c3652068dfb1e233256a Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 11:21:41 -0500 Subject: [PATCH 11/27] Fix some errors with KartAirDrop_OnChange --- src/d_netcmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ec4f42434..3af66c799 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -8478,10 +8478,11 @@ static void KartAirDrop_OnChange(void) if (leveltime < starttime) { CONS_Printf(M_GetText("Air Drop has been set to \"%s\".\n"), cv_kartairdrop.string); + airdropactive = (UINT8)cv_kartairdrop.value; } else { - CONS_Printf(M_GetText("Air Drop will be set to \"%s\" Next Round.\n"), cv_kartairdrop.string); + CONS_Printf(M_GetText("Air Drop will be set to \"%s\" next round.\n"), cv_kartairdrop.string); } } } From 1e868d6bfae7d28a76b45e6cb47dfde8cd1ff411 Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 11:31:45 -0500 Subject: [PATCH 12/27] Make this consistent with bumpspark --- src/d_netcmd.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3af66c799..a3d0b41db 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -8473,17 +8473,14 @@ static void KartAirDrop_OnChange(void) return; } - if (!K_AirDropActive() && cv_kartairdrop.value) + if (leveltime < starttime) { - if (leveltime < starttime) - { - CONS_Printf(M_GetText("Air Drop has been set to \"%s\".\n"), cv_kartairdrop.string); - airdropactive = (UINT8)cv_kartairdrop.value; - } - else - { - CONS_Printf(M_GetText("Air Drop will be set to \"%s\" next round.\n"), cv_kartairdrop.string); - } + CONS_Printf(M_GetText("Air Drop has been set to \"%s\".\n"), cv_kartairdrop.string); + airdropactive = (UINT8)cv_kartairdrop.value; + } + else + { + CONS_Printf(M_GetText("Air Drop will be set to \"%s\" next round.\n"), cv_kartairdrop.string); } } From f719824f378c9fef00fc3dd97003932038592fc1 Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 12:11:24 -0500 Subject: [PATCH 13/27] Revise Heavy Air Drop's sound design, add ringspill to PAF_BOUNCYAIRDROP --- src/k_kart.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index efad1547c..e851b1665 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7036,6 +7036,28 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) S_StopSoundByID(player->mo, sfx_s3kd9l); } + if (player->rings > 0) + { + // Spill some rings with every bounce. Ring Drop lives! + + // Convert the bounce data to floats. It's goddamned 2026. + float fbounce, fmaxbounce; + + fbounce = FIXED_TO_FLOAT(max(0, bounce - minBounce)); + fmaxbounce = FIXED_TO_FLOAT(maxBounce - minBounce); + + fbounce /= fmaxbounce; + + INT32 ringloss = (INT32)(3.0f * fbounce); + + // Better bounces means more rings lost. + if (ringloss) + { + P_PlayerRingBurst(player, min(ringloss, player->rings)); + } + } + + // POOMP! S_StartSound(player->mo, sfx_vclgna); } @@ -7047,6 +7069,9 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } player->startboost = TICRATE/2; + // Take off! + S_StartSound(player->mo, sfx_s3k81); + // POOMP! S_StartSound(player->mo, sfx_vclgna); } @@ -7062,7 +7087,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (airdropactive == AIRDROP_HEAVY && !(player->airdropflags & (PAF_AIRDROP|PAF_BOUNCYAIRDROP))) { // TODO: heavy air drop should allow keeping current boost stack - S_StartSound(player->mo, sfx_cdfm01); + S_StartSound(player->mo, sfx_s3k66); S_StartSound(player->mo, sfx_s3k51); player->mo->momx = FixedMul(player->mo->momx, 90*FRACUNIT/100); From 5895959265d65a43ebde03d11fe0d97d8856c4e8 Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 13:07:12 -0500 Subject: [PATCH 14/27] Kill bouncy air drop Too contentious of a mechanic to hardcode --- src/k_kart.c | 110 ++++++--------------------------------------------- 1 file changed, 13 insertions(+), 97 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index e851b1665..392267ec1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6993,88 +6993,17 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) { if (airdropactive == AIRDROP_HEAVY) { - if (player->airdropflags & PAF_BOUNCYAIRDROP) + if (player->rings > 0) { - const fixed_t maxBounce = mapobjectscale * 10; - const fixed_t minBounce = mapobjectscale; - fixed_t bounce = 2 * abs(player->airdroptime) / 3; - - // Lose speed on bad bounce. - // Slow down more as horizontal momentum shrinks - // compared to vertical momentum. - angle_t a = R_PointToAngle2(0, 0, 4 * maxBounce, player->speed); - fixed_t f = FSIN(a); - player->mo->momx = FixedMul(player->mo->momx, f); - player->mo->momy = FixedMul(player->mo->momy, f); - if (bounce > maxBounce) - { - bounce = maxBounce; - } - else if (bounce < minBounce) - { - bounce = minBounce; - } - - if (player->mo->eflags & MFE_UNDERWATER) - bounce = (117 * bounce) / 200; - - // Nudge the player in their facing angle, and provide a little starting momentum if they need it. - // The bounce is already a strong tradeoff, so this allows it to be used for saves and get you back into flow. - angle_t momangle = K_MomentumAngle(player->mo); - fixed_t minspeed = K_GetKartSpeed(player, false, false)/2; - fixed_t returnspeed = max(FixedHypot(player->mo->momx, player->mo->momy), minspeed); - - // Initial momentum set uses real speed to avoid some weird twitchy behavior at low XY speed - P_InstaThrust(player->mo, momangle, FixedHypot(player->mo->momx, player->mo->momy)/2); - P_Thrust(player->mo, player->mo->angle, returnspeed/2); - - player->mo->z += P_MobjFlip(player->mo); - player->mo->momz = bounce * P_MobjFlip(player->mo); - - if (S_SoundPlaying(player->mo, sfx_s3kd9l)) - { - S_StopSoundByID(player->mo, sfx_s3kd9l); - } - - if (player->rings > 0) - { - // Spill some rings with every bounce. Ring Drop lives! - - // Convert the bounce data to floats. It's goddamned 2026. - float fbounce, fmaxbounce; - - fbounce = FIXED_TO_FLOAT(max(0, bounce - minBounce)); - fmaxbounce = FIXED_TO_FLOAT(maxBounce - minBounce); - - fbounce /= fmaxbounce; - - INT32 ringloss = (INT32)(3.0f * fbounce); - - // Better bounces means more rings lost. - if (ringloss) - { - P_PlayerRingBurst(player, min(ringloss, player->rings)); - } - } - - - // POOMP! - S_StartSound(player->mo, sfx_vclgna); + P_PlayerRingBurst(player, min(2, player->rings)); } - else - { - if (player->rings > 0) - { - P_PlayerRingBurst(player, min(2, player->rings)); - } - player->startboost = TICRATE/2; + player->startboost = TICRATE/2; - // Take off! - S_StartSound(player->mo, sfx_s3k81); + // Take off! + S_StartSound(player->mo, sfx_s3k81); - // POOMP! - S_StartSound(player->mo, sfx_vclgna); - } + // POOMP! + S_StartSound(player->mo, sfx_vclgna); } } player->airdroptime = 0; @@ -7084,7 +7013,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if ((cmd->buttons & BT_BRAKE) && !(player->airdropflags & PAF_WANTSAIRDROP)) { - if (airdropactive == AIRDROP_HEAVY && !(player->airdropflags & (PAF_AIRDROP|PAF_BOUNCYAIRDROP))) + if (airdropactive == AIRDROP_HEAVY && !(player->airdropflags & (PAF_AIRDROP))) { // TODO: heavy air drop should allow keeping current boost stack S_StartSound(player->mo, sfx_s3k66); @@ -7109,26 +7038,13 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (airdropactive == AIRDROP_HEAVY) { - if (player->airdropflags & PAF_BOUNCYAIRDROP) - { - player->airdroptime = player->mo->momz; - if (!S_SoundPlaying(player->mo, sfx_s3kd9l)) - { - S_StartSound(player->mo, sfx_s3kd9l); - } + if (player->airdroptime < UINT8_MAX) + player->airdroptime++; - player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); - } - else + if (player->airdroptime <= TICRATE/4) { - if (player->airdroptime < UINT8_MAX) - player->airdroptime++; - - if (player->airdroptime <= TICRATE/4) - { - player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); - K_SpawnAirdropTrail(player); - } + player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); + K_SpawnAirdropTrail(player); } } else if (airdropactive == AIRDROP_LIGHT) From 0d7d6a60eb816db76b88391ff57d5b1b75442a8a Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 4 Feb 2026 18:23:40 -0500 Subject: [PATCH 15/27] Almost forgot the bitflags --- src/d_player.h | 1 - src/deh_tables.c | 1 - 2 files changed, 2 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index d64edf356..310354fdd 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -115,7 +115,6 @@ typedef enum { PAF_WANTSAIRDROP = 1<<0, // Wants air drop (active while holding down brake, used for bouncy air drop) PAF_AIRDROP = 1<<1, // Is in Air Drop (activation / deactivation criteria change if light or heavy) - PAF_BOUNCYAIRDROP = 1<<2, // Enables bouncy (Ring Racers) air-drop for players. } p_airdropflags_t; typedef enum diff --git a/src/deh_tables.c b/src/deh_tables.c index e0100cf1c..abaa80275 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -428,7 +428,6 @@ struct int_const_s const PLAYERFLAG_ALIASES[] = { const char *const AIRDROPFLAG_LIST[] = { "WANTSAIRDROP", // Wants air drop (active while holding down brake, used for bouncy air drop) "AIRDROP", // Is in Air Drop (activation / deactivation criteria change if light or heavy) - "BOUNCYAIRDROP", // Enables bouncy (Ring Racers) air-drop for players. NULL // stop loop here. }; From 667616ed0c1687ea839d6f900c0c711989c45698 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 4 Feb 2026 20:55:35 -0500 Subject: [PATCH 16/27] feel-tuning todo: make holding brake before going airborne not activate heavy drop (maybe allow 2 ticks of leeway?) --- src/d_main.cpp | 2 +- src/d_player.h | 9 +++---- src/k_kart.c | 71 +++++++++++++++++++++++++++++++++----------------- src/p_user.c | 5 ++-- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 5d00a3442..7e4518156 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -93,7 +93,7 @@ #define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291 #define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b #define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9 -#define ASSET_HASH_MAIN_PK3 0xebb499f36118246b +#define ASSET_HASH_MAIN_PK3 0x7af447cd2ac5ee74 #define ASSET_HASH_MAPPATCH_PK3 0xbbc2c6a7a685da3a #define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461 #ifdef USE_PATCH_FILE diff --git a/src/d_player.h b/src/d_player.h index 310354fdd..8f28b683e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -205,6 +205,8 @@ typedef enum khud_boostcam, // Camera push forward on boost khud_destboostcam, // Ditto khud_timeovercam, // Camera timer for leaving behind or not + khud_heavydropcam, // Camera timer for heavy air drop + khud_postdropcam, // Camera timer for landing after heavy air drop (both timers superposition for feel) // Sounds khud_enginesnd, // Engine sound offset this player is using. @@ -512,9 +514,6 @@ struct player_t // See pflags_t, above. pflags_t pflags; - // Airdrop-exclusive bitflags. - p_airdropflags_t airdropflags; - // playing animation. panim_t panim; @@ -681,8 +680,8 @@ struct player_t UINT8 ringvolume; // When consuming lots of rings, lower the sound a little. UINT8 ringtransparency; // When consuming lots of rings, fade out the rings again. - fixed_t airdroptime; // Tracks how long airdrop has been active, used for delay before airdrop kicks in. - //boolean ringdrop; // Set when having ringdrop applied. + tic_t airdroptime; // Tracks how long airdrop has been active, used for delay before airdrop kicks in. + p_airdropflags_t airdropflags; // Airdrop-exclusive bitflags. mobj_t *shieldtracer; // Blankart: Shield mobj diff --git a/src/k_kart.c b/src/k_kart.c index 392267ec1..6c9091755 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6982,6 +6982,43 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) { player->airdropflags &= ~PAF_WANTSAIRDROP; } + if (!(player->airdropflags & PAF_AIRDROP)) + { + if (player->karthud[khud_heavydropcam] > 0) + { + player->karthud[khud_heavydropcam] = max(player->karthud[khud_heavydropcam] - 5, 0); + } + } + if (player->karthud[khud_postdropcam] > 0) + { + player->karthud[khud_postdropcam]--; + } + + if (P_IsObjectOnGround(player->mo)) + { + if (K_AirDropActive() && (player->airdropflags & PAF_AIRDROP)) + { + if (airdropactive == AIRDROP_HEAVY) + { + if (player->rings > 0 && player->airdroptime > TICRATE/4) + { + P_PlayerRingBurst(player, min(2, player->rings)); + } + if (player->airdroptime > 1) + { + player->startboost = TICRATE/2; + + // Take off! + S_StartSound(player->mo, sfx_shrpgo); + } + + // POOMP! + S_StartSound(player->mo, sfx_vclgna); + + player->karthud[khud_postdropcam] = min(2*player->karthud[khud_heavydropcam], TICRATE/4); + } + } + } if (!K_AirDropActive() || P_IsObjectOnGround(player->mo) || P_PlayerInPain(player) || player->loop.radius @@ -6989,23 +7026,6 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) || player->respawn ) { - if ((player->airdropflags & PAF_AIRDROP)) - { - if (airdropactive == AIRDROP_HEAVY) - { - if (player->rings > 0) - { - P_PlayerRingBurst(player, min(2, player->rings)); - } - player->startboost = TICRATE/2; - - // Take off! - S_StartSound(player->mo, sfx_s3k81); - - // POOMP! - S_StartSound(player->mo, sfx_vclgna); - } - } player->airdroptime = 0; player->airdropflags &= ~PAF_AIRDROP; return; @@ -7016,7 +7036,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (airdropactive == AIRDROP_HEAVY && !(player->airdropflags & (PAF_AIRDROP))) { // TODO: heavy air drop should allow keeping current boost stack - S_StartSound(player->mo, sfx_s3k66); + S_StartSound(player->mo, sfx_s3k77); S_StartSound(player->mo, sfx_s3k51); player->mo->momx = FixedMul(player->mo->momx, 90*FRACUNIT/100); @@ -7038,14 +7058,18 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (airdropactive == AIRDROP_HEAVY) { - if (player->airdroptime < UINT8_MAX) - player->airdroptime++; - if (player->airdroptime <= TICRATE/4) { player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); } + else + { + player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); + } + + if (player->karthud[khud_heavydropcam] < TICRATE) + player->karthud[khud_heavydropcam]++; } else if (airdropactive == AIRDROP_LIGHT) { @@ -7055,10 +7079,9 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); } - - if (player->airdroptime < UINT8_MAX) - player->airdroptime++; } + if (player->airdroptime < UINT8_MAX) + player->airdroptime++; } } diff --git a/src/p_user.c b/src/p_user.c index a58cb63be..e226f3803 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3364,6 +3364,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } z += player->cameraOffset; + z -= 2*(player->karthud[khud_heavydropcam] + player->karthud[khud_postdropcam])*FRACUNIT*P_MobjFlip(mo); // point viewed by the camera // this point is just 64 unit forward the player @@ -3415,13 +3416,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (mo->eflags & MFE_VERTICALFLIP) { - angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, mo->z + mo->height - player->mo->height + player->cameraOffset); + angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, mo->z + mo->height - player->mo->height + player->cameraOffset + 2*player->karthud[khud_heavydropcam]*FRACUNIT); if (thiscam->pitch < ANGLE_180 && thiscam->pitch > angle) angle += (thiscam->pitch - angle)/2; } else { - angle = R_PointToAngle2(0, thiscam->z, dist, mo->z + player->mo->height + player->cameraOffset); + angle = R_PointToAngle2(0, thiscam->z, dist, mo->z + player->mo->height + player->cameraOffset - 2*player->karthud[khud_heavydropcam]*FRACUNIT); if (thiscam->pitch >= ANGLE_180 && thiscam->pitch < angle) angle -= (angle - thiscam->pitch)/2; } From fc455e9079e227a14634fab5bda626f2267fc6fb Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 5 Feb 2026 20:50:38 -0500 Subject: [PATCH 17/27] make holding brake while grounded not trigger airdrop --- src/d_player.h | 6 ++++-- src/k_kart.c | 42 +++++++++++++++++++++++++++++++++--------- src/p_saveg.c | 3 ++- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 8f28b683e..bd194f8ae 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -113,8 +113,9 @@ typedef enum typedef enum { - PAF_WANTSAIRDROP = 1<<0, // Wants air drop (active while holding down brake, used for bouncy air drop) + PAF_WANTSAIRDROP = 1<<0, // Wants Air drop (input pressed and buffered) PAF_AIRDROP = 1<<1, // Is in Air Drop (activation / deactivation criteria change if light or heavy) + PAF_AIRDROPINPUT = 1<<2, // Air drop input held } p_airdropflags_t; typedef enum @@ -680,7 +681,8 @@ struct player_t UINT8 ringvolume; // When consuming lots of rings, lower the sound a little. UINT8 ringtransparency; // When consuming lots of rings, fade out the rings again. - tic_t airdroptime; // Tracks how long airdrop has been active, used for delay before airdrop kicks in. + tic_t airdroptime; // Tracks how long the player has been in airdrop. In light airdrop, handles the delay before it kicks in. + tic_t airdropbuffer; // Time during which heavy air drop will instantly trigger upon going airborne. p_airdropflags_t airdropflags; // Airdrop-exclusive bitflags. mobj_t *shieldtracer; // Blankart: Shield mobj diff --git a/src/k_kart.c b/src/k_kart.c index 6c9091755..897bbc5dc 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6978,10 +6978,34 @@ static void K_SpawnFallLines(player_t *player, boolean ringdrop) static void K_AirDrop(player_t *player, ticcmd_t *cmd) { - if (!(cmd->buttons & BT_BRAKE)) + if (player->airdropbuffer > 0) + { + player->airdropbuffer--; + } + + if ((cmd->buttons & BT_BRAKE)) + { + if (!(player->airdropflags & PAF_AIRDROPINPUT)) + { + player->airdropflags |= PAF_WANTSAIRDROP|PAF_AIRDROPINPUT; + if (P_IsObjectOnGround(player->mo)) + player->airdropbuffer = 2; + } + } + else + { + player->airdropflags &= ~PAF_AIRDROPINPUT; + if (airdropactive == AIRDROP_LIGHT) + { + player->airdropflags &= ~PAF_WANTSAIRDROP; + } + } + + if (P_IsObjectOnGround(player->mo) && player->airdropbuffer == 0) { player->airdropflags &= ~PAF_WANTSAIRDROP; } + if (!(player->airdropflags & PAF_AIRDROP)) { if (player->karthud[khud_heavydropcam] > 0) @@ -7031,10 +7055,12 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) return; } - if ((cmd->buttons & BT_BRAKE) && !(player->airdropflags & PAF_WANTSAIRDROP)) + if (player->airdropflags & PAF_WANTSAIRDROP) { if (airdropactive == AIRDROP_HEAVY && !(player->airdropflags & (PAF_AIRDROP))) { + player->airdropbuffer = 0; + // TODO: heavy air drop should allow keeping current boost stack S_StartSound(player->mo, sfx_s3k77); S_StartSound(player->mo, sfx_s3k51); @@ -7043,22 +7069,22 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->mo->momy = FixedMul(player->mo->momy, 90*FRACUNIT/100); player->mo->momz -= 8*P_MobjFlip(player->mo)*mapobjectscale; } - player->airdropflags |= PAF_AIRDROP|PAF_WANTSAIRDROP; + player->airdropflags |= PAF_AIRDROP; } else { - if (airdropactive == AIRDROP_LIGHT && !(cmd->buttons & BT_BRAKE)) + if (airdropactive == AIRDROP_LIGHT) player->airdropflags &= ~PAF_AIRDROP; } if (player->airdropflags & PAF_AIRDROP) { - + tic_t airbrakedelay = TICRATE/3; K_SpawnFallLines(player, false); if (airdropactive == AIRDROP_HEAVY) { - if (player->airdroptime <= TICRATE/4) + if (player->airdroptime <= airbrakedelay) { player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); @@ -7073,15 +7099,13 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } else if (airdropactive == AIRDROP_LIGHT) { - SINT8 airbrakedelay = TICRATE/3; if (player->airdroptime >= airbrakedelay) { player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); } } - if (player->airdroptime < UINT8_MAX) - player->airdroptime++; + player->airdroptime++; } } diff --git a/src/p_saveg.c b/src/p_saveg.c index 1ce77719c..cb8d27cd1 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -710,7 +710,8 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].ringtransparency); SYNC(players[i].airdroptime); - //SYNCBOOLEAN(players[i].ringdrop); + SYNC(players[i].airdropbuffer); + SYNC(players[i].airdropflags); RSYNC(players[i].shieldtracer); From d915af3cb5e38544673c7619aabb5002d7ce75cf Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 5 Feb 2026 21:33:45 -0500 Subject: [PATCH 18/27] "fusion" air drop --- src/d_netcmd.c | 1 + src/d_player.h | 9 +++-- src/k_kart.c | 104 +++++++++++++++++++++++++------------------------ src/k_kart.h | 1 + 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a3d0b41db..2f1fdbb80 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -616,6 +616,7 @@ consvar_t cv_kartdrafting_basedistance = CVAR_INIT ("kartdrafting_basedistance", static CV_PossibleValue_t airdrop_cons_t[] = {{AIRDROP_NONE, "Off"}, {AIRDROP_LIGHT, "Light"}, {AIRDROP_HEAVY, "Heavy"}, + {AIRDROP_FUSION, "Fusion"}, {0, NULL}}; consvar_t cv_kartairdrop = CVAR_INIT ("kartairdrop", "Off", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, airdrop_cons_t, KartAirDrop_OnChange); diff --git a/src/d_player.h b/src/d_player.h index bd194f8ae..29640e1c9 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -113,9 +113,12 @@ typedef enum typedef enum { - PAF_WANTSAIRDROP = 1<<0, // Wants Air drop (input pressed and buffered) - PAF_AIRDROP = 1<<1, // Is in Air Drop (activation / deactivation criteria change if light or heavy) - PAF_AIRDROPINPUT = 1<<2, // Air drop input held + PAF_AIRDROPINPUT = 1<<0, // Air drop input held + PAF_WANTSAIRDROP = 1<<1, // Wants Air drop (input pressed and buffered) + PAF_AIRDROP_LIGHT = 1<<2, // Is in Light Air Drop + PAF_AIRDROP_HEAVY = 1<<3, // Is in Heavy Air Drop + + PAF_AIRDROP_MASK = (PAF_AIRDROP_LIGHT|PAF_AIRDROP_HEAVY), // Is in any Air Drop state } p_airdropflags_t; typedef enum diff --git a/src/k_kart.c b/src/k_kart.c index 897bbc5dc..464827e6c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6978,6 +6978,8 @@ static void K_SpawnFallLines(player_t *player, boolean ringdrop) static void K_AirDrop(player_t *player, ticcmd_t *cmd) { + const tic_t airbrakedelay = TICRATE/3; + if (player->airdropbuffer > 0) { player->airdropbuffer--; @@ -6995,7 +6997,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) else { player->airdropflags &= ~PAF_AIRDROPINPUT; - if (airdropactive == AIRDROP_LIGHT) + if (airdropactive == AIRDROP_LIGHT || airdropactive == AIRDROP_FUSION) { player->airdropflags &= ~PAF_WANTSAIRDROP; } @@ -7006,7 +7008,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->airdropflags &= ~PAF_WANTSAIRDROP; } - if (!(player->airdropflags & PAF_AIRDROP)) + if (!(player->airdropflags & PAF_AIRDROP_HEAVY)) { if (player->karthud[khud_heavydropcam] > 0) { @@ -7018,29 +7020,26 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->karthud[khud_postdropcam]--; } - if (P_IsObjectOnGround(player->mo)) + if (K_AirDropActive() && P_IsObjectOnGround(player->mo)) { - if (K_AirDropActive() && (player->airdropflags & PAF_AIRDROP)) + if ((player->airdropflags & PAF_AIRDROP_HEAVY)) { - if (airdropactive == AIRDROP_HEAVY) + if (player->rings > 0 && player->airdroptime > TICRATE/4) { - if (player->rings > 0 && player->airdroptime > TICRATE/4) - { - P_PlayerRingBurst(player, min(2, player->rings)); - } - if (player->airdroptime > 1) - { - player->startboost = TICRATE/2; - - // Take off! - S_StartSound(player->mo, sfx_shrpgo); - } - - // POOMP! - S_StartSound(player->mo, sfx_vclgna); - - player->karthud[khud_postdropcam] = min(2*player->karthud[khud_heavydropcam], TICRATE/4); + P_PlayerRingBurst(player, min(2, player->rings)); } + if (player->airdroptime > 1) + { + player->startboost = TICRATE/2; + + // Take off! + S_StartSound(player->mo, sfx_shrpgo); + } + + // POOMP! + S_StartSound(player->mo, sfx_vclgna); + + player->karthud[khud_postdropcam] = min(2*player->karthud[khud_heavydropcam], TICRATE/4); } } @@ -7051,14 +7050,20 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) ) { player->airdroptime = 0; - player->airdropflags &= ~PAF_AIRDROP; + player->airdropflags &= ~PAF_AIRDROP_MASK; return; } if (player->airdropflags & PAF_WANTSAIRDROP) { - if (airdropactive == AIRDROP_HEAVY && !(player->airdropflags & (PAF_AIRDROP))) + if ((airdropactive == AIRDROP_LIGHT) || (airdropactive == AIRDROP_FUSION && (cmd->buttons & BT_ACCELERATE))) { + player->airdropflags |= PAF_AIRDROP_LIGHT; + } + else if (!(player->airdropflags & (PAF_AIRDROP_HEAVY))) // in fusion, fires if brake is held but not accel + { + player->airdropflags |= PAF_AIRDROP_HEAVY; + player->airdroptime = 0; player->airdropbuffer = 0; // TODO: heavy air drop should allow keeping current boost stack @@ -7067,43 +7072,42 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->mo->momx = FixedMul(player->mo->momx, 90*FRACUNIT/100); player->mo->momy = FixedMul(player->mo->momy, 90*FRACUNIT/100); - player->mo->momz -= 8*P_MobjFlip(player->mo)*mapobjectscale; + player->mo->momz -= 12*P_MobjFlip(player->mo)*mapobjectscale; } - player->airdropflags |= PAF_AIRDROP; } else { - if (airdropactive == AIRDROP_LIGHT) - player->airdropflags &= ~PAF_AIRDROP; + if (airdropactive == AIRDROP_LIGHT || airdropactive == AIRDROP_FUSION) + { + player->airdropflags &= ~PAF_AIRDROP_LIGHT; + } } - if (player->airdropflags & PAF_AIRDROP) + // heavy air drop always overrides light air drop + if (player->airdropflags & PAF_AIRDROP_HEAVY) { - tic_t airbrakedelay = TICRATE/3; K_SpawnFallLines(player, false); - - if (airdropactive == AIRDROP_HEAVY) + if (player->airdroptime <= airbrakedelay) { - if (player->airdroptime <= airbrakedelay) - { - player->mo->momz -= FixedMul(4*gravity, mapobjectscale)*P_MobjFlip(player->mo); - K_SpawnAirdropTrail(player); - } - else - { - player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); - } - - if (player->karthud[khud_heavydropcam] < TICRATE) - player->karthud[khud_heavydropcam]++; + player->mo->momz -= FixedMul(5*gravity, mapobjectscale)*P_MobjFlip(player->mo); + K_SpawnAirdropTrail(player); } - else if (airdropactive == AIRDROP_LIGHT) + else { - if (player->airdroptime >= airbrakedelay) - { - player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); - K_SpawnAirdropTrail(player); - } + player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); + } + + player->airdroptime++; + if (player->karthud[khud_heavydropcam] < TICRATE) + player->karthud[khud_heavydropcam]++; + } + else if (player->airdropflags & PAF_AIRDROP_LIGHT) + { + K_SpawnFallLines(player, false); + if (player->airdroptime >= airbrakedelay) + { + player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); + K_SpawnAirdropTrail(player); } player->airdroptime++; } @@ -11728,7 +11732,7 @@ boolean K_DraftingActive(void) boolean K_AirDropActive(void) { - if (airdropactive) + if (airdropactive > AIRDROP_NONE) { // Air Drop is enabled! return true; diff --git a/src/k_kart.h b/src/k_kart.h index b980d8945..2f9a5d776 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -424,6 +424,7 @@ typedef enum AIRDROP_NONE = 0, AIRDROP_LIGHT, AIRDROP_HEAVY, + AIRDROP_FUSION, } airdroptype_t; #ifdef __cplusplus From de2e58204db23853b3dd6c75ec45c7b73d4ec3d9 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 5 Feb 2026 22:25:32 -0500 Subject: [PATCH 19/27] scaling heavy drop boost --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 464827e6c..e08e91e58 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7030,7 +7030,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } if (player->airdroptime > 1) { - player->startboost = TICRATE/2; + player->dropdash = TICRATE/4 + min(TICRATE, ((FixedDiv(player->airdroptime, TICRATE) * TICRATE) / FRACUNIT)); // Take off! S_StartSound(player->mo, sfx_shrpgo); From 74338f19961b267b0130d4bd060b25cad790eb38 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 6 Feb 2026 00:40:11 -0500 Subject: [PATCH 20/27] Use a more impactful sound for heavyairdrop --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index e08e91e58..f6e413a7f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7037,7 +7037,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } // POOMP! - S_StartSound(player->mo, sfx_vclgna); + S_StartSound(player->mo, sfx_doord2); player->karthud[khud_postdropcam] = min(2*player->karthud[khud_heavydropcam], TICRATE/4); } From cc146b502155be669a97819502d953b29aa456d5 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 6 Feb 2026 01:07:50 -0500 Subject: [PATCH 21/27] Fix stiffness feeling of airdrop inputs and fix landingboost timer --- src/d_player.h | 4 ++-- src/k_kart.c | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 29640e1c9..1196387fc 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -684,8 +684,8 @@ struct player_t UINT8 ringvolume; // When consuming lots of rings, lower the sound a little. UINT8 ringtransparency; // When consuming lots of rings, fade out the rings again. - tic_t airdroptime; // Tracks how long the player has been in airdrop. In light airdrop, handles the delay before it kicks in. - tic_t airdropbuffer; // Time during which heavy air drop will instantly trigger upon going airborne. + INT32 airdroptime; // Tracks how long the player has been in airdrop. In light airdrop, handles the delay before it kicks in. + INT32 airdropbuffer; // Time during which heavy air drop will instantly trigger upon going airborne. p_airdropflags_t airdropflags; // Airdrop-exclusive bitflags. mobj_t *shieldtracer; // Blankart: Shield mobj diff --git a/src/k_kart.c b/src/k_kart.c index f6e413a7f..b451ccbdf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6978,7 +6978,7 @@ static void K_SpawnFallLines(player_t *player, boolean ringdrop) static void K_AirDrop(player_t *player, ticcmd_t *cmd) { - const tic_t airbrakedelay = TICRATE/3; + const INT32 airbrakedelay = TICRATE/3; if (player->airdropbuffer > 0) { @@ -7030,7 +7030,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) } if (player->airdroptime > 1) { - player->dropdash = TICRATE/4 + min(TICRATE, ((FixedDiv(player->airdroptime, TICRATE) * TICRATE) / FRACUNIT)); + player->startboost = TICRATE/4 + min(TICRATE, ((FixedDiv(player->airdroptime, TICRATE) * TICRATE) / FRACUNIT)); // Take off! S_StartSound(player->mo, sfx_shrpgo); @@ -7097,20 +7097,21 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) player->mo->momz -= FixedMul(2*gravity, mapobjectscale)*P_MobjFlip(player->mo); } - player->airdroptime++; if (player->karthud[khud_heavydropcam] < TICRATE) player->karthud[khud_heavydropcam]++; } else if (player->airdropflags & PAF_AIRDROP_LIGHT) { - K_SpawnFallLines(player, false); if (player->airdroptime >= airbrakedelay) { player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); + K_SpawnFallLines(player, false); K_SpawnAirdropTrail(player); } - player->airdroptime++; } + + if (K_AirDropActive() && !P_IsObjectOnGround(player->mo)) + player->airdroptime++; } // Returns the bumpspark value as an enum. From d59c1060c7b395d9658ed0b7a0c4ca4fe32e6373 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Fri, 6 Feb 2026 11:22:23 -0500 Subject: [PATCH 22/27] separate light drop delay and heavy drop high power --- src/d_player.h | 3 ++- src/k_kart.c | 13 ++++++++++--- src/p_saveg.c | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 1196387fc..16325844f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -684,7 +684,8 @@ struct player_t UINT8 ringvolume; // When consuming lots of rings, lower the sound a little. UINT8 ringtransparency; // When consuming lots of rings, fade out the rings again. - INT32 airdroptime; // Tracks how long the player has been in airdrop. In light airdrop, handles the delay before it kicks in. + INT32 airdroppredelay; // In light airdrop, handles the delay before it can be activated once going airborne. + INT32 airdroptime; // Tracks how long the player has been in airdrop. INT32 airdropbuffer; // Time during which heavy air drop will instantly trigger upon going airborne. p_airdropflags_t airdropflags; // Airdrop-exclusive bitflags. diff --git a/src/k_kart.c b/src/k_kart.c index b451ccbdf..0f3bd6349 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6978,6 +6978,7 @@ static void K_SpawnFallLines(player_t *player, boolean ringdrop) static void K_AirDrop(player_t *player, ticcmd_t *cmd) { + const INT32 heavydrophi = TICRATE/3; const INT32 airbrakedelay = TICRATE/3; if (player->airdropbuffer > 0) @@ -7050,6 +7051,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) ) { player->airdroptime = 0; + player->airdroppredelay = 0; player->airdropflags &= ~PAF_AIRDROP_MASK; return; } @@ -7080,6 +7082,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (airdropactive == AIRDROP_LIGHT || airdropactive == AIRDROP_FUSION) { player->airdropflags &= ~PAF_AIRDROP_LIGHT; + player->airdroptime = 0; } } @@ -7087,7 +7090,7 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (player->airdropflags & PAF_AIRDROP_HEAVY) { K_SpawnFallLines(player, false); - if (player->airdroptime <= airbrakedelay) + if (player->airdroptime <= heavydrophi) { player->mo->momz -= FixedMul(5*gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnAirdropTrail(player); @@ -7099,19 +7102,23 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (player->karthud[khud_heavydropcam] < TICRATE) player->karthud[khud_heavydropcam]++; + + player->airdroptime++; } else if (player->airdropflags & PAF_AIRDROP_LIGHT) { - if (player->airdroptime >= airbrakedelay) + if (player->airdroppredelay >= airbrakedelay) { player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo); K_SpawnFallLines(player, false); K_SpawnAirdropTrail(player); } + + player->airdroptime++; } if (K_AirDropActive() && !P_IsObjectOnGround(player->mo)) - player->airdroptime++; + player->airdroppredelay++; } // Returns the bumpspark value as an enum. diff --git a/src/p_saveg.c b/src/p_saveg.c index cb8d27cd1..9acfa53e0 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -709,6 +709,7 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].ringvolume); SYNC(players[i].ringtransparency); + SYNC(players[i].airdroppredelay); SYNC(players[i].airdroptime); SYNC(players[i].airdropbuffer); SYNC(players[i].airdropflags); From 922bb9b873c73944886bc0c7d7bf959c4c0132a4 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Fri, 6 Feb 2026 12:15:02 -0500 Subject: [PATCH 23/27] fix air drop time being constantly reset in fusion --- src/k_kart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 0f3bd6349..b79ac1ddd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7082,7 +7082,8 @@ static void K_AirDrop(player_t *player, ticcmd_t *cmd) if (airdropactive == AIRDROP_LIGHT || airdropactive == AIRDROP_FUSION) { player->airdropflags &= ~PAF_AIRDROP_LIGHT; - player->airdroptime = 0; + if (!(player->airdropflags & PAF_AIRDROP_HEAVY)) + player->airdroptime = 0; } } From 9e54882b593b19223b8e2584ec776d49184f1851 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 6 Feb 2026 14:26:42 -0500 Subject: [PATCH 24/27] Fix up mod displays for airdrop --- src/k_hud.c | 7 ++++--- src/k_kart.c | 10 ++-------- src/k_kart.h | 2 +- src/lua_baselib.c | 3 ++- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 34ab6a275..893efb279 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2523,7 +2523,7 @@ void K_SetScoreboardModStatus(const char *name, SINT8 active) CONS_Alert(CONS_WARNING, "Server mod '%s' does not exist so status cannot be changed.\n", name); } -#define BASEMODS 16 +#define BASEMODS 19 static void K_DrawServerMods(INT32 x, INT32 y) { UINT8 i, j; @@ -2542,8 +2542,9 @@ static void K_DrawServerMods(INT32 x, INT32 y) {"Chain Offroad", 0, &cv_kartchainingoffroad, -1, true}, {"Slope Boost", 0, NULL, K_PurpleDriftActive() > 0, true}, {"Drafting", 0, NULL, K_DraftingActive() > 0, true}, - {"Light Air Drop", 0, NULL, K_AirDropActive() == AIRDROP_LIGHT, true}, - {"Heavy Air Drop", 0, NULL, K_AirDropActive() >= AIRDROP_HEAVY, true}, + {"Light AirDrop", 0, NULL, K_AirDropActive() == AIRDROP_LIGHT, true}, + {"Heavy AirDrop", 0, NULL, K_AirDropActive() == AIRDROP_HEAVY, true}, + {"Fus. AirDrop", 0, NULL, K_AirDropActive() == AIRDROP_FUSION, true}, {"Air Thrust", 0, NULL, K_AirThrustActive() > 0, true}, {"Recovery Dash", 0, NULL, K_RecoveryDashActive() > 0, true}, {"Bump Spark", 0, &cv_kartbumpspark, -1, true}, diff --git a/src/k_kart.c b/src/k_kart.c index b79ac1ddd..09bd99cac 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11739,15 +11739,9 @@ boolean K_DraftingActive(void) return false; } -boolean K_AirDropActive(void) +SINT8 K_AirDropActive(void) { - if (airdropactive > AIRDROP_NONE) - { - // Air Drop is enabled! - return true; - } - - return false; + return airdropactive; } boolean K_AirThrustActive(void) diff --git a/src/k_kart.h b/src/k_kart.h index 2f9a5d776..72e8d9998 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -381,7 +381,7 @@ boolean K_ChainingActive(void); boolean K_SlipdashActive(void); boolean K_SlopeBoostActive(void); boolean K_DraftingActive(void); -boolean K_AirDropActive(void); +SINT8 K_AirDropActive(void); boolean K_AirThrustActive(void); boolean K_RecoveryDashActive(void); boolean K_WaterskipBricksActive(void); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 791efe442..593f6cb0d 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -10,6 +10,7 @@ /// \file lua_baselib.c /// \brief basic functions for Lua scripting +#include "blua/lua.h" #include "doomdef.h" #include "p_local.h" #include "p_setup.h" // So we can have P_SetupLevelSky @@ -4406,7 +4407,7 @@ static int lib_kDraftingActive(lua_State *L) // Checks if Air Drop is active. static int lib_kAirDropActive(lua_State *L) { - lua_pushboolean(L, K_AirDropActive()); + lua_pushinteger(L, K_AirDropActive()); return 1; } From c13f92a3b9cf8d042c6f3735c56a5ea345c15407 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 6 Feb 2026 14:33:52 -0500 Subject: [PATCH 25/27] Expose airdrop stuff to Lua --- src/deh_tables.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index abaa80275..e55e178d1 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -426,8 +426,12 @@ struct int_const_s const PLAYERFLAG_ALIASES[] = { }; const char *const AIRDROPFLAG_LIST[] = { - "WANTSAIRDROP", // Wants air drop (active while holding down brake, used for bouncy air drop) - "AIRDROP", // Is in Air Drop (activation / deactivation criteria change if light or heavy) + "AIRDROPINPUT", // Wants air drop (active while holding down brake, used for bouncy air drop) + "WANTSAIRDROP", // Is in Air Drop (activation / deactivation criteria change if light or heavy) + "AIRDROP_LIGHT", // Is in Light Air Drop + "AIRDROP_HEAVY", // Is in Heavy Air Drop + + "AIRDROP_MASK", // Is in any Air Drop state NULL // stop loop here. }; @@ -1806,5 +1810,11 @@ struct int_const_s const INT_CONST[] = { {"BLANKART", 1}, + // airdroptype_t + {"AIRDROP_NONE", AIRDROP_NONE}, + {"AIRDROP_LIGHT", AIRDROP_LIGHT}, + {"AIRDROP_HEAVY", AIRDROP_HEAVY}, + {"AIRDROP_FUSION", AIRDROP_FUSION}, + {NULL,0} }; From 485a54479fe23948dd0fe6a49b2de7cefcf3dc8b Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 6 Feb 2026 14:40:12 -0500 Subject: [PATCH 26/27] Expose new player airdrop fields to Lua --- src/lua_playerlib.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 0dc19f581..29ae5fdb5 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -390,7 +390,9 @@ static int lib_lenLocalplayers(lua_State *L) X(nextringaward) \ X(ringvolume) \ X(ringtransparency) \ + X(airdroppredelay) \ X(airdroptime) \ + X(airdropbuffer) \ X(airdropflags) \ X(ringdrop) \ X(shieldtracer) \ @@ -830,9 +832,15 @@ static int player_get(lua_State *L) case player_ringtransparency: lua_pushinteger(L, plr->ringtransparency); break; + case player_airdroppredelay: + lua_pushinteger(L, plr->airdroppredelay); + break; case player_airdroptime: lua_pushinteger(L, plr->airdroptime); break; + case player_airdropbuffer: + lua_pushinteger(L, plr->airdropbuffer); + break; case player_airdropflags: lua_pushinteger(L, plr->airdropflags); break; @@ -1614,9 +1622,15 @@ static int player_set(lua_State *L) case player_ringtransparency: plr->ringtransparency = luaL_checkinteger(L, 3); break; + case player_airdroppredelay: + plr->airdroppredelay = luaL_checkinteger(L, 3); + break; case player_airdroptime: plr->airdroptime = luaL_checkinteger(L, 3); break; + case player_airdropbuffer: + plr->airdropbuffer = luaL_checkinteger(L, 3); + break; case player_airdropflags: plr->airdropflags = luaL_checkinteger(L, 3); break; From 0db116083557e690a44b722aeca6c41aca06d967 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 6 Feb 2026 16:13:47 -0500 Subject: [PATCH 27/27] Don't allow heavy airdrop to waterskip --- src/k_kart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 09bd99cac..3857d1dd6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11961,6 +11961,11 @@ void K_QuiteSaltyHop(player_t *player) boolean K_CheckWaterskipLockout(player_t *player) { + if (player->airdropflags & PAF_AIRDROP_HEAVY) + { + return false; + } + if (K_WaterskipBricksActive()) { return (player->cmd.buttons & BT_ACCELERATE) == BT_ACCELERATE;