Merge pull request 'Heavy Airdrop and Fusion Air drop' (#215) from heavyairdrop into next

Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/215
This commit is contained in:
NepDisk 2026-02-07 20:49:09 +01:00
commit 38f9419fc2
17 changed files with 251 additions and 85 deletions

View file

@ -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_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);
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);
@ -8469,29 +8474,14 @@ static void KartAirDrop_OnChange(void)
return;
}
if (!K_AirDropActive() && cv_kartairdrop.value)
if (leveltime < starttime)
{
if (leveltime < starttime)
{
airdropactive = true;
CONS_Printf(M_GetText("Air Drop has been turned \"On\".\n"));
}
else
{
CONS_Printf(M_GetText("Air Drop will be turned \"On\" Next Round.\n"));
}
CONS_Printf(M_GetText("Air Drop has been set to \"%s\".\n"), cv_kartairdrop.string);
airdropactive = (UINT8)cv_kartairdrop.value;
}
else if (K_AirDropActive() && !cv_kartairdrop.value)
else
{
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);
}
}

View file

@ -71,8 +71,8 @@ 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
// free: 1<<5 and 1<<6
PF_WANTSTOJOIN = 1<<7, // Spectator that wants to join
@ -111,6 +111,16 @@ typedef enum
PF_SLIDING = 1<<31, // For lua compat, don't use!
} pflags_t;
typedef enum
{
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
{
// Are animation frames playing?
@ -199,6 +209,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.
@ -676,8 +688,10 @@ 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.
//boolean ringdrop; // Set when having ringdrop applied.
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.
mobj_t *shieldtracer; // Blankart: Shield mobj

View file

@ -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<<i));
return 1;
}
}
if (mathlib) return luaL_error(L, "airdropflag '%s' could not be found.\n", word);
return 0;
}
else if (fastncmp("IF_", word, 3)) {
p = word+3;
for (i = 0; ITEMFLAG_LIST[i]; i++)

View file

@ -425,6 +425,17 @@ struct int_const_s const PLAYERFLAG_ALIASES[] = {
{ NULL, 0 }
};
const char *const AIRDROPFLAG_LIST[] = {
"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.
};
const char *const ITEMFLAG_LIST[] = {
"USERINGS",
"ITEMOUT",
@ -1799,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}
};

View file

@ -93,6 +93,7 @@ extern const char *const MOBJEFLAG_LIST[];
extern const char *const MAPTHINGFLAG_LIST[4];
extern const char *const PLAYERFLAG_LIST[];
extern struct int_const_s const PLAYERFLAG_ALIASES[];
extern const char *const AIRDROPFLAG_LIST[];
extern const char *const ITEMFLAG_LIST[];
extern const char *const GAMETYPERULE_LIST[];
extern const char *const ML_LIST[]; // Linedef flags

View file

@ -1020,3 +1020,6 @@ _(rainbr)
// Horncode
_(horn00)
// Fastfall bounce
_(vclgna)

View file

@ -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,7 +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},
{"Air Drop", 0, NULL, K_AirDropActive() > 0, 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},

View file

@ -6995,65 +6995,148 @@ 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)
{
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 || airdropactive == AIRDROP_FUSION)
{
player->airdropflags &= ~PAF_WANTSAIRDROP;
}
}
if (P_IsObjectOnGround(player->mo) && player->airdropbuffer == 0)
{
player->airdropflags &= ~PAF_WANTSAIRDROP;
}
if (!(player->airdropflags & PAF_AIRDROP_HEAVY))
{
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 (K_AirDropActive() && P_IsObjectOnGround(player->mo))
{
if ((player->airdropflags & PAF_AIRDROP_HEAVY))
{
if (player->rings > 0 && player->airdroptime > TICRATE/4)
{
P_PlayerRingBurst(player, min(2, player->rings));
}
if (player->airdroptime > 1)
{
player->startboost = TICRATE/4 + min(TICRATE, ((FixedDiv(player->airdroptime, TICRATE) * TICRATE) / FRACUNIT));
// Take off!
S_StartSound(player->mo, sfx_shrpgo);
}
// POOMP!
S_StartSound(player->mo, sfx_doord2);
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
|| (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT)
|| player->respawn
)
{
// Conditions!
player->airdroptime = 0;
//player->ringdrop = false;
player->airdroppredelay = 0;
player->airdropflags &= ~PAF_AIRDROP_MASK;
return;
}
if (cmd->buttons & BT_BRAKE)
if (player->airdropflags & PAF_WANTSAIRDROP)
{
SINT8 airbrakedelay = TICRATE/3;
if (player->airdroptime < airbrakedelay)
if ((airdropactive == AIRDROP_LIGHT) || (airdropactive == AIRDROP_FUSION && (cmd->buttons & BT_ACCELERATE)))
{
/*if (player->rings > 0)
{
player->ringlock = 6;
}
player->ringdrop = false;*/
player->airdropflags |= PAF_AIRDROP_LIGHT;
}
else
else if (!(player->airdropflags & (PAF_AIRDROP_HEAVY))) // in fusion, fires if brake is held but not accel
{
/*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);
player->airdropflags |= PAF_AIRDROP_HEAVY;
player->airdroptime = 0;
player->airdropbuffer = 0;
ring->renderflags |= RF_ADD;
ring->renderflags |= RF_TRANS60;
ring->colorized = true;
ring->color = SKINCOLOR_SILVER;
// TODO: heavy air drop should allow keeping current boost stack
S_StartSound(player->mo, sfx_s3k77);
S_StartSound(player->mo, sfx_s3k51);
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);
K_SpawnAirdropTrail(player);
player->mo->momz -= FixedMul(gravity, mapobjectscale)*P_MobjFlip(player->mo);
player->mo->momx = FixedMul(player->mo->momx, 90*FRACUNIT/100);
player->mo->momy = FixedMul(player->mo->momy, 90*FRACUNIT/100);
player->mo->momz -= 12*P_MobjFlip(player->mo)*mapobjectscale;
}
}
else
{
if (airdropactive == AIRDROP_LIGHT || airdropactive == AIRDROP_FUSION)
{
player->airdropflags &= ~PAF_AIRDROP_LIGHT;
if (!(player->airdropflags & PAF_AIRDROP_HEAVY))
player->airdroptime = 0;
}
}
if (player->airdroptime < UINT8_MAX)
// heavy air drop always overrides light air drop
if (player->airdropflags & PAF_AIRDROP_HEAVY)
{
K_SpawnFallLines(player, false);
if (player->airdroptime <= heavydrophi)
{
player->mo->momz -= FixedMul(5*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->airdroptime++;
}
else if (player->airdropflags & PAF_AIRDROP_LIGHT)
{
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->airdroppredelay++;
}
// Returns the bumpspark value as an enum.
@ -11720,15 +11803,9 @@ boolean K_DraftingActive(void)
return false;
}
boolean K_AirDropActive(void)
SINT8 K_AirDropActive(void)
{
if (airdropactive)
{
// Air Drop is enabled!
return true;
}
return false;
return airdropactive;
}
boolean K_AirThrustActive(void)
@ -11948,6 +12025,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;

View file

@ -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);
@ -419,6 +419,14 @@ typedef enum
BUMPSPARK_ALL
} bumpsparktype_t;
typedef enum
{
AIRDROP_NONE = 0,
AIRDROP_LIGHT,
AIRDROP_HEAVY,
AIRDROP_FUSION,
} airdroptype_t;
boolean K_NullDriftTiltEnalbed();
#ifdef __cplusplus

View file

@ -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;
}

View file

@ -1507,6 +1507,17 @@ static int libd_getDrawInfo(lua_State *L)
drawinfo_t info = {0};
rouletteinfo_t rinfo = {0};
// 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;

View file

@ -390,7 +390,10 @@ 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) \
X(bubblecool) \
@ -829,9 +832,18 @@ 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;
/*case player_ringdrop:
lua_pushinteger(L, plr->ringdrop);
break;*/
@ -1610,9 +1622,18 @@ 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;
/*case player_ringdrop:
plr->ringdrop = luaL_checkinteger(L, 3);
break;*/

View file

@ -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);

View file

@ -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;

View file

@ -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);
@ -711,8 +712,10 @@ static void P_NetSyncPlayers(savebuffer_t *save)
SYNC(players[i].ringvolume);
SYNC(players[i].ringtransparency);
SYNC(players[i].airdroppredelay);
SYNC(players[i].airdroptime);
//SYNCBOOLEAN(players[i].ringdrop);
SYNC(players[i].airdropbuffer);
SYNC(players[i].airdropflags);
RSYNC(players[i].shieldtracer);
@ -4266,13 +4269,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);

View file

@ -161,7 +161,7 @@ boolean slipdashactive;
boolean purpledriftactive;
boolean slopeboostactive;
boolean draftingactive;
boolean airdropactive;
UINT8 airdropactive;
boolean airthrustactive;
boolean recoverydashactive;
boolean waterskipbricks;
@ -8185,7 +8185,7 @@ static void P_InitLevelSettings(boolean reloadinggamestate)
purpledriftactive = false;
slopeboostactive = false;
draftingactive = false;
airdropactive = false;
airdropactive = 0;
airthrustactive = false;
recoverydashactive = false;
waterskipbricks = false;
@ -8216,8 +8216,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;

View file

@ -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;
}