From eade241d3b15d1cb763baf5212044a93e75d0459 Mon Sep 17 00:00:00 2001 From: Anonimus Date: Fri, 26 Sep 2025 00:05:06 -0400 Subject: [PATCH] Add SPBs to battle mode The ultimate means against camping --- src/deh_tables.c | 1 + src/doomstat.h | 36 +++++++++++++++++++----------------- src/g_game.c | 3 ++- src/k_battle.c | 20 ++++++++++++++++++++ src/k_battle.h | 1 + src/k_odds.c | 13 ++++++++++++- src/p_enemy.c | 32 ++++++++++++++++++++++++++++---- src/p_saveg.c | 4 ++++ src/p_setup.c | 2 ++ 9 files changed, 89 insertions(+), 23 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index b6051e8b3..db7cce5fe 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -383,6 +383,7 @@ const char *const GAMETYPERULE_LIST[] = { "BATTLEODDS", "PAPERITEMS", "WANTED", + "WANTEDSPB", "KARMA", "ITEMARROWS", "ITEMBREAKER", diff --git a/src/doomstat.h b/src/doomstat.h index a01791eec..a9f7edadb 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -489,30 +489,31 @@ enum GameTypeRules GTR_BATTLEODDS = 1<<6, // ItemOdds used in this mode are for battling. GTR_PAPERITEMS = 1<<7, // Replaces item boxes with paper item spawners. GTR_WANTED = 1<<8, // Enables the wanted anti-camping system. - GTR_KARMA = 1<<9, // Enables the Karma system if you're out of bumpers. - GTR_ITEMARROWS = 1<<10, // Show item box arrows above players. - GTR_ITEMBREAKER = 1<<11, // Enables the use of Item Breaker in this Gamemode. - GTR_BATTLESTARTS = 1<<12, // Use Battle Mode start positions. - GTR_CLOSERPLAYERS = 1<<13, // Enables specfic gameplay tweaks with closer players. - GTR_BATTLEBOXES = 1<<14, // Itemboxes respawn differently. + GTR_WANTEDSPB = 1<<9, // Unleash Self-Propelled Bombs on Wanted players. + GTR_KARMA = 1<<10, // Enables the Karma system if you're out of bumpers. + GTR_ITEMARROWS = 1<<11, // Show item box arrows above players. + GTR_ITEMBREAKER = 1<<12, // Enables the use of Item Breaker in this Gamemode. + GTR_BATTLESTARTS = 1<<13, // Use Battle Mode start positions. + GTR_CLOSERPLAYERS = 1<<14, // Enables specfic gameplay tweaks with closer players. + GTR_BATTLEBOXES = 1<<15, // Itemboxes respawn differently. - GTR_POINTLIMIT = 1<<15, // Reaching point limit ends the round. - GTR_TIMELIMIT = 1<<16, // Reaching time limit ends the round. - GTR_OVERTIME = 1<<17, // Allow overtime behavior. + GTR_POINTLIMIT = 1<<16, // Reaching point limit ends the round. + GTR_TIMELIMIT = 1<<17, // Reaching time limit ends the round. + GTR_OVERTIME = 1<<18, // Allow overtime behavior. // Custom gametype rules - GTR_TEAMS = 1<<18, // Teams are forced on. - GTR_NOTEAMS = 1<<19, // Teams are forced off. - GTR_TEAMSTARTS = 1<<20, // Use team-based start positions. + GTR_TEAMS = 1<<19, // Teams are forced on. + GTR_NOTEAMS = 1<<20, // Teams are forced off. + GTR_TEAMSTARTS = 1<<21, // Use team-based start positions. // Grand Prix rules - //Free = 1<<21, - GTR_LIVES = 1<<22, // Lives system, players are forced to spectate during Game Over. - GTR_SPECIALBOTS = 1<<23, // Bot difficulty gets stronger between rounds, and the rival system is enabled. + //Free = 1<<22, + GTR_LIVES = 1<<23, // Lives system, players are forced to spectate during Game Over. + GTR_SPECIALBOTS = 1<<24, // Bot difficulty gets stronger between rounds, and the rival system is enabled. // Misc - GTR_NOCOUNTDOWN = 1<<24, // Disables Countdown timer and control lock at the start of levels. - GTR_ENCORE = 1<<25, // Enable Encore mode. + GTR_NOCOUNTDOWN = 1<<25, // Disables Countdown timer and control lock at the start of levels. + GTR_ENCORE = 1<<26, // Enable Encore mode. // free: to and including 1<<31 }; @@ -670,6 +671,7 @@ extern boolean franticitems; extern boolean encoremode, prevencoremode; extern boolean comeback; +extern SINT8 mostwanted; extern SINT8 battlewanted[4]; extern tic_t wantedcalcdelay; extern tic_t indirectitemcooldown; diff --git a/src/g_game.c b/src/g_game.c index 3c1709fba..45c547574 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -296,6 +296,7 @@ SINT8 pickedvote; // What vote the host rolls // Server-sided, synched variables SINT8 battlewanted[4]; // WANTED players in battle, worth x2 points +SINT8 mostwanted; // The "most wanted" (first in line) player. tic_t wantedcalcdelay; // Time before it recalculates WANTED tic_t indirectitemcooldown; // Cooldown before any more Shrink, SPB, or any other item that works indirectly is awarded tic_t hyubgone; // Cooldown before hyudoro is allowed to be rerolled @@ -3373,7 +3374,7 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] = // Race GTR_CIRCUIT|GTR_BOTS|GTR_RINGS|GTR_ENCORE|GTR_RACEODDS, // Battle - GTR_BUMPERS|GTR_POINTS|GTR_RINGS|GTR_KARMA|GTR_WANTED|GTR_ITEMARROWS|GTR_ITEMBREAKER|GTR_BATTLESTARTS|GTR_TIMELIMIT|GTR_POINTLIMIT|GTR_BATTLEODDS|GTR_CLOSERPLAYERS|GTR_BATTLEBOXES + GTR_BUMPERS|GTR_POINTS|GTR_RINGS|GTR_KARMA|GTR_WANTED|GTR_WANTEDSPB|GTR_ITEMARROWS|GTR_ITEMBREAKER|GTR_BATTLESTARTS|GTR_TIMELIMIT|GTR_POINTLIMIT|GTR_BATTLEODDS|GTR_CLOSERPLAYERS|GTR_BATTLEBOXES }; // diff --git a/src/k_battle.c b/src/k_battle.c index fc8f6ab45..8e6b0dc90 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -55,6 +55,20 @@ boolean K_IsPlayerWanted(player_t *player) return false; } +boolean K_IsPlayerMostWanted(player_t *player) +{ + if (mostwanted == -1) + return false; + + if (!(gametyperules & GTR_WANTEDSPB)) + return false; + + if (player == &players[mostwanted]) + return true; + + return false; +} + void K_CalculateBattleWanted(void) { UINT8 numingame = 0, numwanted = 0; @@ -62,6 +76,8 @@ void K_CalculateBattleWanted(void) UINT8 ties = 0, nextcamppos = 0; UINT8 i, j; + mostwanted = -1; + if (!(gametyperules & GTR_WANTED)) { memset(battlewanted, -1, sizeof (battlewanted)); @@ -162,6 +178,10 @@ void K_CalculateBattleWanted(void) if (ties < (numwanted-i)) // Is it still low enough after counting? { battlewanted[i] = camppos[nextcamppos]; + + if (!nextcamppos) + mostwanted = battlewanted[i]; + nextcamppos++; } else diff --git a/src/k_battle.h b/src/k_battle.h index b014dd5a4..b0dd90bd9 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -14,6 +14,7 @@ extern UINT8 numtargets; INT32 K_StartingBumperCount(void); boolean K_IsPlayerWanted(player_t *player); +boolean K_IsPlayerMostWanted(player_t *player); void K_CalculateBattleWanted(void); void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount); void K_CheckBumpers(void); diff --git a/src/k_odds.c b/src/k_odds.c index 5b3c9841e..540e8ead0 100644 --- a/src/k_odds.c +++ b/src/k_odds.c @@ -1014,6 +1014,16 @@ UINT32 K_CalculatePDIS(const player_t *player, UINT8 numPlayers, boolean *spbrus return pdis; } +static boolean K_CanForceSPB(player_t *player, UINT32 pdis) +{ + boolean battlecond, racecond; + + battlecond = ((gametyperules & GTR_WANTED) && (gametyperules & GTR_WANTEDSPB) && (mostwanted != -1) && (!K_IsPlayerMostWanted(player))); + racecond = ((gametyperules & GTR_CIRCUIT) && player->position == 2 && pdis > SPBFORCEDIST); + + return ((battlecond) || (racecond)); +} + void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) { INT32 i; @@ -1198,7 +1208,8 @@ void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) // SPECIAL CASE No. 6: // Force SPB onto 2nd if they get too far behind - if ((gametyperules & GTR_CIRCUIT) && player->position == 2 && pdis > SPBFORCEDIST + // In battle, an SPB is forced onto players to target the "most wanted" player + if (K_CanForceSPB(player, pdis) && spbplace == -1 && !indirectitemcooldown && !dontforcespb && cv_selfpropelledbomb.value) { diff --git a/src/p_enemy.c b/src/p_enemy.c index 96a566a7c..046278b80 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -10775,7 +10775,8 @@ void A_SPBChase(mobj_t *actor) return; } - // Find the player with the best rank + // Find the player with the best rank. In gametypes with SPB forcing on WANTED + // players, find our most-wanted player. for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator || players[i].exiting) @@ -10790,7 +10791,15 @@ void A_SPBChase(mobj_t *actor) if (players[i].kartstuff[k_respawn]) continue;*/ // respawning - if (players[i].position < bestrank) + if ((gametyperules & GTR_WANTED) && (gametyperules & GTR_WANTEDSPB)) + { + if (K_IsPlayerMostWanted(&players[i])) + { + player = &players[i]; + break; + } + } + else if (players[i].position < bestrank) { bestrank = players[i].position; player = &players[i]; @@ -10843,7 +10852,14 @@ void A_SPBChase(mobj_t *actor) else if (actor->extravalue2-- <= 0) actor->extravalue1 = 0; // back to SEEKING - spbplace = actor->tracer->player->position; + if ((gametyperules & GTR_WANTED) && (gametyperules & GTR_WANTEDSPB)) + { + spbplace = (actor->tracer->player - players); + } + else + { + spbplace = actor->tracer->player->position; + } } if ((K_RingsActive() == true) ) // Spawn rings to make up for the fact first has ring grabbing @@ -10962,7 +10978,15 @@ void A_SPBChase(mobj_t *actor) && !players[actor->lastlook].spectator && !players[actor->lastlook].exiting) { - spbplace = players[actor->lastlook].position; + if ((gametyperules & GTR_WANTED) && (gametyperules & GTR_WANTEDSPB)) + { + spbplace = actor->lastlook; + } + else + { + spbplace = players[actor->lastlook].position; + } + if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) { P_SetTarget(&actor->tracer, players[actor->lastlook].mo); diff --git a/src/p_saveg.c b/src/p_saveg.c index fd5cfe258..c65902eca 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -5388,6 +5388,9 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) WRITESINT8(save->p, speedscramble); WRITESINT8(save->p, encorescramble); + + // WANTED system + WRITESINT8(save->p, mostwanted); for (i = 0; i < 4; i++) WRITESINT8(save->p, battlewanted[i]); @@ -5774,6 +5777,7 @@ FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(savebuffer_t *save, bool speedscramble = READSINT8(save->p); encorescramble = READSINT8(save->p); + mostwanted = READSINT8(save->p); for (i = 0; i < 4; i++) battlewanted[i] = READSINT8(save->p); diff --git a/src/p_setup.c b/src/p_setup.c index 6e75ab7f5..1329d14cd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8206,6 +8206,8 @@ static void P_InitLevelSettings(boolean reloadinggamestate) for (i = 0; i < 4; i++) battlewanted[i] = -1; + mostwanted = -1; + speedscramble = encorescramble = -1; }