From d95ac73f3bb0b1336d55afe181b4dcda1fc88433 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Dec 2022 17:03:36 +0000 Subject: [PATCH 01/76] Remove Gametype Preference and Voting Rule Change cvars * The consensus has moved against gametype changes in voting, so strip it out * Encorescramble already independently controls setting Encore, so don't keep the outdated randomisation method Done in this branch because it uses gametype constants directly, both in a cvar and function, and I want to get rid of as many of those as possible --- src/d_clisrv.c | 5 +--- src/d_netcmd.c | 17 ++++-------- src/d_netcmd.h | 2 -- src/g_game.c | 74 ++++++++----------------------------------------- src/g_game.h | 2 +- src/k_kart.c | 2 -- src/k_menudef.c | 3 -- 7 files changed, 19 insertions(+), 86 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 27a489916..224ae709d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -899,9 +899,6 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) UINT8 *p; size_t mirror_length; const char *httpurl = cv_httpsource.string; - UINT8 prefgametype = (cv_kartgametypepreference.value == -1) - ? gametype - : cv_kartgametypepreference.value; netbuffer->packettype = PT_SERVERINFO; netbuffer->u.serverinfo._255 = 255; @@ -933,7 +930,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) else netbuffer->u.serverinfo.refusereason = 0; - strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[prefgametype], + strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], sizeof netbuffer->u.serverinfo.gametypename); netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9679b5631..a8241c14f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -399,10 +399,6 @@ consvar_t cv_kartbumpers = CVAR_INIT ("battlebumpers", "3", CV_NETVAR, kartbumpe consvar_t cv_kartfrantic = CVAR_INIT ("franticitems", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartFrantic_OnChange); static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, "On"}, {0, NULL}}; consvar_t cv_kartencore = CVAR_INIT ("encore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange); -static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; -consvar_t cv_kartvoterulechanges = CVAR_INIT ("voterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL); -static CV_PossibleValue_t kartgametypepreference_cons_t[] = {{-1, "None"}, {GT_RACE, "Race"}, {GT_BATTLE, "Battle"}, {0, NULL}}; -consvar_t cv_kartgametypepreference = CVAR_INIT ("gametypepreference", "None", CV_NETVAR, kartgametypepreference_cons_t, NULL); static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}}; consvar_t cv_kartspeedometer = CVAR_INIT ("speedometer", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; @@ -2596,14 +2592,13 @@ void D_SetupVote(void) UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes UINT8 *p = buf; INT32 i; - UINT8 gt = (cv_kartgametypepreference.value == -1) ? gametype : cv_kartgametypepreference.value; - UINT8 secondgt = G_SometimesGetDifferentGametype(gt); + UINT8 secondgt = G_SometimesGetDifferentGametype(); INT16 votebuffer[4] = {-1,-1,-1,0}; - if ((cv_kartencore.value == 1) && (gametypedefaultrules[gt] & GTR_CIRCUIT)) - WRITEUINT8(p, (gt|VOTEMODIFIER_ENCORE)); + if ((cv_kartencore.value == 1) && (gametypedefaultrules[gametype] & GTR_CIRCUIT)) + WRITEUINT8(p, (gametype|VOTEMODIFIER_ENCORE)); else - WRITEUINT8(p, gt); + WRITEUINT8(p, gametype); WRITEUINT8(p, secondgt); secondgt &= ~VOTEMODIFIER_ENCORE; @@ -2613,9 +2608,9 @@ void D_SetupVote(void) if (i == 2) // sometimes a different gametype m = G_RandMap(G_TOLFlag(secondgt), prevmap, ((secondgt != gametype) ? 2 : 0), 0, true, votebuffer); else if (i >= 3) // unknown-random and formerly force-unknown MAP HELL - m = G_RandMap(G_TOLFlag(gt), prevmap, 0, (i-2), (i < 4), votebuffer); + m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, (i-2), (i < 4), votebuffer); else - m = G_RandMap(G_TOLFlag(gt), prevmap, 0, 0, true, votebuffer); + m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer); if (i < 3) votebuffer[i] = m; WRITEUINT16(p, m); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index ee1fb6ec7..7fcdfb4b3 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -79,8 +79,6 @@ extern consvar_t cv_kartspeed; extern consvar_t cv_kartbumpers; extern consvar_t cv_kartfrantic; extern consvar_t cv_kartencore; -extern consvar_t cv_kartvoterulechanges; -extern consvar_t cv_kartgametypepreference; extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartvoices; extern consvar_t cv_kartbot; diff --git a/src/g_game.c b/src/g_game.c index fb49b598c..8df843a1f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -318,9 +318,8 @@ typedef struct { INT16 *mapbuffer; // Pointer to zone memory INT32 lastnummapheaders; // Reset if nummapheaders != this - UINT8 counttogametype; // Time to gametype change event } randmaps_t; -static randmaps_t randmaps = {NULL, 0, 0}; +static randmaps_t randmaps = {NULL, 0}; static void G_ResetRandMapBuffer(void) { @@ -330,7 +329,6 @@ static void G_ResetRandMapBuffer(void) randmaps.mapbuffer = Z_Malloc(randmaps.lastnummapheaders * sizeof(INT16), PU_STATIC, NULL); for (i = 0; i < randmaps.lastnummapheaders; i++) randmaps.mapbuffer[i] = -1; - //intentionally not resetting randmaps.counttogametype here } typedef struct joystickvector2_s @@ -3294,80 +3292,30 @@ boolean G_GametypeHasSpectators(void) // // G_SometimesGetDifferentGametype // -// Oh, yeah, and we sometimes flip encore mode on here too. +// Because gametypes are no longer on the vote screen, all this does is sometimes flip encore mode. +// However, it remains a seperate function for long-term possibility. // -INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype) +INT16 G_SometimesGetDifferentGametype(void) { - // Most of the gametype references in this condition are intentionally not prefgametype. - // This is so a server CAN continue playing a gametype if they like the taste of it. - // The encore check needs prefgametype so can't use G_RaceGametype... boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE, false) || encorescramble == 1) - && ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT)); + && ((gametyperules|gametypedefaultrules[gametype]) & GTR_CIRCUIT)); UINT8 encoremodifier = 0; // -- the below is only necessary if you want to use randmaps.mapbuffer here //if (randmaps.lastnummapheaders != nummapheaders) //G_ResetRandMapBuffer(); - if (encorepossible) + // FORCE to what was scrambled on intermission? + if (encorepossible && encorescramble != -1) { - if (encorescramble != -1) + // FORCE to what was scrambled on intermission + if ((encorescramble != 0) != (cv_kartencore.value == 1)) { - encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission - } - else - { - switch (cv_kartvoterulechanges.value) - { - case 3: // always - encorepossible = true; - break; - case 2: // frequent - encorepossible = M_RandomChance(FRACUNIT>>1); - break; - case 1: // sometimes - encorepossible = M_RandomChance(FRACUNIT>>2); - break; - default: - break; - } - } - if (encorepossible != (cv_kartencore.value == 1)) encoremodifier = VOTEMODIFIER_ENCORE; + } } - if (!cv_kartvoterulechanges.value) // never - return (gametype|encoremodifier); - - if (randmaps.counttogametype > 0 && (cv_kartvoterulechanges.value != 3)) - { - randmaps.counttogametype--; - return (gametype|encoremodifier); - } - - switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv? - { - case 1: // sometimes - randmaps.counttogametype = 5; // per "cup" - break; - default: - // fallthrough - happens when clearing buffer, but needs a reasonable countdown if cvar is modified - case 2: // frequent - randmaps.counttogametype = 2; // ...every 1/2th-ish cup? - break; - } - - // Only this response is prefgametype-based. - // todo custom gametypes - if (prefgametype == GT_BATTLE) - { - // Intentionally does not use encoremodifier! - if (cv_kartencore.value == 1) - return (GT_RACE|VOTEMODIFIER_ENCORE); - return (GT_RACE); - } - // This might appear wrong HERE, but the game will display the Encore possibility on the second voting choice instead. - return (GT_BATTLE|encoremodifier); + return (gametype|encoremodifier); } // diff --git a/src/g_game.h b/src/g_game.h index 0108cc06d..3a8d77941 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -195,7 +195,7 @@ boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); #define VOTEMODIFIER_ENCORE 0x80 -INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype); +INT16 G_SometimesGetDifferentGametype(void); UINT8 G_GetGametypeColor(INT16 gt); void G_ExitLevel(void); void G_NextLevel(void); diff --git a/src/k_kart.c b/src/k_kart.c index f1c0b8be2..88a07bc3e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -310,8 +310,6 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartbumpers); CV_RegisterVar(&cv_kartfrantic); CV_RegisterVar(&cv_kartencore); - CV_RegisterVar(&cv_kartvoterulechanges); - CV_RegisterVar(&cv_kartgametypepreference); CV_RegisterVar(&cv_kartspeedometer); CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartbot); diff --git a/src/k_menudef.c b/src/k_menudef.c index 99903d788..f100db28d 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1132,9 +1132,6 @@ menuitem_t OPTIONS_Server[] = {IT_STRING | IT_CVAR, "Vote Timer", "Set how long players have to vote.", NULL, {.cvar = &cv_votetime}, 0, 0}, - {IT_STRING | IT_CVAR, "Vote Mode Change", "Set how often voting proposes a different gamemode.", - NULL, {.cvar = &cv_kartvoterulechanges}, 0, 0}, - {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, From ab63be694a3835b465f2e1a884ccedd31da1c751 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Dec 2022 21:01:25 +0000 Subject: [PATCH 02/76] Correct NUMBASETOLNAMES, which was incorrectly set too low. --- src/doomstat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doomstat.h b/src/doomstat.h index 045924213..d5559361b 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -525,7 +525,7 @@ enum TypeOfLevel }; #define MAXTOL (1<<31) -#define NUMBASETOLNAMES (4) +#define NUMBASETOLNAMES (5) #define NUMTOLNAMES (NUMBASETOLNAMES + NUMGAMETYPEFREESLOTS) struct tolinfo_t From fbc3af20967b382f5c3d6f9cf8d1f991e1263405 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Dec 2022 21:21:03 +0000 Subject: [PATCH 03/76] Pre-emptive fixes for custom gametypes - Handle mismatched gametypes for client and server on voting screen - I_Error when running out of gametypes - Reduce gametype freeslots slightly to avoid colliding with VOTEMODIFIER_ENCORE --- src/d_netcmd.c | 18 ++++++++++++++++++ src/deh_soc.c | 2 +- src/doomstat.h | 4 ++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a8241c14f..bcab54eef 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5313,6 +5313,24 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) gt &= ~VOTEMODIFIER_ENCORE; } + if ((gt & ~VOTEMODIFIER_ENCORE) >= gametypecount) + { + gt &= ~VOTEMODIFIER_ENCORE; + if (server) + I_Error("Got_SetupVotecmd: Internal gametype ID %d not found (gametypecount = %d)", gt, gametypecount); + CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad gametype ID %d received from %s\n"), gt, player_names[playernum]); + return; + } + + if ((secondgt & ~VOTEMODIFIER_ENCORE) >= gametypecount) + { + secondgt &= ~VOTEMODIFIER_ENCORE; + if (server) + I_Error("Got_SetupVotecmd: Internal second gametype ID %d not found (gametypecount = %d)", secondgt, gametypecount); + CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad second gametype ID %d received from %s\n"), secondgt, player_names[playernum]); + return; + } + for (i = 0; i < 4; i++) { tempvotelevels[i][0] = (UINT16)READUINT16(*cp); diff --git a/src/deh_soc.c b/src/deh_soc.c index 1d7b94214..9d39652f0 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -881,7 +881,7 @@ void readgametype(MYFILE *f, char *gtname) // Ran out of gametype slots if (gametypecount == NUMGAMETYPEFREESLOTS) { - CONS_Alert(CONS_WARNING, "Ran out of free gametype slots!\n"); + I_Error("Out of Gametype Freeslots while allocating \"%s\"\nLoad less addons to fix this.", gtname); return; } diff --git a/src/doomstat.h b/src/doomstat.h index d5559361b..5eae8208f 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -454,7 +454,7 @@ extern mapheader_t** mapheaderinfo; extern INT32 nummapheaders, mapallocsize; // Gametypes -#define NUMGAMETYPEFREESLOTS 128 +#define NUMGAMETYPEFREESLOTS (NUMGAMETYPES-GT_FIRSTFREESLOT) enum GameType { @@ -462,7 +462,7 @@ enum GameType GT_BATTLE, GT_FIRSTFREESLOT, - GT_LASTFREESLOT = GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1, + GT_LASTFREESLOT = 127, // Previously (GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1) - it would be necessary to rewrite VOTEMODIFIER_ENCORE to go higher than this. NUMGAMETYPES }; // If you alter this list, update deh_tables.c, MISC_ChangeGameTypeMenu in m_menu.c, and Gametype_Names in g_game.c From 8431e526871b1694a7014f77fde1921e664b9dc0 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Dec 2022 22:43:00 +0000 Subject: [PATCH 04/76] All playsim-internal distinctions between Race and Battle are now gametype rules. New: - GTR_POWERSTONES - Handles spawning in Battle Emeralds (currently only works stacked with GTR_PAPERITEMS) - GTR_ENCORE - Codifies that Race can use Encore and Battle can't. - GTR_CLOSERPLAYERS - A gametype where players are encouraged/expected to be closer together. (All of the following was GT_BATTLE specific) - Drafting/tether has increased strength/effective distance - Spindashing is stronger - Invincibility chaining has less effect - Grow has a lower total duration - Flame shield is more uncontrollable Extra functionality - GTR_CAPSULES - Prevents usage of lives in Grand Prix (so Race, and the upcoming Special and Boss gametypes, can have 'em) - GTR_CIRCUIT - When not present, Flame Shield has perma-full meter - When not present, overrides gamespeed with KARTSPEED_EASY - Presence of Best Lap sticker in Time Attack menu - Seperation between Time Attack and Break The Capsules modeattacking roulettes - GTR_POINTLIMIT - Handles the switch between a gametype recording/displaying Times and Scores in a few places - Handles displaying "WANTED" players on the minimap Missing simple substitutions - A whole bunch of cases where player->bumpers was checked with gametype == GT_BATTLE rather than GTR_BUMPER - GTR_OVERTIME handles the overtime special icon on the minimap - GTR_BATTLESTARTS is honoured in K_DoIngamerespawn - The Replay hut is closer to supporting custom gametypes Removals - GTR_LIVES - GTR_SPECIALBOTS - Given that grand prix persists between modes, these are special game-controlled features and not gametype-specific. - GTR_WANTED - WANTED as it existed is functionally dead --- src/d_netcmd.c | 10 +++--- src/discord.c | 2 +- src/doomstat.h | 9 ++--- src/g_game.c | 14 +++----- src/k_battle.c | 7 +++- src/k_hud.c | 48 +++++++++++++-------------- src/k_kart.c | 86 ++++++++++++++++++++++-------------------------- src/k_menudraw.c | 74 +++++++++++++++++++++++++---------------- src/k_respawn.c | 18 ++++------ src/k_roulette.c | 28 +++++++--------- src/p_inter.c | 2 +- src/p_mobj.c | 8 ++--- src/p_setup.c | 7 ++-- 13 files changed, 156 insertions(+), 157 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index bcab54eef..ffc53e02d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2595,7 +2595,7 @@ void D_SetupVote(void) UINT8 secondgt = G_SometimesGetDifferentGametype(); INT16 votebuffer[4] = {-1,-1,-1,0}; - if ((cv_kartencore.value == 1) && (gametypedefaultrules[gametype] & GTR_CIRCUIT)) + if ((cv_kartencore.value == 1) && (gametypedefaultrules[gametype] & GTR_ENCORE)) WRITEUINT8(p, (gametype|VOTEMODIFIER_ENCORE)); else WRITEUINT8(p, gametype); @@ -3029,7 +3029,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) else if (gametype != lastgametype) D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype - if (!(gametyperules & GTR_CIRCUIT) && !bossinfo.boss) + if (!(gametyperules & GTR_ENCORE) && !bossinfo.boss) pencoremode = false; skipprecutscene = ((flags & (1<<2)) != 0); @@ -5308,7 +5308,7 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) // Strip illegal Encore flag. if ((gt & VOTEMODIFIER_ENCORE) - && !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) + && !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_ENCORE)) { gt &= ~VOTEMODIFIER_ENCORE; } @@ -5346,11 +5346,11 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) // If third entry has an illelegal Encore flag... (illelegal!?) if ((secondgt & VOTEMODIFIER_ENCORE) - && !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) + && !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_ENCORE)) { secondgt &= ~VOTEMODIFIER_ENCORE; // Apply it to the second entry instead, gametype permitting! - if (gametypedefaultrules[gt] & GTR_CIRCUIT) + if (gametypedefaultrules[gt] & GTR_ENCORE) { tempvotelevels[1][1] |= VOTEMODIFIER_ENCORE; } diff --git a/src/discord.c b/src/discord.c index 28381d250..b5c6a5a88 100644 --- a/src/discord.c +++ b/src/discord.c @@ -500,7 +500,7 @@ void DRPC_UpdatePresence(void) { snprintf(detailstr, 48, "%s%s%s", gametype_cons_t[gametype].strvalue, - (gametype == GT_RACE) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "", + (gametyperules & GTR_CIRCUIT) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "", (encoremode == true) ? " | Encore" : "" ); discordPresence.details = detailstr; diff --git a/src/doomstat.h b/src/doomstat.h index 5eae8208f..038ce8572 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -478,10 +478,10 @@ enum GameTypeRules GTR_BUMPERS = 1<<3, // Enables the bumper health system GTR_SPHERES = 1<<4, // Replaces rings with blue spheres GTR_PAPERITEMS = 1<<5, // Replaces item boxes with paper item spawners - GTR_WANTED = 1<<6, // unused + GTR_POWERSTONES = 1<<6, // Battle Emerald collectables. GTR_KARMA = 1<<7, // Enables the Karma system if you're out of bumpers GTR_ITEMARROWS = 1<<8, // Show item box arrows above players - GTR_CAPSULES = 1<<9, // Enables the wanted anti-camping system + GTR_CAPSULES = 1<<9, // Can enter Break The Capsules mode GTR_BATTLESTARTS = 1<<10, // Use Battle Mode start positions. GTR_POINTLIMIT = 1<<11, // Reaching point limit ends the round @@ -495,10 +495,11 @@ enum GameTypeRules // Grand Prix rules GTR_CAMPAIGN = 1<<17, // Handles cup-based progression - GTR_LIVES = 1<<18, // Lives system, players are forced to spectate during Game Over. - GTR_SPECIALBOTS = 1<<19, // Bot difficulty gets stronger between rounds, and the rival system is enabled. + // To be rearranged later GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. ...mutually exclusive with GTR_CAMPAIGN. + GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos + GTR_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping // free: to and including 1<<31 }; diff --git a/src/g_game.c b/src/g_game.c index 8df843a1f..64afccba9 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3016,9 +3016,9 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] = UINT32 gametypedefaultrules[NUMGAMETYPES] = { // Race - GTR_CAMPAIGN|GTR_CIRCUIT|GTR_BOTS, + GTR_CAMPAIGN|GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE, // Battle - GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME + GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS }; // @@ -3241,13 +3241,7 @@ boolean G_GametypeUsesLives(void) return false; if ((grandprixinfo.gp == true) // In Grand Prix - && (gametype == GT_RACE) // NOT in bonus round - && grandprixinfo.eventmode == GPEVENT_NONE) // NOT in bonus - { - return true; - } - - if (bossinfo.boss == true) // Fighting a boss? + && !(gametyperules & GTR_CAPSULES)) // NOT in Break The Capsules { return true; } @@ -3298,7 +3292,7 @@ boolean G_GametypeHasSpectators(void) INT16 G_SometimesGetDifferentGametype(void) { boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE, false) || encorescramble == 1) - && ((gametyperules|gametypedefaultrules[gametype]) & GTR_CIRCUIT)); + && (gametyperules & GTR_ENCORE)); UINT8 encoremodifier = 0; // -- the below is only necessary if you want to use randmaps.mapbuffer here diff --git a/src/k_battle.c b/src/k_battle.c index 15b81f81c..781ff108c 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -196,6 +196,11 @@ void K_CheckEmeralds(player_t *player) { UINT8 i; + if (!(gametyperules & GTR_POWERSTONES)) + { + return; + } + if (!ALLCHAOSEMERALDS(player->emeralds)) { return; @@ -341,7 +346,7 @@ void K_RunPaperItemSpawners(void) const boolean overtime = (battleovertime.enabled >= 10*TICRATE); tic_t interval = 8*TICRATE; - const boolean canmakeemeralds = true; //(!(battlecapsules || bossinfo.boss)); + const boolean canmakeemeralds = (gametyperules & GTR_POWERSTONES); UINT32 emeraldsSpawned = 0; UINT32 firstUnspawnedEmerald = 0; diff --git a/src/k_hud.c b/src/k_hud.c index 8da9ca089..d55c4d822 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1863,7 +1863,7 @@ static boolean K_drawKartPositionFaces(void) ranklines--; i = ranklines; - if (gametype == GT_BATTLE || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) + if ((gametyperules & GTR_POINTLIMIT) || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) { if (i > 4) // could be both... i = 4; @@ -1951,7 +1951,7 @@ static boolean K_drawKartPositionFaces(void) if (i == strank) V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); - if (gametype == GT_BATTLE && players[rankplayer[i]].bumpers <= 0) + if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumpers <= 0) V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers); else { @@ -2265,7 +2265,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); V_DrawMappedPatch(x, y-4, 0, faceprefix[players[tab[i].num].skin][FACE_RANK], colormap); - /*if (gametype == GT_BATTLE && players[tab[i].num].bumpers > 0) -- not enough space for this + /*if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].bumpers > 0) -- not enough space for this { INT32 bumperx = x+19; V_DrawMappedPatch(bumperx-2, y-4, 0, kp_tinybumper[0], colormap); @@ -2280,7 +2280,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN if (tab[i].num == whiteplayer) V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); - if (gametype == GT_BATTLE && players[tab[i].num].bumpers <= 0) + if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].bumpers <= 0) V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); else { @@ -2291,7 +2291,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]); } - if (gametype == GT_RACE) + if ((gametyperules & GTR_CIRCUIT)) { #define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) if (scorelines >= 8) @@ -2801,6 +2801,7 @@ static void K_drawKartBumpersOrKarma(void) } } +#if 0 static void K_drawKartWanted(void) { UINT8 i, numwanted = 0; @@ -2875,6 +2876,7 @@ static void K_drawKartWanted(void) } } } +#endif //if 0 static void K_drawKartPlayerCheck(void) { @@ -3810,7 +3812,7 @@ static void K_drawKartMinimap(void) y -= SHORT(AutomapPic->topoffset); // Draw the super item in Battle - if (gametype == GT_BATTLE && battleovertime.enabled) + if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled) { if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) { @@ -3920,8 +3922,8 @@ static void K_drawKartMinimap(void) K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic); // Target reticule - if ((gametype == GT_RACE && players[i].position == spbplace) - || (gametype == GT_BATTLE && K_IsPlayerWanted(&players[i]))) + if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace) + || ((gametyperules & GTR_POINTLIMIT) && K_IsPlayerWanted(&players[i]))) { K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } @@ -4047,8 +4049,8 @@ static void K_drawKartMinimap(void) K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, AutomapPic); // Target reticule - if ((gametype == GT_RACE && players[localplayers[i]].position == spbplace) - || (gametype == GT_BATTLE && K_IsPlayerWanted(&players[localplayers[i]]))) + if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace) + || ((gametyperules & GTR_POINTLIMIT) && K_IsPlayerWanted(&players[localplayers[i]]))) { K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } @@ -5043,11 +5045,13 @@ void K_drawKartHUD(void) K_drawKartNameTags(); // Draw WANTED status +#if 0 if (gametype == GT_BATTLE) { if (LUA_HudEnabled(hud_wanted)) K_drawKartWanted(); } +#endif if (LUA_HudEnabled(hud_minimap)) K_drawKartMinimap(); @@ -5098,21 +5102,15 @@ void K_drawKartHUD(void) { K_drawBossHealthBar(); } - else if (gametype == GT_RACE) // Race-only elements (not currently gametyperuleable) + else if (freecam) + ; + else if ((gametyperules & GTR_POWERSTONES)) { - if (!islonesome) - { - // Draw the numerical position - K_DrawKartPositionNum(stplyr->position); - } - } - else if (gametype == GT_BATTLE) // Battle-only (ditto) - { - if (!freecam && !battlecapsules) - { + if (!battlecapsules) K_drawKartEmeralds(); - } } + else if (!islonesome) + K_DrawKartPositionNum(stplyr->position); } if (LUA_HudEnabled(hud_gametypeinfo)) @@ -5172,10 +5170,12 @@ void K_drawKartHUD(void) } // Race overlays - if (gametype == GT_RACE && !freecam) + if (!freecam) { if (stplyr->exiting) K_drawKartFinish(true); + else if (!(gametyperules & GTR_CIRCUIT)) + ; else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen) K_drawLapStartAnim(); } @@ -5187,7 +5187,7 @@ void K_drawKartHUD(void) if (modeattacking || freecam) // everything after here is MP and debug only return; - if (gametype == GT_BATTLE && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * + if ((gametyperules & GTR_KARMA) && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); // Draw FREE PLAY. diff --git a/src/k_kart.c b/src/k_kart.c index 88a07bc3e..932ac0b28 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1270,10 +1270,9 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed player->draftpower += add; } - if (gametype == GT_BATTLE) + if (gametyperules & GTR_CLOSERPLAYERS) { - // TODO: gametyperules - // Double speed in Battle + // Double speed in smaller environments player->draftpower += add; } } @@ -1333,9 +1332,8 @@ static void K_UpdateDraft(player_t *player) minDist = 640 * player->mo->scale; - if (gametype == GT_BATTLE) + if (gametyperules & GTR_CLOSERPLAYERS) { - // TODO: gametyperules minDist /= 4; draftdistance *= 2; leniency *= 4; @@ -2452,7 +2450,7 @@ void K_PlayOvertakeSound(mobj_t *source) { boolean tasteful = (!source->player || !source->player->karthud[khud_voices]); - if (!gametype == GT_RACE) // Only in race + if (!(gametyperules & GTR_CIRCUIT)) // Only in race return; // 4 seconds from before race begins, 10 seconds afterwards @@ -2961,8 +2959,7 @@ fixed_t K_GetSpindashChargeSpeed(player_t *player) // (can be higher than this value when overcharged) const fixed_t val = (10*FRACUNIT/277) + (((player->kartspeed + player->kartweight) + 2) * FRACUNIT) / 45; - // TODO: gametyperules - return (gametype == GT_BATTLE) ? (4 * val) : val; + return (gametyperules & GTR_CLOSERPLAYERS) ? (4 * val) : val; } // sets boostpower, speedboost, accelboost, and handleboost to whatever we need it to be @@ -3097,9 +3094,8 @@ static void K_GetKartBoostPower(player_t *player) // 30% - 44%, each point of speed adds 1.75% fixed_t draftspeed = ((3*FRACUNIT)/10) + ((player->kartspeed-1) * ((7*FRACUNIT)/400)); - if (gametype == GT_BATTLE) + if (gametyperules & GTR_CLOSERPLAYERS) { - // TODO: gametyperules draftspeed *= 2; } @@ -3222,7 +3218,7 @@ fixed_t K_GetKartAccel(player_t *player) k_accel += 17 * (9 - player->kartspeed); // 121 - 257 // karma bomb gets 2x acceleration - if (gametype == GT_BATTLE && player->bumpers <= 0) + if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) k_accel *= 2; // Marble Garden Top gets 1200% accel @@ -3236,9 +3232,8 @@ UINT16 K_GetKartFlashing(player_t *player) { UINT16 tics = flashingtics; - if (gametype == GT_BATTLE) + if (gametyperules & GTR_BUMPERS) { - // TODO: gametyperules return 1; } @@ -3543,6 +3538,12 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN UINT8 points = 1; boolean trapItem = false; + if (!(gametyperules & GTR_POINTLIMIT)) + { + // No points in this gametype. + return; + } + if (player == NULL || victim == NULL) { // Invalid player or victim @@ -3578,11 +3579,8 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN } } - if (gametyperules & GTR_POINTLIMIT) - { - P_AddPlayerScore(player, points); - K_SpawnBattlePoints(player, victim, points); - } + P_AddPlayerScore(player, points); + K_SpawnBattlePoints(player, victim, points); } void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) @@ -5054,7 +5052,7 @@ void K_SpawnDraftDust(mobj_t *mo) { UINT8 leniency = (3*TICRATE)/4 + ((mo->player->kartweight-1) * (TICRATE/4)); - if (gametype == GT_BATTLE) + if (gametyperules & GTR_CLOSERPLAYERS) leniency *= 4; ang = mo->player->drawangle; @@ -7470,7 +7468,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_SpawnGrowShrinkParticles(player->mo, player->growshrinktimer); } - if (gametype == GT_RACE && player->rings <= 0) // spawn ring debt indicator + if (!(gametyperules & GTR_SPHERES) && player->rings <= 0) // spawn ring debt indicator { mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, player->mo->z + P_GetMobjZMovement(player->mo) + player->mo->height + (24*player->mo->scale), MT_THOK); @@ -7788,7 +7786,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->eggmanexplode) { - if (player->spectator || (gametype == GT_BATTLE && !player->bumpers)) + if (player->spectator || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)) player->eggmanexplode = 0; else { @@ -9330,15 +9328,18 @@ static INT32 K_FlameShieldMax(player_t *player) UINT8 numplayers = 0; UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) + if (gametyperules & GTR_CIRCUIT) { - if (playeringame[i] && !players[i].spectator) - numplayers++; - if (players[i].position == 1) - disttofinish = players[i].distancetofinish; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + numplayers++; + if (players[i].position == 1) + disttofinish = players[i].distancetofinish; + } } - if (numplayers <= 1 || gametype == GT_BATTLE) + if (numplayers <= 1) { return 16; // max when alone, for testing // and when in battle, for chaos @@ -9592,8 +9593,7 @@ static void K_KartSpindash(player_t *player) { fixed_t thrust = FixedMul(player->mo->scale, player->spindash*FRACUNIT/5); - // TODO: gametyperules - if (gametype == GT_BATTLE) + if (gametyperules & GTR_CLOSERPLAYERS) thrust *= 2; // Give a bit of a boost depending on charge. @@ -10400,8 +10400,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); } - // TODO: gametyperules - player->growshrinktimer = max(player->growshrinktimer, (gametype == GT_BATTLE ? 8 : 12) * TICRATE); + player->growshrinktimer = max(player->growshrinktimer, ((gametyperules & GTR_CLOSERPLAYERS) ? 8 : 12) * TICRATE); if (player->invincibilitytimer > 0) { @@ -10559,8 +10558,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY)) { - // TODO: gametyperules - const INT32 incr = gametype == GT_BATTLE ? 4 : 2; + const INT32 incr = (gametyperules & GTR_CLOSERPLAYERS) ? 4 : 2; if (player->flamedash == 0) { @@ -10596,8 +10594,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { player->pflags |= PF_HOLDREADY; - // TODO: gametyperules - if (gametype != GT_BATTLE || leveltime % 6 == 0) + if (!(gametyperules & GTR_CLOSERPLAYERS) || leveltime % 6 == 0) { if (player->flamemeter > 0) player->flamemeter--; @@ -10719,18 +10716,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->hyudorotimer > 0) { - INT32 hyu = hyudorotime; - - if (gametype == GT_RACE) - hyu *= 2; // double in race - if (leveltime & 1) { player->mo->renderflags |= RF_DONTDRAW; } else { - if (player->hyudorotimer >= (TICRATE/2) && player->hyudorotimer <= hyu-(TICRATE/2)) + if (player->hyudorotimer >= (TICRATE/2) && player->hyudorotimer <= hyudorotime-(TICRATE/2)) player->mo->renderflags &= ~K_GetPlayerDontDrawFlag(player); else player->mo->renderflags &= ~RF_DONTDRAW; @@ -10743,17 +10735,17 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->renderflags &= ~RF_DONTDRAW; } - if (gametype == GT_BATTLE && player->bumpers <= 0) // dead in match? you da bomb + if (!(gametyperules & GTR_BUMPERS) || player->bumpers > 0) + { + player->mo->renderflags &= ~(RF_TRANSMASK|RF_BRIGHTMASK); + } + else // dead in match? you da bomb { K_DropItems(player); //K_StripItems(player); K_StripOther(player); player->mo->renderflags |= RF_GHOSTLY; player->flashing = player->karmadelay; } - else if (gametype == GT_RACE || player->bumpers > 0) - { - player->mo->renderflags &= ~(RF_TRANSMASK|RF_BRIGHTMASK); - } if (player->trickpanel == 1) { @@ -10966,7 +10958,7 @@ void K_CheckSpectateStatus(void) continue; if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in return; - if (gametype == GT_RACE && players[i].laps >= 2) // DON'T allow if the race is at 2 laps + if ((gametyperules & GTR_CIRCUIT) && players[i].laps >= 2) // DON'T allow if the race is at 2 laps return; continue; } diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9071907bc..b1e3557c7 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2261,7 +2261,7 @@ void M_DrawTimeAttack(void) laprec = mapheaderinfo[map]->mainrecord->lap; } - if (levellist.newgametype != GT_BATTLE) + if (gametypedefaultrules[levellist.newgametype] & GTR_CIRCUIT) { V_DrawRightAlignedString(rightedge-12, timeheight, highlightflags, "BEST LAP:"); K_drawKartTimestamp(laprec, 162+t, timeheight+6, 0, 2); @@ -3964,9 +3964,20 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref) if (demoref->numlaps) V_DrawThinString(x, y+9, V_SNAPTOTOP|V_ALLOWLOWERCASE, va("(%d laps)", demoref->numlaps)); - V_DrawString(x, y+20, V_SNAPTOTOP|V_ALLOWLOWERCASE, demoref->gametype == GT_RACE ? - va("Race (%s speed)", kartspeed_cons_t[(demoref->kartspeed & ~DF_ENCORE) + 1].strvalue) : - "Battle Mode"); + { + const char *gtstring = "???"; + if (demoref->gametype >= gametypecount) + ; + else + { + gtstring = Gametype_Names[demoref->gametype]; + + if ((gametypedefaultrules[demoref->gametype] & GTR_CIRCUIT)) + gtstring = va("%s (%s)", gtstring, kartspeed_cons_t[(demoref->kartspeed & ~DF_ENCORE) + 1].strvalue); + } + + V_DrawString(x, y+20, V_SNAPTOTOP|V_ALLOWLOWERCASE, gtstring); + } if (!demoref->standings[0].ranking) { @@ -3979,30 +3990,33 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref) V_DrawThinString(x, y+29, V_SNAPTOTOP|highlightflags, "WINNER"); V_DrawString(x+38, y+30, V_SNAPTOTOP|V_ALLOWLOWERCASE, demoref->standings[0].name); - if (demoref->gametype == GT_RACE) + if (demoref->gametype < gametypecount) { - V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME"); - } - else - { - V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE"); - } + if (gametypedefaultrules[demoref->gametype] & GTR_POINTLIMIT) + { + V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE"); + } + else + { + V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME"); + } - if (demoref->standings[0].timeorscore == (UINT32_MAX-1)) - { - V_DrawThinString(x+32, y+39, V_SNAPTOTOP, "NO CONTEST"); - } - else if (demoref->gametype == GT_RACE) - { - V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d", - G_TicsToMinutes(demoref->standings[0].timeorscore, true), - G_TicsToSeconds(demoref->standings[0].timeorscore), - G_TicsToCentiseconds(demoref->standings[0].timeorscore) - )); - } - else - { - V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demoref->standings[0].timeorscore)); + if (demoref->standings[0].timeorscore == (UINT32_MAX-1)) + { + V_DrawThinString(x+32, y+39, V_SNAPTOTOP, "NO CONTEST"); + } + else if (gametypedefaultrules[demoref->gametype] & GTR_POINTLIMIT) + { + V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demoref->standings[0].timeorscore)); + } + else + { + V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d", + G_TicsToMinutes(demoref->standings[0].timeorscore, true), + G_TicsToSeconds(demoref->standings[0].timeorscore), + G_TicsToCentiseconds(demoref->standings[0].timeorscore) + )); + } } // Character face! @@ -4197,14 +4211,16 @@ void M_DrawReplayStartMenu(void) if (demoref->standings[i].timeorscore == UINT32_MAX-1) V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); - else if (demoref->gametype == GT_RACE) + else if (demoref->gametype >= gametypecount) + ; + else if (gametypedefaultrules[demoref->gametype] & GTR_POINTLIMIT) + V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demoref->standings[i].timeorscore)); + else V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d", G_TicsToMinutes(demoref->standings[i].timeorscore, true), G_TicsToSeconds(demoref->standings[i].timeorscore), G_TicsToCentiseconds(demoref->standings[i].timeorscore) )); - else - V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demoref->standings[i].timeorscore)); // Character face! diff --git a/src/k_respawn.c b/src/k_respawn.c index 0b4c02ccf..5e3a3ecc6 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -175,14 +175,14 @@ void K_DoIngameRespawn(player_t *player) mapthing_t *beststart = NULL; UINT8 numstarts = 0; - if (gametype == GT_RACE) - { - numstarts = numcoopstarts; - } - else if (gametype == GT_BATTLE) + if (gametyperules & GTR_BATTLESTARTS) { numstarts = numdmstarts; } + else + { + numstarts = numcoopstarts; + } if (numstarts > 0) { @@ -193,17 +193,13 @@ void K_DoIngameRespawn(player_t *player) UINT32 dist = UINT32_MAX; mapthing_t *checkstart = NULL; - if (gametype == GT_RACE) - { - checkstart = playerstarts[i]; - } - else if (gametype == GT_BATTLE) + if (gametyperules & GTR_BATTLESTARTS) { checkstart = deathmatchstarts[i]; } else { - break; + checkstart = playerstarts[i]; } dist = (UINT32)P_AproxDistance((player->mo->x >> FRACBITS) - checkstart->x, diff --git a/src/k_roulette.c b/src/k_roulette.c index 4f3de015f..7c283ca1f 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -1125,27 +1125,23 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet } else if (K_TimeAttackRules() == true) { - switch (gametype) + kartitems_t *presetlist = K_KartItemReelTimeAttack; + + // If the objective is not to go fast, it's to cause serious damage. + if (!(gametyperules & GTR_CIRCUIT)) { - case GT_RACE: - default: + presetlist = K_KartItemReelBreakTheCapsules; + for (i = 0; K_KartItemReelBreakTheCapsules[i] != KITEM_NONE; i++) { - for (i = 0; K_KartItemReelTimeAttack[i] != KITEM_NONE; i++) - { - K_PushToRouletteItemList(roulette, K_KartItemReelTimeAttack[i]); - } - break; - } - case GT_BATTLE: - { - for (i = 0; K_KartItemReelBreakTheCapsules[i] != KITEM_NONE; i++) - { - K_PushToRouletteItemList(roulette, K_KartItemReelBreakTheCapsules[i]); - } - break; + K_PushToRouletteItemList(roulette, K_KartItemReelBreakTheCapsules[i]); } } + for (i = 0; presetlist[i] != KITEM_NONE; i++) + { + K_PushToRouletteItemList(roulette, presetlist[i]); + } + return; } diff --git a/src/p_inter.c b/src/p_inter.c index ff6ba99b0..cb1de723d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2198,7 +2198,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da { tic_t kinvextend; - if (gametype == GT_BATTLE) + if (gametyperules & GTR_CLOSERPLAYERS) kinvextend = 2*TICRATE; else kinvextend = 5*TICRATE; diff --git a/src/p_mobj.c b/src/p_mobj.c index 26b38d631..50717459a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6163,7 +6163,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->color = mobj->target->color; K_MatchGenericExtraFlags(mobj, mobj->target); - if ((gametype == GT_RACE || mobj->target->player->bumpers <= 0) + if ((!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers <= 0) #if 1 // Set to 0 to test without needing to host || (P_IsDisplayPlayer(mobj->target->player)) #endif @@ -8362,7 +8362,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) statenum_t state = (mobj->state-states); if (!mobj->target || !mobj->target->health || !mobj->target->player || mobj->target->player->spectator - || (gametype == GT_RACE || mobj->target->player->bumpers)) + || (!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers)) { P_RemoveMobj(mobj); return false; @@ -9389,12 +9389,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } // FALLTHRU case MT_SPHEREBOX: - if (gametype == GT_BATTLE && mobj->threshold == 70) + if (mobj->threshold == 70) { mobj->color = K_RainbowColor(leveltime); mobj->colorized = true; - if (battleovertime.enabled) + if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled) { angle_t ang = FixedAngle((leveltime % 360) << FRACBITS); fixed_t z = battleovertime.z; diff --git a/src/p_setup.c b/src/p_setup.c index 61c2a9ec3..7934874a5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -6857,7 +6857,7 @@ static void P_InitLevelSettings(void) // SRB2Kart: map load variables if (grandprixinfo.gp == true) { - if ((gametyperules & GTR_BUMPERS)) + if (!(gametyperules & GTR_CIRCUIT)) { gamespeed = KARTSPEED_EASY; } @@ -6875,8 +6875,7 @@ static void P_InitLevelSettings(void) } else if (modeattacking) { - // Just play it safe and set everything - if ((gametyperules & GTR_BUMPERS)) + if (!(gametyperules & GTR_CIRCUIT)) gamespeed = KARTSPEED_EASY; else gamespeed = KARTSPEED_HARD; @@ -6884,7 +6883,7 @@ static void P_InitLevelSettings(void) } else { - if ((gametyperules & GTR_BUMPERS)) + if (!(gametyperules & GTR_CIRCUIT)) gamespeed = KARTSPEED_EASY; else { From 59bc094face4379f4c5ddcb5601e34b48fc5c2e9 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Dec 2022 22:44:40 +0000 Subject: [PATCH 05/76] We don't have *one* Single Player mode - we have as many gametypes as there are, now. --- src/d_netcmd.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ffc53e02d..6b8199409 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2880,8 +2880,7 @@ static void Command_Map_f(void) mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype) )) { - CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), - (multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player")); + CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), (gametype_cons_t[newgametype].strvalue)); Z_Free(realmapname); Z_Free(mapname); return; @@ -4807,12 +4806,6 @@ static void Command_ShowGametype_f(void) { const char *gametypestr = NULL; - if (!(netgame || multiplayer)) // print "Single player" instead of "Race" - { - CONS_Printf(M_GetText("Current gametype is %s\n"), "Single Player"); - return; - } - // get name string for current gametype if (gametype >= 0 && gametype < gametypecount) gametypestr = Gametype_Names[gametype]; From 185b36bd27de557116deb3af5e9cb3499d911b30 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 01:07:57 +0000 Subject: [PATCH 06/76] gametype_t - New array of pointers to structures in memory (currently mixing static for base-game and Callocated for custom) - Centralises a metric-ton of previously seperately handled properties into one struct - Gametype_Names[] - Gametype_ConstantNames[] - gametypetol[] - timelimits[] - pointlimits[] - gametypedefaultrules[] - Don't attempt to guess custom gametype in Replay Hut (requires more work to make custom gametypes behave across the entire experience) - I_Error if invalid gametype set - gametyperules is deprecated since it will never be modified seperately from gametype (temporarily a #define, don't wanna bloat this commit too much) --- src/d_clisrv.c | 2 +- src/d_main.c | 7 +-- src/d_netcmd.c | 49 +++++++-------- src/deh_lua.c | 9 ++- src/deh_soc.c | 49 +++++++-------- src/doomstat.h | 40 +++++++----- src/g_game.c | 151 +++++++++++++++++++--------------------------- src/g_game.h | 8 +-- src/hu_stuff.c | 4 +- src/k_kart.c | 2 +- src/k_menudraw.c | 22 +++---- src/k_menufunc.c | 4 +- src/lua_baselib.c | 54 ++++++++--------- src/p_setup.c | 1 - src/typedef.h | 1 + src/y_inter.c | 19 ++---- src/y_inter.h | 1 - 17 files changed, 189 insertions(+), 234 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 224ae709d..f3b2fc391 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -930,7 +930,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) else netbuffer->u.serverinfo.refusereason = 0; - strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], + strncpy(netbuffer->u.serverinfo.gametypename, gametypes[gametype]->name, sizeof netbuffer->u.serverinfo.gametypename); netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); diff --git a/src/d_main.c b/src/d_main.c index 8ba822e21..f8b7b0845 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1516,11 +1516,6 @@ void D_SRB2Main(void) CON_SetLoadingProgress(LOADED_HUINIT); - memset(timelimits, 0, sizeof(timelimits)); - memset(pointlimits, 0, sizeof(pointlimits)); - - timelimits[GT_BATTLE] = 2; - D_RegisterServerCommands(); D_RegisterClientCommands(); // be sure that this is called before D_CheckNetGame R_RegisterEngineStuff(); @@ -1802,7 +1797,7 @@ void D_SRB2Main(void) if (newgametype == -1) // reached end of the list with no match { j = atoi(sgametype); // assume they gave us a gametype number, which is okay too - if (j >= 0 && j < gametypecount) + if (j >= 0 && j < numgametypes) newgametype = (INT16)j; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 6b8199409..2e0e30dac 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -481,10 +481,6 @@ consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_NETVAR|CV_CALL|CV_NO static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {MAX_LAPS, "MAX"}, {0, "Map default"}, {0, NULL}}; consvar_t cv_numlaps = CVAR_INIT ("numlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, numlaps_cons_t, NumLaps_OnChange); -// Point and time limits for every gametype -INT32 pointlimits[NUMGAMETYPES]; -INT32 timelimits[NUMGAMETYPES]; - consvar_t cv_forceskin = CVAR_INIT ("forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT, NULL, ForceSkin_OnChange); consvar_t cv_downloading = CVAR_INIT ("downloading", "On", 0, CV_OnOff, NULL); @@ -553,8 +549,7 @@ char timedemo_csv_id[256]; boolean timedemo_quit; INT16 gametype = GT_RACE; -UINT32 gametyperules = 0; -INT16 gametypecount = GT_FIRSTFREESLOT; +INT16 numgametypes = GT_FIRSTFREESLOT; boolean forceresetplayers = false; boolean deferencoremode = false; @@ -2526,7 +2521,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d pencoremode=%d resetplayers=%d delay=%d skipprecutscene=%d\n", mapnum, newgametype, pencoremode, resetplayers, delay, skipprecutscene); - if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypedefaultrules[newgametype] & GTR_CAMPAIGN))) + if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypes[newgametype]->rules & GTR_CAMPAIGN))) FLS = false; // Too lazy to change the input value for every instance of this function....... @@ -2595,7 +2590,7 @@ void D_SetupVote(void) UINT8 secondgt = G_SometimesGetDifferentGametype(); INT16 votebuffer[4] = {-1,-1,-1,0}; - if ((cv_kartencore.value == 1) && (gametypedefaultrules[gametype] & GTR_ENCORE)) + if ((cv_kartencore.value == 1) && (gametyperules & GTR_ENCORE)) WRITEUINT8(p, (gametype|VOTEMODIFIER_ENCORE)); else WRITEUINT8(p, gametype); @@ -2817,7 +2812,7 @@ static void Command_Map_f(void) if (isdigit(gametypename[0])) { d = atoi(gametypename); - if (d >= 0 && d < gametypecount) + if (d >= 0 && d < numgametypes) newgametype = d; else { @@ -2825,7 +2820,7 @@ static void Command_Map_f(void) "Gametype number %d is out of range. Use a number between" " 0 and %d inclusive. ...Or just use the name. :v\n", d, - gametypecount-1); + numgametypes-1); Z_Free(realmapname); Z_Free(mapname); return; @@ -2890,7 +2885,7 @@ static void Command_Map_f(void) fromlevelselect = ( netgame || multiplayer ) && newgametype == gametype && - gametypedefaultrules[newgametype] & GTR_CAMPAIGN; + gametypes[newgametype]->rules & GTR_CAMPAIGN; } } @@ -3023,7 +3018,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) gametype = READUINT8(*cp); G_SetGametype(gametype); // I fear putting that macro as an argument - if (gametype < 0 || gametype >= gametypecount) + if (gametype < 0 || gametype >= numgametypes) gametype = lastgametype; else if (gametype != lastgametype) D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype @@ -4807,8 +4802,8 @@ static void Command_ShowGametype_f(void) const char *gametypestr = NULL; // get name string for current gametype - if (gametype >= 0 && gametype < gametypecount) - gametypestr = Gametype_Names[gametype]; + if (gametype >= 0 && gametype < numgametypes) + gametypestr = gametypes[gametype]->name; if (gametypestr) CONS_Printf(M_GetText("Current gametype is %s\n"), gametypestr); @@ -4987,10 +4982,10 @@ void D_GameTypeChanged(INT32 lastgametype) { const char *oldgt = NULL, *newgt = NULL; - if (lastgametype >= 0 && lastgametype < gametypecount) - oldgt = Gametype_Names[lastgametype]; - if (gametype >= 0 && lastgametype < gametypecount) - newgt = Gametype_Names[gametype]; + if (lastgametype >= 0 && lastgametype < numgametypes) + oldgt = gametypes[lastgametype]->name; + if (gametype >= 0 && gametype < numgametypes) + newgt = gametypes[gametype]->name; if (oldgt && newgt) CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), oldgt, newgt); @@ -5002,11 +4997,11 @@ void D_GameTypeChanged(INT32 lastgametype) { if (!cv_timelimit.changed) // user hasn't changed limits { - CV_SetValue(&cv_timelimit, timelimits[gametype]); + CV_SetValue(&cv_timelimit, gametypes[gametype]->timelimit); } if (!cv_pointlimit.changed) { - CV_SetValue(&cv_pointlimit, pointlimits[gametype]); + CV_SetValue(&cv_pointlimit, gametypes[gametype]->pointlimit); } } @@ -5301,25 +5296,25 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) // Strip illegal Encore flag. if ((gt & VOTEMODIFIER_ENCORE) - && !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_ENCORE)) + && !(gametypes[(gt & ~VOTEMODIFIER_ENCORE)]->rules & GTR_ENCORE)) { gt &= ~VOTEMODIFIER_ENCORE; } - if ((gt & ~VOTEMODIFIER_ENCORE) >= gametypecount) + if ((gt & ~VOTEMODIFIER_ENCORE) >= numgametypes) { gt &= ~VOTEMODIFIER_ENCORE; if (server) - I_Error("Got_SetupVotecmd: Internal gametype ID %d not found (gametypecount = %d)", gt, gametypecount); + I_Error("Got_SetupVotecmd: Internal gametype ID %d not found (numgametypes = %d)", gt, numgametypes); CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad gametype ID %d received from %s\n"), gt, player_names[playernum]); return; } - if ((secondgt & ~VOTEMODIFIER_ENCORE) >= gametypecount) + if ((secondgt & ~VOTEMODIFIER_ENCORE) >= numgametypes) { secondgt &= ~VOTEMODIFIER_ENCORE; if (server) - I_Error("Got_SetupVotecmd: Internal second gametype ID %d not found (gametypecount = %d)", secondgt, gametypecount); + I_Error("Got_SetupVotecmd: Internal second gametype ID %d not found (numgametypes = %d)", secondgt, numgametypes); CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad second gametype ID %d received from %s\n"), secondgt, player_names[playernum]); return; } @@ -5339,11 +5334,11 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) // If third entry has an illelegal Encore flag... (illelegal!?) if ((secondgt & VOTEMODIFIER_ENCORE) - && !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_ENCORE)) + && !(gametypes[(secondgt & ~VOTEMODIFIER_ENCORE)]->rules & GTR_ENCORE)) { secondgt &= ~VOTEMODIFIER_ENCORE; // Apply it to the second entry instead, gametype permitting! - if (gametypedefaultrules[gt] & GTR_ENCORE) + if (gametypes[gt]->rules & GTR_ENCORE) { tempvotelevels[1][1] |= VOTEMODIFIER_ENCORE; } diff --git a/src/deh_lua.c b/src/deh_lua.c index 56bfdf4ea..f131617c6 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -309,11 +309,16 @@ static inline int lib_getenum(lua_State *L) } else if (fastncmp("GT_", word, 3)) { p = word; - for (i = 0; Gametype_ConstantNames[i]; i++) - if (fastcmp(p, Gametype_ConstantNames[i])) { + i = 0; + while (gametypes[i] != NULL) + { + if (fastcmp(p, gametypes[i]->constant)) + { lua_pushinteger(L, i); return 1; } + i++; + } if (mathlib) return luaL_error(L, "gametype '%s' could not be found.\n", word); return 0; } diff --git a/src/deh_soc.c b/src/deh_soc.c index 9d39652f0..65a347169 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -764,13 +764,13 @@ void readgametype(MYFILE *f, char *gtname) char *tmp; INT32 i, j; - INT16 newgtidx = 0; + gametype_t *newgametype = NULL; + UINT32 newgtrules = 0; UINT32 newgttol = 0; INT32 newgtpointlimit = 0; INT32 newgttimelimit = 0; - INT16 newgtrankingstype = -1; - int newgtinttype = 0; + UINT8 newgtinttype = 0; char gtconst[MAXLINELEN]; // Empty strings. @@ -821,12 +821,6 @@ void readgametype(MYFILE *f, char *gtname) newgtpointlimit = (INT32)i; else if (fastcmp(word, "DEFAULTTIMELIMIT")) newgttimelimit = (INT32)i; - // Rankings type - else if (fastcmp(word, "RANKINGTYPE")) - { - // Case insensitive - newgtrankingstype = (int)get_number(word2); - } // Intermission type else if (fastcmp(word, "INTERMISSIONTYPE")) { @@ -879,36 +873,35 @@ void readgametype(MYFILE *f, char *gtname) Z_Free(word2lwr); // Ran out of gametype slots - if (gametypecount == NUMGAMETYPEFREESLOTS) + if (numgametypes == GT_LASTFREESLOT) { I_Error("Out of Gametype Freeslots while allocating \"%s\"\nLoad less addons to fix this.", gtname); - return; } // Add the new gametype - newgtidx = G_AddGametype(newgtrules); - G_AddGametypeTOL(newgtidx, newgttol); + newgametype = Z_Calloc(sizeof (gametype_t), PU_STATIC, NULL); + if (!newgametype) + { + I_Error("Out of memory allocating gametype \"%s\"", gtname); + } - // Not covered by G_AddGametype alone. - if (newgtrankingstype == -1) - newgtrankingstype = newgtidx; - gametyperankings[newgtidx] = newgtrankingstype; - intermissiontypes[newgtidx] = newgtinttype; - pointlimits[newgtidx] = newgtpointlimit; - timelimits[newgtidx] = newgttimelimit; - - // Write the new gametype name. - Gametype_Names[newgtidx] = Z_StrDup((const char *)gtname); - - // Write the constant name. if (gtconst[0] == '\0') strncpy(gtconst, gtname, MAXLINELEN); - G_AddGametypeConstant(newgtidx, (const char *)gtconst); + + newgametype->name = Z_StrDup((const char *)gtname); + newgametype->rules = newgtrules; + newgametype->constant = G_PrepareGametypeConstant((const char *)gtconst); + newgametype->tol = newgttol; + newgametype->intermission = newgtinttype; + newgametype->pointlimit = newgtpointlimit; + newgametype->timelimit = newgttimelimit; + + gametypes[numgametypes++] = newgametype; // Update gametype_cons_t accordingly. G_UpdateGametypeSelections(); - CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]); + CONS_Printf("Added gametype %s\n", gtname); } void readlevelheader(MYFILE *f, char * name) @@ -3670,7 +3663,7 @@ sfxenum_t get_sfx(const char *word) return atoi(word); if (fastncmp("GT_",word,3)) word += 3; // take off the GT_ - for (i = 0; i < NUMGAMETYPES; i++) + for (i = 0; i < MAXGAMETYPES; i++) if (fastcmp(word, Gametype_ConstantNames[i]+3)) return i; deh_warning("Couldn't find gametype named 'GT_%s'",word); diff --git a/src/doomstat.h b/src/doomstat.h index 038ce8572..8c01cd474 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -147,11 +147,6 @@ extern boolean addedtogame; // true after the server has added you // Only true if >1 player. netgame => multiplayer but not (multiplayer=>netgame) extern boolean multiplayer; -extern INT16 gametype; - -extern UINT32 gametyperules; -extern INT16 gametypecount; - extern UINT8 splitscreen; extern int r_splitscreen; @@ -454,7 +449,7 @@ extern mapheader_t** mapheaderinfo; extern INT32 nummapheaders, mapallocsize; // Gametypes -#define NUMGAMETYPEFREESLOTS (NUMGAMETYPES-GT_FIRSTFREESLOT) +#define NUMGAMETYPEFREESLOTS (MAXGAMETYPES-GT_FIRSTFREESLOT) enum GameType { @@ -463,9 +458,29 @@ enum GameType GT_FIRSTFREESLOT, GT_LASTFREESLOT = 127, // Previously (GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1) - it would be necessary to rewrite VOTEMODIFIER_ENCORE to go higher than this. - NUMGAMETYPES + MAXGAMETYPES }; -// If you alter this list, update deh_tables.c, MISC_ChangeGameTypeMenu in m_menu.c, and Gametype_Names in g_game.c +// If you alter this list, update defaultgametypes and *gametypes in g_game.c + +#define MAXTOL (1<<31) +#define NUMBASETOLNAMES (5) +#define NUMTOLNAMES (NUMBASETOLNAMES + NUMGAMETYPEFREESLOTS) + +struct gametype_t +{ + const char *name; + const char *constant; + UINT32 rules; + UINT32 tol; + UINT8 intermission; + INT32 pointlimit; + INT32 timelimit; +}; + +extern gametype_t *gametypes[MAXGAMETYPES+1]; +extern INT16 numgametypes; + +extern INT16 gametype; // Gametype rules enum GameTypeRules @@ -504,13 +519,8 @@ enum GameTypeRules // free: to and including 1<<31 }; -// String names for gametypes -extern const char *Gametype_Names[NUMGAMETYPES]; -extern const char *Gametype_ConstantNames[NUMGAMETYPES]; - -// Point and time limits for every gametype -extern INT32 pointlimits[NUMGAMETYPES]; -extern INT32 timelimits[NUMGAMETYPES]; +// TODO: replace every instance +#define gametyperules (gametypes[gametype]->rules) // TypeOfLevel things enum TypeOfLevel diff --git a/src/g_game.c b/src/g_game.c index 64afccba9..dc7c905e9 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2998,28 +2998,55 @@ void G_ExitLevel(void) } } -// See also the enum GameType in doomstat.h -const char *Gametype_Names[NUMGAMETYPES] = +static gametype_t defaultgametypes[] = { - "Race", // GT_RACE - "Battle" // GT_BATTLE + // GT_RACE + { + "Race", + "GT_RACE", + GTR_CAMPAIGN|GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE, + TOL_RACE, + int_race, + 0, + 0, + }, + + // GT_BATTLE + { + "Battle", + "GT_BATTLE", + GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS, + TOL_BATTLE, + int_battle, + 0, + 2, + }, }; -// For dehacked -const char *Gametype_ConstantNames[NUMGAMETYPES] = +gametype_t *gametypes[MAXGAMETYPES+1] = { - "GT_RACE", // GT_RACE - "GT_BATTLE" // GT_BATTLE + &defaultgametypes[GT_RACE], + &defaultgametypes[GT_BATTLE], }; -// Gametype rules -UINT32 gametypedefaultrules[NUMGAMETYPES] = +// +// G_GetGametypeByName +// +// Returns the number for the given gametype name string, or -1 if not valid. +// +INT32 G_GetGametypeByName(const char *gametypestr) { - // Race - GTR_CAMPAIGN|GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE, - // Battle - GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS -}; + INT32 i = 0; + + while (gametypes[i] != NULL) + { + if (!stricmp(gametypestr, gametypes[i]->name)) + return i; + i++; + } + + return -1; // unknown gametype +} // // G_SetGametype @@ -3028,41 +3055,26 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] = // void G_SetGametype(INT16 gtype) { + if (gtype < 0 || gtype > numgametypes) + { + I_Error("G_SetGametype: Bad gametype change %d (was %d/\"%s\")", gtype, gametype, gametypes[gametype]->name); + } + gametype = gtype; - gametyperules = gametypedefaultrules[gametype]; } // -// G_AddGametype -// -// Add a gametype. Returns the new gametype number. -// -INT16 G_AddGametype(UINT32 rules) -{ - INT16 newgtype = gametypecount; - gametypecount++; - - // Set gametype rules. - gametypedefaultrules[newgtype] = rules; - Gametype_Names[newgtype] = "???"; - - // Update gametype_cons_t accordingly. - G_UpdateGametypeSelections(); - - return newgtype; -} - -// -// G_AddGametypeConstant +// G_PrepareGametypeConstant // // Self-explanatory. Filters out "bad" characters. // -void G_AddGametypeConstant(INT16 gtype, const char *newgtconst) +char *G_PrepareGametypeConstant(const char *newgtconst) { size_t r = 0; // read size_t w = 0; // write - char *gtconst = Z_Calloc(strlen(newgtconst) + 4, PU_STATIC, NULL); - char *tmpconst = Z_Calloc(strlen(newgtconst) + 1, PU_STATIC, NULL); + size_t len = strlen(newgtconst); + char *gtconst = Z_Calloc(len + 4, PU_STATIC, NULL); + char *tmpconst = Z_Calloc(len + 1, PU_STATIC, NULL); // Copy the gametype name. strcpy(tmpconst, newgtconst); @@ -3122,8 +3134,8 @@ void G_AddGametypeConstant(INT16 gtype, const char *newgtconst) // Free the temporary string. Z_Free(tmpconst); - // Finally, set the constant string. - Gametype_ConstantNames[gtype] = gtconst; + // Finally, return the constant string. + return gtconst; } // @@ -3134,30 +3146,15 @@ void G_AddGametypeConstant(INT16 gtype, const char *newgtconst) void G_UpdateGametypeSelections(void) { INT32 i; - for (i = 0; i < gametypecount; i++) + for (i = 0; i < numgametypes; i++) { gametype_cons_t[i].value = i; - gametype_cons_t[i].strvalue = Gametype_Names[i]; + gametype_cons_t[i].strvalue = gametypes[i]->name; } - gametype_cons_t[NUMGAMETYPES].value = 0; - gametype_cons_t[NUMGAMETYPES].strvalue = NULL; + gametype_cons_t[numgametypes].value = 0; + gametype_cons_t[numgametypes].strvalue = NULL; } -// Gametype rankings -INT16 gametyperankings[NUMGAMETYPES] = -{ - GT_RACE, - GT_BATTLE, -}; - -// Gametype to TOL (Type Of Level) -UINT32 gametypetol[NUMGAMETYPES] = -{ - TOL_RACE, // Race - TOL_BATTLE, // Battle - TOL_TV, // Midnight Channel effect -}; - tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = { {"RACE",TOL_RACE}, {"BATTLE",TOL_BATTLE}, @@ -3184,32 +3181,6 @@ void G_AddTOL(UINT32 newtol, const char *tolname) TYPEOFLEVEL[i].flag = newtol; } -// -// G_AddGametypeTOL -// -// Assigns a type of level to a gametype. -// -void G_AddGametypeTOL(INT16 gtype, UINT32 newtol) -{ - gametypetol[gtype] = newtol; -} - -// -// G_GetGametypeByName -// -// Returns the number for the given gametype name string, or -1 if not valid. -// -INT32 G_GetGametypeByName(const char *gametypestr) -{ - INT32 i; - - for (i = 0; i < gametypecount; i++) - if (!stricmp(gametypestr, Gametype_Names[i])) - return i; - - return -1; // unknown gametype -} - // // G_IsSpecialStage // @@ -3339,7 +3310,9 @@ UINT8 G_GetGametypeColor(INT16 gt) */ UINT32 G_TOLFlag(INT32 pgametype) { - return gametypetol[pgametype]; + if (pgametype >= 0 && pgametype < numgametypes) + return gametypes[pgametype]->tol; + return 0; } INT16 G_GetFirstMapOfGametype(UINT8 pgametype) @@ -3350,7 +3323,7 @@ INT16 G_GetFirstMapOfGametype(UINT8 pgametype) templevelsearch.cup = NULL; templevelsearch.typeoflevel = G_TOLFlag(pgametype); - templevelsearch.cupmode = (!(gametypedefaultrules[pgametype] & GTR_NOCUPSELECT)); + templevelsearch.cupmode = (!(gametypes[pgametype]->rules & GTR_NOCUPSELECT)); templevelsearch.timeattack = false; templevelsearch.checklocked = true; diff --git a/src/g_game.h b/src/g_game.h index 3a8d77941..04a9df9c9 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -179,16 +179,10 @@ void G_SaveGame(UINT32 slot, INT16 mapnum); void G_SaveGameOver(UINT32 slot, boolean modifylives); -extern UINT32 gametypedefaultrules[NUMGAMETYPES]; -extern UINT32 gametypetol[NUMGAMETYPES]; -extern INT16 gametyperankings[NUMGAMETYPES]; - void G_SetGametype(INT16 gametype); -INT16 G_AddGametype(UINT32 rules); -void G_AddGametypeConstant(INT16 gtype, const char *newgtconst); +char *G_PrepareGametypeConstant(const char *newgtconst); void G_UpdateGametypeSelections(void); void G_AddTOL(UINT32 newtol, const char *tolname); -void G_AddGametypeTOL(INT16 gtype, UINT32 newtol); INT32 G_GetGametypeByName(const char *gametypestr); boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index bbccb232a..e9d2a7fbd 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2406,8 +2406,8 @@ static void HU_DrawRankings(void) // draw the current gametype in the lower right if (modeattacking) V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Record Attack"); - else - V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, Gametype_Names[gametype]); + else if (gametype >= 0 && gametype < numgametypes) + V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametypes[gametype]->name); if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)) && !bossinfo.boss) { diff --git a/src/k_kart.c b/src/k_kart.c index 932ac0b28..3aa21376a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -162,7 +162,7 @@ void K_TimerInit(void) } else { - timelimitintics = timelimits[gametype] * (60*TICRATE); + timelimitintics = gametypes[gametype]->timelimit * (60*TICRATE); } } else diff --git a/src/k_menudraw.c b/src/k_menudraw.c index b1e3557c7..dc2101f8c 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2002,7 +2002,7 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup) else { if (currentMenu == &PLAY_LevelSelectDef) - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", Gametype_Names[levellist.newgametype])); + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", gametypes[levellist.newgametype]->name)); } } @@ -2261,7 +2261,7 @@ void M_DrawTimeAttack(void) laprec = mapheaderinfo[map]->mainrecord->lap; } - if (gametypedefaultrules[levellist.newgametype] & GTR_CIRCUIT) + if (gametypes[levellist.newgametype]->rules & GTR_CIRCUIT) { V_DrawRightAlignedString(rightedge-12, timeheight, highlightflags, "BEST LAP:"); K_drawKartTimestamp(laprec, 162+t, timeheight+6, 0, 2); @@ -3966,13 +3966,13 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref) { const char *gtstring = "???"; - if (demoref->gametype >= gametypecount) - ; + if (demoref->gametype >= GT_FIRSTFREESLOT) + ; // TODO: Support custom gametypes in netreplays (would require deeper changes than this) else { - gtstring = Gametype_Names[demoref->gametype]; + gtstring = gametypes[demoref->gametype]->name; - if ((gametypedefaultrules[demoref->gametype] & GTR_CIRCUIT)) + if ((gametypes[demoref->gametype]->rules & GTR_CIRCUIT)) gtstring = va("%s (%s)", gtstring, kartspeed_cons_t[(demoref->kartspeed & ~DF_ENCORE) + 1].strvalue); } @@ -3990,9 +3990,9 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref) V_DrawThinString(x, y+29, V_SNAPTOTOP|highlightflags, "WINNER"); V_DrawString(x+38, y+30, V_SNAPTOTOP|V_ALLOWLOWERCASE, demoref->standings[0].name); - if (demoref->gametype < gametypecount) + if (demoref->gametype < GT_FIRSTFREESLOT) { - if (gametypedefaultrules[demoref->gametype] & GTR_POINTLIMIT) + if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT) { V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE"); } @@ -4005,7 +4005,7 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref) { V_DrawThinString(x+32, y+39, V_SNAPTOTOP, "NO CONTEST"); } - else if (gametypedefaultrules[demoref->gametype] & GTR_POINTLIMIT) + else if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT) { V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demoref->standings[0].timeorscore)); } @@ -4211,9 +4211,9 @@ void M_DrawReplayStartMenu(void) if (demoref->standings[i].timeorscore == UINT32_MAX-1) V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); - else if (demoref->gametype >= gametypecount) + else if (demoref->gametype >= GT_FIRSTFREESLOT) ; - else if (gametypedefaultrules[demoref->gametype] & GTR_POINTLIMIT) + else if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT) V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demoref->standings[i].timeorscore)); else V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d", diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 71a447a9e..264c91505 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -149,7 +149,7 @@ static consvar_t cv_menujam = CVAR_INIT ("menujam", "0", CV_SAVE, menujam_cons_t // This gametype list is integral for many different reasons. // When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! -CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; +CV_PossibleValue_t gametype_cons_t[MAXGAMETYPES+1]; static CV_PossibleValue_t serversort_cons_t[] = { {0,"Ping"}, @@ -3537,7 +3537,7 @@ static void M_LevelListFromGametype(INT16 gt) { levellist.newgametype = gt; levellist.levelsearch.typeoflevel = G_TOLFlag(gt); - levellist.levelsearch.cupmode = (!(gametypedefaultrules[gt] & GTR_NOCUPSELECT)); + levellist.levelsearch.cupmode = (!(gametypes[gt]->rules & GTR_NOCUPSELECT)); levellist.levelsearch.cup = NULL; first = false; } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 87e720bea..16347dfe2 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2971,15 +2971,15 @@ static int lib_gAddGametype(lua_State *L) const char *k; lua_Integer i; + gametype_t *newgametype = NULL; + const char *gtname = NULL; const char *gtconst = NULL; - INT16 newgtidx = 0; UINT32 newgtrules = 0; UINT32 newgttol = 0; INT32 newgtpointlimit = 0; INT32 newgttimelimit = 0; - INT16 newgtrankingstype = -1; - int newgtinttype = 0; + UINT8 newgtinttype = 0; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. @@ -2988,8 +2988,10 @@ static int lib_gAddGametype(lua_State *L) return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); // Ran out of gametype slots - if (gametypecount == NUMGAMETYPEFREESLOTS) - return luaL_error(L, "Ran out of free gametype slots!"); + if (numgametypes == GT_LASTFREESLOT) + { + I_Error("Out of Gametype Freeslots while allocating \"%s\"\nLoad less addons to fix this.", gtname); + } #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to " LUA_QL("G_AddGametype") " (%s)", e); #define TYPEERROR(f, t) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t), luaL_typename(L, -1))) @@ -3022,19 +3024,15 @@ static int lib_gAddGametype(lua_State *L) if (!lua_isnumber(L, 3)) TYPEERROR("typeoflevel", LUA_TNUMBER) newgttol = (UINT32)lua_tointeger(L, 3); - } else if (i == 5 || (k && fasticmp(k, "rankingtype"))) { - if (!lua_isnumber(L, 3)) - TYPEERROR("rankingtype", LUA_TNUMBER) - newgtrankingstype = (INT16)lua_tointeger(L, 3); - } else if (i == 6 || (k && fasticmp(k, "intermissiontype"))) { + } else if (i == 5 || (k && fasticmp(k, "intermissiontype"))) { if (!lua_isnumber(L, 3)) TYPEERROR("intermissiontype", LUA_TNUMBER) newgtinttype = (int)lua_tointeger(L, 3); - } else if (i == 7 || (k && fasticmp(k, "defaultpointlimit"))) { + } else if (i == 6 || (k && fasticmp(k, "defaultpointlimit"))) { if (!lua_isnumber(L, 3)) TYPEERROR("defaultpointlimit", LUA_TNUMBER) newgtpointlimit = (INT32)lua_tointeger(L, 3); - } else if (i == 8 || (k && fasticmp(k, "defaulttimelimit"))) { + } else if (i == 7 || (k && fasticmp(k, "defaulttimelimit"))) { if (!lua_isnumber(L, 3)) TYPEERROR("defaulttimelimit", LUA_TNUMBER) newgttimelimit = (INT32)lua_tointeger(L, 3); @@ -3053,30 +3051,30 @@ static int lib_gAddGametype(lua_State *L) gtname = Z_StrDup("Unnamed gametype"); // Add the new gametype - newgtidx = G_AddGametype(newgtrules); - G_AddGametypeTOL(newgtidx, newgttol); + newgametype = Z_Calloc(sizeof (gametype_t), PU_STATIC, NULL); + if (!newgametype) + { + I_Error("Out of memory allocating gametype \"%s\"", gtname); + } - // Not covered by G_AddGametype alone. - if (newgtrankingstype == -1) - newgtrankingstype = newgtidx; - gametyperankings[newgtidx] = newgtrankingstype; - intermissiontypes[newgtidx] = newgtinttype; - pointlimits[newgtidx] = newgtpointlimit; - timelimits[newgtidx] = newgttimelimit; - - // Write the new gametype name. - Gametype_Names[newgtidx] = gtname; - - // Write the constant name. if (gtconst == NULL) gtconst = gtname; - G_AddGametypeConstant(newgtidx, gtconst); + + newgametype->name = gtname; + newgametype->rules = newgtrules; + newgametype->constant = G_PrepareGametypeConstant(gtconst); + newgametype->tol = newgttol; + newgametype->intermission = newgtinttype; + newgametype->pointlimit = newgtpointlimit; + newgametype->timelimit = newgttimelimit; + + gametypes[numgametypes++] = newgametype; // Update gametype_cons_t accordingly. G_UpdateGametypeSelections(); // done - CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]); + CONS_Printf("Added gametype %s\n", gtname); return 0; } diff --git a/src/p_setup.c b/src/p_setup.c index 7934874a5..66f2bebc4 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7221,7 +7221,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // This is needed. Don't touch. maptol = mapheaderinfo[gamemap-1]->typeoflevel; - gametyperules = gametypedefaultrules[gametype]; CON_Drawer(); // let the user know what we are going to do I_FinishUpdate(); // page flip or blit buffer diff --git a/src/typedef.h b/src/typedef.h index 34c652f5e..a43064404 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -104,6 +104,7 @@ TYPEDEF (textpage_t); TYPEDEF (textprompt_t); TYPEDEF (mappoint_t); TYPEDEF (customoption_t); +TYPEDEF (gametype_t); TYPEDEF (mapheader_t); TYPEDEF (tolinfo_t); TYPEDEF (cupheader_t); diff --git a/src/y_inter.c b/src/y_inter.c index 8cb21fe04..413e29e8d 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -98,7 +98,6 @@ static INT32 endtic = -1; static INT32 sorttic = -1; intertype_t intertype = int_none; -intertype_t intermissiontypes[NUMGAMETYPES]; static huddrawlist_h luahuddrawlist_intermission; @@ -751,15 +750,14 @@ void Y_Ticker(void) // void Y_DetermineIntermissionType(void) { - // set to int_none initially - intertype = int_none; + // set initially + intertype = gametypes[gametype]->intermission; - if (gametype == GT_RACE) - intertype = int_race; - else if (gametype == GT_BATTLE) + // TODO: special cases + if (gametype == GT_BATTLE) { if (grandprixinfo.gp == true && bossinfo.boss == false) - intertype = int_none; + return; else { UINT8 i = 0, nump = 0; @@ -772,8 +770,6 @@ void Y_DetermineIntermissionType(void) intertype = (nump < 2 ? int_battletime : int_battle); } } - else //if (intermissiontypes[gametype] != int_none) - intertype = intermissiontypes[gametype]; } // @@ -830,9 +826,6 @@ void Y_StartIntermission(void) sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); } - if (intermissiontypes[gametype] != int_none) - intertype = intermissiontypes[gametype]; - // We couldn't display the intermission even if we wanted to. // But we still need to give the players their score bonuses, dummy. //if (dedicated) return; @@ -1571,7 +1564,7 @@ void Y_StartVote(void) // set up the gtc and gts levelinfo[i].gtc = G_GetGametypeColor(votelevels[i][1]); if (i == 2 && votelevels[i][1] != votelevels[0][1]) - levelinfo[i].gts = Gametype_Names[votelevels[i][1]]; + levelinfo[i].gts = gametypes[votelevels[i][1]]->name; else levelinfo[i].gts = NULL; } diff --git a/src/y_inter.h b/src/y_inter.h index 6a887dea8..5bcd0be17 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -32,4 +32,3 @@ typedef enum } intertype_t; extern intertype_t intertype; -extern intertype_t intermissiontypes[NUMGAMETYPES]; From eb74ec390488be75e4558b6dc198736d55f016f0 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 22:06:26 +0000 Subject: [PATCH 07/76] Change the rules for some things selected in the previous commit. - Only show lap count and gamespeed on rankings if GTR_CIRCUIT - Adjust offsets for speedometer/accessability icons with GTR_BUMPERS|GTR_SPHERES - Only show Karma on Bumpers hud with GTR_KARMA - Permit battle fullscreen without GTR_KARMA - Make the Break The Capsules roulette show up with GTR_CAPSULES, not absence of GTR_CIRCUIT - Don't push Break The Capsules roulette to the item list twice - Make the special mode switching of int_battle happen for all instances of int_battle, not just GT_BATTLE --- src/hu_stuff.c | 2 +- src/k_hud.c | 14 +++++++------- src/k_roulette.c | 6 +----- src/y_inter.c | 8 +++++--- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e9d2a7fbd..d9d2cb6d2 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2438,7 +2438,7 @@ static void HU_DrawRankings(void) V_DrawCenteredString(256, 16, hilicol, va("%d", cv_pointlimit.value)); } } - else + else if (gametyperules & GTR_CIRCUIT) { if (circuitmap) { diff --git a/src/k_hud.c b/src/k_hud.c index d55c4d822..4c2b3fe16 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2498,7 +2498,7 @@ static void K_drawKartAccessibilityIcons(INT32 fx) if (r_splitscreen < 2) // adjust to speedometer height { - if (gametype == GT_BATTLE) + if (gametyperules & (GTR_BUMPERS|GTR_SPHERES)) fy -= 4; } else @@ -2588,7 +2588,7 @@ static void K_drawKartSpeedometer(void) numbers[1] = ((convSpeed / 10) % 10); numbers[2] = (convSpeed % 10); - if (gametype == GT_BATTLE) + if (gametyperules & (GTR_BUMPERS|GTR_SPHERES)) battleoffset = -4; V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometersticker); @@ -2793,10 +2793,10 @@ static void K_drawKartBumpersOrKarma(void) else V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap); - if (bossinfo.boss) - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper)); - else // TODO BETTER HUD + if (gametyperules & GTR_KARMA) // TODO BETTER HUD V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d %d", stplyr->bumpers, maxbumper, stplyr->overtimekarma / TICRATE)); + else + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper)); } } } @@ -5026,10 +5026,10 @@ void K_drawKartHUD(void) return; } - battlefullscreen = ((gametyperules & (GTR_BUMPERS|GTR_KARMA)) == (GTR_BUMPERS|GTR_KARMA) + battlefullscreen = ((gametyperules & (GTR_BUMPERS)) && (stplyr->exiting || (stplyr->bumpers <= 0 - && stplyr->karmadelay > 0 + && ((gametyperules & GTR_KARMA) && (stplyr->karmadelay > 0)) && !(stplyr->pflags & PF_ELIMINATED) && stplyr->playerstate == PST_LIVE))); diff --git a/src/k_roulette.c b/src/k_roulette.c index 7c283ca1f..20d7d19b6 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -1128,13 +1128,9 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet kartitems_t *presetlist = K_KartItemReelTimeAttack; // If the objective is not to go fast, it's to cause serious damage. - if (!(gametyperules & GTR_CIRCUIT)) + if (gametyperules & GTR_CAPSULES) { presetlist = K_KartItemReelBreakTheCapsules; - for (i = 0; K_KartItemReelBreakTheCapsules[i] != KITEM_NONE; i++) - { - K_PushToRouletteItemList(roulette, K_KartItemReelBreakTheCapsules[i]); - } } for (i = 0; presetlist[i] != KITEM_NONE; i++) diff --git a/src/y_inter.c b/src/y_inter.c index 413e29e8d..24c24dd3f 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -754,10 +754,12 @@ void Y_DetermineIntermissionType(void) intertype = gametypes[gametype]->intermission; // TODO: special cases - if (gametype == GT_BATTLE) + if (intertype == int_battle) { - if (grandprixinfo.gp == true && bossinfo.boss == false) - return; + if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) + { + intertype = int_none; + } else { UINT8 i = 0, nump = 0; From a8a60460fbfb88e7bd612c4594b359c4d02ad42d Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 22:16:14 +0000 Subject: [PATCH 08/76] Server startup cleanup * Don't do a shoddy attempt at guessing initial gametype in G_DeferedInitNew * Fix map command on main menu regression (no longer HOM void) --- src/d_clisrv.c | 3 ++- src/g_game.c | 12 +----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f3b2fc391..e2e3da806 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2081,8 +2081,9 @@ static void CL_ConnectToServer(void) Y_EndVote(); DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); - M_ClearMenus(true); G_SetGamestate(GS_WAITINGPLAYERS); + if (wipegamestate == GS_MENU) + M_ClearMenus(true); wipegamestate = GS_WAITINGPLAYERS; ClearAdminPlayers(); diff --git a/src/g_game.c b/src/g_game.c index dc7c905e9..827cd2966 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4816,7 +4816,6 @@ cleanup: void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS) { UINT16 color = SKINCOLOR_NONE; - INT32 dogametype; paused = false; @@ -4827,17 +4826,8 @@ void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ss G_ResetRandMapBuffer(); - if ((modeattacking == ATTACKING_CAPSULES) || (bossinfo.boss == true)) - { - dogametype = GT_BATTLE; - } - else - { - dogametype = GT_RACE; - } - // this leave the actual game if needed - SV_StartSinglePlayerServer(dogametype, false); + SV_StartSinglePlayerServer(gametype, false); if (splitscreen != ssplayers) { From d29e43f80d2d0eb278ec4f8335ccf1bffd0865a7 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 22:29:31 +0000 Subject: [PATCH 09/76] Remove GTR_CAMPAIGN Controlled three things: Vintage SRB2 cutscenes, a certain type of reset on map commands, and whether to go to ceremony/evaluation/credits. All three should be controlled by grandprixinfo.gp instead, since that persists cross-gametype. --- src/d_netcmd.c | 5 ++--- src/doomstat.h | 3 --- src/g_game.c | 8 ++++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2e0e30dac..f5576696d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2521,7 +2521,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d pencoremode=%d resetplayers=%d delay=%d skipprecutscene=%d\n", mapnum, newgametype, pencoremode, resetplayers, delay, skipprecutscene); - if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypes[newgametype]->rules & GTR_CAMPAIGN))) + if ((netgame || multiplayer) && (grandprixinfo.gp != false)) FLS = false; // Too lazy to change the input value for every instance of this function....... @@ -2884,8 +2884,7 @@ static void Command_Map_f(void) { fromlevelselect = ( netgame || multiplayer ) && - newgametype == gametype && - gametypes[newgametype]->rules & GTR_CAMPAIGN; + grandprixinfo.gp != false; } } diff --git a/src/doomstat.h b/src/doomstat.h index 8c01cd474..588a548c5 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -508,9 +508,6 @@ enum GameTypeRules GTR_NOTEAMS = 1<<15, // Teams are forced off GTR_TEAMSTARTS = 1<<16, // Use team-based start positions - // Grand Prix rules - GTR_CAMPAIGN = 1<<17, // Handles cup-based progression - // To be rearranged later GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. ...mutually exclusive with GTR_CAMPAIGN. GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos diff --git a/src/g_game.c b/src/g_game.c index 827cd2966..e7ec8ffa4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3004,7 +3004,7 @@ static gametype_t defaultgametypes[] = { "Race", "GT_RACE", - GTR_CAMPAIGN|GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE, + GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE, TOL_RACE, int_race, 0, @@ -4026,7 +4026,7 @@ void G_AfterIntermission(void) G_HandleSaveLevel(); } - if ((gametyperules & GTR_CAMPAIGN) && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene. + if ((grandprixinfo.gp == true) && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene. F_StartCustomCutscene(mapheaderinfo[prevmap]->cutscenenum-1, false, false); else { @@ -4163,7 +4163,7 @@ void G_EndGame(void) } // Only do evaluation and credits in singleplayer contexts - if (!netgame && (gametyperules & GTR_CAMPAIGN)) + if (!netgame && grandprixinfo.gp == true) { if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony { @@ -4927,7 +4927,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr automapactive = false; imcontinuing = false; - if ((gametyperules & GTR_CAMPAIGN) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene. + if ((grandprixinfo.gp == true) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene. F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); else { From 17dd15b99853fc13947b7d156fedd6d9891add39 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 23:06:24 +0000 Subject: [PATCH 10/76] "Special Mode" (Sealed Stars) and "Versus Mode" (bosses) are now gametypes * The existing structs are now exclusively for handling extra data. * `specialStage` has been renamed to `specialstageinfo`, to reflect that it is not the sole arbiter. * `specialstageinfo.valid` and `bossinfo.valid` are what must be checked before grabbing data from either struct. * These are turned on when the gametype extra data is successfully initialised, not on map start. * `K_InitBossHealthBar(...)` for `bossinfo.valid` * `K_InitSpecialStage(void)` for `specialstageinfo.valid` * `K_CanChangeRules(...)` no longer checks these * No longer uses duplicate encore information. * The map command (and -warp) now guesses gametype using a general `G_GuessGametypeByTOL(UINT32)` function * Grabs the first gametype with an overlap between the requested TOL and the gametype's TOL. * The cool Versus-specific intro is now checked via `K_CheckBossIntro()`. --- src/d_clisrv.c | 1 - src/d_main.c | 30 ++++++------ src/d_netcmd.c | 64 ++++++++++--------------- src/doomstat.h | 9 +++- src/g_game.c | 112 ++++++++++++++++++++++++------------------- src/g_game.h | 2 + src/hu_stuff.c | 3 +- src/k_battle.c | 5 +- src/k_boss.c | 39 +++++++++++++-- src/k_boss.h | 17 ++++++- src/k_bot.c | 3 +- src/k_grandprix.c | 17 +------ src/k_hud.c | 14 +++--- src/k_kart.c | 34 ++++++------- src/k_pwrlv.c | 3 +- src/k_roulette.c | 18 +++---- src/k_specialstage.c | 26 ++++++---- src/k_specialstage.h | 7 ++- src/objects/spb.c | 8 ++-- src/objects/ufo.c | 10 ++-- src/p_inter.c | 1 - src/p_mobj.c | 7 ++- src/p_setup.c | 47 +++++------------- src/p_tick.c | 2 +- src/p_user.c | 5 +- src/s_sound.c | 1 - src/st_stuff.c | 4 +- src/v_video.c | 2 +- src/y_inter.c | 4 +- 29 files changed, 257 insertions(+), 238 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e2e3da806..ac602a28f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -54,7 +54,6 @@ #include "k_pwrlv.h" #include "k_bot.h" #include "k_grandprix.h" -#include "k_boss.h" #include "doomstat.h" #include "s_sound.h" // sfx_syfail #include "m_cond.h" // netUnlocked diff --git a/src/d_main.c b/src/d_main.c index f8b7b0845..75101ceb6 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -72,7 +72,6 @@ // SRB2Kart #include "k_grandprix.h" -#include "k_boss.h" #include "doomstat.h" #include "m_random.h" // P_ClearRandom #include "k_specialstage.h" @@ -969,12 +968,6 @@ void D_StartTitle(void) // Reset GP memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); - // Reset boss info - K_ResetBossInfo(); - - // Reset Special Stage - K_ResetSpecialStage(); - // empty maptol so mario/etc sounds don't play in sound test when they shouldn't maptol = 0; @@ -1204,13 +1197,14 @@ D_ConvertVersionNumbers (void) // void D_SRB2Main(void) { - INT32 i, p; + INT32 i, j, p; #ifdef DEVELOP INT32 pstartmap = 1; // default to first loaded map (Test Run) #else INT32 pstartmap = 0; // default to random map (0 is not a valid map number) #endif boolean autostart = false; + INT32 newgametype = -1; /* break the version string into version numbers, for netplay */ D_ConvertVersionNumbers(); @@ -1788,8 +1782,6 @@ void D_SRB2Main(void) if (M_CheckParm("-gametype") && M_IsNextParm()) { // from Command_Map_f - INT32 j; - INT16 newgametype = -1; const char *sgametype = M_GetNextParm(); newgametype = G_GetGametypeByName(sgametype); @@ -1811,7 +1803,6 @@ void D_SRB2Main(void) if (M_CheckParm("-skill") && M_IsNextParm()) { - INT32 j; INT16 newskill = -1; const char *sskill = M_GetNextParm(); @@ -1864,11 +1855,20 @@ void D_SRB2Main(void) if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1]) { - if (mapheaderinfo[pstartmap-1]->typeoflevel & TOL_SPECIAL) + if (newgametype == -1) { - specialStage.active = true; - specialStage.encore = grandprixinfo.encore; - grandprixinfo.eventmode = GPEVENT_SPECIAL; + newgametype = G_GuessGametypeByTOL(mapheaderinfo[pstartmap-1]->typeoflevel); + if (newgametype != -1) + { + j = gametype; + G_SetGametype(newgametype); + D_GameTypeChanged(j); + } + + if (gametyperules & (GTR_BOSS|GTR_CATCHER)) + grandprixinfo.eventmode = GPEVENT_SPECIAL; + else if (gametype != GT_RACE) + grandprixinfo.eventmode = GPEVENT_BONUS; } G_SetUsedCheats(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f5576696d..32fe1fc8c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -57,7 +57,6 @@ #include "k_color.h" #include "k_respawn.h" #include "k_grandprix.h" -#include "k_boss.h" #include "k_follower.h" #include "doomstat.h" #include "deh_tables.h" @@ -2525,15 +2524,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r FLS = false; // Too lazy to change the input value for every instance of this function....... - if (bossinfo.boss == true) - { - pencoremode = bossinfo.encore; - } - else if (specialStage.active == true) - { - pencoremode = specialStage.encore; - } - else if (grandprixinfo.gp == true) + if (grandprixinfo.gp == true) { pencoremode = grandprixinfo.encore; } @@ -2843,7 +2834,15 @@ static void Command_Map_f(void) if (mapheaderinfo[newmapnum-1]) { // Let's just guess so we don't have to specify the gametype EVERY time... - newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & (TOL_BATTLE|TOL_BOSS)) ? GT_BATTLE : GT_RACE; + newgametype = G_GuessGametypeByTOL(mapheaderinfo[newmapnum-1]->typeoflevel); + + if (newgametype == -1) + { + CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support any known gametype!\n"), realmapname, G_BuildMapName(newmapnum)); + Z_Free(realmapname); + Z_Free(mapname); + return; + } } } @@ -2855,6 +2854,8 @@ static void Command_Map_f(void) if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats) { CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n")); + Z_Free(realmapname); + Z_Free(mapname); return; } } @@ -2875,7 +2876,7 @@ static void Command_Map_f(void) mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype) )) { - CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), (gametype_cons_t[newgametype].strvalue)); + CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), gametypes[newgametype]->name); Z_Free(realmapname); Z_Free(mapname); return; @@ -2940,35 +2941,18 @@ static void Command_Map_f(void) grandprixinfo.eventmode = GPEVENT_NONE; - if (newgametype == GT_BATTLE) + if (gametypes[newgametype]->rules & (GTR_BOSS|GTR_CATCHER)) + { + grandprixinfo.eventmode = GPEVENT_SPECIAL; + } + else if (newgametype != GT_RACE) { grandprixinfo.eventmode = GPEVENT_BONUS; - - if (mapheaderinfo[newmapnum-1] && - mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS) - { - bossinfo.boss = true; - bossinfo.encore = newencoremode; - } - else - { - bossinfo.boss = false; - K_ResetBossInfo(); - } } - else + + if (!Playing()) { - if (mapheaderinfo[newmapnum-1] && - mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage - { - specialStage.active = true; - specialStage.encore = newencoremode; - grandprixinfo.eventmode = GPEVENT_SPECIAL; - } - else - { - specialStage.active = false; - } + multiplayer = true; } } @@ -3022,7 +3006,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) else if (gametype != lastgametype) D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype - if (!(gametyperules & GTR_ENCORE) && !bossinfo.boss) + if (!(gametyperules & GTR_ENCORE)) pencoremode = false; skipprecutscene = ((flags & (1<<2)) != 0); @@ -5737,11 +5721,11 @@ void Command_Retry_f(void) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); } - else if (grandprixinfo.gp == false && bossinfo.boss == false) + else if (grandprixinfo.gp == false) { CONS_Printf(M_GetText("This only works in singleplayer games.\n")); } - else if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) + else if (grandprixinfo.eventmode == GPEVENT_BONUS) { CONS_Printf(M_GetText("You can't retry right now!\n")); } diff --git a/src/doomstat.h b/src/doomstat.h index 588a548c5..1e2c23ee0 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -455,6 +455,8 @@ enum GameType { GT_RACE = 0, GT_BATTLE, + GT_SPECIAL, + GT_VERSUS, GT_FIRSTFREESLOT, GT_LASTFREESLOT = 127, // Previously (GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1) - it would be necessary to rewrite VOTEMODIFIER_ENCORE to go higher than this. @@ -509,12 +511,16 @@ enum GameTypeRules GTR_TEAMSTARTS = 1<<16, // Use team-based start positions // To be rearranged later - GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. ...mutually exclusive with GTR_CAMPAIGN. + GTR_CATCHER = 1<<17, // UFO Catcher (only works with GTR_CIRCUIT) + GTR_BOSS = 1<<18, // Boss intro and spawning + + GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos GTR_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping // free: to and including 1<<31 }; +// Remember to update GAMETYPERULE_LIST in deh_soc.c // TODO: replace every instance #define gametyperules (gametypes[gametype]->rules) @@ -531,6 +537,7 @@ enum TypeOfLevel // Modifiers TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD }; +// Make sure to update TYPEOFLEVEL too #define MAXTOL (1<<31) #define NUMBASETOLNAMES (5) diff --git a/src/g_game.c b/src/g_game.c index e7ec8ffa4..045255fd4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1425,9 +1425,9 @@ void G_StartTitleCard(void) // play the sound { sfxenum_t kstart = sfx_kstart; - if (bossinfo.boss) + if (K_CheckBossIntro() == true) kstart = sfx_ssa021; - else if (encoremode) + else if (encoremode == true) kstart = sfx_ruby2; S_StartSound(NULL, kstart); } @@ -2790,22 +2790,9 @@ mapthing_t *G_FindMapStart(INT32 playernum) if (!playeringame[playernum]) return NULL; - // -- Spectators -- - // Order in platform gametypes: Race->DM->CTF - // And, with deathmatch starts: DM->CTF->Race - if (players[playernum].spectator) - { - // In platform gametypes, spawn in Co-op starts first - // Overriden by GTR_BATTLESTARTS. - if (gametyperules & GTR_BATTLESTARTS && bossinfo.boss == false) - spawnpoint = G_FindBattleStartOrFallback(playernum); - else - spawnpoint = G_FindRaceStartOrFallback(playernum); - } - - // -- Grand Prix / Time Attack -- + // -- Time Attack -- // Order: Race->DM->CTF - else if (grandprixinfo.gp || modeattacking) + if (K_TimeAttackRules() == true) spawnpoint = G_FindRaceStartOrFallback(playernum); // -- CTF -- @@ -2911,7 +2898,7 @@ void G_ExitLevel(void) { UINT8 i; boolean youlost = false; - if (bossinfo.boss == true) + if (gametyperules & GTR_BOSS) { youlost = true; for (i = 0; i < MAXPLAYERS; i++) @@ -3021,12 +3008,35 @@ static gametype_t defaultgametypes[] = 0, 2, }, + // GT_SPECIAL + { + "Special", + "GT_SPECIAL", + GTR_CATCHER|GTR_CIRCUIT, + TOL_SPECIAL, + int_race, + 0, + 0, + }, + + // GT_VERSUS + { + "Versus", + "GT_VERSUS", + GTR_BOSS|GTR_SPHERES|GTR_BUMPERS|GTR_POINTLIMIT|GTR_CLOSERPLAYERS|GTR_NOCUPSELECT|GTR_ENCORE, + TOL_BOSS, + int_battle, + 0, + 0, + }, }; gametype_t *gametypes[MAXGAMETYPES+1] = { &defaultgametypes[GT_RACE], &defaultgametypes[GT_BATTLE], + &defaultgametypes[GT_SPECIAL], + &defaultgametypes[GT_VERSUS], }; // @@ -3048,6 +3058,25 @@ INT32 G_GetGametypeByName(const char *gametypestr) return -1; // unknown gametype } +// +// G_GuessGametypeByTOL +// +// Returns the first valid number for the given typeoflevel, or -1 if not valid. +// +INT32 G_GuessGametypeByTOL(UINT32 tol) +{ + INT32 i = 0; + + while (gametypes[i] != NULL) + { + if (tol & gametypes[i]->tol) + return i; + i++; + } + + return -1; // unknown gametype +} + // // G_SetGametype // @@ -3651,7 +3680,7 @@ static void G_GetNextMap(void) } else { - INT32 lastgametype = gametype; + INT32 lastgametype = gametype, newgametype = GT_RACE; // 5 levels, 2 bonus stages: after rounds 2 and 4 (but flexible enough to accomodate other solutions) UINT8 bonusmodulo = (grandprixinfo.cup->numlevels+1)/(grandprixinfo.cup->numbonus+1); UINT8 bonusindex = (grandprixinfo.roundnum / bonusmodulo) - 1; @@ -3668,9 +3697,6 @@ static void G_GetNextMap(void) G_SetGametype(GT_RACE); if (gametype != lastgametype) D_GameTypeChanged(lastgametype); - - specialStage.active = false; - bossinfo.boss = false; } // Special stage else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) @@ -3691,11 +3717,11 @@ static void G_GetNextMap(void) if (totaltotalring >= 50) { const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL]; - if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] - && mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_SPECIAL|TOL_BOSS|TOL_BATTLE)) + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]) { grandprixinfo.eventmode = GPEVENT_SPECIAL; nextmap = cupLevelNum; + newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel); } } } @@ -3706,37 +3732,27 @@ static void G_GetNextMap(void) // todo any other condition? { const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS + bonusindex]; - if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] - && mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_BOSS|TOL_BATTLE)) + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]) { grandprixinfo.eventmode = GPEVENT_BONUS; nextmap = cupLevelNum; + newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel); } } } + if (newgametype == -1) + { + // Don't permit invalid changes. + grandprixinfo.eventmode = GPEVENT_NONE; + newgametype = gametype; + } + if (grandprixinfo.eventmode != GPEVENT_NONE) { - // nextmap is set above - const INT32 newtol = mapheaderinfo[nextmap]->typeoflevel; - - if (newtol & TOL_SPECIAL) - { - specialStage.active = true; - specialStage.encore = grandprixinfo.encore; - } - else //(if newtol & (TOL_BATTLE|TOL_BOSS)) -- safe to assume?? - { - G_SetGametype(GT_BATTLE); - if (gametype != lastgametype) - D_GameTypeChanged(lastgametype); - if (newtol & TOL_BOSS) - { - K_ResetBossInfo(); - bossinfo.boss = true; - bossinfo.encore = grandprixinfo.encore; - } - } + G_SetGametype(newgametype); + if (gametype != lastgametype) + D_GameTypeChanged(lastgametype); } else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map { @@ -3760,10 +3776,6 @@ static void G_GetNextMap(void) } } } - else if (bossinfo.boss == true) - { - nextmap = NEXTMAP_TITLE; // temporary - } else { UINT32 tolflag = G_TOLFlag(gametype); diff --git a/src/g_game.h b/src/g_game.h index 04a9df9c9..407e12d64 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -184,6 +184,8 @@ char *G_PrepareGametypeConstant(const char *newgtconst); void G_UpdateGametypeSelections(void); void G_AddTOL(UINT32 newtol, const char *tolname); INT32 G_GetGametypeByName(const char *gametypestr); +INT32 G_GuessGametypeByTOL(UINT32 tol); + boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index d9d2cb6d2..95087a389 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -55,7 +55,6 @@ // SRB2Kart #include "s_sound.h" // song credits #include "k_kart.h" -#include "k_boss.h" #include "k_color.h" #include "k_hud.h" #include "r_fps.h" @@ -2409,7 +2408,7 @@ static void HU_DrawRankings(void) else if (gametype >= 0 && gametype < numgametypes) V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametypes[gametype]->name); - if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)) && !bossinfo.boss) + if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT))) { if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0) { diff --git a/src/k_battle.c b/src/k_battle.c index 781ff108c..26b1265f3 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -2,7 +2,6 @@ /// \brief SRB2Kart Battle Mode specific code #include "k_battle.h" -#include "k_boss.h" #include "k_kart.h" #include "doomtype.h" #include "doomdata.h" @@ -357,7 +356,7 @@ void K_RunPaperItemSpawners(void) UINT8 pcount = 0; INT16 i; - if (battlecapsules || bossinfo.boss) + if (battlecapsules) { // Gametype uses paper items, but this specific expression doesn't return; @@ -794,7 +793,7 @@ void K_BattleInit(boolean singleplayercontext) { size_t i; - if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules && !bossinfo.boss) + if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules) { mapthing_t *mt = mapthings; for (i = 0; i < nummapthings; i++, mt++) diff --git a/src/k_boss.c b/src/k_boss.c index 490adb27e..ff10231ea 100644 --- a/src/k_boss.c +++ b/src/k_boss.c @@ -23,7 +23,7 @@ struct bossinfo bossinfo; /*-------------------------------------------------- - void K_ClearBossInfo(void) + void K_ResetBossInfo(void) See header file for description. --------------------------------------------------*/ @@ -32,6 +32,8 @@ void K_ResetBossInfo(void) Z_Free(bossinfo.enemyname); Z_Free(bossinfo.subtitle); memset(&bossinfo, 0, sizeof(struct bossinfo)); + bossinfo.barlen = BOSSHEALTHBARLEN; + bossinfo.titlesound = sfx_typri1; } /*-------------------------------------------------- @@ -43,7 +45,7 @@ void K_BossInfoTicker(void) { UINT8 i; - if (bossinfo.boss == false) + if (bossinfo.valid == false) return; // Update healthbar data. (only if the hud is visible) @@ -55,7 +57,7 @@ void K_BossInfoTicker(void) bossinfo.visualbar--; // If the boss is dying, start shrinking the healthbar. if (bossinfo.visualbar == 0) - bossinfo.barlen-= 2; + bossinfo.barlen -= 2; } // Less than the actual health? else if (bossinfo.visualbar < bossinfo.healthbar) @@ -108,6 +110,18 @@ void K_BossInfoTicker(void) void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions) { + if (!(gametyperules & GTR_BOSS)) + { + return; + } + + bossinfo.valid = true; + + if (!leveltime) + { + bossinfo.coolintro = true; + } + if (enemyname && enemyname[0]) { Z_Free(bossinfo.enemyname); @@ -158,6 +172,9 @@ void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen) { + if (bossinfo.valid == false) + return; + if (magnitude > FRACUNIT) magnitude = FRACUNIT; else if (magnitude < 0) @@ -177,6 +194,9 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean { UINT8 i; + if (bossinfo.valid == false) + return; + // First check whether the spot is already in the list and simply redeclaring weakness (for example, a vulnerable moment in the pattern). for (i = 0; i < NUMWEAKSPOTS; i++) if (bossinfo.weakspots[i].spot == spot) @@ -206,3 +226,16 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean bossinfo.doweakspotsound = spottype; } } + +/*-------------------------------------------------- + boolean K_CheckBossIntro(void); + + See header file for description. +--------------------------------------------------*/ + +boolean K_CheckBossIntro(void) +{ + if (bossinfo.valid == false) + return false; + return bossinfo.coolintro; +} diff --git a/src/k_boss.h b/src/k_boss.h index f24365b29..6c77bd800 100644 --- a/src/k_boss.h +++ b/src/k_boss.h @@ -39,7 +39,8 @@ struct weakspot_t extern struct bossinfo { - boolean boss; ///< If true, then we are fighting a boss + boolean valid; ///< If true, then data in this struct is valid + UINT8 healthbar; ///< Actual health bar fill amount UINT8 visualbar; ///< Tracks above, but with delay fixed_t visualdiv; ///< How far apart health bar divisions should appear @@ -48,7 +49,7 @@ extern struct bossinfo UINT8 barlen; ///< The length of the bar (only reduced when a boss is deceased) char *enemyname; ///< The name next to the bar weakspot_t weakspots[NUMWEAKSPOTS]; ///< Array of weak spots (for minimap/object tracking) - boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars + boolean coolintro; ///< Determines whether the map start(s/ed) with a boss-specific intro. spottype_t doweakspotsound; ///< If nonzero, at least one weakspot was declared this tic tic_t titleshow; ///< Show this many letters on the titlecard sfxenum_t titlesound; ///< Sound to play when title typing @@ -112,4 +113,16 @@ void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen); void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean minimap); +/*-------------------------------------------------- + boolean K_CheckBossIntro(void); + + Checks whether the Versus-specific intro is playing for this map start. + + Return:- + true if cool intro in action, + otherwise false. +--------------------------------------------------*/ + +boolean K_CheckBossIntro(void); + #endif diff --git a/src/k_bot.c b/src/k_bot.c index caf6aa55f..e6604d338 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -27,7 +27,6 @@ #include "m_random.h" #include "r_things.h" // numskins #include "k_race.h" // finishBeamLine -#include "k_boss.h" #include "m_perfstats.h" @@ -168,7 +167,7 @@ void K_UpdateMatchRaceBots(void) } } - if (difficulty == 0 || !(gametyperules & GTR_BOTS) || bossinfo.boss == true) + if (difficulty == 0 || !(gametyperules & GTR_BOTS)) { wantedbots = 0; } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 221e9d10c..4ca90fe04 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -11,7 +11,6 @@ /// \brief Grand Prix mode game logic & bot behaviors #include "k_grandprix.h" -#include "k_boss.h" #include "k_specialstage.h" #include "doomdef.h" #include "d_player.h" @@ -529,7 +528,7 @@ void K_RetireBots(void) UINT8 i; if (grandprixinfo.gp == true - && ((grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) + && ((grandprixinfo.cup != NULL && grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) || grandprixinfo.eventmode != GPEVENT_NONE)) { // No replacement. @@ -703,24 +702,12 @@ void K_PlayerLoseLife(player_t *player) --------------------------------------------------*/ boolean K_CanChangeRules(boolean allowdemos) { - if (grandprixinfo.gp == true && grandprixinfo.roundnum > 0) + if (grandprixinfo.gp == true /*&& grandprixinfo.roundnum > 0*/) { // Don't cheat the rules of the GP! return false; } - if (bossinfo.boss == true) - { - // Don't cheat the boss! - return false; - } - - if (specialStage.active == true) - { - // Don't cheat special stages! - return false; - } - if (marathonmode) { // Don't cheat the endurance challenge! diff --git a/src/k_hud.c b/src/k_hud.c index 4c2b3fe16..0d92d2035 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3360,7 +3360,7 @@ static void K_drawKartNameTags(void) c.z = viewz; // Maybe shouldn't be handling this here... but the camera info is too good. - if (bossinfo.boss) + if (bossinfo.valid == true) { weakspotdraw_t weakspotdraw[NUMWEAKSPOTS]; UINT8 numdraw = 0; @@ -3923,7 +3923,7 @@ static void K_drawKartMinimap(void) // Target reticule if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace) - || ((gametyperules & GTR_POINTLIMIT) && K_IsPlayerWanted(&players[i]))) + || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i]))) { K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } @@ -3981,7 +3981,7 @@ static void K_drawKartMinimap(void) } // ...but first, any boss targets. - if (bossinfo.boss) + if (bossinfo.valid == true) { for (i = 0; i < NUMWEAKSPOTS; i++) { @@ -4050,7 +4050,7 @@ static void K_drawKartMinimap(void) // Target reticule if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace) - || ((gametyperules & GTR_POINTLIMIT) && K_IsPlayerWanted(&players[localplayers[i]]))) + || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]]))) { K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } @@ -4838,7 +4838,7 @@ void K_drawKartFreePlay(void) if (!LUA_HudEnabled(hud_freeplay)) return; - if (modeattacking || grandprixinfo.gp || bossinfo.boss || stplyr->spectator) + if (modeattacking || grandprixinfo.gp || bossinfo.valid || stplyr->spectator) return; if (lt_exitticker < TICRATE/2) @@ -5098,7 +5098,7 @@ void K_drawKartHUD(void) { if (LUA_HudEnabled(hud_position)) { - if (bossinfo.boss) + if (bossinfo.valid) { K_drawBossHealthBar(); } @@ -5140,7 +5140,7 @@ void K_drawKartHUD(void) K_drawBlueSphereMeter(); } - if (modeattacking && !bossinfo.boss) + if (modeattacking && !bossinfo.valid) { // Draw the input UI if (LUA_HudEnabled(hud_position)) diff --git a/src/k_kart.c b/src/k_kart.c index 3aa21376a..296b8cb69 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6,7 +6,6 @@ #include "k_kart.h" #include "k_battle.h" -#include "k_boss.h" #include "k_pwrlv.h" #include "k_color.h" #include "k_respawn.h" @@ -41,6 +40,7 @@ #include "k_follower.h" #include "k_objects.h" #include "k_grandprix.h" +#include "k_boss.h" #include "k_specialstage.h" #include "k_roulette.h" @@ -109,11 +109,13 @@ void K_TimerInit(void) boolean domodeattack = ((modeattacking != ATTACKING_NONE) || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)); - if (specialStage.active == true) + if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT)) { K_InitSpecialStage(); } - else if (bossinfo.boss == false) + else if (K_CheckBossIntro() == true) + ; + else { if (!domodeattack) { @@ -152,7 +154,7 @@ void K_TimerInit(void) K_BattleInit(domodeattack); - if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss && !modeattacking) + if ((gametyperules & GTR_TIMELIMIT) && !modeattacking) { if (!K_CanChangeRules(true)) { @@ -343,7 +345,7 @@ boolean K_IsPlayerLosing(player_t *player) if (battlecapsules && numtargets == 0) return true; // Didn't even TRY? - if (battlecapsules || bossinfo.boss) + if (battlecapsules || (gametyperules & GTR_BOSS)) return (player->bumpers <= 0); // anything short of DNF is COOL if (player->position == 1) @@ -513,7 +515,7 @@ boolean K_TimeAttackRules(void) UINT8 playing = 0; UINT8 i; - if (specialStage.active == true) + if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT)) { // Kind of a hack -- Special Stages // are expected to be 1-player, so @@ -1301,8 +1303,8 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed */ static void K_UpdateDraft(player_t *player) { - const boolean addUfo = ((specialStage.active == true) - && (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)); + const boolean addUfo = ((specialstageinfo.valid == true) + && (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false)); fixed_t topspd = K_GetKartSpeed(player, false, false); fixed_t draftdistance; @@ -1345,7 +1347,7 @@ static void K_UpdateDraft(player_t *player) if (addUfo == true) { // Tether off of the UFO! - if (K_TryDraft(player, specialStage.ufo, minDist, draftdistance, leniency) == true) + if (K_TryDraft(player, specialstageinfo.ufo, minDist, draftdistance, leniency) == true) { return; // Finished doing our draft. } @@ -1402,8 +1404,8 @@ static void K_UpdateDraft(player_t *player) else if (addUfo == true) { // kind of a hack to not have to mess with how lastdraft works - fixed_t dist = P_AproxDistance(P_AproxDistance(specialStage.ufo->x - player->mo->x, specialStage.ufo->y - player->mo->y), specialStage.ufo->z - player->mo->z); - K_DrawDraftCombiring(player, specialStage.ufo, dist, draftdistance, true); + fixed_t dist = P_AproxDistance(P_AproxDistance(specialstageinfo.ufo->x - player->mo->x, specialstageinfo.ufo->y - player->mo->y), specialstageinfo.ufo->z - player->mo->z); + K_DrawDraftCombiring(player, specialstageinfo.ufo, dist, draftdistance, true); } } else // Remove draft speed boost. @@ -4128,7 +4130,7 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) player->karmadelay = comebacktime; - if (bossinfo.boss) + if (gametyperules & GTR_BOSS) { P_DoTimeOver(player); } @@ -6789,10 +6791,10 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) mobj_t *wtarg = NULL; INT32 i; - if (specialStage.active == true) + if (specialstageinfo.valid == true) { // Always target the UFO. - return specialStage.ufo; + return specialstageinfo.ufo; } for (i = 0; i < MAXPLAYERS; i++) @@ -8028,10 +8030,10 @@ void K_KartPlayerAfterThink(player_t *player) mobj_t *ret = NULL; - if (specialStage.active == true && lastTargID == MAXPLAYERS) + if (specialstageinfo.valid == true && lastTargID == MAXPLAYERS) { // Aiming at the UFO. - lastTarg = specialStage.ufo; + lastTarg = specialstageinfo.ufo; } else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS) && playeringame[lastTargID] == true) diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index 6ad088cae..db46a9266 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -16,7 +16,6 @@ #include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems #include "p_tick.h" // leveltime #include "k_grandprix.h" -#include "k_boss.h" #include "k_profiles.h" // Client-sided calculations done for Power Levels. @@ -38,7 +37,7 @@ SINT8 K_UsingPowerLevels(void) { SINT8 pt = PWRLV_DISABLED; - if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true || bossinfo.boss == true) + if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true) { return PWRLV_DISABLED; } diff --git a/src/k_roulette.c b/src/k_roulette.c index 20d7d19b6..9a5442a03 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -359,7 +359,7 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers return 0; } - if (specialStage.active == true) + if (specialstageinfo.valid == true) { UINT32 ufoDis = K_GetSpecialUFODistance(); @@ -506,7 +506,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table newOdds = K_KartItemOddsBattle[item-1][pos]; } - else if (specialStage.active == true) + else if (specialstageinfo.valid == true) { I_Assert(pos < 4); // Ditto newOdds = K_KartItemOddsSpecial[item-1][pos]; @@ -573,7 +573,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, return 0; } - if (specialStage.active == false) + if (specialstageinfo.valid == false) { if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done || position == 1) // No SPB for 1st ever @@ -705,7 +705,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett oddsValid[i] = false; continue; } - else if (specialStage.active == true && i > 3) + else if (specialstageinfo.valid == true && i > 3) { oddsValid[i] = false; continue; @@ -734,7 +734,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett } else { - if (specialStage.active == true) // Special Stages + if (specialstageinfo.valid == true) // Special Stages { SETUPDISTTABLE(0,2); SETUPDISTTABLE(1,2); @@ -808,7 +808,7 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett return false; } - if (specialStage.active == true) + if (specialstageinfo.valid == true) { return false; } @@ -904,7 +904,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->exiting++; } - if (specialStage.active == true) + if (specialstageinfo.valid == true) { UINT32 dis = K_UndoMapScaling(players[i].distancetofinish); if (dis < roulette->secondDist) @@ -926,7 +926,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) } } - if (specialStage.active == true) + if (specialstageinfo.valid == true) { roulette->firstDist = K_UndoMapScaling(K_GetSpecialUFODistance()); } @@ -1114,7 +1114,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet // SPECIAL CASE No. 2: // Use a special, pre-determined item reel for Time Attack / Free Play - if (bossinfo.boss == true) + if (gametyperules & GTR_BOSS) { for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++) { diff --git a/src/k_specialstage.c b/src/k_specialstage.c index e0551bef1..88ce729bb 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -22,7 +22,7 @@ #include "k_waypoint.h" #include "k_objects.h" -struct specialStage specialStage; +struct specialstageinfo specialstageinfo; /*-------------------------------------------------- void K_ResetSpecialStage(void) @@ -31,7 +31,8 @@ struct specialStage specialStage; --------------------------------------------------*/ void K_ResetSpecialStage(void) { - memset(&specialStage, 0, sizeof(struct specialStage)); + memset(&specialstageinfo, 0, sizeof(struct specialstageinfo)); + specialstageinfo.beamDist = UINT32_MAX; } /*-------------------------------------------------- @@ -43,8 +44,15 @@ void K_InitSpecialStage(void) { INT32 i; - specialStage.beamDist = UINT32_MAX; // TODO: make proper value - P_SetTarget(&specialStage.ufo, Obj_CreateSpecialUFO()); + if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) != (GTR_CATCHER|GTR_CIRCUIT)) + { + return; + } + + specialstageinfo.valid = true; + + specialstageinfo.beamDist = UINT32_MAX; // TODO: make proper value + P_SetTarget(&specialstageinfo.ufo, Obj_CreateSpecialUFO()); for (i = 0; i < MAXPLAYERS; i++) { @@ -88,15 +96,15 @@ static void K_MoveExitBeam(void) moveDist = (8 * mapobjectscale) / FRACUNIT; - if (specialStage.beamDist <= moveDist) + if (specialstageinfo.beamDist <= moveDist) { - specialStage.beamDist = 0; + specialstageinfo.beamDist = 0; // TODO: Fail Special Stage } else { - specialStage.beamDist -= moveDist; + specialstageinfo.beamDist -= moveDist; } // Find players who are now outside of the level. @@ -118,7 +126,7 @@ static void K_MoveExitBeam(void) continue; } - if (player->distancetofinish > specialStage.beamDist) + if (player->distancetofinish > specialstageinfo.beamDist) { P_DoTimeOver(player); } @@ -132,7 +140,7 @@ static void K_MoveExitBeam(void) --------------------------------------------------*/ void K_TickSpecialStage(void) { - if (specialStage.active == false) + if (specialstageinfo.valid == false) { return; } diff --git a/src/k_specialstage.h b/src/k_specialstage.h index c93136b99..091a39f7d 100644 --- a/src/k_specialstage.h +++ b/src/k_specialstage.h @@ -16,14 +16,13 @@ #include "doomdef.h" #include "doomstat.h" -extern struct specialStage +extern struct specialstageinfo { - boolean active; ///< If true, then we are in a special stage - boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars + boolean valid; ///< If true, then data in this struct is valid UINT32 beamDist; ///< Where the exit beam is. mobj_t *ufo; ///< The Chaos Emerald capsule. -} specialStage; +} specialstageinfo; /*-------------------------------------------------- void K_ResetSpecialStage(void); diff --git a/src/objects/spb.c b/src/objects/spb.c index b76dfd4f8..19bb4d7a1 100644 --- a/src/objects/spb.c +++ b/src/objects/spb.c @@ -549,7 +549,7 @@ static void SPBSeek(mobj_t *spb, mobj_t *bestMobj) SetSPBSpeed(spb, xySpeed, zSpeed); - if (specialStage.active == false) + if (specialstageinfo.valid == false) { // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! steerDist = 1536 * mapobjectscale; @@ -874,12 +874,12 @@ void Obj_SPBThink(mobj_t *spb) } else { - if (specialStage.active == true) + if (specialstageinfo.valid == true) { - if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false) + if (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false) { bestRank = 1; - bestMobj = specialStage.ufo; + bestMobj = specialstageinfo.ufo; } } diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 5eaf7f645..d2fffc008 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -239,9 +239,9 @@ static void UFOUpdateAngle(mobj_t *ufo) waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo) { - if ((ufo == NULL) && (specialStage.active == true)) + if ((ufo == NULL) && (specialstageinfo.valid == true)) { - ufo = specialStage.ufo; + ufo = specialstageinfo.ufo; } if (ufo != NULL && P_MobjWasRemoved(ufo) == false @@ -820,11 +820,11 @@ mobj_t *Obj_CreateSpecialUFO(void) UINT32 K_GetSpecialUFODistance(void) { - if (specialStage.active == true) + if (specialstageinfo.valid == true) { - if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false) + if (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false) { - return (UINT32)ufo_distancetofinish(specialStage.ufo); + return (UINT32)ufo_distancetofinish(specialstageinfo.ufo); } } diff --git a/src/p_inter.c b/src/p_inter.c index cb1de723d..77020eb08 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -34,7 +34,6 @@ #include "k_battle.h" #include "k_pwrlv.h" #include "k_grandprix.h" -#include "k_boss.h" #include "k_respawn.h" #include "p_spec.h" #include "k_objects.h" diff --git a/src/p_mobj.c b/src/p_mobj.c index 50717459a..a9562b38a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -39,7 +39,6 @@ // SRB2kart #include "k_kart.h" #include "k_battle.h" -#include "k_boss.h" #include "k_color.h" #include "k_respawn.h" #include "k_bot.h" @@ -9366,7 +9365,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { if (gametyperules & GTR_PAPERITEMS) { - if (battlecapsules == true || bossinfo.boss == true) + if (battlecapsules == true) { ; } @@ -12075,7 +12074,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) // No bosses outside of a combat situation. // (just in case we want boss arenas to do double duty as battle maps) - if (!bossinfo.boss && (mobjinfo[i].flags & MF_BOSS)) + if (!(gametyperules & GTR_BOSS) && (mobjinfo[i].flags & MF_BOSS)) { return false; } @@ -12096,7 +12095,7 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i) if ((i == MT_RING) && (gametyperules & GTR_SPHERES)) return MT_BLUESPHERE; - if ((i == MT_RANDOMITEM) && (gametyperules & (GTR_PAPERITEMS|GTR_CIRCUIT)) == (GTR_PAPERITEMS|GTR_CIRCUIT) && !bossinfo.boss) + if ((i == MT_RANDOMITEM) && (gametyperules & (GTR_PAPERITEMS|GTR_CIRCUIT)) == (GTR_PAPERITEMS|GTR_CIRCUIT)) return MT_PAPERITEMSPOT; return i; diff --git a/src/p_setup.c b/src/p_setup.c index 66f2bebc4..f35f32382 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -6844,7 +6844,7 @@ static void P_InitLevelSettings(void) if (playeringame[i] && !players[i].spectator) p++; - if (grandprixinfo.gp == false && bossinfo.boss == false) + if (grandprixinfo.gp == false) players[i].lives = 3; G_PlayerReborn(i, true); @@ -6854,38 +6854,27 @@ static void P_InitLevelSettings(void) racecountdown = exitcountdown = exitfadestarted = 0; curlap = bestlap = 0; // SRB2Kart - // SRB2Kart: map load variables + // Gamespeed and frantic items + gamespeed = KARTSPEED_EASY; + franticitems = false; + if (grandprixinfo.gp == true) { - if (!(gametyperules & GTR_CIRCUIT)) - { - gamespeed = KARTSPEED_EASY; - } - else + if (gametyperules & GTR_CIRCUIT) { gamespeed = grandprixinfo.gamespeed; } - - franticitems = false; - } - else if (bossinfo.boss) - { - gamespeed = KARTSPEED_EASY; - franticitems = false; } else if (modeattacking) { - if (!(gametyperules & GTR_CIRCUIT)) - gamespeed = KARTSPEED_EASY; - else + if (gametyperules & GTR_CIRCUIT) + { gamespeed = KARTSPEED_HARD; - franticitems = false; + } } else { - if (!(gametyperules & GTR_CIRCUIT)) - gamespeed = KARTSPEED_EASY; - else + if (gametyperules & GTR_CIRCUIT) { if (cv_kartspeed.value == KARTSPEED_AUTO) gamespeed = ((speedscramble == -1) ? KARTSPEED_NORMAL : (UINT8)speedscramble); @@ -6900,6 +6889,9 @@ static void P_InitLevelSettings(void) memset(&battleovertime, 0, sizeof(struct battleovertime)); speedscramble = encorescramble = -1; + + K_ResetSpecialStage(); + K_ResetBossInfo(); } #if 0 @@ -7611,19 +7603,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) K_UpdateMatchRaceBots(); } - if (bossinfo.boss) - { - // Reset some pesky boss state that can't be handled elsewhere. - bossinfo.barlen = BOSSHEALTHBARLEN; - bossinfo.visualbar = 0; - Z_Free(bossinfo.enemyname); - Z_Free(bossinfo.subtitle); - bossinfo.enemyname = bossinfo.subtitle = NULL; - bossinfo.titleshow = 0; - bossinfo.titlesound = sfx_typri1; - memset(&(bossinfo.weakspots), 0, sizeof(weakspot_t)*NUMWEAKSPOTS); - } - if (!fromnetsave) // uglier hack { // to make a newly loaded level start on the second frame. INT32 buf = gametic % BACKUPTICS; diff --git a/src/p_tick.c b/src/p_tick.c index 8312a18a8..4ad34d57e 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -646,7 +646,7 @@ void P_Ticker(boolean run) P_PlayerAfterThink(&players[i]); // Bosses have a punchy start, so no position. - if (bossinfo.boss == true) + if (K_CheckBossIntro() == true) { if (leveltime == 3) { diff --git a/src/p_user.c b/src/p_user.c index ac96ea345..25698bca8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -828,7 +828,8 @@ void P_RestoreMusic(player_t *player) return; // Event - Level Start - if (bossinfo.boss == false && (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled + if ((K_CheckBossIntro() == false) + && (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled return; { @@ -3612,7 +3613,7 @@ void P_DoTimeOver(player_t *player) legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p } - if (netgame && !player->bot && !bossinfo.boss) + if (netgame && !player->bot && !(gametyperules & GTR_BOSS)) { CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players])); } diff --git a/src/s_sound.c b/src/s_sound.c index 96211d653..a721a9ff4 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -30,7 +30,6 @@ #include "m_misc.h" // for tunes command #include "m_cond.h" // for conditionsets #include "lua_hook.h" // MusicChange hook -#include "k_boss.h" // bossinfo #include "byteptr.h" #ifdef HW3SOUND diff --git a/src/st_stuff.c b/src/st_stuff.c index 5c2def212..7583b4c37 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -597,7 +597,7 @@ void ST_runTitleCard(void) // SRB2KART // side Zig-Zag positions... - if (bossinfo.boss == true) + if (K_CheckBossIntro() == true) { // Handle name info... if (bossinfo.enemyname) @@ -792,7 +792,7 @@ void ST_drawTitleCard(void) if (lt_ticker < TTANIMSTART) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); - if (bossinfo.boss == true) + if (K_CheckBossIntro() == true) { // WARNING! // https://twitter.com/matthewseiji/status/1485003284196716544 diff --git a/src/v_video.c b/src/v_video.c index d65b34cea..9fcda2425 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -589,7 +589,7 @@ void V_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du { const tic_t length = TICRATE/4; tic_t timer = lt_exitticker; - if (bossinfo.boss == true) + if (K_CheckBossIntro() == true) { if (leveltime <= 3) timer = 0; diff --git a/src/y_inter.c b/src/y_inter.c index 24c24dd3f..601be46e3 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -209,7 +209,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) else { // set up the levelstring - if (bossinfo.boss == true && bossinfo.enemyname) + if (bossinfo.valid == true && bossinfo.enemyname) { snprintf(data.levelstring, sizeof data.levelstring, @@ -574,7 +574,7 @@ skiptallydrawer: if (!LUA_HudEnabled(hud_intermissionmessages)) return; - if (timer && grandprixinfo.gp == false && bossinfo.boss == false && !modeattacking) + if (timer && grandprixinfo.gp == false && !modeattacking) { char *string; INT32 tickdown = (timer+1)/TICRATE; From 8bb29e340c38126fa81a9086f58be1dbd732da9d Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 23:07:32 +0000 Subject: [PATCH 11/76] Fix a crash in K_drawBossHealthBar exposed by the new random functions --- src/k_hud.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 0d92d2035..ebf6f9ceb 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1973,7 +1973,7 @@ static void K_drawBossHealthBar(void) UINT8 i = 0, barstatus = 1, randlen = 0, darken = 0; const INT32 startx = BASEVIDWIDTH - 23; INT32 starty = BASEVIDHEIGHT - 25; - INT32 rolrand = 0; + INT32 rolrand = 0, randtemp = 0; boolean randsign = false; if (bossinfo.barlen <= 1) @@ -2019,7 +2019,9 @@ static void K_drawBossHealthBar(void) barstatus = 2; } - randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1; + randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)); + if (randtemp > 0) + randlen = M_RandomKey(randtemp)+1; randsign = M_RandomChance(FRACUNIT/2); // Right wing. @@ -2034,7 +2036,9 @@ static void K_drawBossHealthBar(void) randlen--; if (!randlen) { - randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1; + randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)); + if (randtemp > 0) + randlen = M_RandomKey(randtemp)+1; if (barstatus > 1) { rolrand = M_RandomKey(barstatus)+1; From a3d0c197ecf473f868760c56b20cf7583250751b Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 23:08:06 +0000 Subject: [PATCH 12/76] Fix GAMETYPERULE_LIST --- src/deh_tables.c | 49 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 8e7bee9c1..40009f21a 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5791,38 +5791,31 @@ const char *const PLAYERFLAG_LIST[] = { }; const char *const GAMETYPERULE_LIST[] = { - "CAMPAIGN", - "RINGSLINGER", - "SPECTATORS", - "LIVES", - "TEAMS", - "FIRSTPERSON", + "CIRCUIT", + "BOTS", + + "BUMPERS", + "SPHERES", + "PAPERITEMS", "POWERSTONES", - "TEAMFLAGS", - "FRIENDLY", - "SPECIALSTAGES", - "EMERALDTOKENS", - "EMERALDHUNT", - "RACE", - "TAG", + "KARMA", + "ITEMARROWS", + "CAPSULES", + "BATTLESTARTS", + "POINTLIMIT", "TIMELIMIT", "OVERTIME", - "HURTMESSAGES", - "FRIENDLYFIRE", - "STARTCOUNTDOWN", - "HIDEFROZEN", - "BLINDFOLDED", - "RESPAWNDELAY", - "PITYSHIELD", - "DEATHPENALTY", - "NOSPECTATORSPAWN", - "DEATHMATCHSTARTS", - "SPAWNINVUL", - "SPAWNENEMIES", - "ALLOWEXIT", - "NOTITLECARD", - "CUTSCENES", + + "TEAMS", + "NOTEAMS", + "TEAMSTARTS", + + "CATCHER", + "BOSS", + "NOCUPSELECT", + "CLOSERPLAYERS", + "ENCORE", NULL }; From 38a515f8e5ce21865537b5333785cc6247953162 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 23:11:23 +0000 Subject: [PATCH 13/76] Also make Grand Prix bots spectators if the gametype doesn't support them (or if the initialisation happens into a GPEVENT) --- src/d_clisrv.c | 3 +++ src/k_grandprix.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ac602a28f..0be6c8d96 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3748,6 +3748,9 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) sprintf(player_names[newplayernum], "%s", skins[skinnum].realname); SetPlayerSkinByNum(newplayernum, skinnum); + players[newplayernum].spectator = !(gametyperules & GTR_BOTS) + || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE); + if (netgame) { HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false); diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 4ca90fe04..d26787100 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -338,7 +338,7 @@ void K_UpdateGrandPrixBots(void) continue; } - players[i].spectator = (grandprixinfo.eventmode != GPEVENT_NONE); + players[i].spectator = !(gametyperules & GTR_BOTS) || (grandprixinfo.eventmode != GPEVENT_NONE); } // Find the rival. From 470f82104d5f74f00ff22ef0cc9c17842b7f4089 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 14:33:58 +0000 Subject: [PATCH 14/76] Rework intertype_t - Now you select based on whether you want to rank by - Time always (Race, Special) - Score always (might be useful for custom gametypes..?) - Time in 1P, Score otherwise (Battle, Versus) - No longer has gametype-specific text colours on the intermission - Also cleans up a case where invalid music could play for winning a custom gametype without GTR_CIRCUIT *or* GTR_BUMPERS --- src/deh_tables.c | 6 ++--- src/g_game.c | 9 ++++--- src/p_user.c | 9 ++++--- src/y_inter.c | 64 ++++++++++++++++++++---------------------------- src/y_inter.h | 6 ++--- 5 files changed, 44 insertions(+), 50 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 40009f21a..817551bcf 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6396,9 +6396,9 @@ struct int_const_s const INT_CONST[] = { // Intermission types {"int_none",int_none}, - {"int_race",int_race}, - {"int_battle",int_battle}, - {"int_battletime", int_battletime}, + {"int_time",int_time}, + {"int_score",int_score}, + {"int_scoreortimeattack", int_scoreortimeattack}, // Jingles (jingletype_t) {"JT_NONE",JT_NONE}, diff --git a/src/g_game.c b/src/g_game.c index f44bd1c2f..92c2ab068 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2995,7 +2995,7 @@ static gametype_t defaultgametypes[] = "GT_RACE", GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE, TOL_RACE, - int_race, + int_time, 0, 0, }, @@ -3006,17 +3006,18 @@ static gametype_t defaultgametypes[] = "GT_BATTLE", GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS, TOL_BATTLE, - int_battle, + int_scoreortimeattack, 0, 2, }, + // GT_SPECIAL { "Special", "GT_SPECIAL", GTR_CATCHER|GTR_CIRCUIT, TOL_SPECIAL, - int_race, + int_time, 0, 0, }, @@ -3027,7 +3028,7 @@ static gametype_t defaultgametypes[] = "GT_VERSUS", GTR_BOSS|GTR_SPHERES|GTR_BUMPERS|GTR_POINTLIMIT|GTR_CLOSERPLAYERS|GTR_NOCUPSELECT|GTR_ENCORE, TOL_BOSS, - int_battle, + int_scoreortimeattack, 0, 0, }, diff --git a/src/p_user.c b/src/p_user.c index 25698bca8..b333eabe3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -720,6 +720,7 @@ boolean P_EndingMusic(player_t *player) { char buffer[9]; boolean looping = true; + boolean racetracks = !!(gametyperules & GTR_CIRCUIT); INT32 bestlocalpos, test; player_t *bestlocalplayer; @@ -773,7 +774,7 @@ boolean P_EndingMusic(player_t *player) #undef getplayerpos - if ((gametyperules & GTR_CIRCUIT) && bestlocalpos == MAXPLAYERS+1) + if (racetracks == true && bestlocalpos == MAXPLAYERS+1) sprintf(buffer, "k*fail"); // F-Zero death results theme else { @@ -787,9 +788,11 @@ boolean P_EndingMusic(player_t *player) S_SpeedMusic(1.0f); - if ((gametyperules & GTR_CIRCUIT)) + if (racetracks == true) + { buffer[1] = 'r'; - else if ((gametyperules & GTR_BUMPERS)) + } + else { buffer[1] = 'b'; looping = false; diff --git a/src/y_inter.c b/src/y_inter.c index 601be46e3..1d4393dc3 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -334,7 +334,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) // void Y_IntermissionDrawer(void) { - INT32 i, whiteplayer = MAXPLAYERS, x = 4, hilicol = V_YELLOWMAP; // fallback + INT32 i, whiteplayer = MAXPLAYERS, x = 4, hilicol = highlightflags; if (intertype == int_none || rendermode == render_none) return; @@ -357,11 +357,6 @@ void Y_IntermissionDrawer(void) if (!r_splitscreen) whiteplayer = demo.playback ? displayplayers[0] : consoleplayer; - if (modeattacking) - hilicol = V_ORANGEMAP; - else - hilicol = ((intertype == int_race) ? V_SKYMAP : V_REDMAP); - if (sorttic != -1 && intertic > sorttic) { INT32 count = (intertic - sorttic); @@ -374,7 +369,7 @@ void Y_IntermissionDrawer(void) x += (((16 - count) * vid.width) / (8 * vid.dupx)); } - if (intertype == int_race || intertype == int_battle || intertype == int_battletime) + if (intertype == int_time || intertype == int_score) { #define NUMFORNEWCOLUMN 8 INT32 y = 41, gutter = ((data.numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2)); @@ -397,7 +392,7 @@ void Y_IntermissionDrawer(void) { switch (intertype) { - case int_battle: + case int_score: timeheader = "SCORE"; break; default: @@ -532,7 +527,7 @@ void Y_IntermissionDrawer(void) V_DrawRightAlignedThinString(x+152+gutter, y-1, (data.numplayers > NUMFORNEWCOLUMN ? V_6WIDTHSPACE : 0), "NO CONTEST."); else { - if (intertype == int_race || intertype == int_battletime) + if (intertype == int_time) { snprintf(strtime, sizeof strtime, "%i'%02i\"%02i", G_TicsToMinutes(data.val[i], true), G_TicsToSeconds(data.val[i]), G_TicsToCentiseconds(data.val[i])); @@ -668,7 +663,7 @@ void Y_Ticker(void) if (intertic < TICRATE || intertic & 1 || endtic != -1) return; - if (intertype == int_race || intertype == int_battle || intertype == int_battletime) + if (intertype == int_time || intertype == int_score) { { if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8)) @@ -750,27 +745,27 @@ void Y_Ticker(void) // void Y_DetermineIntermissionType(void) { + // no intermission for GP events + if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) + { + intertype = int_none; + return; + } + // set initially intertype = gametypes[gametype]->intermission; - // TODO: special cases - if (intertype == int_battle) + // special cases + if (intertype == int_scoreortimeattack) { - if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) + UINT8 i = 0, nump = 0; + for (i = 0; i < MAXPLAYERS; i++) { - intertype = int_none; - } - else - { - UINT8 i = 0, nump = 0; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - nump++; - } - intertype = (nump < 2 ? int_battletime : int_battle); + if (!playeringame[i] || players[i].spectator) + continue; + nump++; } + intertype = (nump < 2 ? int_time : int_score); } } @@ -836,23 +831,18 @@ void Y_StartIntermission(void) if (prevmap >= nummapheaders || !mapheaderinfo[prevmap]) I_Error("Y_StartIntermission: Internal map ID %d not found (nummapheaders = %d)", prevmap, nummapheaders); + if (!(gametyperules & GTR_CIRCUIT) && (timer > 1)) + S_ChangeMusicInternal("racent", true); // loop it + switch (intertype) { - case int_battle: - case int_battletime: + case int_score: { - if (timer > 1) - S_ChangeMusicInternal("racent", true); // loop it - // Calculate who won - if (intertype == int_battle) - { - Y_CalculateMatchData(0, Y_CompareScore); - break; - } + Y_CalculateMatchData(0, Y_CompareScore); + break; } - // FALLTHRU - case int_race: + case int_time: { // Calculate who won Y_CalculateMatchData(0, Y_CompareTime); diff --git a/src/y_inter.h b/src/y_inter.h index 5bcd0be17..991c3f5d4 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -26,9 +26,9 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level); typedef enum { int_none, - int_race, // Race - int_battle, // Battle (score-based) - int_battletime, // Battle (time-based) + int_time, // Always time + int_score, // Always score + int_scoreortimeattack, // Score unless 1P } intertype_t; extern intertype_t intertype; From 1ac0c44f77730dd734e82f33e903196fdc667652 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 14:34:54 +0000 Subject: [PATCH 15/76] Permit using the gametype option for Command_Map_f when not playing multiplayer --- src/d_netcmd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 54743e331..0d4a8da9d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2739,13 +2739,16 @@ static void Command_Map_f(void) if (option_gametype) { +#if 0 if (!multiplayer) { CONS_Printf(M_GetText( "You can't switch gametypes in single player!\n")); return; } - else if (COM_Argc() < option_gametype + 2)/* no argument after? */ + else +#endif //#if 0 + if (COM_Argc() < option_gametype + 2)/* no argument after? */ { CONS_Alert(CONS_ERROR, "No gametype name follows parameter '%s'.\n", From f8999bea363480e0c4495dde3f96555ef252d444 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 16:06:10 +0000 Subject: [PATCH 16/76] Changes to tab rankings * No longer has gametype-specific highlight * Shows Grand Prix and Capsules instead of gametype name if relevant * Operates on a heirarchy for important information * Shows grand prix round on left if in GP (resolves #360) * Shows capsules remaining on right if in BTC * Attempts to draw timelimit and pointlimit on both left and right * Number of laps remaining/gamespeed is least priority for left and right sides --- src/hu_stuff.c | 113 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 95087a389..2a75663e2 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -55,6 +55,8 @@ // SRB2Kart #include "s_sound.h" // song credits #include "k_kart.h" +#include "k_battle.h" +#include "k_grandprix.h" #include "k_color.h" #include "k_hud.h" #include "r_fps.h" @@ -2391,51 +2393,67 @@ static inline void HU_DrawSpectatorTicker(void) static void HU_DrawRankings(void) { playersort_t tab[MAXPLAYERS]; - INT32 i, j, scorelines, hilicol, numplayersingame = 0; + INT32 i, j, scorelines, numplayersingame = 0, hilicol = highlightflags; boolean completed[MAXPLAYERS]; UINT32 whiteplayer = MAXPLAYERS; + boolean timedone = false, pointsdone = false; V_DrawFadeScreen(0xFF00, 16); // A little more readable, and prevents cheating the fades under other circumstances. - if (modeattacking) - hilicol = V_ORANGEMAP; - else - hilicol = ((gametype == GT_RACE) ? V_SKYMAP : V_REDMAP); - // draw the current gametype in the lower right - if (modeattacking) - V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Record Attack"); + if (grandprixinfo.gp == true) + V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Grand Prix"); + else if (battlecapsules) + V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Capsules"); else if (gametype >= 0 && gametype < numgametypes) V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametypes[gametype]->name); - if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT))) + // Left hand side + if (grandprixinfo.gp == true) { - if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0) + const char *roundstr = NULL; + V_DrawCenteredString(64, 8, 0, "ROUND"); + switch (grandprixinfo.eventmode) { - UINT32 timeval = (timelimitintics + starttime + 1 - leveltime); - if (timeval > timelimitintics+1) - timeval = timelimitintics+1; - timeval /= TICRATE; + case GPEVENT_BONUS: + roundstr = "BONUS"; + break; + case GPEVENT_SPECIAL: + roundstr = "SPECIAL"; + break; + default: + roundstr = va("%d", grandprixinfo.roundnum); + break; + } + V_DrawCenteredString(64, 16, hilicol, roundstr); + } + else if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0) + { + UINT32 timeval = (timelimitintics + starttime + 1 - leveltime); + if (timeval > timelimitintics+1) + timeval = timelimitintics+1; + timeval /= TICRATE; - if (leveltime <= (timelimitintics + starttime)) - { - V_DrawCenteredString(64, 8, 0, "TIME LEFT"); - V_DrawCenteredString(64, 16, hilicol, va("%u", timeval)); - } - - // overtime - if (!players[consoleplayer].exiting && (leveltime > (timelimitintics + starttime + TICRATE/2)) && cv_overtime.value) - { - V_DrawCenteredString(64, 8, 0, "TIME LEFT"); - V_DrawCenteredString(64, 16, hilicol, "OVERTIME"); - } + if (leveltime <= (timelimitintics + starttime)) + { + V_DrawCenteredString(64, 8, 0, "TIME LEFT"); + V_DrawCenteredString(64, 16, hilicol, va("%u", timeval)); } - if ((gametyperules & GTR_POINTLIMIT) && cv_pointlimit.value > 0) + // overtime + if (!players[consoleplayer].exiting && (leveltime > (timelimitintics + starttime + TICRATE/2)) && cv_overtime.value) { - V_DrawCenteredString(256, 8, 0, "POINT LIMIT"); - V_DrawCenteredString(256, 16, hilicol, va("%d", cv_pointlimit.value)); + V_DrawCenteredString(64, 8, 0, "TIME LEFT"); + V_DrawCenteredString(64, 16, hilicol, "OVERTIME"); } + + timedone = true; + } + else if ((gametyperules & GTR_POINTLIMIT) && cv_pointlimit.value > 0) + { + V_DrawCenteredString(64, 8, 0, "POINT LIMIT"); + V_DrawCenteredString(64, 16, hilicol, va("%d", cv_pointlimit.value)); + pointsdone = true; } else if (gametyperules & GTR_CIRCUIT) { @@ -2444,7 +2462,44 @@ static void HU_DrawRankings(void) V_DrawCenteredString(64, 8, 0, "LAP COUNT"); V_DrawCenteredString(64, 16, hilicol, va("%d", numlaps)); } + } + // Right hand side + if (battlecapsules == true) + { + if (numtargets < maptargets) + { + V_DrawCenteredString(256, 8, 0, "CAPSULES"); + V_DrawCenteredString(256, 16, hilicol, va("%d", maptargets - numtargets)); + } + } + else if (!timedone && (gametyperules & GTR_TIMELIMIT) && timelimitintics > 0) + { + UINT32 timeval = (timelimitintics + starttime + 1 - leveltime); + if (timeval > timelimitintics+1) + timeval = timelimitintics+1; + timeval /= TICRATE; + + if (leveltime <= (timelimitintics + starttime)) + { + V_DrawCenteredString(256, 8, 0, "TIME LEFT"); + V_DrawCenteredString(256, 16, hilicol, va("%u", timeval)); + } + + // overtime + if (!players[consoleplayer].exiting && (leveltime > (timelimitintics + starttime + TICRATE/2)) && cv_overtime.value) + { + V_DrawCenteredString(256, 8, 0, "TIME LEFT"); + V_DrawCenteredString(256, 16, hilicol, "OVERTIME"); + } + } + else if (!pointsdone && (gametyperules & GTR_POINTLIMIT) && cv_pointlimit.value > 0) + { + V_DrawCenteredString(256, 8, 0, "POINT LIMIT"); + V_DrawCenteredString(256, 16, hilicol, va("%d", cv_pointlimit.value)); + } + else if (gametyperules & GTR_CIRCUIT) + { V_DrawCenteredString(256, 8, 0, "GAME SPEED"); V_DrawCenteredString(256, 16, hilicol, kartspeed_cons_t[1+gamespeed].strvalue); } From 03c8fd543f3e82705b941c6b0d14a9058e69a8ca Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 17:58:35 +0000 Subject: [PATCH 17/76] More fun rules for Special Stages - GTR_ROLLINGSTART * Initial instathrust, as before * Forced MAXPLMOVE forward * Disable finish line beam * "Super transformation" sound - Reference to the previous entry in the series' Perfect Startboost mechanic - GTR_SPECIALSTART * Instant white fade * No titlecard (overridden by Boss intro) * Starting warp sound - Match slidein time with no traditional titlecard to the end of the playsim intro fade - Remove G_IsSpecialStage --- src/deh_tables.c | 2 ++ src/doomstat.h | 3 ++- src/g_game.c | 36 ++++++++++++------------------------ src/g_game.h | 1 - src/k_kart.c | 33 ++++++++++++++++++++++++++++++++- src/k_race.c | 2 +- src/k_specialstage.c | 26 -------------------------- src/lua_baselib.c | 10 ---------- src/p_setup.c | 22 ++++++++++++++-------- src/p_spec.c | 4 ++-- src/st_stuff.c | 2 +- src/v_video.c | 8 ++++---- 12 files changed, 70 insertions(+), 79 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 817551bcf..8b9be00ff 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5813,9 +5813,11 @@ const char *const GAMETYPERULE_LIST[] = { "CATCHER", "BOSS", + "ROLLINGSTART", "NOCUPSELECT", "CLOSERPLAYERS", "ENCORE", + "SPECIALSTART", NULL }; diff --git a/src/doomstat.h b/src/doomstat.h index 1e2c23ee0..9222e7381 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -513,10 +513,11 @@ enum GameTypeRules // To be rearranged later GTR_CATCHER = 1<<17, // UFO Catcher (only works with GTR_CIRCUIT) GTR_BOSS = 1<<18, // Boss intro and spawning - + GTR_ROLLINGSTART = 1<<19, // Rolling start (only works with GTR_CIRCUIT) GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos GTR_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping + GTR_SPECIALSTART = 1<<23, // White fade instant start // free: to and including 1<<31 }; diff --git a/src/g_game.c b/src/g_game.c index 92c2ab068..2c7f85efb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1412,7 +1412,7 @@ void G_StartTitleCard(void) { // The title card has been disabled for this map. // Oh well. - if (!G_IsTitleCardAvailable() || demo.rewinding) + if (demo.rewinding || !G_IsTitleCardAvailable()) { WipeStageTitle = false; return; @@ -1476,11 +1476,17 @@ void G_PreLevelTitleCard(void) // boolean G_IsTitleCardAvailable(void) { -#if 0 + // Overwrites all other title card exceptions. + if (K_CheckBossIntro() == true) + return true; + // The current level has no name. if (!mapheaderinfo[gamemap-1]->lvlttl[0]) return false; -#endif + + // Instant white fade. + if (gametyperules & GTR_SPECIALSTART) + return false; // The title card is available. return true; @@ -3015,7 +3021,7 @@ static gametype_t defaultgametypes[] = { "Special", "GT_SPECIAL", - GTR_CATCHER|GTR_CIRCUIT, + GTR_CATCHER|GTR_SPECIALSTART|GTR_ROLLINGSTART|GTR_CIRCUIT, TOL_SPECIAL, int_time, 0, @@ -3213,25 +3219,6 @@ void G_AddTOL(UINT32 newtol, const char *tolname) TYPEOFLEVEL[i].flag = newtol; } -// -// G_IsSpecialStage -// -// Returns TRUE if -// the given map is a special stage. -// -boolean G_IsSpecialStage(INT32 mapnum) -{ - mapnum--; // gamemap-based to 0 indexed - - if (mapnum > nummapheaders || !mapheaderinfo[mapnum]) - return false; - - if (!mapheaderinfo[mapnum]->cup || mapheaderinfo[mapnum]->cup->cachedlevels[CUPCACHE_SPECIAL] != mapnum) - return false; - - return true; -} - // // G_GametypeUsesLives // @@ -3666,7 +3653,6 @@ static void G_HandleSaveLevel(void) static void G_GetNextMap(void) { - boolean spec = G_IsSpecialStage(prevmap+1); INT32 i; // go to next level @@ -3917,7 +3903,9 @@ static void G_GetNextMap(void) if (nextmap == NEXTMAP_INVALID || (nextmap < NEXTMAP_SPECIAL && (nextmap >= nummapheaders || !mapheaderinfo[nextmap] || mapheaderinfo[nextmap]->lumpnum == LUMPERROR))) I_Error("G_GetNextMap: Internal map ID %d not found (nummapheaders = %d)\n", nextmap, nummapheaders); +#if 0 // This is a surprise tool that will help us later. if (!spec) +#endif //#if 0 lastmap = nextmap; } diff --git a/src/g_game.h b/src/g_game.h index 407e12d64..5675da0dd 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -186,7 +186,6 @@ void G_AddTOL(UINT32 newtol, const char *tolname); INT32 G_GetGametypeByName(const char *gametypestr); INT32 G_GuessGametypeByTOL(UINT32 tol); -boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); diff --git a/src/k_kart.c b/src/k_kart.c index 296b8cb69..652ec5fb6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -109,6 +109,36 @@ void K_TimerInit(void) boolean domodeattack = ((modeattacking != ATTACKING_NONE) || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)); + // Rooooooolllling staaaaaaart + if ((gametyperules & (GTR_ROLLINGSTART|GTR_CIRCUIT)) == (GTR_ROLLINGSTART|GTR_CIRCUIT)) + { + S_StartSound(NULL, sfx_s25f); + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + continue; + } + + player = &players[i]; + if (player->spectator == true) + { + continue; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + continue; + } + + // Rolling start? lol + P_InstaThrust(player->mo, player->mo->angle, K_GetKartSpeed(player, false, false)); + } + } + if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT)) { K_InitSpecialStage(); @@ -3298,7 +3328,8 @@ SINT8 K_GetForwardMove(player_t *player) return 0; } - if (player->sneakertimer || player->spindashboost) + if (player->sneakertimer || player->spindashboost + || (((gametyperules & (GTR_ROLLINGSTART|GTR_CIRCUIT)) == (GTR_ROLLINGSTART|GTR_CIRCUIT)) && (leveltime < TICRATE/2))) { return MAXPLMOVE; } diff --git a/src/k_race.c b/src/k_race.c index 2e7f477e9..0bbe0bfe0 100644 --- a/src/k_race.c +++ b/src/k_race.c @@ -400,7 +400,7 @@ static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t * void K_RunFinishLineBeam(void) { - if (!(leveltime < starttime || rainbowstartavailable == true)) + if ((gametyperules & GTR_ROLLINGSTART) || !(leveltime < starttime || rainbowstartavailable == true)) { return; } diff --git a/src/k_specialstage.c b/src/k_specialstage.c index 88ce729bb..76f986bcb 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -42,8 +42,6 @@ void K_ResetSpecialStage(void) --------------------------------------------------*/ void K_InitSpecialStage(void) { - INT32 i; - if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) != (GTR_CATCHER|GTR_CIRCUIT)) { return; @@ -53,30 +51,6 @@ void K_InitSpecialStage(void) specialstageinfo.beamDist = UINT32_MAX; // TODO: make proper value P_SetTarget(&specialstageinfo.ufo, Obj_CreateSpecialUFO()); - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *player = NULL; - - if (playeringame[i] == false) - { - continue; - } - - player = &players[i]; - if (player->spectator == true) - { - continue; - } - - if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) - { - continue; - } - - // Rolling start? lol - P_InstaThrust(player->mo, player->mo->angle, K_GetKartSpeed(player, false, false)); - } } /*-------------------------------------------------- diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 16347dfe2..61587bd62 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3282,15 +3282,6 @@ static int lib_gExitLevel(lua_State *L) return 0; } -static int lib_gIsSpecialStage(lua_State *L) -{ - INT32 mapnum = luaL_optinteger(L, 1, gamemap); - //HUDSAFE - INLEVEL - lua_pushboolean(L, G_IsSpecialStage(mapnum)); - return 1; -} - static int lib_gGametypeUsesLives(lua_State *L) { //HUDSAFE @@ -4099,7 +4090,6 @@ static luaL_Reg lib[] = { {"G_DoReborn",lib_gDoReborn}, {"G_SetCustomExitVars",lib_gSetCustomExitVars}, {"G_ExitLevel",lib_gExitLevel}, - {"G_IsSpecialStage",lib_gIsSpecialStage}, {"G_GametypeUsesLives",lib_gGametypeUsesLives}, {"G_GametypeHasTeams",lib_gGametypeHasTeams}, {"G_GametypeHasSpectators",lib_gGametypeHasSpectators}, diff --git a/src/p_setup.c b/src/p_setup.c index f35f32382..e09141271 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7326,13 +7326,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } G_ClearModeAttackRetryFlag(); } - /* - else if (rendermode != render_none && G_IsSpecialStage(gamemap)) - { - P_RunSpecialStageWipe(); - ranspecialwipe = 1; - } - */ // Make sure all sounds are stopped before Z_FreeTags. S_StopSounds(); @@ -7367,7 +7360,20 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) S_Start(); } - levelfadecol = (encoremode ? 0 : 31); + if (gametyperules & GTR_SPECIALSTART) + { + if (ranspecialwipe != 2) + S_StartSound(NULL, sfx_s3kaf); + levelfadecol = 0; + } + else if (encoremode) + { + levelfadecol = 0; + } + else + { + levelfadecol = 31; + } if (rendermode != render_none) { diff --git a/src/p_spec.c b/src/p_spec.c index 4988ad881..b85746db6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1843,7 +1843,7 @@ static void K_HandleLapIncrement(player_t *player) { if (player) { - if (leveltime < starttime) + if (leveltime < starttime && !(gametyperules & GTR_ROLLINGSTART)) { // Will fault the player K_DoIngameRespawn(player); @@ -1902,7 +1902,7 @@ static void K_HandleLapIncrement(player_t *player) if (P_IsDisplayPlayer(player)) { - if (player->laps == numlaps) // final lap + if (numlaps > 1 && player->laps == numlaps) // final lap S_StartSound(NULL, sfx_s3k68); else if ((player->laps > 1) && (player->laps < numlaps)) // non-final lap S_StartSound(NULL, sfx_s221); diff --git a/src/st_stuff.c b/src/st_stuff.c index 7583b4c37..76ad09b9b 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -802,7 +802,7 @@ void ST_drawTitleCard(void) #define HITIME 15 patch_t *localwarn = (encoremode ? twarn2 : twarn); INT32 transp = (lt_ticker+HITIME) % (LOTIME+HITIME); - boolean encorehack = (encoremode && lt_ticker <= PRELEVELTIME+4); + boolean encorehack = ((levelfadecol == 0) && lt_ticker <= PRELEVELTIME+4); if ((localwarn->width > 0) && (lt_ticker + (HITIME-transp) <= lt_endtime)) { diff --git a/src/v_video.c b/src/v_video.c index 9fcda2425..e633b4e3f 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -585,16 +585,16 @@ void V_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du } } - if (options & V_SLIDEIN) + if ((options & V_SLIDEIN)) { const tic_t length = TICRATE/4; tic_t timer = lt_exitticker; - if (K_CheckBossIntro() == true) + if (K_CheckBossIntro() == true || G_IsTitleCardAvailable() == false) { - if (leveltime <= 3) + if (leveltime <= 16) timer = 0; else - timer = leveltime-3; + timer = leveltime-16; } if (timer < length) From 15587417c7965b5c4f08e8cefd3a2cdd002043e3 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 22:24:18 +0000 Subject: [PATCH 18/76] Coherency changes to special stage behaviour in anticipation of timeattack support * Make everyone PF_NOCONTEST (but not explode) if the UFO/emerald reaches the end of its waypoint path. - Possibly temporary: Make the UFO/emerald go straight up at its final waypoint * If you have PF_NOCONTEST, K_IsPlayerLosing is true * If special stage in action and the only reason you'd be behind is your position, nobody loses * Never eliminate last in special stage * Time Over funny camera no longer occurs when PF_NOCONTEST but not dead --- src/k_kart.c | 8 ++++++-- src/objects/ufo.c | 29 ++++++++++++++++++++++++----- src/p_inter.c | 3 ++- src/p_user.c | 14 ++++++++++++-- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 652ec5fb6..ea05bbde1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -372,6 +372,9 @@ boolean K_IsPlayerLosing(player_t *player) INT32 winningpos = 1; UINT8 i, pcount = 0; + if (player->pflags & PF_NOCONTEST) + return true; + if (battlecapsules && numtargets == 0) return true; // Didn't even TRY? @@ -381,6 +384,9 @@ boolean K_IsPlayerLosing(player_t *player) if (player->position == 1) return false; + if (specialstageinfo.valid == true) + return false; // anything short of DNF is COOL + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) @@ -7567,8 +7573,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) //CONS_Printf("cam: %d, dest: %d\n", player->karthud[khud_boostcam], player->karthud[khud_destboostcam]); } - player->karthud[khud_timeovercam] = 0; - // Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations. if (player->spinouttimer != 0 || player->wipeoutslow != 0) { diff --git a/src/objects/ufo.c b/src/objects/ufo.c index d2fffc008..6fe0af881 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -281,10 +281,11 @@ static void UFOMove(mobj_t *ufo) if (curWaypoint == NULL || destWaypoint == NULL) { // Waypoints aren't valid. - // Just stand still. + // Just go straight up. + // :japanese_ogre: : "Abrupt and funny is the funniest way to end the special stage anyways" ufo->momx = 0; ufo->momy = 0; - ufo->momz = 0; + ufo->momz = ufo_speed(ufo); return; } @@ -365,8 +366,23 @@ static void UFOMove(mobj_t *ufo) if (reachedEnd == true) { - CONS_Printf("You lost...\n"); - ufo_waypoint(ufo) = -1; // Invalidate + UINT8 i; + + // Invalidate UFO/emerald + ufo_waypoint(ufo) = -1; + ufo->flags &= ~(MF_SPECIAL|MF_PICKUPFROMBELOW); + + // Disable player + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (players[i].spectator) + continue; + + players[i].pflags |= PF_NOCONTEST; + P_DoPlayerExit(&players[i]); + } } if (pathfindsuccess == true) @@ -655,7 +671,10 @@ void Obj_UFOPieceThink(mobj_t *piece) fixed_t sc = FixedDiv(FixedDiv(ufo->ceilingz - stemZ, piece->scale), 15 * FRACUNIT); UFOMoveTo(piece, ufo->x, ufo->y, stemZ); - piece->spriteyscale = sc; + if (sc > 0) + { + piece->spriteyscale = sc; + } break; } default: diff --git a/src/p_inter.c b/src/p_inter.c index 77020eb08..89229f8ac 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -32,6 +32,7 @@ // SRB2kart #include "k_kart.h" #include "k_battle.h" +#include "k_specialstage.h" #include "k_pwrlv.h" #include "k_grandprix.h" #include "k_respawn.h" @@ -892,7 +893,7 @@ boolean P_CheckRacers(void) } } - if (numPlaying <= 1) + if (numPlaying <= 1 || specialstageinfo.valid == true) { // Never do this without enough players. eliminateLast = false; diff --git a/src/p_user.c b/src/p_user.c index b333eabe3..a27890817 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -54,6 +54,7 @@ #include "k_bot.h" #include "k_grandprix.h" #include "k_boss.h" +#include "k_specialstage.h" #include "k_terrain.h" // K_SpawnSplashForMobj #include "k_color.h" #include "k_follower.h" @@ -1307,7 +1308,16 @@ void P_DoPlayerExit(player_t *player) P_EndingMusic(player); if (P_CheckRacers() && !exitcountdown) - exitcountdown = raceexittime+1; + { + if (specialstageinfo.valid == true && losing == true) + { + exitcountdown = (5*TICRATE)/2; + } + else + { + exitcountdown = raceexittime+1; + } + } } else if ((gametyperules & GTR_BUMPERS)) // Battle Mode exiting { @@ -3048,7 +3058,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall return true; } - if ((player->pflags & PF_NOCONTEST) && (gametyperules & GTR_CIRCUIT)) // 1 for momentum keep, 2 for turnaround + if ((player->pflags & PF_NOCONTEST) && (gametyperules & GTR_CIRCUIT) && player->karthud[khud_timeovercam] != 0) // 1 for momentum keep, 2 for turnaround timeover = (player->karthud[khud_timeovercam] > 2*TICRATE ? 2 : 1); else timeover = 0; From 04f2ac41212e4467db220bae368fdd8de0c4e4f6 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 22:34:02 +0000 Subject: [PATCH 19/76] "Special" time attack mode for 1P. * Both GT_SPECIAL and GT_VERSUS. * Access controlled by SECRET_SPECIALATTACK. (You're blue now.) Related changes to precipitate: * Cups that only have one map in them get selected immediately, rather than off-the-cuff. * Done by seperating out a new function M_LevelSelected from M_LevelSelectHandler * Maps that only have one lap in them don't have a visible lap timestamp sticker. * Fix a cup with *no* valid maps for the current ruleset being hypothetically selectable --- src/deh_soc.c | 2 + src/k_menu.h | 1 + src/k_menudef.c | 3 + src/k_menudraw.c | 10 +- src/k_menufunc.c | 247 ++++++++++++++++++++++++++++------------------- src/m_cond.h | 3 +- 6 files changed, 162 insertions(+), 104 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 65a347169..fdba5c507 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2260,6 +2260,8 @@ void readunlockable(MYFILE *f, INT32 num) unlockables[num].type = SECRET_TIMEATTACK; else if (fastcmp(word2, "BREAKTHECAPSULES")) unlockables[num].type = SECRET_BREAKTHECAPSULES; + else if (fastcmp(word2, "SPECIALATTACK")) + unlockables[num].type = SECRET_SPECIALATTACK; else if (fastcmp(word2, "SOUNDTEST")) unlockables[num].type = SECRET_SOUNDTEST; else if (fastcmp(word2, "ALTTITLE")) diff --git a/src/k_menu.h b/src/k_menu.h index eb57069b7..832e2146e 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -709,6 +709,7 @@ extern struct levellist_s { UINT16 dest; INT16 choosemap; UINT8 newgametype; + UINT8 guessgt; levelsearch_t levelsearch; boolean netgame; // Start the game in an actual server } levellist; diff --git a/src/k_menudef.c b/src/k_menudef.c index f100db28d..3f10aa313 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -95,6 +95,9 @@ menuitem_t PLAY_GamemodesMenu[] = {IT_STRING | IT_CALL, "Capsules", "Bust up all of the capsules in record time!", NULL, {.routine = M_LevelSelectInit}, 1, GT_BATTLE}, + {IT_STRING | IT_CALL, "Special", "Strike your target and secure the prize!", + NULL, {.routine = M_LevelSelectInit}, 1, GT_SPECIAL}, + {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index dc2101f8c..1a3f7a8d8 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1933,7 +1933,7 @@ static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch) V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); - if (levelsearch->cup && !M_CupLocked(levelsearch->cup)) + if (levelsearch->cup && !M_CupLocked(levelsearch->cup) && maxlevels > 0) { add = (cupgrid.previewanim / 82) % maxlevels; map = start; @@ -2002,7 +2002,10 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup) else { if (currentMenu == &PLAY_LevelSelectDef) - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", gametypes[levellist.newgametype]->name)); + { + UINT8 namedgt = (levellist.guessgt != MAXGAMETYPES) ? levellist.guessgt : levellist.newgametype; + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", gametypes[namedgt]->name)); + } } } @@ -2261,7 +2264,8 @@ void M_DrawTimeAttack(void) laprec = mapheaderinfo[map]->mainrecord->lap; } - if (gametypes[levellist.newgametype]->rules & GTR_CIRCUIT) + if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT) + && (mapheaderinfo[map]->numlaps != 1)) { V_DrawRightAlignedString(rightedge-12, timeheight, highlightflags, "BEST LAP:"); K_drawKartTimestamp(laprec, 162+t, timeheight+6, 0, 2); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 264c91505..89aba301b 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3295,25 +3295,40 @@ void M_SetupGametypeMenu(INT32 choice) PLAY_GamemodesDef.prevMenu = currentMenu; - // Battle and Capsules disabled + // Battle and Capsules (and Special) disabled PLAY_GamemodesMenu[1].status = IT_DISABLED; PLAY_GamemodesMenu[2].status = IT_DISABLED; + PLAY_GamemodesMenu[3].status = IT_DISABLED; if (cv_splitplayers.value > 1) { // Re-add Battle PLAY_GamemodesMenu[1].status = IT_STRING | IT_CALL; } - else if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true)) - { - // Re-add Capsules - PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL; - } else { - // Only one non-Back entry, let's skip straight to Race. - M_SetupRaceMenu(-1); - return; + boolean anyunlocked = false; + + if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true)) + { + // Re-add Capsules + PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL; + anyunlocked = true; + } + + if (M_SecretUnlocked(SECRET_SPECIALATTACK, true)) + { + // Re-add Special + PLAY_GamemodesMenu[3].status = IT_STRING | IT_CALL; + anyunlocked = true; + } + + if (!anyunlocked) + { + // Only one non-Back entry, let's skip straight to Race. + M_SetupRaceMenu(-1); + return; + } } M_SetupNextMenu(&PLAY_GamemodesDef, false); @@ -3533,12 +3548,25 @@ static void M_LevelSelectScrollDest(void) static void M_LevelListFromGametype(INT16 gt) { static boolean first = true; - if (first || gt != levellist.newgametype) + if (first || gt != levellist.newgametype || levellist.guessgt != MAXGAMETYPES) { levellist.newgametype = gt; + levellist.levelsearch.typeoflevel = G_TOLFlag(gt); + if (levellist.levelsearch.timeattack == true && gt == GT_SPECIAL) + { + // Sneak in an extra. + levellist.levelsearch.typeoflevel |= G_TOLFlag(GT_VERSUS); + levellist.guessgt = gt; + } + else + { + levellist.guessgt = MAXGAMETYPES; + } + levellist.levelsearch.cupmode = (!(gametypes[gt]->rules & GTR_NOCUPSELECT)); levellist.levelsearch.cup = NULL; + first = false; } @@ -3680,6 +3708,100 @@ void M_LevelSelectInit(INT32 choice) M_LevelListFromGametype(currentMenu->menuitems[itemOn].mvar2); } +static void M_LevelSelected(INT16 add) +{ + UINT8 i = 0; + INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch); + + while (add > 0) + { + map = M_GetNextLevelInList(map, &i, &levellist.levelsearch); + + if (map >= nummapheaders) + { + break; + } + + add--; + } + + if (map >= nummapheaders) + { + // This shouldn't happen + return; + } + + levellist.choosemap = map; + + if (levellist.levelsearch.timeattack) + { + S_StartSound(NULL, sfx_s3k63); + + if (levellist.guessgt != MAXGAMETYPES) + levellist.newgametype = G_GuessGametypeByTOL(levellist.levelsearch.typeoflevel); + + PLAY_TimeAttackDef.prevMenu = currentMenu; + M_SetupNextMenu(&PLAY_TimeAttackDef, false); + } + else + { + if (gamestate == GS_MENU) + { + UINT8 ssplayers = cv_splitplayers.value-1; + + netgame = false; + multiplayer = true; + + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + + // Still need to reset devmode + cht_debug = 0; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + /*if (levellist.choosemap == 0) + levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/ + + if (cv_maxconnections.value < ssplayers+1) + CV_SetValue(&cv_maxconnections, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + S_StartSound(NULL, sfx_s3k63); + + paused = false; + + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame); + + CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); + CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto"); + CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto" : cv_dummykartspeed.string); + + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); + } + else + { + // directly do the map change + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); + } + + M_ClearMenus(true); + } +} + void M_CupSelectHandler(INT32 choice) { const UINT8 pid = 0; @@ -3733,13 +3855,18 @@ void M_CupSelectHandler(INT32 choice) if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/) { + INT16 count; cupheader_t *newcup = cupgrid.builtgrid[CUPMENU_CURSORID]; + cupheader_t *oldcup = levellist.levelsearch.cup; M_SetMenuDelay(pid); + levellist.levelsearch.cup = newcup; + count = M_CountLevelsToShowInList(&levellist.levelsearch); + if ((!newcup) - || (M_CupLocked(newcup)) - || (newcup->cachedlevels[0] == NEXTMAP_INVALID)) + || (count <= 0) + || (cupgrid.grandprix == true && newcup->cachedlevels[0] == NEXTMAP_INVALID)) { S_StartSound(NULL, sfx_s3kb2); return; @@ -3803,13 +3930,17 @@ void M_CupSelectHandler(INT32 choice) M_ClearMenus(true); } + else if (count == 1) + { + PLAY_TimeAttackDef.transitionID = currentMenu->transitionID+1; + M_LevelSelected(0); + } else { // Keep cursor position if you select the same cup again, reset if it's a different cup - if (levellist.levelsearch.cup != newcup) + if (oldcup != newcup || levellist.cursor >= count) { levellist.cursor = 0; - levellist.levelsearch.cup = newcup; } M_LevelSelectScrollDest(); @@ -3868,94 +3999,10 @@ void M_LevelSelectHandler(INT32 choice) if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/) { - UINT8 i = 0; - INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch); - INT16 add = levellist.cursor; - M_SetMenuDelay(pid); - while (add > 0) - { - map = M_GetNextLevelInList(map, &i, &levellist.levelsearch); - - if (map >= nummapheaders) - { - break; - } - - add--; - } - - if (map >= nummapheaders) - { - // This shouldn't happen - return; - } - - levellist.choosemap = map; - - if (levellist.levelsearch.timeattack) - { - M_SetupNextMenu(&PLAY_TimeAttackDef, false); - S_StartSound(NULL, sfx_s3k63); - } - else - { - if (gamestate == GS_MENU) - { - UINT8 ssplayers = cv_splitplayers.value-1; - - netgame = false; - multiplayer = true; - - strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); - - // Still need to reset devmode - cht_debug = 0; - - if (demo.playback) - G_StopDemo(); - if (metalrecording) - G_StopMetalDemo(); - - /*if (levellist.choosemap == 0) - levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/ - - if (cv_maxconnections.value < ssplayers+1) - CV_SetValue(&cv_maxconnections, ssplayers+1); - - if (splitscreen != ssplayers) - { - splitscreen = ssplayers; - SplitScreen_OnChange(); - } - - S_StartSound(NULL, sfx_s3k63); - - paused = false; - - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - - SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame); - - CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); - CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto"); - CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto" : cv_dummykartspeed.string); - - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); - } - else - { - // directly do the map change - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); - } - - M_ClearMenus(true); - } + PLAY_TimeAttackDef.transitionID = currentMenu->transitionID; + M_LevelSelected(levellist.cursor); } else if (M_MenuBackPressed(pid)) { diff --git a/src/m_cond.h b/src/m_cond.h index 5c50146f0..9f4ede99b 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -116,7 +116,8 @@ typedef enum // Menu restrictions SECRET_TIMEATTACK, // Permit Time attack - SECRET_BREAKTHECAPSULES, // Permit SP Capsules + SECRET_BREAKTHECAPSULES, // Permit SP Capsule attack + SECRET_SPECIALATTACK, // Permit Special attack (You're blue now!) SECRET_SOUNDTEST, // Permit Sound Test SECRET_ALTTITLE, // Permit alternate titlescreen From 978040ca3f94b2e6669f81bdfc22587f9dff969e Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 22:36:20 +0000 Subject: [PATCH 20/76] Two menu bugbears - Clear console prints immediately after menu clear (fixes the prints on the black screen before loading level from menu) - Last kart standing, not last hedgehog standing :face_holding_back_tears: --- src/k_menudef.c | 2 +- src/k_menufunc.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index 3f10aa313..b0de23761 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -89,7 +89,7 @@ menuitem_t PLAY_GamemodesMenu[] = {IT_STRING | IT_CALL, "Race", "A contest to see who's the fastest of them all!", NULL, {.routine = M_SetupRaceMenu}, 0, 0}, - {IT_STRING | IT_CALL, "Battle", "It's last hedgehog standing in this free-for-all!", + {IT_STRING | IT_CALL, "Battle", "It's last kart standing in this free-for-all!", "MENIMG00", {.routine = M_LevelSelectInit}, 0, GT_BATTLE}, {IT_STRING | IT_CALL, "Capsules", "Bust up all of the capsules in record time!", diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 89aba301b..6ee35a9dd 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1009,6 +1009,8 @@ void M_ClearMenus(boolean callexitmenufunc) if (!menuactive) return; + CON_ClearHUD(); + if (currentMenu->quitroutine && callexitmenufunc && !currentMenu->quitroutine()) return; // we can't quit this menu (also used to set parameter from the menu) From 03c6eb3e305ee65baae4b27237dd5e7ca8858935 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 22:41:30 +0000 Subject: [PATCH 21/76] Another bugbear: start on the Start for timeattack menu flow --- src/k_menufunc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6ee35a9dd..bbe0b353c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3742,6 +3742,7 @@ static void M_LevelSelected(INT16 add) if (levellist.guessgt != MAXGAMETYPES) levellist.newgametype = G_GuessGametypeByTOL(levellist.levelsearch.typeoflevel); + PLAY_TimeAttackDef.lastOn = ta_start; PLAY_TimeAttackDef.prevMenu = currentMenu; M_SetupNextMenu(&PLAY_TimeAttackDef, false); } From edea4e21104f9eac12f707c9627e8fc8a455dcb6 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 22:56:07 +0000 Subject: [PATCH 22/76] Message says return to menu instead of title screen --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index bbe0b353c..3ffc8403a 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -6319,7 +6319,7 @@ void M_EndGame(INT32 choice) if (!Playing()) return; - M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO); + M_StartMessage(M_GetText("Are you sure you want to return\nto the menu?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO); } From c47faa37ab195730534a5de05be2fcf76f13a555 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 23:12:04 +0000 Subject: [PATCH 23/76] ONLY have Super Ring in item reel if UFO is busted --- src/k_roulette.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 9a5442a03..1c3c2db62 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -176,6 +176,12 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = { 0, 0, 0, 0 } // Gachabom x3 }; +static kartitems_t K_KartItemReelSpecialEnd[] = +{ + KITEM_SUPERRING, + KITEM_NONE +}; + static kartitems_t K_KartItemReelTimeAttack[] = { KITEM_SNEAKER, @@ -1113,8 +1119,21 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet } // SPECIAL CASE No. 2: - // Use a special, pre-determined item reel for Time Attack / Free Play - if (gametyperules & GTR_BOSS) + // Use a special, pre-determined item reel for Time Attack / Free Play / End of Sealed Stars + if (specialstageinfo.valid) + { + if (specialstageinfo.ufo == NULL + || P_MobjWasRemoved(specialstageinfo.ufo) + || specialstageinfo.ufo->health == 1) + { + for (i = 0; K_KartItemReelSpecialEnd[i] != KITEM_NONE; i++) + { + K_PushToRouletteItemList(roulette, K_KartItemReelSpecialEnd[i]); + } + return; + } + } + else if (gametyperules & GTR_BOSS) { for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++) { From c992438e3c93ed0427b3efbfdd8751c2b0e26a75 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Dec 2022 23:13:02 +0000 Subject: [PATCH 24/76] Only tether off UFO if it's not just a teeny emerald --- 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 ea05bbde1..9bea5e721 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1340,7 +1340,8 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed static void K_UpdateDraft(player_t *player) { const boolean addUfo = ((specialstageinfo.valid == true) - && (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false)); + && (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false) + && specialstageinfo.ufo->health > 1); fixed_t topspd = K_GetKartSpeed(player, false, false); fixed_t draftdistance; From e45282bdff1e2407bf1da605132bc394ffe9ad73 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 28 Dec 2022 23:31:46 +0000 Subject: [PATCH 25/76] K_GetPossibleSpecialTarget() * Standardises conditions under which the UFO Catcher can be "targeted" (Jawz, SPB, tether) * Makes SPBs explode a little ahead of driving distance if UFO Catcher is no longer targetable --- src/k_kart.c | 25 ++++++++++++------------- src/k_roulette.c | 4 +--- src/k_specialstage.c | 15 +++++++++++++++ src/k_specialstage.h | 11 +++++++++++ src/objects/spb.c | 23 ++++++++++++++--------- 5 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9bea5e721..80ff8790f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1339,9 +1339,7 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed */ static void K_UpdateDraft(player_t *player) { - const boolean addUfo = ((specialstageinfo.valid == true) - && (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false) - && specialstageinfo.ufo->health > 1); + mobj_t *addUfo = K_GetPossibleSpecialTarget(); fixed_t topspd = K_GetKartSpeed(player, false, false); fixed_t draftdistance; @@ -1381,10 +1379,10 @@ static void K_UpdateDraft(player_t *player) // Not enough speed to draft. if (player->speed >= 20 * player->mo->scale) { - if (addUfo == true) + if (addUfo != NULL) { // Tether off of the UFO! - if (K_TryDraft(player, specialstageinfo.ufo, minDist, draftdistance, leniency) == true) + if (K_TryDraft(player, addUfo, minDist, draftdistance, leniency) == true) { return; // Finished doing our draft. } @@ -1438,11 +1436,11 @@ static void K_UpdateDraft(player_t *player) fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z); K_DrawDraftCombiring(player, victim->mo, dist, draftdistance, true); } - else if (addUfo == true) + else if (addUfo != NULL) { // kind of a hack to not have to mess with how lastdraft works - fixed_t dist = P_AproxDistance(P_AproxDistance(specialstageinfo.ufo->x - player->mo->x, specialstageinfo.ufo->y - player->mo->y), specialstageinfo.ufo->z - player->mo->z); - K_DrawDraftCombiring(player, specialstageinfo.ufo, dist, draftdistance, true); + fixed_t dist = P_AproxDistance(P_AproxDistance(addUfo->x - player->mo->x, addUfo->y - player->mo->y), addUfo->z - player->mo->z); + K_DrawDraftCombiring(player, addUfo, dist, draftdistance, true); } } else // Remove draft speed boost. @@ -6831,8 +6829,8 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) if (specialstageinfo.valid == true) { - // Always target the UFO. - return specialstageinfo.ufo; + // Always target the UFO (but not the emerald) + return K_GetPossibleSpecialTarget(); } for (i = 0; i < MAXPLAYERS; i++) @@ -8066,10 +8064,11 @@ void K_KartPlayerAfterThink(player_t *player) mobj_t *ret = NULL; - if (specialstageinfo.valid == true && lastTargID == MAXPLAYERS) + if (specialstageinfo.valid == true + && lastTargID == MAXPLAYERS) { - // Aiming at the UFO. - lastTarg = specialstageinfo.ufo; + // Aiming at the UFO (but never the emerald). + lastTarg = K_GetPossibleSpecialTarget(); } else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS) && playeringame[lastTargID] == true) diff --git a/src/k_roulette.c b/src/k_roulette.c index 1c3c2db62..da12d5209 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -1122,9 +1122,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet // Use a special, pre-determined item reel for Time Attack / Free Play / End of Sealed Stars if (specialstageinfo.valid) { - if (specialstageinfo.ufo == NULL - || P_MobjWasRemoved(specialstageinfo.ufo) - || specialstageinfo.ufo->health == 1) + if (K_GetPossibleSpecialTarget() == NULL) { for (i = 0; K_KartItemReelSpecialEnd[i] != KITEM_NONE; i++) { diff --git a/src/k_specialstage.c b/src/k_specialstage.c index 76f986bcb..d5eb172ce 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -121,3 +121,18 @@ void K_TickSpecialStage(void) K_MoveExitBeam(); } + +mobj_t *K_GetPossibleSpecialTarget(void) +{ + if (specialstageinfo.valid == false) + return NULL; + + if (specialstageinfo.ufo == NULL + || P_MobjWasRemoved(specialstageinfo.ufo)) + return NULL; + + if (specialstageinfo.ufo->health <= 1) //UFOEmeraldChase(specialstageinfo.ufo) + return NULL; + + return specialstageinfo.ufo; +} diff --git a/src/k_specialstage.h b/src/k_specialstage.h index 091a39f7d..d12691144 100644 --- a/src/k_specialstage.h +++ b/src/k_specialstage.h @@ -50,5 +50,16 @@ void K_InitSpecialStage(void); void K_TickSpecialStage(void); +/*-------------------------------------------------- + mobj_t *K_GetPossibleSpecialTarget(void) + + Gets the global special stage target if valid + (for Jawz, tether, etc) + + Return:- + Target or NULL +--------------------------------------------------*/ + +mobj_t *K_GetPossibleSpecialTarget(void); #endif diff --git a/src/objects/spb.c b/src/objects/spb.c index 19bb4d7a1..cdb654c5f 100644 --- a/src/objects/spb.c +++ b/src/objects/spb.c @@ -857,6 +857,20 @@ void Obj_SPBThink(mobj_t *spb) ghost->colorized = true; } + if (spb_nothink(spb) <= 1) + { + if (specialstageinfo.valid == true) + { + bestRank = 0; + + if ((bestMobj = K_GetPossibleSpecialTarget()) == NULL) + { + spb->fuse = TICRATE/3; + spb_nothink(spb) = spb->fuse + 2; + } + } + } + if (spb_nothink(spb) > 0) { // Init values, don't think yet. @@ -874,15 +888,6 @@ void Obj_SPBThink(mobj_t *spb) } else { - if (specialstageinfo.valid == true) - { - if (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false) - { - bestRank = 1; - bestMobj = specialstageinfo.ufo; - } - } - // Find the player with the best rank for (i = 0; i < MAXPLAYERS; i++) { From 7482e11992229b6ee852b7efa4a986b737b15935 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 28 Dec 2022 23:35:27 +0000 Subject: [PATCH 26/76] SPB special stage experiment If you smuggle an SPB to after the death of the UFO Catcher, spawn a single, free-on-straightways manta ring before it blows up. Testing shows that you can get hit by the SPB's explosion only if you use the free manta ring just before a sneaker panel... but also Sal thinks it's cool and I think it adds depth --- src/objects/spb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/objects/spb.c b/src/objects/spb.c index cdb654c5f..af1041049 100644 --- a/src/objects/spb.c +++ b/src/objects/spb.c @@ -865,6 +865,13 @@ void Obj_SPBThink(mobj_t *spb) if ((bestMobj = K_GetPossibleSpecialTarget()) == NULL) { + // experimental - I think it's interesting IMO + Obj_MantaRingCreate( + spb, + spb_owner(spb), + NULL + ); + spb->fuse = TICRATE/3; spb_nothink(spb) = spb->fuse + 2; } From df3624d96cabd9d57f0c85050f8f2e89768301fd Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 29 Dec 2022 17:44:19 +0000 Subject: [PATCH 27/76] Executor calls for UFO Catcher special events Requested by Charyb. - LE_PINCHPHASE (-2 or 65534) for busting open the Catcher - LE_BOSSDEAD (-4 or 65532) for grabbing the emerald Would like to use args for UDMF compatibility but there is no spawnpoint for the Catcher so it's hardcoded for now --- src/objects/ufo.c | 2 ++ src/p_inter.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 6fe0af881..f9a44127b 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -607,6 +607,8 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); ufo->shadowscale = FRACUNIT/3; + P_LinedefExecute(LE_PINCHPHASE, ufo, NULL); + ufo_speed(ufo) += addSpeed; // Even more speed! return true; } diff --git a/src/p_inter.c b/src/p_inter.c index 89229f8ac..a630d96bf 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -414,6 +414,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (toucher->hitlag > 0) return; + P_LinedefExecute(LE_BOSSDEAD, ufo, NULL); + CONS_Printf("You win!\n"); break; /* From d1852adf30066ebc54f26aedda6b415ed1890cdf Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 29 Dec 2022 17:46:11 +0000 Subject: [PATCH 28/76] Forgot to stage this --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index a630d96bf..8944b8b50 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -414,7 +414,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (toucher->hitlag > 0) return; - P_LinedefExecute(LE_BOSSDEAD, ufo, NULL); + P_LinedefExecute(LE_BOSSDEAD, toucher, NULL); CONS_Printf("You win!\n"); break; From 881507889dfb8950a3bebc1f7725209c1a5b38aa Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 29 Dec 2022 17:54:19 +0000 Subject: [PATCH 29/76] Fix Broly SIGFPE - Don't spawn the KI if the duration <= 0 - If the KI spawned but the duration <= 0 for any other reason remove the object instead of performing the division --- src/k_objects.h | 2 +- src/objects/broly.c | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/k_objects.h b/src/k_objects.h index 284ec1c61..222ead574 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -56,7 +56,7 @@ void Obj_DuelBombInit(mobj_t *bomb); /* Broly Ki */ mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); -void Obj_BrolyKiThink(mobj_t *ki); +boolean Obj_BrolyKiThink(mobj_t *ki); /* Special Stage UFO */ waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo); diff --git a/src/objects/broly.c b/src/objects/broly.c index d041c23b7..4c283a175 100644 --- a/src/objects/broly.c +++ b/src/objects/broly.c @@ -34,14 +34,16 @@ Obj_SpawnBrolyKi ( mobj_t * source, tic_t duration) { - mobj_t *x = P_SpawnMobjFromMobj( - source, 0, 0, 0, MT_BROLY); + mobj_t *x; - if (duration == 0) + if (duration <= 0) { - return x; + return NULL; } + x = P_SpawnMobjFromMobj( + source, 0, 0, 0, MT_BROLY); + // Shrink into center of source object. x->z = (source->z + source->height / 2); @@ -61,12 +63,20 @@ Obj_SpawnBrolyKi return x; } -void +boolean Obj_BrolyKiThink (mobj_t *x) { + if (broly_duration(x) <= 0) + { + P_RemoveMobj(x); + return false; + } + const fixed_t t = get_unit_linear(x), n = Easing_OutSine(t, 0, broly_maxscale(x)); P_InstaScale(x, n); + + return true; } From 1f18aa727a883e5c03e7f60e397cc9c7845f1484 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 29 Dec 2022 17:57:19 +0000 Subject: [PATCH 30/76] Forgot to *stash* this time --- src/p_mobj.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 26b38d631..2b8101aeb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6534,7 +6534,10 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->renderflags ^= RF_DONTDRAW; break; case MT_BROLY: - Obj_BrolyKiThink(mobj); + if (Obj_BrolyKiThink(mobj) == false) + { + return; + } break; case MT_VWREF: case MT_VWREB: From 834150585b2977a05c9789acc8ace8006d5affe7 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 29 Dec 2022 21:30:04 +0000 Subject: [PATCH 31/76] Rework demo time/laptime handling * `ATTACKING_` constants have been changed to be flags - `ATTACKING_TIME` contains time data for all gametypes - `ATTACKING_LAPS` contains laps data for `GTR_CIRCUIT` on maps with more than 1 lap * `demoflags` now contains raw `ATTACKING_` flags * Best time/best lap demo files will now be saved properly again (broken since `new-menus`) * Ghosts will now be loaded properly again (broken since `unlockables-undefeatable`) --- src/doomstat.h | 6 +- src/g_demo.c | 140 +++++++++++++++++++---------------------------- src/g_game.c | 50 ++++++++++------- src/k_menufunc.c | 12 ++-- src/p_setup.c | 17 +++--- 5 files changed, 104 insertions(+), 121 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index 9222e7381..63739358f 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -131,9 +131,9 @@ extern boolean usedCheats; extern boolean imcontinuing; // Temporary flag while continuing extern boolean metalrecording; -#define ATTACKING_NONE 0 -#define ATTACKING_TIME 1 -#define ATTACKING_CAPSULES 2 +#define ATTACKING_NONE 0 +#define ATTACKING_TIME 1 +#define ATTACKING_LAP (1<<1) extern UINT8 modeattacking; // menu demo things diff --git a/src/g_demo.c b/src/g_demo.c index e47d8ba28..ddf0b342f 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -120,18 +120,14 @@ demoghost *ghosts = NULL; #define DEMOVERSION 0x0007 #define DEMOHEADER "\xF0" "KartReplay" "\x0F" -#define DF_GHOST 0x01 // This demo contains ghost data too! -#define DF_TIMEATTACK 0x02 // This demo is from Time Attack and contains its final completion time & best lap! -#define DF_BREAKTHECAPSULES 0x04 // This demo is from Break the Capsules and contains its final completion time! -#define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? +#define DF_ATTACKMASK (ATTACKING_TIME|ATTACKING_LAP) // This demo contains time/lap data -// 0x08 free +#define DF_GHOST 0x08 // This demo contains ghost data too! #define DF_NONETMP 0x10 // multiplayer but not netgame #define DF_LUAVARS 0x20 // this demo contains extra lua vars -#define DF_ATTACKSHIFT 1 #define DF_ENCORE 0x40 #define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode! @@ -2339,10 +2335,19 @@ void G_BeginRecording(void) memset(name,0,sizeof(name)); demo_p = demobuffer; - demoflags = DF_GHOST|(multiplayer ? DF_MULTIPLAYER : (modeattacking<>DF_ATTACKSHIFT) + if ((demoflags & DF_ATTACKMASK)) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_TIME: // 1 - demotime_p = demo_p; + demotime_p = demo_p; + + if (demoflags & ATTACKING_TIME) WRITEUINT32(demo_p,UINT32_MAX); // time + if (demoflags & ATTACKING_LAP) WRITEUINT32(demo_p,UINT32_MAX); // lap - break; - case ATTACKING_CAPSULES: // 2 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - break; - default: // 3 - break; + } + else + { + demotime_p = NULL; } for (i = 0; i < PRNUMCLASS; i++) @@ -2600,18 +2602,15 @@ void G_SetDemoTime(UINT32 ptime, UINT32 plap) { if (!demo.recording || !demotime_p) return; - if (demoflags & DF_TIMEATTACK) + if (demoflags & ATTACKING_TIME) { WRITEUINT32(demotime_p, ptime); + } + if (demoflags & ATTACKING_LAP) + { WRITEUINT32(demotime_p, plap); - demotime_p = NULL; - } - else if (demoflags & DF_BREAKTHECAPSULES) - { - WRITEUINT32(demotime_p, ptime); - (void)plap; - demotime_p = NULL; } + demotime_p = NULL; } // Returns bitfield: @@ -2622,7 +2621,8 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) { UINT8 *buffer,*p; UINT8 flags; - UINT32 oldtime, newtime, oldlap, newlap; + UINT32 oldtime = UINT32_MAX, newtime = UINT32_MAX; + UINT32 oldlap = UINT32_MAX, newlap = UINT32_MAX; UINT16 oldversion; size_t bufsize ATTRUNUSED; UINT8 c; @@ -2658,17 +2658,16 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) G_SkipDemoSkins(&p); - aflags = flags & (DF_TIMEATTACK|DF_BREAKTHECAPSULES); + aflags = flags & DF_ATTACKMASK; I_Assert(aflags); - if (flags & DF_TIMEATTACK) - uselaps = true; // get around uninitalized error + if (aflags & ATTACKING_LAP) + uselaps = true; - newtime = READUINT32(p); + if (aflags & ATTACKING_TIME) + newtime = READUINT32(p); if (uselaps) newlap = READUINT32(p); - else - newlap = UINT32_MAX; Z_Free(buffer); @@ -2724,11 +2723,10 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) G_SkipDemoSkins(&p); - oldtime = READUINT32(p); + if (flags & ATTACKING_TIME) + oldtime = READUINT32(p); if (uselaps) oldlap = READUINT32(p); - else - oldlap = 0; Z_Free(buffer); @@ -3136,7 +3134,7 @@ void G_DoPlayDemo(char *defdemoname) return; } - modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; + modeattacking = (demoflags & DF_ATTACKMASK); multiplayer = !!(demoflags & DF_MULTIPLAYER); demo.netgame = (multiplayer && !(demoflags & DF_NONETMP)); CON_ToggleOff(); @@ -3144,21 +3142,10 @@ void G_DoPlayDemo(char *defdemoname) hu_demotime = UINT32_MAX; hu_demolap = UINT32_MAX; - switch (modeattacking) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_TIME: // 1 - hu_demotime = READUINT32(demo_p); - hu_demolap = READUINT32(demo_p); - break; - case ATTACKING_CAPSULES: // 2 - hu_demotime = READUINT32(demo_p); - break; - default: // 3 - modeattacking = ATTACKING_NONE; - break; - } + if (modeattacking & ATTACKING_TIME) + hu_demotime = READUINT32(demo_p); + if (modeattacking & ATTACKING_LAP) + hu_demolap = READUINT32(demo_p); // Random seed for (i = 0; i < PRNUMCLASS; i++) @@ -3545,19 +3532,10 @@ void G_AddGhost(char *defdemoname) return; } - switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_TIME: // 1 - p += 8; // demo time, lap - break; - case ATTACKING_CAPSULES: // 2 - p += 4; // demo time - break; - default: // 3 - break; - } + if (flags & ATTACKING_TIME) + p += 4; + if (flags & ATTACKING_LAP) + p += 4; for (i = 0; i < PRNUMCLASS; i++) { @@ -3587,9 +3565,10 @@ void G_AddGhost(char *defdemoname) p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once // any invalidating flags? - if ((READUINT8(p) & (DEMO_SPECTATOR|DEMO_BOT)) != 0) + i = READUINT8(p); + if ((i & (DEMO_SPECTATOR|DEMO_BOT)) != 0) { - CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot (spectator/bot)\n"), pdemoname); Z_Free(skinlist); Z_Free(pdemoname); Z_Free(buffer); @@ -3600,6 +3579,8 @@ void G_AddGhost(char *defdemoname) M_Memcpy(name, p, 16); p += 16; + p += MAXAVAILABILITY; + // Skin i = READUINT8(p); if (i < worknumskins) @@ -3620,7 +3601,7 @@ void G_AddGhost(char *defdemoname) if (READUINT8(p) != 0xFF) { - CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot (bad terminator)\n"), pdemoname); Z_Free(skinlist); Z_Free(pdemoname); Z_Free(buffer); @@ -3761,19 +3742,10 @@ void G_UpdateStaffGhostName(lumpnum_t l) G_SkipDemoSkins(&p); - switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_TIME: // 1 - p += 8; // demo time, lap - break; - case ATTACKING_CAPSULES: // 2 - p += 4; // demo time - break; - default: // 3 - break; - } + if (flags & ATTACKING_TIME) + p += 4; + if (flags & ATTACKING_LAP) + p += 4; for (i = 0; i < PRNUMCLASS; i++) { diff --git a/src/g_game.c b/src/g_game.c index 2c7f85efb..8ba6a4aa5 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -520,13 +520,18 @@ static void G_UpdateRecordReplays(void) players[consoleplayer].realtime = UINT32_MAX; } - if (((mapheaderinfo[gamemap-1]->mainrecord->time == 0) || (players[consoleplayer].realtime < mapheaderinfo[gamemap-1]->mainrecord->time)) - && (players[consoleplayer].realtime < UINT32_MAX)) // DNF + if (modeattacking & ATTACKING_TIME) { - mapheaderinfo[gamemap-1]->mainrecord->time = players[consoleplayer].realtime; + if (((mapheaderinfo[gamemap-1]->mainrecord->time == 0) || (players[consoleplayer].realtime < mapheaderinfo[gamemap-1]->mainrecord->time)) + && (players[consoleplayer].realtime < UINT32_MAX)) // DNF + mapheaderinfo[gamemap-1]->mainrecord->time = players[consoleplayer].realtime; + } + else + { + mapheaderinfo[gamemap-1]->mainrecord->time = 0; } - if (modeattacking == ATTACKING_TIME) + if (modeattacking & ATTACKING_LAP) { if ((mapheaderinfo[gamemap-1]->mainrecord->lap == 0) || (bestlap < mapheaderinfo[gamemap-1]->mainrecord->lap)) mapheaderinfo[gamemap-1]->mainrecord->lap = bestlap; @@ -549,27 +554,32 @@ static void G_UpdateRecordReplays(void) strcat(gpath, PATHSEP); strcat(gpath, G_BuildMapName(gamemap)); - snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, cv_chooseskin.string); + snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, cv_skin[0].string); - gpath = Z_StrDup(gpath); - - if (FIL_FileExists(lastdemo)) + if (modeattacking != ATTACKING_NONE && FIL_FileExists(lastdemo)) { UINT8 *buf; - size_t len = FIL_ReadFile(lastdemo, &buf); + size_t len; - snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, cv_chooseskin.string); - if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1) - { // Better time, save this demo. - if (FIL_FileExists(bestdemo)) - remove(bestdemo); - FIL_WriteFile(bestdemo, buf, len); - CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo); + gpath = Z_StrDup(gpath); + + len = FIL_ReadFile(lastdemo, &buf); + + if (modeattacking & ATTACKING_TIME) + { + snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, cv_skin[0].string); + if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1) + { // Better time, save this demo. + if (FIL_FileExists(bestdemo)) + remove(bestdemo); + FIL_WriteFile(bestdemo, buf, len); + CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo); + } } - if (modeattacking == ATTACKING_TIME) + if (modeattacking & ATTACKING_LAP) { - snprintf(bestdemo, 255, "%s-%s-lap-best.lmp", gpath, cv_chooseskin.string); + snprintf(bestdemo, 255, "%s-%s-lap-best.lmp", gpath, cv_skin[0].string); if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)) { // Better lap time, save this demo. if (FIL_FileExists(bestdemo)) @@ -582,9 +592,9 @@ static void G_UpdateRecordReplays(void) //CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo); Z_Free(buf); - } - Z_Free(gpath); + Z_Free(gpath); + } // Check emblems when level data is updated if ((earnedEmblems = M_CheckLevelEmblems())) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 3ffc8403a..abd3baa99 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4056,14 +4056,12 @@ void M_StartTimeAttack(INT32 choice) (void)choice; - switch (levellist.newgametype) + modeattacking = ATTACKING_TIME; + + if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT) + && (mapheaderinfo[levellist.choosemap]->numlaps != 1)) { - case GT_BATTLE: - modeattacking = ATTACKING_CAPSULES; - break; - default: - modeattacking = ATTACKING_TIME; - break; + modeattacking |= ATTACKING_LAP; } // Still need to reset devmode diff --git a/src/p_setup.c b/src/p_setup.c index e09141271..36a589f22 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -6993,20 +6993,23 @@ static void P_LoadRecordGhosts(void) gpath = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap))); // Best Time ghost - if (cv_ghost_besttime.value) + if (modeattacking & ATTACKING_TIME) { - for (i = 0; i < numskins; ++i) + if (cv_ghost_besttime.value) { - if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i) - continue; + for (i = 0; i < numskins; ++i) + { + if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i) + continue; - if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name))) + G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name)); + } } } // Best Lap ghost - if (modeattacking != ATTACKING_CAPSULES) + if (modeattacking & ATTACKING_LAP) { if (cv_ghost_bestlap.value) { From 52040c1248676a2ec3b1454b8fac5980b93831d4 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 29 Dec 2022 23:13:15 +0000 Subject: [PATCH 32/76] Replays (net and timeeattack) now recognise gametypes by name - Should support custom gametypes, but haven't been thoroughly testing those - Custom gametypes must now be unique by name - Custom gametypes now have a maximum name length of 31 --- src/deh_soc.c | 22 ++++++++++++++++++++++ src/doomstat.h | 1 + src/g_demo.c | 43 ++++++++++++++++++++++++++++++++----------- src/g_demo.h | 2 +- src/k_menudraw.c | 12 +++++++----- src/lua_baselib.c | 18 ++++++++++++++---- 6 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index fdba5c507..241ddb8eb 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -878,6 +878,28 @@ void readgametype(MYFILE *f, char *gtname) I_Error("Out of Gametype Freeslots while allocating \"%s\"\nLoad less addons to fix this.", gtname); } + if (gtname[0] == '\0') + { + deh_warning("Custom gametype must have a name"); + return; + } + + if (strlen(gtname) >= MAXGAMETYPELENGTH) + { + deh_warning("Custom gametype \"%s\"'s name must be %d long at most", gtname, MAXGAMETYPELENGTH-1); + return; + } + + for (i = 0; i < numgametypes; i++) + if (fastcmp(gtname, gametypes[i]->name)) + break; + + if (i < numgametypes) + { + deh_warning("Custom gametype \"%s\"'s name is already in use", gtname); + return; + } + // Add the new gametype newgametype = Z_Calloc(sizeof (gametype_t), PU_STATIC, NULL); if (!newgametype) diff --git a/src/doomstat.h b/src/doomstat.h index 63739358f..e7ff2f093 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -450,6 +450,7 @@ extern INT32 nummapheaders, mapallocsize; // Gametypes #define NUMGAMETYPEFREESLOTS (MAXGAMETYPES-GT_FIRSTFREESLOT) +#define MAXGAMETYPELENGTH 32 enum GameType { diff --git a/src/g_demo.c b/src/g_demo.c index ddf0b342f..f82e96325 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2378,7 +2378,9 @@ void G_BeginRecording(void) M_Memcpy(demo_p, mapmd5, 16); demo_p += 16; WRITEUINT8(demo_p, demoflags); - WRITEUINT8(demo_p, gametype & 0xFF); + + WRITESTRINGN(demo_p, gametypes[gametype]->name, MAXGAMETYPELENGTH); + WRITEUINT8(demo_p, numlaps); // file list @@ -2652,7 +2654,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) SKIPSTRING(p); // gamemap p += 16; // map md5 flags = READUINT8(p); // demoflags - p++; // gametype + SKIPSTRING(p); // gametype p++; // numlaps G_SkipDemoExtraFiles(&p); @@ -2711,7 +2713,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) SKIPSTRING(p); // gamemap p += 16; // mapmd5 flags = READUINT8(p); - p++; // gametype + SKIPSTRING(p); // gametype p++; // numlaps G_SkipDemoExtraFiles(&p); if (!(flags & aflags)) @@ -2756,7 +2758,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo) UINT8 version, subversion, pdemoflags, worknumskins, skinid; democharlist_t *skinlist = NULL; UINT16 pdemoversion, count; - char mapname[MAXMAPLUMPNAME]; + char mapname[MAXMAPLUMPNAME],gtname[MAXGAMETYPELENGTH]; INT32 i; if (!FIL_ReadFile(pdemo->filepath, &infobuffer)) @@ -2820,7 +2822,9 @@ void G_LoadDemoInfo(menudemo_t *pdemo) return; } - pdemo->gametype = READUINT8(info_p); + READSTRINGN(info_p, gtname, sizeof(gtname)); // gametype + pdemo->gametype = G_GetGametypeByName(gtname); + pdemo->numlaps = READUINT8(info_p); pdemo->addonstatus = G_CheckDemoExtraFiles(&info_p, true); @@ -2932,9 +2936,11 @@ void G_DeferedPlayDemo(const char *name) void G_DoPlayDemo(char *defdemoname) { - UINT8 i, p, numslots = 0; + INT32 i; + UINT8 p, numslots = 0; lumpnum_t l; - char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname; + char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],gtname[MAXGAMETYPELENGTH]; + char *n,*pdemoname; UINT8 availabilities[MAXPLAYERS][MAXAVAILABILITY]; UINT8 version,subversion; UINT32 randseed[PRNUMCLASS]; @@ -2951,6 +2957,7 @@ void G_DoPlayDemo(char *defdemoname) follower[16] = '\0'; color[MAXCOLORNAME] = '\0'; + gtname[MAXGAMETYPELENGTH-1] = '\0'; // No demo name means we're restarting the current demo if (defdemoname == NULL) @@ -3060,8 +3067,22 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 16; // mapmd5 demoflags = READUINT8(demo_p); - gametype = READUINT8(demo_p); - G_SetGametype(gametype); + + READSTRINGN(demo_p, gtname, sizeof(gtname)); // gametype + i = G_GetGametypeByName(gtname); + if (i < 0) + { + snprintf(msg, 1024, M_GetText("%s is in a gametype that is not currently loaded and cannot be played.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + G_SetGametype(i); + numlaps = READUINT8(demo_p); if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. @@ -3519,7 +3540,7 @@ void G_AddGhost(char *defdemoname) return; } - p++; // gametype + SKIPSTRING(p); // gametype p++; // numlaps G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. @@ -3736,7 +3757,7 @@ void G_UpdateStaffGhostName(lumpnum_t l) goto fail; // we don't NEED to do it here, but whatever } - p++; // Gametype + SKIPSTRING(p); // gametype p++; // numlaps G_SkipDemoExtraFiles(&p); diff --git a/src/g_demo.h b/src/g_demo.h index db5158b9a..8ecf08448 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -84,7 +84,7 @@ struct menudemo_t { char title[65]; // Null-terminated for string prints UINT16 map; UINT8 addonstatus; // What do we need to do addon-wise to play this demo? - UINT8 gametype; + INT16 gametype; SINT8 kartspeed; // Add OR DF_ENCORE for encore mode, idk UINT8 numlaps; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 1a3f7a8d8..f47689f8b 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3969,9 +3969,11 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref) V_DrawThinString(x, y+9, V_SNAPTOTOP|V_ALLOWLOWERCASE, va("(%d laps)", demoref->numlaps)); { - const char *gtstring = "???"; - if (demoref->gametype >= GT_FIRSTFREESLOT) - ; // TODO: Support custom gametypes in netreplays (would require deeper changes than this) + const char *gtstring; + if (demoref->gametype < 0) + { + gtstring = "Custom (not loaded)"; + } else { gtstring = gametypes[demoref->gametype]->name; @@ -3994,7 +3996,7 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref) V_DrawThinString(x, y+29, V_SNAPTOTOP|highlightflags, "WINNER"); V_DrawString(x+38, y+30, V_SNAPTOTOP|V_ALLOWLOWERCASE, demoref->standings[0].name); - if (demoref->gametype < GT_FIRSTFREESLOT) + if (demoref->gametype >= 0) { if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT) { @@ -4215,7 +4217,7 @@ void M_DrawReplayStartMenu(void) if (demoref->standings[i].timeorscore == UINT32_MAX-1) V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); - else if (demoref->gametype >= GT_FIRSTFREESLOT) + else if (demoref->gametype < 0) ; else if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT) V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demoref->standings[i].timeorscore)); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 61587bd62..1a33438fb 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2980,6 +2980,7 @@ static int lib_gAddGametype(lua_State *L) INT32 newgtpointlimit = 0; INT32 newgttimelimit = 0; UINT8 newgtinttype = 0; + INT16 j; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. @@ -3043,13 +3044,22 @@ static int lib_gAddGametype(lua_State *L) #undef FIELDERROR #undef TYPEERROR + if (gtname == NULL) + return luaL_error(L, "Custom gametype must have a name"); + + if (strlen(gtname) >= MAXGAMETYPELENGTH) + return luaL_error(L, "Custom gametype \"%s\"'s name must be %d long at most", gtname, MAXGAMETYPELENGTH-1); + + for (j = 0; j < numgametypes; j++) + if (!strcmp(gtname, gametypes[j]->name)) + break; + + if (j < numgametypes) + return luaL_error(L, "Custom gametype \"%s\"'s name is already in use", gtname); + // pop gametype table lua_pop(L, 1); - // Set defaults - if (gtname == NULL) - gtname = Z_StrDup("Unnamed gametype"); - // Add the new gametype newgametype = Z_Calloc(sizeof (gametype_t), PU_STATIC, NULL); if (!newgametype) From 3ee8713e46355566931e50762b27efb755e757fb Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 30 Dec 2022 15:23:26 +0000 Subject: [PATCH 33/76] Dehardcode menu gametype selection, part 1 - Introduce `menugametype` - Controlled by IT_KEYHANDLER/M_HandleMenuGametype - Excludes gametypes that do not support multiplayer by default - GTR_CAPSULES and GTR_BOSS for now, but also user-specifiable GTR_NOMP - Remove gametype_cons_t and G_UpdateGametypeSelections, an obstacle in the way of infinitely allocatable custom gametypes --- src/d_netcmd.c | 2 +- src/deh_soc.c | 3 -- src/discord.c | 2 +- src/doomstat.h | 3 ++ src/g_game.c | 17 --------- src/g_game.h | 1 - src/hu_stuff.c | 2 +- src/k_menu.h | 4 +-- src/k_menudef.c | 4 +-- src/k_menudraw.c | 14 ++++++++ src/k_menufunc.c | 91 +++++++++++++++++++++++++++++++++-------------- src/lua_baselib.c | 3 -- 12 files changed, 89 insertions(+), 57 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 0d4a8da9d..2cdb92bd2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3091,7 +3091,7 @@ static void Command_RandomMap(void) } else { - newgametype = cv_dummygametype.value; // Changed from cv_newgametype to match newmenus + newgametype = menugametype; newencoremode = false; newresetplayers = true; oldmapnum = -1; diff --git a/src/deh_soc.c b/src/deh_soc.c index 241ddb8eb..930ace846 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -920,9 +920,6 @@ void readgametype(MYFILE *f, char *gtname) gametypes[numgametypes++] = newgametype; - // Update gametype_cons_t accordingly. - G_UpdateGametypeSelections(); - CONS_Printf("Added gametype %s\n", gtname); } diff --git a/src/discord.c b/src/discord.c index b5c6a5a88..9d6011c11 100644 --- a/src/discord.c +++ b/src/discord.c @@ -499,7 +499,7 @@ void DRPC_UpdatePresence(void) else { snprintf(detailstr, 48, "%s%s%s", - gametype_cons_t[gametype].strvalue, + gametypes[gametype]->name, (gametyperules & GTR_CIRCUIT) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "", (encoremode == true) ? " | Encore" : "" ); diff --git a/src/doomstat.h b/src/doomstat.h index e7ff2f093..7520342a9 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -519,11 +519,14 @@ enum GameTypeRules GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos GTR_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping GTR_SPECIALSTART = 1<<23, // White fade instant start + GTR_NOMP = 1<<24, // No multiplayer // free: to and including 1<<31 }; // Remember to update GAMETYPERULE_LIST in deh_soc.c +#define GTR_FORBIDMP (GTR_NOMP|GTR_CATCHER|GTR_BOSS) + // TODO: replace every instance #define gametyperules (gametypes[gametype]->rules) diff --git a/src/g_game.c b/src/g_game.c index 8ba6a4aa5..ac0190c4e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3186,23 +3186,6 @@ char *G_PrepareGametypeConstant(const char *newgtconst) return gtconst; } -// -// G_UpdateGametypeSelections -// -// Updates gametype_cons_t. -// -void G_UpdateGametypeSelections(void) -{ - INT32 i; - for (i = 0; i < numgametypes; i++) - { - gametype_cons_t[i].value = i; - gametype_cons_t[i].strvalue = gametypes[i]->name; - } - gametype_cons_t[numgametypes].value = 0; - gametype_cons_t[numgametypes].strvalue = NULL; -} - tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = { {"RACE",TOL_RACE}, {"BATTLE",TOL_BATTLE}, diff --git a/src/g_game.h b/src/g_game.h index 5675da0dd..227274590 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -181,7 +181,6 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives); void G_SetGametype(INT16 gametype); char *G_PrepareGametypeConstant(const char *newgtconst); -void G_UpdateGametypeSelections(void); void G_AddTOL(UINT32 newtol, const char *tolname); INT32 G_GetGametypeByName(const char *gametypestr); INT32 G_GuessGametypeByTOL(UINT32 tol); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 2a75663e2..dd682e1bd 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -16,7 +16,7 @@ #include "hu_stuff.h" #include "font.h" -#include "k_menu.h" // gametype_cons_t +#include "k_menu.h" // highlightflags #include "m_cond.h" // emblems #include "m_misc.h" // word jumping diff --git a/src/k_menu.h b/src/k_menu.h index 832e2146e..4dd5db602 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -115,7 +115,8 @@ struct menucolor_t { extern menucolor_t *menucolorhead, *menucolortail; -extern CV_PossibleValue_t gametype_cons_t[]; +extern INT16 menugametype; +void M_HandleMenuGametype(INT32 choice); // // MENU TYPEDEFS @@ -770,7 +771,6 @@ void M_MPOptSelect(INT32 choice); void M_MPOptSelectInit(INT32 choice); void M_MPOptSelectTick(void); boolean M_MPResetOpts(void); -extern consvar_t cv_dummygametype; // lazy hack to allow us to select the GT on the server host submenu extern consvar_t cv_dummyip; // I HAVE // HAVE YOUR IP ADDRESS (This just the hack Cvar we'll type into and then it apends itself to "connect" in the console for IP join) diff --git a/src/k_menudef.c b/src/k_menudef.c index b0de23761..374c44968 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -362,8 +362,8 @@ menuitem_t PLAY_MP_Host[] = {IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.", NULL, {.cvar = &cv_maxplayers}, 0, 0}, - {IT_STRING | IT_CVAR, "Gamemode", "Are we racing? Or perhaps battling?", - NULL, {.cvar = &cv_dummygametype}, 0, 0}, + {IT_STRING | IT_KEYHANDLER, "Gamemode", "Choose the type of play on your server.", + NULL, {.routine = M_HandleMenuGametype}, 0, 0}, {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", NULL, {.routine = M_MPSetupNetgameMapSelect}, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f47689f8b..4087fdd7a 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2466,6 +2466,20 @@ void M_DrawMPHost(void) } break; } + case IT_KEYHANDLER: + { + if (currentMenu->menuitems[i].itemaction.routine != M_HandleMenuGametype) + break; + + w = V_ThinStringWidth(gametypes[menugametype]->name, V_6WIDTHSPACE); + V_DrawThinString(xp + 138 - w, yp, highlightflags|V_6WIDTHSPACE, gametypes[menugametype]->name); + if (i == itemOn) + { + V_DrawCharacter(xp + 138 - 10 - w - (skullAnimCounter/5), yp, '\x1C' | highlightflags, false); // left arrow + V_DrawCharacter(xp + 138 + 2 + (skullAnimCounter/5), yp, '\x1D' | highlightflags, false); // right arrow + } + break; + } } xp += 5; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index abd3baa99..0cd21d572 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -147,10 +147,6 @@ consvar_t cv_menujam_update = CVAR_INIT ("menujam_update", "Off", CV_SAVE, CV_On static CV_PossibleValue_t menujam_cons_t[] = {{0, "menu"}, {1, "menu2"}, {2, "menu3"}, {0, NULL}}; static consvar_t cv_menujam = CVAR_INIT ("menujam", "0", CV_SAVE, menujam_cons_t, NULL); -// This gametype list is integral for many different reasons. -// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! -CV_PossibleValue_t gametype_cons_t[MAXGAMETYPES+1]; - static CV_PossibleValue_t serversort_cons_t[] = { {0,"Ping"}, {1,"AVG. Power Level"}, @@ -189,18 +185,18 @@ static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2 static CV_PossibleValue_t dummyspectate_cons_t[] = {{0, "Spectator"}, {1, "Playing"}, {0, NULL}}; static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"}, {0, NULL}}; static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; -static CV_PossibleValue_t dummygametype_cons_t[] = {{0, "Race"}, {1, "Battle"}, {0, NULL}}; //static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL); //static cv_dummyspectate = CVAR_INITconsvar_t ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); static consvar_t cv_dummyscramble = CVAR_INIT ("dummyscramble", "Random", CV_HIDDEN, dummyscramble_cons_t, NULL); static consvar_t cv_dummystaff = CVAR_INIT ("dummystaff", "0", CV_HIDDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange); -consvar_t cv_dummygametype = CVAR_INIT ("dummygametype", "Race", CV_HIDDEN, dummygametype_cons_t, NULL); consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDDEN, NULL, NULL); consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); +INT16 menugametype = GT_RACE; + consvar_t cv_dummyprofilename = CVAR_INIT ("dummyprofilename", "", CV_HIDDEN, NULL, NULL); consvar_t cv_dummyprofileplayername = CVAR_INIT ("dummyprofileplayername", "", CV_HIDDEN, NULL, NULL); consvar_t cv_dummyprofilekickstart = CVAR_INIT ("dummyprofilekickstart", "Off", CV_HIDDEN, CV_OnOff, NULL); @@ -1715,7 +1711,6 @@ void M_Init(void) CV_RegisterVar(&cv_dummyspectate); CV_RegisterVar(&cv_dummyscramble); CV_RegisterVar(&cv_dummystaff); - CV_RegisterVar(&cv_dummygametype); CV_RegisterVar(&cv_dummyip); CV_RegisterVar(&cv_dummyprofilename); @@ -4167,10 +4162,70 @@ void M_MPHostInit(INT32 choice) itemOn = mhost_go; } +void M_HandleMenuGametype(INT32 choice) +{ + const UINT8 pid = 0; + const INT16 currentmenugametype = menugametype; + UINT32 forbidden = GTR_FORBIDMP; + + (void)choice; + + if (currentMenu->menuitems[itemOn].mvar1 != 0) + forbidden = currentMenu->menuitems[itemOn].mvar1; + + if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid)) + { + do + { + menugametype++; + if (menugametype >= numgametypes) + menugametype = 0; + + if (!(gametypes[menugametype]->rules & forbidden)) + break; + } while (menugametype != currentmenugametype); + + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_lr < 0) + { + do + { + if (menugametype == 0) + menugametype = numgametypes; + menugametype--; + + if (!(gametypes[menugametype]->rules & forbidden)) + break; + } while (menugametype != currentmenugametype); + + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (M_MenuBackPressed(pid)) + { + M_GoBack(0); + M_SetMenuDelay(pid); + return; + } + + if (menucmd[pid].dpad_ud > 0) + { + M_NextOpt(); + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_ud < 0) + { + M_PrevOpt(); + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } +} + void M_MPSetupNetgameMapSelect(INT32 choice) { - - INT16 gt = GT_RACE; (void)choice; // Yep, we'll be starting a netgame. @@ -4181,26 +4236,10 @@ void M_MPSetupNetgameMapSelect(INT32 choice) levellist.levelsearch.checklocked = true; cupgrid.grandprix = false; - // In case we ever want to add new gamemodes there somehow, have at it! - switch (cv_dummygametype.value) - { - case 1: // Battle - { - gt = GT_BATTLE; - break; - } - - default: - { - gt = GT_RACE; - break; - } - } - // okay this is REALLY stupid but this fixes the host menu re-folding on itself when we go back. mpmenu.modewinextend[0][0] = 1; - M_LevelListFromGametype(gt); // Setup the level select. + M_LevelListFromGametype(menugametype); // Setup the level select. // (This will also automatically send us to the apropriate menu) } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 1a33438fb..54f3bacf1 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3080,9 +3080,6 @@ static int lib_gAddGametype(lua_State *L) gametypes[numgametypes++] = newgametype; - // Update gametype_cons_t accordingly. - G_UpdateGametypeSelections(); - // done CONS_Printf("Added gametype %s\n", gtname); return 0; From ebb2a79666d037cc1b12626296c5c09ab7d729e1 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 30 Dec 2022 20:34:35 +0000 Subject: [PATCH 34/76] Dehardcode menu gametype selection, part 2 - Add "Gametype" toggle option to pause menu for admins - A/Confirm button on any gametype other than current to do random map in new gametype - C/Extra button to return to current gametype - A/Confirm button on current gametype does a funny noise and nothing else right now, idk what to do - `randomlevel` supports the same `-gametype` parameter as `map` - Both `randomlevel` and `map`'s `-gametype` parameter prohibits `GTR_FORBIDMP` mask gametype changes in netgames - `randomlevel` properly sets encore based on `cv_kartencore`'s value (and permitting `GTR_ENCORE`) - Split out `menugametype` change functions - `yellowmap` now remaps all intermediary shades of grey to match funny pause menu remaps --- src/console.c | 15 ++++++ src/d_netcmd.c | 90 +++++++++++++++++++++++++++++------- src/k_menu.h | 4 +- src/k_menudef.c | 5 +- src/k_menudraw.c | 26 ++++++++--- src/k_menufunc.c | 118 ++++++++++++++++++++++++++++++++--------------- 6 files changed, 196 insertions(+), 62 deletions(-) diff --git a/src/console.c b/src/console.c index ede2f633a..10baa4254 100644 --- a/src/console.c +++ b/src/console.c @@ -358,7 +358,22 @@ static void CON_SetupColormaps(void) *memorysrc = (UINT8)(i & 0xFF); // remap each color to itself... purplemap[0] = (UINT8)163; + yellowmap[0] = (UINT8)73; + yellowmap[1] = (UINT8)73; + yellowmap[3] = (UINT8)74; + yellowmap[6] = (UINT8)74; + yellowmap[7] = (UINT8)190; + yellowmap[8] = (UINT8)190; + yellowmap[10] = (UINT8)190; + yellowmap[12] = (UINT8)190; + yellowmap[14] = (UINT8)149; + yellowmap[15] = (UINT8)149; + yellowmap[16] = (UINT8)149; + yellowmap[21] = (UINT8)152; + yellowmap[23] = (UINT8)173; + yellowmap[24] = (UINT8)167; + greenmap[0] = (UINT8)98; bluemap[0] = (UINT8)148; redmap[0] = (UINT8)34; // battle diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2cdb92bd2..e7a343a03 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2739,15 +2739,6 @@ static void Command_Map_f(void) if (option_gametype) { -#if 0 - if (!multiplayer) - { - CONS_Printf(M_GetText( - "You can't switch gametypes in single player!\n")); - return; - } - else -#endif //#if 0 if (COM_Argc() < option_gametype + 2)/* no argument after? */ { CONS_Alert(CONS_ERROR, @@ -2823,13 +2814,23 @@ static void Command_Map_f(void) else { CONS_Alert(CONS_ERROR, - "'%s' is not a gametype.\n", + "'%s' is not a valid gametype.\n", gametypename); Z_Free(realmapname); Z_Free(mapname); return; } } + + if (Playing() && netgame && (gametypes[newgametype]->rules & GTR_FORBIDMP)) + { + CONS_Alert(CONS_ERROR, + "'%s' is not a net-compatible gametype.\n", + gametypename); + Z_Free(realmapname); + Z_Free(mapname); + return; + } } else if (!Playing()) { @@ -3061,9 +3062,10 @@ static void Command_RandomMap(void) { INT32 oldmapnum; INT32 newmapnum; - INT32 newgametype; - boolean newencoremode; + INT32 newgametype = (Playing() ? gametype : menugametype); + boolean newencore = false; boolean newresetplayers; + size_t option_gametype; if (client && !IsPlayerAdmin(consoleplayer)) { @@ -3071,13 +3073,69 @@ static void Command_RandomMap(void) return; } + if ((option_gametype = COM_CheckPartialParm("-g"))) + { + const char *gametypename; + + if (COM_Argc() < option_gametype + 2)/* no argument after? */ + { + CONS_Alert(CONS_ERROR, + "No gametype name follows parameter '%s'.\n", + COM_Argv(option_gametype)); + return; + } + + // new gametype value + // use current one by default + gametypename = COM_Argv(option_gametype + 1); + + newgametype = G_GetGametypeByName(gametypename); + + if (newgametype == -1) // reached end of the list with no match + { + /* Did they give us a gametype number? That's okay too! */ + if (isdigit(gametypename[0])) + { + INT16 d = atoi(gametypename); + if (d >= 0 && d < numgametypes) + newgametype = d; + else + { + CONS_Alert(CONS_ERROR, + "Gametype number %d is out of range. Use a number between" + " 0 and %d inclusive. ...Or just use the name. :v\n", + d, + numgametypes-1); + return; + } + } + else + { + CONS_Alert(CONS_ERROR, + "'%s' is not a valid gametype.\n", + gametypename); + return; + } + } + + if (Playing() && netgame && (gametypes[newgametype]->rules & GTR_FORBIDMP)) + { + CONS_Alert(CONS_ERROR, + "'%s' is not a net-compatible gametype.\n", + gametypename); + return; + } + } + // TODO: Handle singleplayer conditions. // The existing ones are way too annoyingly complicated and "anti-cheat" for my tastes. if (Playing()) { - newgametype = gametype; - newencoremode = encoremode; + if (cv_kartencore.value == 1 && (gametypes[newgametype]->rules & GTR_ENCORE)) + { + newencore = true; + } newresetplayers = false; if (gamestate == GS_LEVEL) @@ -3091,14 +3149,12 @@ static void Command_RandomMap(void) } else { - newgametype = menugametype; - newencoremode = false; newresetplayers = true; oldmapnum = -1; } newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, 0, 0, false, NULL) + 1; - D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); + D_MapChange(newmapnum, newgametype, newencore, newresetplayers, 0, false, false); } static void Command_RestartLevel(void) diff --git a/src/k_menu.h b/src/k_menu.h index 4dd5db602..9382c8647 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -116,7 +116,8 @@ struct menucolor_t { extern menucolor_t *menucolorhead, *menucolortail; extern INT16 menugametype; -void M_HandleMenuGametype(INT32 choice); +void M_HandleHostMenuGametype(INT32 choice); +void M_HandlePauseMenuGametype(INT32 choice); // // MENU TYPEDEFS @@ -414,6 +415,7 @@ extern menu_t MISC_StatisticsDef; typedef enum { mpause_addons = 0, + mpause_changegametype, mpause_switchmap, mpause_restartmap, mpause_tryagain, diff --git a/src/k_menudef.c b/src/k_menudef.c index 374c44968..74260d6e4 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -363,7 +363,7 @@ menuitem_t PLAY_MP_Host[] = NULL, {.cvar = &cv_maxplayers}, 0, 0}, {IT_STRING | IT_KEYHANDLER, "Gamemode", "Choose the type of play on your server.", - NULL, {.routine = M_HandleMenuGametype}, 0, 0}, + NULL, {.routine = M_HandleHostMenuGametype}, 0, 0}, {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", NULL, {.routine = M_MPSetupNetgameMapSelect}, 0, 0}, @@ -1593,6 +1593,9 @@ menuitem_t PAUSE_Main[] = {IT_STRING | IT_CALL, "ADDONS", "M_ICOADD", NULL, {.routine = M_Addons}, 0, 0}, + {IT_STRING | IT_KEYHANDLER, "GAMETYPE", "M_ICOGAM", + NULL, {.routine = M_HandlePauseMenuGametype}, 0, 0}, + {IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP", NULL, {.submenu = &PAUSE_GamemodesDef}, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 4087fdd7a..b872c768d 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2468,7 +2468,7 @@ void M_DrawMPHost(void) } case IT_KEYHANDLER: { - if (currentMenu->menuitems[i].itemaction.routine != M_HandleMenuGametype) + if (currentMenu->menuitems[i].itemaction.routine != M_HandleHostMenuGametype) break; w = V_ThinStringWidth(gametypes[menugametype]->name, V_6WIDTHSPACE); @@ -3778,12 +3778,26 @@ void M_DrawPause(void) word1[word1len] = '\0'; word2[word2len] = '\0'; - // If there's no 2nd word, take this opportunity to center this line of text. - if (word1len) - V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); + if (itemOn == mpause_changegametype) + { + INT32 w = V_LSTitleLowStringWidth(gametypes[menugametype]->name, 0)/2; - if (word2len) - V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2); + if (word1len) + V_DrawCenteredLSTitleHighString(220 + offset*2, 75, 0, word1); + + V_DrawLSTitleLowString(220-w + offset*2, 103, V_YELLOWMAP, gametypes[menugametype]->name); + V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | V_YELLOWMAP, false); // left arrow + V_DrawCharacter(220+w + offset*2 + 4 + (skullAnimCounter/5), 103+6, '\x1D' | V_YELLOWMAP, false); // right arrow + } + else + { + // If there's no 2nd word, take this opportunity to center this line of text. + if (word1len) + V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); + + if (word2len) + V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2); + } } tic_t playback_last_menu_interaction_leveltime = 0; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 0cd21d572..759e35de9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4162,10 +4162,37 @@ void M_MPHostInit(INT32 choice) itemOn = mhost_go; } -void M_HandleMenuGametype(INT32 choice) +static void M_NextMenuGametype(UINT32 forbidden) +{ + const INT16 currentmenugametype = menugametype; + do + { + menugametype++; + if (menugametype >= numgametypes) + menugametype = 0; + + if (!(gametypes[menugametype]->rules & forbidden)) + break; + } while (menugametype != currentmenugametype); +} + +static void M_PrevMenuGametype(UINT32 forbidden) +{ + const INT16 currentmenugametype = menugametype; + do + { + if (menugametype == 0) + menugametype = numgametypes; + menugametype--; + + if (!(gametypes[menugametype]->rules & forbidden)) + break; + } while (menugametype != currentmenugametype); +} + +void M_HandleHostMenuGametype(INT32 choice) { const UINT8 pid = 0; - const INT16 currentmenugametype = menugametype; UINT32 forbidden = GTR_FORBIDMP; (void)choice; @@ -4173,42 +4200,24 @@ void M_HandleMenuGametype(INT32 choice) if (currentMenu->menuitems[itemOn].mvar1 != 0) forbidden = currentMenu->menuitems[itemOn].mvar1; - if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid)) + if (M_MenuBackPressed(pid)) { - do - { - menugametype++; - if (menugametype >= numgametypes) - menugametype = 0; - - if (!(gametypes[menugametype]->rules & forbidden)) - break; - } while (menugametype != currentmenugametype); - + M_GoBack(0); + M_SetMenuDelay(pid); + return; + } + else if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid)) + { + M_NextMenuGametype(forbidden); S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } else if (menucmd[pid].dpad_lr < 0) { - do - { - if (menugametype == 0) - menugametype = numgametypes; - menugametype--; - - if (!(gametypes[menugametype]->rules & forbidden)) - break; - } while (menugametype != currentmenugametype); - + M_PrevMenuGametype(forbidden); S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } - else if (M_MenuBackPressed(pid)) - { - M_GoBack(0); - M_SetMenuDelay(pid); - return; - } if (menucmd[pid].dpad_ud > 0) { @@ -6150,6 +6159,7 @@ void M_OpenPauseMenu(void) // By default, disable anything sensitive: PAUSE_Main[mpause_addons].status = IT_DISABLED; + PAUSE_Main[mpause_changegametype].status = IT_DISABLED; PAUSE_Main[mpause_switchmap].status = IT_DISABLED; PAUSE_Main[mpause_restartmap].status = IT_DISABLED; PAUSE_Main[mpause_tryagain].status = IT_DISABLED; @@ -6171,14 +6181,8 @@ void M_OpenPauseMenu(void) if (server || IsPlayerAdmin(consoleplayer)) { - PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU; - for (i = 0; i < PAUSE_GamemodesDef.numitems; i++) - { - if (PAUSE_GamemodesMenu[i].mvar2 != gametype) - continue; - PAUSE_GamemodesDef.lastOn = i; - break; - } + PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_KEYHANDLER; + PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL; PAUSE_Main[mpause_restartmap].status = IT_STRING | IT_CALL; PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; } @@ -6279,6 +6283,46 @@ boolean M_PauseInputs(INT32 ch) return false; } +// Change gametype +void M_HandlePauseMenuGametype(INT32 choice) +{ + const UINT8 pid = 0; + UINT32 forbidden = GTR_FORBIDMP; + + (void)choice; + + if (M_MenuConfirmPressed(pid)) + { + if (menugametype != gametype) + { + M_ClearMenus(true); + COM_ImmedExecute(va("randommap -gt %s", gametypes[menugametype]->name)); + return; + } + + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_s3k7b); + } + else if (M_MenuExtraPressed(pid)) + { + menugametype = gametype; + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_s3k7b); + } + else if (menucmd[pid].dpad_lr > 0) + { + M_NextMenuGametype(forbidden); + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_lr < 0) + { + M_PrevMenuGametype(forbidden); + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } +} + // Restart map void M_RestartMap(INT32 choice) { From be49e99a9e909366a99ef3aa2ae6959d5dd520d7 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 30 Dec 2022 23:37:29 +0000 Subject: [PATCH 35/76] Guarantee `menugametype`'s value is good before it's relevant --- src/k_menu.h | 2 ++ src/k_menufunc.c | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 9382c8647..6db215834 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -116,6 +116,8 @@ struct menucolor_t { extern menucolor_t *menucolorhead, *menucolortail; extern INT16 menugametype; +void M_NextMenuGametype(UINT32 forbidden); +void M_PrevMenuGametype(UINT32 forbidden); void M_HandleHostMenuGametype(INT32 choice); void M_HandlePauseMenuGametype(INT32 choice); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 759e35de9..120d39f3c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4121,6 +4121,7 @@ void M_MPOptSelectInit(INT32 choice) { INT16 arrcpy[3][3] = {{0,68,0}, {0,12,0}, {0,74,0}}; UINT8 i = 0, j = 0; // To copy the array into the struct + const UINT32 forbidden = GTR_FORBIDMP; (void)choice; @@ -4131,6 +4132,10 @@ void M_MPOptSelectInit(INT32 choice) for (j = 0; j < 3; j++) mpmenu.modewinextend[i][j] = arrcpy[i][j]; // I miss Lua already + // Guarantee menugametype is good + M_NextMenuGametype(forbidden); + M_PrevMenuGametype(forbidden); + M_SetupNextMenu(&PLAY_MP_OptSelectDef, false); } @@ -4162,7 +4167,7 @@ void M_MPHostInit(INT32 choice) itemOn = mhost_go; } -static void M_NextMenuGametype(UINT32 forbidden) +void M_NextMenuGametype(UINT32 forbidden) { const INT16 currentmenugametype = menugametype; do @@ -4176,7 +4181,7 @@ static void M_NextMenuGametype(UINT32 forbidden) } while (menugametype != currentmenugametype); } -static void M_PrevMenuGametype(UINT32 forbidden) +void M_PrevMenuGametype(UINT32 forbidden) { const INT16 currentmenugametype = menugametype; do @@ -4193,13 +4198,10 @@ static void M_PrevMenuGametype(UINT32 forbidden) void M_HandleHostMenuGametype(INT32 choice) { const UINT8 pid = 0; - UINT32 forbidden = GTR_FORBIDMP; + const UINT32 forbidden = GTR_FORBIDMP; (void)choice; - if (currentMenu->menuitems[itemOn].mvar1 != 0) - forbidden = currentMenu->menuitems[itemOn].mvar1; - if (M_MenuBackPressed(pid)) { M_GoBack(0); @@ -6182,6 +6184,8 @@ void M_OpenPauseMenu(void) if (server || IsPlayerAdmin(consoleplayer)) { PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_KEYHANDLER; + menugametype = gametype; + PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL; PAUSE_Main[mpause_restartmap].status = IT_STRING | IT_CALL; PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; @@ -6287,7 +6291,7 @@ boolean M_PauseInputs(INT32 ch) void M_HandlePauseMenuGametype(INT32 choice) { const UINT8 pid = 0; - UINT32 forbidden = GTR_FORBIDMP; + const UINT32 forbidden = GTR_FORBIDMP; (void)choice; From b09045f6578d2fb749da2f3f20df41bb8cb3bc93 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 30 Dec 2022 23:58:22 +0000 Subject: [PATCH 36/76] Dehardcode menu gametype selection, part 3 Multiplayer map select now uses the value of `menugametype` accessible just above it on the menu, instead of having to select from a hardcoded set of options --- src/k_menu.h | 3 --- src/k_menudef.c | 18 ++---------------- src/k_menufunc.c | 9 ++++++++- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 6db215834..2ac8ecf35 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -436,9 +436,6 @@ typedef enum mpause_title, } mpause_e; -extern menuitem_t PAUSE_GamemodesMenu[]; -extern menu_t PAUSE_GamemodesDef; - extern menuitem_t PAUSE_PlaybackMenu[]; extern menu_t PAUSE_PlaybackMenuDef; diff --git a/src/k_menudef.c b/src/k_menudef.c index 74260d6e4..a4b8de7bd 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1596,8 +1596,8 @@ menuitem_t PAUSE_Main[] = {IT_STRING | IT_KEYHANDLER, "GAMETYPE", "M_ICOGAM", NULL, {.routine = M_HandlePauseMenuGametype}, 0, 0}, - {IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP", - NULL, {.submenu = &PAUSE_GamemodesDef}, 0, 0}, + {IT_STRING | IT_CALL, "CHANGE MAP", "M_ICOMAP", + NULL, {.routine = M_LevelSelectInit}, 0, -1}, {IT_STRING | IT_CALL, "RESTART MAP", "M_ICORE", NULL, {.routine = M_RestartMap}, 0, 0}, @@ -1650,20 +1650,6 @@ menu_t PAUSE_MainDef = { M_PauseInputs }; -// PAUSE : Map switching gametype selection (In case you want to pick from battle / race...) -menuitem_t PAUSE_GamemodesMenu[] = -{ - {IT_STRING | IT_CALL, "Race", "Select which gamemode to choose a new map from.", - NULL, {.routine = M_LevelSelectInit}, 0, GT_RACE}, - - {IT_STRING | IT_CALL, "Battle", "Select which gamemode to choose a new map from.", - NULL, {.routine = M_LevelSelectInit}, 0, GT_BATTLE}, - - {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, -}; - -menu_t PAUSE_GamemodesDef = KARTGAMEMODEMENU(PAUSE_GamemodesMenu, &PAUSE_MainDef); - // Replay popup menu menuitem_t PAUSE_PlaybackMenu[] = { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 120d39f3c..a421ba8a5 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3676,6 +3676,8 @@ static void M_LevelListFromGametype(INT16 gt) void M_LevelSelectInit(INT32 choice) { + INT32 gt = currentMenu->menuitems[itemOn].mvar2; + (void)choice; // Make sure this is reset as we'll only be using this function for offline games! @@ -3702,7 +3704,12 @@ void M_LevelSelectInit(INT32 choice) return; } - M_LevelListFromGametype(currentMenu->menuitems[itemOn].mvar2); + if (gt == -1) + { + gt = menugametype; + } + + M_LevelListFromGametype(gt); } static void M_LevelSelected(INT16 add) From 21858082d25f72033585d3f1688e3534eede8f85 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 00:01:44 +0000 Subject: [PATCH 37/76] EXPERIMENTAL: Use `yellowmap` for all pause menu icons (except Try Again/Restart Map) instead of custom Will make long-term management of this menu easier, and pushes aside some inconsistencies between graphics May be reverted in part or full if it doesn't work for particular assets --- src/k_menudraw.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index b872c768d..8ef321bbe 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3698,27 +3698,19 @@ void M_DrawPause(void) case IT_STRING: { patch_t *pp; + UINT8 *colormap = NULL; - if (i == itemOn) + if (i == itemOn && (i == mpause_restartmap || i == mpause_tryagain)) { - if (i == mpause_restartmap || i == mpause_tryagain) - { - pp = W_CachePatchName( - va("M_ICOR2%c", ('A'+(pausemenu.ticker & 1))), - PU_CACHE); - } - else - { - char iconame[9]; // 8 chars + \0 - strcpy(iconame, currentMenu->menuitems[i].tooltip); - iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic. - - pp = W_CachePatchName(iconame, PU_CACHE); - } + pp = W_CachePatchName( + va("M_ICOR2%c", ('A'+(pausemenu.ticker & 1))), + PU_CACHE); } else { pp = W_CachePatchName(currentMenu->menuitems[i].tooltip, PU_CACHE); + if (i == itemOn) + colormap = yellowmap; } // 294 - 261 = 33 @@ -3729,7 +3721,7 @@ void M_DrawPause(void) // This double ternary is awful, yes. dypos = ypos + pausemenu.offset; - V_DrawFixedPatch( ((i == itemOn ? (294 - pausemenu.offset*2/3 * (dypos > 100 ? 1 : -1)) : 261) + offset) << FRACBITS, (dypos)*FRACUNIT, FRACUNIT, 0, pp, NULL); + V_DrawFixedPatch( ((i == itemOn ? (294 - pausemenu.offset*2/3 * (dypos > 100 ? 1 : -1)) : 261) + offset) << FRACBITS, (dypos)*FRACUNIT, FRACUNIT, 0, pp, colormap); ypos += 50; itemsdrawn++; // We drew that! From dc7222821cb211173ced7c110feef44194a9094b Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 16:05:04 +0000 Subject: [PATCH 38/76] Move M_Init() to fix saving of some menu-specific cvars --- src/d_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 07c61d841..8384de07c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1520,14 +1520,14 @@ void D_SRB2Main(void) I_RegisterSysCommands(); + M_Init(); + //--------------------------------------------------------- CONFIG.CFG M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()" // Load Profiles now that default controls have been defined PR_LoadProfiles(); // load control profiles - M_Init(); - #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen #endif From 2db7562c24f0452979902f13fa789fb94d64cf87 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 16:22:46 +0000 Subject: [PATCH 39/76] LF2_FINISHNEEDED - Replacement for LF2_VISITNEEDED, which was itself a Kart-specific inversion of LF2_NOVISITNEEDED - Prevents selection in time attack before you've beaten the level in another context (GP, or MP if applicable) - Should be used for Sealed Stars (and Adventure Example) exclusively IMO, but usable for others too Related adjustments: - Allow a map to have a `TypeOfLevel` of 0 without error - Change the Condition String for UC_MAPVISITED to "Finish a round on X" (previously "Beat X") --- src/deh_soc.c | 8 ++++---- src/deh_tables.c | 2 +- src/doomstat.h | 8 ++++---- src/k_menufunc.c | 21 +++++++++++++++++++-- src/m_cond.c | 7 +++++-- 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 930ace846..4fb40b087 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1140,7 +1140,7 @@ void readlevelheader(MYFILE *f, char * name) } else if (fastcmp(word, "TYPEOFLEVEL")) { - if (i) // it's just a number + if (i || isdigit(word2[0])) // it's just a number mapheaderinfo[num]->typeoflevel = (UINT32)i; else { @@ -1279,12 +1279,12 @@ void readlevelheader(MYFILE *f, char * name) else mapheaderinfo[num]->menuflags &= ~LF2_NOTIMEATTACK; } - else if (fastcmp(word, "VISITNEEDED")) + else if (fastcmp(word, "FINISHNEEDED")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num]->menuflags |= LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags |= LF2_FINISHNEEDED; else - mapheaderinfo[num]->menuflags &= ~LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags &= ~LF2_FINISHNEEDED; } else if (fastcmp(word, "GRAVITY")) mapheaderinfo[num]->gravity = FLOAT_TO_FIXED(atof(word2)); diff --git a/src/deh_tables.c b/src/deh_tables.c index 8b9be00ff..5217e3c6c 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6300,7 +6300,7 @@ struct int_const_s const INT_CONST[] = { {"LF2_HIDEINMENU",LF2_HIDEINMENU}, {"LF2_HIDEINSTATS",LF2_HIDEINSTATS}, {"LF2_NOTIMEATTACK",LF2_NOTIMEATTACK}, - {"LF2_VISITNEEDED",LF2_VISITNEEDED}, + {"LF2_FINISHNEEDED",LF2_FINISHNEEDED}, // Emeralds {"EMERALD_CHAOS1",EMERALD_CHAOS1}, diff --git a/src/doomstat.h b/src/doomstat.h index 7520342a9..76ec0cb25 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -440,10 +440,10 @@ struct mapheader_t #define LF_SECTIONRACE (1<<2) ///< Section race level #define LF_SUBTRACTNUM (1<<3) ///< Use subtractive position number (for bright levels) -#define LF2_HIDEINMENU (1<<0) ///< Hide in the multiplayer menu -#define LF2_HIDEINSTATS (1<<1) ///< Hide in the statistics screen -#define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes -#define LF2_VISITNEEDED (1<<3) ///< Not available in Time Attack modes until you visit the level +#define LF2_HIDEINMENU (1<<0) ///< Hide in the multiplayer menu +#define LF2_HIDEINSTATS (1<<1) ///< Hide in the statistics screen +#define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes +#define LF2_FINISHNEEDED (1<<3) ///< Not available in Time Attack modes until you beat the level extern mapheader_t** mapheaderinfo; extern INT32 nummapheaders, mapallocsize; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a421ba8a5..6e7c9e4dd 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3418,8 +3418,18 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch) if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR) return false; - if (levelsearch->checklocked && M_MapLocked(mapnum+1)) - return false; // not unlocked + // Does the map have lock conditions? + if (levelsearch->checklocked) + { + // Check for completion + if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED) + && !(mapheaderinfo[mapnum]->mapvisited & MV_BEATEN)) + return false; + + // Check for unlock + if (M_MapLocked(mapnum+1)) + return false; + } // Check for TOL if (!(mapheaderinfo[mapnum]->typeoflevel & levelsearch->typeoflevel)) @@ -7688,9 +7698,16 @@ void M_Statistics(INT32 choice) if (!mapheaderinfo[i]) continue; + // Check for no visibility + legacy box if (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU)) continue; + // Check for completion + if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED) + && !(mapheaderinfo[i]->mapvisited & MV_BEATEN)) + continue; + + // Check for unlock if (M_MapLocked(i+1)) continue; diff --git a/src/m_cond.c b/src/m_cond.c index 052f23dc0..14ad2b6b4 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -570,7 +570,10 @@ static char *M_BuildConditionTitle(UINT16 map) { char *title, *ref; - if (M_MapLocked(map+1)) + if (((mapheaderinfo[map]->menuflags & LF2_FINISHNEEDED) + // the following is intentionally not MV_BEATEN, just in case the title is for "Finish a round on X" + && !(mapheaderinfo[map]->mapvisited & MV_VISITED)) + || M_MapLocked(map+1)) return Z_StrDup("???"); title = ref = G_BuildMapTitle(map+1); @@ -629,7 +632,7 @@ static const char *M_GetConditionString(condition_t *cn) title = BUILDCONDITIONTITLE(cn->requirement); work = va("%s %s%s", - (cn->type == UC_MAPVISITED) ? "Visit" : "Beat", + (cn->type == UC_MAPVISITED) ? "Visit" : "Finish a round on", title, (cn->type == UC_MAPENCORE) ? " in Encore Mode" : ""); Z_Free(title); From 36b1f5f4880ded19a6aabc9759172142d63ed750 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 17:50:49 +0000 Subject: [PATCH 40/76] Have a Cup visibly locked if no levels are accessible by the current rules of `menusearch_t` - Solved by leveraging M_GetFirstLevelInList alongside existing M_CountLevelsInList calls - Also optimises the above calls in the M_CupLocked case, since that's no longer being directly used otherwise --- src/k_menudraw.c | 36 ++++++++++++++++++++---------------- src/k_menufunc.c | 42 +++++++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8ef321bbe..6968bde6c 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1933,7 +1933,7 @@ static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch) V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); - if (levelsearch->cup && !M_CupLocked(levelsearch->cup) && maxlevels > 0) + if (levelsearch->cup && maxlevels > 0) { add = (cupgrid.previewanim / 82) % maxlevels; map = start; @@ -1979,16 +1979,18 @@ static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch) } } -static void M_DrawCupTitle(INT16 y, cupheader_t *cup) +static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch) { + UINT8 temp = 0; + V_DrawScaledPatch(0, y, 0, W_CachePatchName("MENUHINT", PU_CACHE)); - if (cup) + if (levelsearch->cup) { - boolean unlocked = !M_CupLocked(cup); + boolean unlocked = (M_GetFirstLevelInList(&temp, levelsearch) != NEXTMAP_INVALID); UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); - patch_t *icon = W_CachePatchName(cup->icon, PU_CACHE); - const char *str = (unlocked ? va("%s Cup", cup->name) : "???"); + patch_t *icon = W_CachePatchName(levelsearch->cup->icon, PU_CACHE); + const char *str = (unlocked ? va("%s Cup", levelsearch->cup->name) : "???"); INT16 offset = V_LSTitleLowStringWidth(str, 0) / 2; V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str); @@ -2011,26 +2013,26 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup) void M_DrawCupSelect(void) { - UINT8 i, j; + UINT8 i, j, temp = 0; levelsearch_t templevelsearch = levellist.levelsearch; // full copy - templevelsearch.cup = cupgrid.builtgrid[CUPMENU_CURSORID]; for (i = 0; i < CUPMENU_COLUMNS; i++) { for (j = 0; j < CUPMENU_ROWS; j++) { size_t id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS)); - cupheader_t *iconcup = cupgrid.builtgrid[id]; patch_t *patch = NULL; INT16 x, y; INT16 icony = 7; - if (!iconcup) + if (!cupgrid.builtgrid[id]) break; - /*if (iconcup->emeraldnum == 0) + templevelsearch.cup = cupgrid.builtgrid[id]; + + /*if (templevelsearch.cup->emeraldnum == 0) patch = W_CachePatchName("CUPMON3A", PU_CACHE); - else*/ if (iconcup->emeraldnum > 7) + else*/ if (templevelsearch.cup->emeraldnum > 7) { patch = W_CachePatchName("CUPMON2A", PU_CACHE); icony = 5; @@ -2043,14 +2045,14 @@ void M_DrawCupSelect(void) V_DrawScaledPatch(x, y, 0, patch); - if (M_CupLocked(iconcup)) + if (M_GetFirstLevelInList(&temp, &templevelsearch) == NEXTMAP_INVALID) { patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE); V_DrawScaledPatch(x + 8, y + icony, 0, st); } else { - V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(iconcup->icon, PU_CACHE)); + V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(templevelsearch.cup->icon, PU_CACHE)); V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE)); } } @@ -2061,8 +2063,10 @@ void M_DrawCupSelect(void) 0, W_CachePatchName("CUPCURS", PU_CACHE) ); + templevelsearch.cup = cupgrid.builtgrid[CUPMENU_CURSORID]; + M_DrawCupPreview(146 + (24*menutransition.tics), &templevelsearch); - M_DrawCupTitle(120 - (24*menutransition.tics), templevelsearch.cup); + M_DrawCupTitle(120 - (24*menutransition.tics), &templevelsearch); } static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) @@ -2228,7 +2232,7 @@ void M_DrawLevelSelect(void) map = M_GetNextLevelInList(map, &j, &levellist.levelsearch); } - M_DrawCupTitle(tay, levellist.levelsearch.cup); + M_DrawCupTitle(tay, &levellist.levelsearch); } void M_DrawTimeAttack(void) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6e7c9e4dd..2440d2a4d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3418,19 +3418,6 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch) if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR) return false; - // Does the map have lock conditions? - if (levelsearch->checklocked) - { - // Check for completion - if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED) - && !(mapheaderinfo[mapnum]->mapvisited & MV_BEATEN)) - return false; - - // Check for unlock - if (M_MapLocked(mapnum+1)) - return false; - } - // Check for TOL if (!(mapheaderinfo[mapnum]->typeoflevel & levelsearch->typeoflevel)) return false; @@ -3449,6 +3436,19 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch) && mapheaderinfo[mapnum]->cup != levelsearch->cup) return false; + // Finally, the most complex check: does the map have lock conditions? + if (levelsearch->checklocked) + { + // Check for completion + if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED) + && !(mapheaderinfo[mapnum]->mapvisited & MV_BEATEN)) + return false; + + // Check for unlock + if (M_MapLocked(mapnum+1)) + return false; + } + // Survived our checks. return true; } @@ -3462,6 +3462,9 @@ UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch) if (levelsearch->cup) { + if (levelsearch->checklocked && M_CupLocked(levelsearch->cup)) + return 0; + for (i = 0; i < CUPCACHE_MAX; i++) { if (!M_CanShowLevelInList(levelsearch->cup->cachedlevels[i], levelsearch)) @@ -3488,6 +3491,12 @@ UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch) if (levelsearch->cup) { + if (levelsearch->checklocked && M_CupLocked(levelsearch->cup)) + { + *i = CUPCACHE_MAX; + return NEXTMAP_INVALID; + } + *i = 0; mapnum = NEXTMAP_INVALID; for (; *i < CUPCACHE_MAX; (*i)++) @@ -3555,6 +3564,8 @@ static void M_LevelSelectScrollDest(void) static void M_LevelListFromGametype(INT16 gt) { static boolean first = true; + UINT8 temp = 0; + if (first || gt != levellist.newgametype || levellist.guessgt != MAXGAMETYPES) { levellist.newgametype = gt; @@ -3589,7 +3600,6 @@ static void M_LevelListFromGametype(INT16 gt) const size_t unitlen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS); templevelsearch.cup = kartcupheaders; - templevelsearch.checklocked = false; // Make sure there's valid cups before going to this menu. if (templevelsearch.cup == NULL) @@ -3612,6 +3622,7 @@ static void M_LevelListFromGametype(INT16 gt) while (templevelsearch.cup) { + templevelsearch.checklocked = false; if (!M_CountLevelsToShowInList(&templevelsearch)) { // No valid maps, skip. @@ -3638,7 +3649,8 @@ static void M_LevelListFromGametype(INT16 gt) cupgrid.builtgrid[currentid] = templevelsearch.cup; - if (!M_CupLocked(templevelsearch.cup)) + templevelsearch.checklocked = true; + if (M_GetFirstLevelInList(&temp, &templevelsearch) != NEXTMAP_INVALID) { highestunlockedid = currentid; if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == templevelsearch.cup) From 44555be7043308051a4b5ac100e19d64882ce8d4 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 17:57:22 +0000 Subject: [PATCH 41/76] Change the conditions for forced retry at end of round - If modeattacking, never - If Sealed Star, if the UFO still exists or the emerald hasn't been picked up --- src/g_game.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index ac0190c4e..156ba98e2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2915,17 +2915,24 @@ void G_ExitLevel(void) if (gamestate == GS_LEVEL) { UINT8 i; - boolean youlost = false; - if (gametyperules & GTR_BOSS) + boolean doretry = false; + + if (modeattacking != ATTACKING_NONE) + ; + else if (specialstageinfo.valid == true) { - youlost = true; + doretry = (specialstageinfo.ufo != NULL); + } + else if (gametyperules & GTR_BOSS) + { + doretry = true; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && !players[i].spectator && !players[i].bot) { if (players[i].bumpers > 0) { - youlost = false; + doretry = false; break; } } @@ -2933,10 +2940,10 @@ void G_ExitLevel(void) } else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE) { - youlost = (grandprixinfo.wonround != true); + doretry = (grandprixinfo.wonround != true); } - if (youlost) + if (doretry) { // You didn't win... From 39e68d1ae3af924801ca8a3cdb42f88dd5fc05ee Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 18:45:04 +0000 Subject: [PATCH 42/76] Clear UFO pointer if its object was removed (matches up with general mobjthinker P_SetTarget handling) --- src/k_specialstage.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_specialstage.c b/src/k_specialstage.c index d5eb172ce..b0defccb9 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -119,6 +119,11 @@ void K_TickSpecialStage(void) return; } + if (P_MobjWasRemoved(specialstageinfo.ufo)) + { + P_SetTarget(&specialstageinfo.ufo, NULL); + } + K_MoveExitBeam(); } From a86ed5c6cb6f2f947635aec304f2ce9fb02899bc Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 18:59:56 +0000 Subject: [PATCH 43/76] Rework the conditions for the previous commit - The "never force a retry" condition is now `!G_GametypeUsesLives()` - In GT_SPECIAL *and* GT_VERSUS, check for K_IsPlayerLosing before retry, instead of bespoke check --- src/g_game.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 156ba98e2..d87be770c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2917,20 +2917,16 @@ void G_ExitLevel(void) UINT8 i; boolean doretry = false; - if (modeattacking != ATTACKING_NONE) - ; - else if (specialstageinfo.valid == true) - { - doretry = (specialstageinfo.ufo != NULL); - } - else if (gametyperules & GTR_BOSS) + if (!G_GametypeUsesLives()) + ; // never force a retry + else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS) { doretry = true; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && !players[i].spectator && !players[i].bot) { - if (players[i].bumpers > 0) + if (!K_IsPlayerLosing(&players[i])) { doretry = false; break; From 5b027ff2feec68328c954ff59abc4dd671dd1970 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 19:00:52 +0000 Subject: [PATCH 44/76] Instead of preventing lives for GTR_CAPSULES, prevent them for it being GPEVENT_BONUS --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index d87be770c..c2de77e36 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3227,7 +3227,7 @@ boolean G_GametypeUsesLives(void) return false; if ((grandprixinfo.gp == true) // In Grand Prix - && !(gametyperules & GTR_CAPSULES)) // NOT in Break The Capsules + && grandprixinfo.eventmode != GPEVENT_BONUS) // NOT in bonus round { return true; } From 6b18f869b9e90a1a401453e0325fe2e657d521d3 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 19:02:22 +0000 Subject: [PATCH 45/76] Prevent a special stage win by sneaking ahead of the UFO to the goal line --- src/p_spec.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/p_spec.c b/src/p_spec.c index b85746db6..208e6c04f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -43,6 +43,7 @@ // SRB2kart #include "k_kart.h" +#include "k_specialstage.h" #include "console.h" // CON_LogMessage #include "k_respawn.h" #include "k_terrain.h" @@ -1925,6 +1926,14 @@ static void K_HandleLapIncrement(player_t *player) // finished race exit setup if (player->laps > numlaps) { + if (specialstageinfo.valid == true) + { + // Don't permit a win just by sneaking ahead of the UFO/emerald. + if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo))) + { + player->pflags |= PF_NOCONTEST; + } + } P_DoPlayerExit(player); P_SetupSignExit(player); } From c857153c29ea8e709d8c1e1e3771158c769fbcec Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 19:03:13 +0000 Subject: [PATCH 46/76] Unstaged closing brace --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index c2de77e36..47e144c42 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2919,7 +2919,7 @@ void G_ExitLevel(void) if (!G_GametypeUsesLives()) ; // never force a retry - else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS) + else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS)) { doretry = true; for (i = 0; i < MAXPLAYERS; i++) From 76ea72ceac563527e5298633e7cdbc1308478204 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 20:33:31 +0000 Subject: [PATCH 47/76] Prevent entering the cup or level select if there's no valid maps to pick from. --- src/k_menufunc.c | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 2440d2a4d..29efb0b49 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3561,7 +3561,7 @@ static void M_LevelSelectScrollDest(void) } // Builds the level list we'll be using from the gametype we're choosing and send us to the apropriate menu. -static void M_LevelListFromGametype(INT16 gt) +static boolean M_LevelListFromGametype(INT16 gt) { static boolean first = true; UINT8 temp = 0; @@ -3588,8 +3588,6 @@ static void M_LevelListFromGametype(INT16 gt) first = false; } - PLAY_CupSelectDef.prevMenu = currentMenu; - // Obviously go to Cup Select in gametypes that have cups. // Use a really long level select in gametypes that don't use cups. @@ -3598,12 +3596,15 @@ static void M_LevelListFromGametype(INT16 gt) levelsearch_t templevelsearch = levellist.levelsearch; // full copy size_t currentid = 0, highestunlockedid = 0; const size_t unitlen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS); + boolean foundany = false; templevelsearch.cup = kartcupheaders; - // Make sure there's valid cups before going to this menu. +#if 0 + // Make sure there's valid cups before going to this menu. -- rip sweet prince if (templevelsearch.cup == NULL) I_Error("Can you really call this a racing game, I didn't recieve any Cups on my pillow or anything"); +#endif if (!cupgrid.builtgrid) { @@ -3630,6 +3631,8 @@ static void M_LevelListFromGametype(INT16 gt) continue; } + foundany = true; + if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * unitlen) { // Double the size of the buffer, and clear the other stuff. @@ -3665,16 +3668,29 @@ static void M_LevelListFromGametype(INT16 gt) templevelsearch.cup = templevelsearch.cup->next; } + if (foundany == false) + { + return false; + } + cupgrid.numpages = (highestunlockedid / (CUPMENU_COLUMNS * CUPMENU_ROWS)) + 1; if (cupgrid.pageno >= cupgrid.numpages) { cupgrid.pageno = 0; } + PLAY_CupSelectDef.prevMenu = currentMenu; PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef; M_SetupNextMenu(&PLAY_CupSelectDef, false); - return; + return true; + } + + // Okay, just a list of maps then. + + if (M_GetFirstLevelInList(&temp, &levellist.levelsearch) == NEXTMAP_INVALID) + { + return false; } // Reset position properly if you go back & forth between gametypes @@ -3690,6 +3706,7 @@ static void M_LevelListFromGametype(INT16 gt) PLAY_LevelSelectDef.prevMenu = currentMenu; M_SetupNextMenu(&PLAY_LevelSelectDef, false); + return true; } // Init level select for use in local play using the last choice we made. @@ -3731,7 +3748,11 @@ void M_LevelSelectInit(INT32 choice) gt = menugametype; } - M_LevelListFromGametype(gt); + if (!M_LevelListFromGametype(gt)) + { + S_StartSound(NULL, sfx_s3kb2); + M_StartMessage(va("No levels available for\n%s Mode!\n\nPress (B)\n", gametypes[gt]->name), NULL, MM_NOTHING); + } } static void M_LevelSelected(INT16 add) @@ -4279,8 +4300,11 @@ void M_MPSetupNetgameMapSelect(INT32 choice) // okay this is REALLY stupid but this fixes the host menu re-folding on itself when we go back. mpmenu.modewinextend[0][0] = 1; - M_LevelListFromGametype(menugametype); // Setup the level select. - // (This will also automatically send us to the apropriate menu) + if (!M_LevelListFromGametype(menugametype)) + { + S_StartSound(NULL, sfx_s3kb2); + M_StartMessage(va("No levels available for\n%s Mode!\n\nPress (B)\n", gametypes[menugametype]->name), NULL, MM_NOTHING); + } } // MULTIPLAYER JOIN BY IP From e0c4c235dfc23b6f43635a659281f4281b938a65 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 20:34:11 +0000 Subject: [PATCH 48/76] Move "return" in "Are you sure you want to return to the menu?" to the next line, to balance the lines out a little better --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 29efb0b49..03bcfc9d0 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -6457,7 +6457,7 @@ void M_EndGame(INT32 choice) if (!Playing()) return; - M_StartMessage(M_GetText("Are you sure you want to return\nto the menu?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO); + M_StartMessage(M_GetText("Are you sure you want to\nreturn to the menu?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO); } From a2c5ff6ac8ed82307c3ea10bff0b1a3d84147af9 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 20:56:00 +0000 Subject: [PATCH 49/76] Remove the duplication of `levellist.netgame` into `cupgrid.netgame` --- src/k_menu.h | 1 - src/k_menufunc.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 2ac8ecf35..9b46cc776 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -694,7 +694,6 @@ extern struct cupgrid_s { size_t cappages; tic_t previewanim; boolean grandprix; // Setup grand prix server after picking - boolean netgame; // Start the game in an actual server } cupgrid; typedef struct levelsearch_s { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 03bcfc9d0..73fab9aaa 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3720,7 +3720,6 @@ void M_LevelSelectInit(INT32 choice) (void)choice; // Make sure this is reset as we'll only be using this function for offline games! - cupgrid.netgame = false; levellist.netgame = false; levellist.levelsearch.checklocked = true; @@ -4291,7 +4290,6 @@ void M_MPSetupNetgameMapSelect(INT32 choice) // Yep, we'll be starting a netgame. levellist.netgame = true; - cupgrid.netgame = true; // Make sure we reset those levellist.levelsearch.timeattack = false; levellist.levelsearch.checklocked = true; From 05241341b8f011091d730f8240ea6904d5ac5c85 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 21:38:52 +0000 Subject: [PATCH 50/76] Remove the last few instances of gametype-specific colorisation --- src/g_game.c | 20 -------------------- src/g_game.h | 1 - src/y_inter.c | 10 +++------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 47e144c42..5ce2dc0a4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3298,26 +3298,6 @@ INT16 G_SometimesGetDifferentGametype(void) return (gametype|encoremodifier); } -// -// G_GetGametypeColor -// -// Pretty and consistent ^u^ -// See also M_GetGametypeColor (if that still exists). -// -UINT8 G_GetGametypeColor(INT16 gt) -{ - if (modeattacking) // == ATTACKING_RECORD - return orangemap[0]; - - if (gt == GT_BATTLE) - return redmap[0]; - - if (gt == GT_RACE) - return skymap[0]; - - return 255; // FALLBACK -} - /** Get the typeoflevel flag needed to indicate support of a gametype. * \param gametype The gametype for which support is desired. * \return The typeoflevel flag to check for that gametype. diff --git a/src/g_game.h b/src/g_game.h index 227274590..eebb956e9 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -190,7 +190,6 @@ boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); #define VOTEMODIFIER_ENCORE 0x80 INT16 G_SometimesGetDifferentGametype(void); -UINT8 G_GetGametypeColor(INT16 gt); void G_ExitLevel(void); void G_NextLevel(void); void G_Continue(void); diff --git a/src/y_inter.c b/src/y_inter.c index 1d4393dc3..9d7a62f3e 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1209,12 +1209,8 @@ void Y_VoteDrawer(void) if (timer) { - INT32 hilicol, tickdown = (timer+1)/TICRATE; - if (gametype == GT_RACE) - hilicol = V_SKYMAP; - else //if (gametype == GT_BATTLE) - hilicol = V_REDMAP; - V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, + INT32 tickdown = (timer+1)/TICRATE; + V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP, va("Vote ends in %d", tickdown)); } } @@ -1554,7 +1550,7 @@ void Y_StartVote(void) levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0'; // set up the gtc and gts - levelinfo[i].gtc = G_GetGametypeColor(votelevels[i][1]); + levelinfo[i].gtc = 255; // TODO rewrite vote screen if (i == 2 && votelevels[i][1] != votelevels[0][1]) levelinfo[i].gts = gametypes[votelevels[i][1]]->name; else From 36d6fcaba497e1d45fc9fceec40e2b7a7c748f67 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 21:43:39 +0000 Subject: [PATCH 51/76] Add a preview for Special Attack unlock type --- src/k_menudraw.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 6968bde6c..937839cd2 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4738,6 +4738,16 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) specialmap = btcmapcache; break; } + case SECRET_SPECIALATTACK: + { + static UINT16 sscmapcache = NEXTMAP_INVALID; + if (sscmapcache > nummapheaders) + { + sscmapcache = G_RandMap(G_TOLFlag(GT_SPECIAL), -1, 2, 0, false, NULL); + } + specialmap = sscmapcache; + break; + } case SECRET_HARDSPEED: { static UINT16 hardmapcache = NEXTMAP_INVALID; From 98646ef65b4febaab94bd986b598703800d2979d Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 22:33:09 +0000 Subject: [PATCH 52/76] Fix arrangement of gametype rules between SOC and constants --- src/deh_tables.c | 17 ++++++++------- src/doomstat.h | 54 +++++++++++++++++++++++++----------------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 5217e3c6c..0c369b92e 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5796,28 +5796,31 @@ const char *const GAMETYPERULE_LIST[] = { "BUMPERS", "SPHERES", + "CLOSERPLAYERS", + + "BATTLESTARTS", "PAPERITEMS", "POWERSTONES", "KARMA", "ITEMARROWS", + "CAPSULES", - "BATTLESTARTS", + "CATCHER", + "ROLLINGSTART", + "SPECIALSTART", + "BOSS", "POINTLIMIT", "TIMELIMIT", "OVERTIME", + "ENCORE", "TEAMS", "NOTEAMS", "TEAMSTARTS", - "CATCHER", - "BOSS", - "ROLLINGSTART", + "NOMP", "NOCUPSELECT", - "CLOSERPLAYERS", - "ENCORE", - "SPECIALSTART", NULL }; diff --git a/src/doomstat.h b/src/doomstat.h index 76ec0cb25..bf1295078 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -489,37 +489,39 @@ extern INT16 gametype; enum GameTypeRules { // Race rules - GTR_CIRCUIT = 1, // Enables the finish line, laps, and the waypoint system. - GTR_BOTS = 1<<2, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype. + GTR_CIRCUIT = 1, // Enables the finish line, laps, and the waypoint system. + GTR_BOTS = 1<<1, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype. // Battle gametype rules - GTR_BUMPERS = 1<<3, // Enables the bumper health system - GTR_SPHERES = 1<<4, // Replaces rings with blue spheres - GTR_PAPERITEMS = 1<<5, // Replaces item boxes with paper item spawners - GTR_POWERSTONES = 1<<6, // Battle Emerald collectables. - GTR_KARMA = 1<<7, // Enables the Karma system if you're out of bumpers - GTR_ITEMARROWS = 1<<8, // Show item box arrows above players - GTR_CAPSULES = 1<<9, // Can enter Break The Capsules mode - GTR_BATTLESTARTS = 1<<10, // Use Battle Mode start positions. + GTR_BUMPERS = 1<<2, // Enables the bumper health system + GTR_SPHERES = 1<<3, // Replaces rings with blue spheres + GTR_CLOSERPLAYERS = 1<<4, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos - GTR_POINTLIMIT = 1<<11, // Reaching point limit ends the round - GTR_TIMELIMIT = 1<<12, // Reaching time limit ends the round - GTR_OVERTIME = 1<<13, // Allow overtime behavior + GTR_BATTLESTARTS = 1<<5, // Use Battle Mode start positions. + GTR_PAPERITEMS = 1<<6, // Replaces item boxes with paper item spawners + GTR_POWERSTONES = 1<<7, // Battle Emerald collectables. + GTR_KARMA = 1<<8, // Enables the Karma system if you're out of bumpers + GTR_ITEMARROWS = 1<<9, // Show item box arrows above players - // Custom gametype rules - GTR_TEAMS = 1<<14, // Teams are forced on - GTR_NOTEAMS = 1<<15, // Teams are forced off - GTR_TEAMSTARTS = 1<<16, // Use team-based start positions + // Bonus gametype rules + GTR_CAPSULES = 1<<10, // Can enter Break The Capsules mode + GTR_CATCHER = 1<<11, // UFO Catcher (only works with GTR_CIRCUIT) + GTR_ROLLINGSTART = 1<<12, // Rolling start (only works with GTR_CIRCUIT) + GTR_SPECIALSTART = 1<<13, // White fade instant start + GTR_BOSS = 1<<14, // Boss intro and spawning - // To be rearranged later - GTR_CATCHER = 1<<17, // UFO Catcher (only works with GTR_CIRCUIT) - GTR_BOSS = 1<<18, // Boss intro and spawning - GTR_ROLLINGSTART = 1<<19, // Rolling start (only works with GTR_CIRCUIT) - GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. - GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos - GTR_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping - GTR_SPECIALSTART = 1<<23, // White fade instant start - GTR_NOMP = 1<<24, // No multiplayer + // General purpose rules + 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_ENCORE = 1<<18, // Alternate Encore mirroring, scripting, and texture remapping + + 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 + + GTR_NOMP = 1<<22, // No multiplayer + GTR_NOCUPSELECT = 1<<23, // Your maps are not selected via cup. // free: to and including 1<<31 }; From 94bcf797109d56a2ab95cb927d9df46d384fce5c Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 22:34:19 +0000 Subject: [PATCH 53/76] Restrict collecting rings in GTR_SPHERES, not GTR_BUMPERS! --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index a27890817..a51b161a3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -502,7 +502,7 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings) if (!player->mo) return 0; - if ((gametyperules & GTR_BUMPERS)) // No rings in Battle Mode + if ((gametyperules & GTR_SPHERES)) // No rings in Battle Mode return 0; test = player->rings + num_rings; From 3357f56c9e5488ba219ad65d6a0192580ce033b2 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 22:37:26 +0000 Subject: [PATCH 54/76] G_RandMap should consider & all valid flags for gametype, not ==. --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 5ce2dc0a4..47b5105fc 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3410,7 +3410,7 @@ tryagain: if (!mapheaderinfo[ix] || mapheaderinfo[ix]->lumpnum == LUMPERROR) continue; - if ((mapheaderinfo[ix]->typeoflevel & tolflags) != tolflags + if (!(mapheaderinfo[ix]->typeoflevel & tolflags) || ix == pprevmap || M_MapLocked(ix+1) || (usehellmaps != (mapheaderinfo[ix]->menuflags & LF2_HIDEINMENU))) // this is bad From c23a2d8dcac605fc5884688a3185fe0d79c29bf3 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 22:49:12 +0000 Subject: [PATCH 55/76] Don't force a reset of the level in GTR_BUMPER when lonesome if not GTR_CAPSULES --- src/k_battle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_battle.c b/src/k_battle.c index 26b1265f3..d0f0451c2 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -145,7 +145,7 @@ void K_CheckBumpers(void) } else if (numingame <= 1) { - if (!battlecapsules) + if ((gametyperules & GTR_CAPSULES) && !battlecapsules) { // Reset map to turn on battle capsules if (server) From 425a02d09bc56055524c59e5666622f487f4c6a9 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 23:12:41 +0000 Subject: [PATCH 56/76] Make GTR_BUMPERS less monolithic * Battle-like item box respawn handling inverse of GTR_CIRCUIT * Make battle fullscreen overlay forbid GTR_CIRCUIT * Make GTR_ITEMARROWS actually do something * Make everything related to points actually use GTR_POINTLIMIT * Fobid sphere addition without GTR_SPHERES, to mirror ring addition forbidding --- src/d_netcmd.c | 2 +- src/k_hud.c | 4 ++-- src/p_enemy.c | 2 +- src/p_inter.c | 2 +- src/p_mobj.c | 9 ++++++--- src/p_tick.c | 4 ++-- src/p_user.c | 5 ++++- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index e7a343a03..de2a9f9ec 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3773,7 +3773,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) // Clear player score and rings if a spectator. if (players[playernum].spectator) { - if (gametyperules & GTR_BUMPERS) // SRB2kart + if (gametyperules & GTR_POINTLIMIT) // SRB2kart { players[playernum].roundscore = 0; K_CalculateBattleWanted(); diff --git a/src/k_hud.c b/src/k_hud.c index ebf6f9ceb..e80135f17 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5030,9 +5030,9 @@ void K_drawKartHUD(void) return; } - battlefullscreen = ((gametyperules & (GTR_BUMPERS)) + battlefullscreen = (!(gametyperules & GTR_CIRCUIT) && (stplyr->exiting - || (stplyr->bumpers <= 0 + || ((gametyperules & GTR_BUMPERS) && (stplyr->bumpers <= 0) && ((gametyperules & GTR_KARMA) && (stplyr->karmadelay > 0)) && !(stplyr->pflags & PF_ELIMINATED) && stplyr->playerstate == PST_LIVE))); diff --git a/src/p_enemy.c b/src/p_enemy.c index 2e1ed8557..fc67cc882 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13053,7 +13053,7 @@ void A_ItemPop(mobj_t *actor) } // Here at mapload in battle? - if ((gametyperules & GTR_BUMPERS) && (actor->flags2 & MF2_BOSSNOTRAP)) + if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP)) { numgotboxes++; diff --git a/src/p_inter.c b/src/p_inter.c index 8944b8b50..9caf37531 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1099,7 +1099,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget { P_SetTarget(&target->target, source); - if (gametyperules & GTR_BUMPERS) + if (!(gametyperules & GTR_CIRCUIT)) { target->fuse = 2; } diff --git a/src/p_mobj.c b/src/p_mobj.c index a9562b38a..839468ce9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9581,7 +9581,7 @@ static boolean P_FuseThink(mobj_t *mobj) { ; } - else if ((gametyperules & GTR_BUMPERS) && (mobj->state == &states[S_INVISIBLE])) + else if (!(gametyperules & GTR_CIRCUIT) && (mobj->state == &states[S_INVISIBLE])) { break; } @@ -11419,7 +11419,7 @@ void P_RespawnBattleBoxes(void) { thinker_t *th; - if (!(gametyperules & GTR_BUMPERS)) + if (gametyperules & GTR_CIRCUIT) return; for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) @@ -11684,13 +11684,16 @@ void P_SpawnPlayer(INT32 playernum) K_InitStumbleIndicator(p); - if (gametyperules & GTR_BUMPERS) + if (gametyperules & GTR_ITEMARROWS) { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW); P_SetTarget(&overheadarrow->target, mobj); overheadarrow->renderflags |= RF_DONTDRAW; P_SetScale(overheadarrow, mobj->destscale); + } + if (gametyperules & GTR_BUMPERS) + { if (p->spectator) { // HEY! No being cheap... diff --git a/src/p_tick.c b/src/p_tick.c index 4ad34d57e..ce390bd1a 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -361,7 +361,7 @@ static inline void P_RunThinkers(void) if (gametyperules & GTR_PAPERITEMS) K_RunPaperItemSpawners(); - if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled) + if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled) K_RunBattleOvertime(); } @@ -719,7 +719,7 @@ void P_Ticker(boolean run) K_TickSpecialStage(); - if ((gametyperules & GTR_BUMPERS)) + if ((gametyperules & GTR_POINTLIMIT)) { if (wantedcalcdelay && --wantedcalcdelay <= 0) K_CalculateBattleWanted(); diff --git a/src/p_user.c b/src/p_user.c index a51b161a3..7de98c92b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -520,6 +520,9 @@ INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres) { num_spheres += player->spheres; + if (!(gametyperules & GTR_SPHERES)) // No spheres in Race mode) + return 0; + // Not alive if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) return 0; @@ -555,7 +558,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) // Adds to the player's score void P_AddPlayerScore(player_t *player, UINT32 amount) { - if (!((gametyperules & GTR_BUMPERS))) + if (!((gametyperules & GTR_POINTLIMIT))) return; if (player->exiting) // srb2kart From 84b066245e91aaf38a06c681465695891991bd4a Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 23:14:43 +0000 Subject: [PATCH 57/76] Demo savemode text is consistent colour now --- src/st_stuff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 76ad09b9b..3f7e793eb 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1253,15 +1253,15 @@ void ST_Drawer(void) switch (demo.savemode) { case DSM_NOTSAVING: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "(B) or (X): Save replay"); + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|V_YELLOWMAP, "(B) or (X): Save replay"); break; case DSM_WILLAUTOSAVE: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved. (Look Backward: Change title)"); + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|V_YELLOWMAP, "Replay will be saved. (Look Backward: Change title)"); break; case DSM_WILLSAVE: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved."); + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|V_YELLOWMAP, "Replay will be saved."); break; case DSM_TITLEENTRY: From 42a6b03e488a99f8e1a7dd4553fcfd4582100778 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 Dec 2022 23:27:59 +0000 Subject: [PATCH 58/76] Remove "circuitmap" An effectively useless global scope boolean that literally checks for the presence of a finish line, created in and used only by things we inherited from vanilla SRB2 --- src/d_netcmd.c | 1 - src/doomstat.h | 1 - src/hu_stuff.c | 14 +++++--------- src/k_hud.c | 4 ++-- src/lua_script.c | 3 --- src/p_setup.c | 1 - src/p_spec.c | 6 ------ src/p_user.c | 4 ++-- 8 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index de2a9f9ec..98dbb7fc9 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -553,7 +553,6 @@ INT16 numgametypes = GT_FIRSTFREESLOT; boolean forceresetplayers = false; boolean deferencoremode = false; UINT8 splitscreen = 0; -boolean circuitmap = false; INT32 adminplayers[MAXPLAYERS]; // Scheduled commands. diff --git a/src/doomstat.h b/src/doomstat.h index bf1295078..6d4fd80a5 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -150,7 +150,6 @@ extern boolean multiplayer; extern UINT8 splitscreen; extern int r_splitscreen; -extern boolean circuitmap; // Does this level have 'circuit mode'? extern boolean fromlevelselect; extern boolean forceresetplayers, deferencoremode; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index dd682e1bd..ed0ab076f 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2457,11 +2457,8 @@ static void HU_DrawRankings(void) } else if (gametyperules & GTR_CIRCUIT) { - if (circuitmap) - { - V_DrawCenteredString(64, 8, 0, "LAP COUNT"); - V_DrawCenteredString(64, 16, hilicol, va("%d", numlaps)); - } + V_DrawCenteredString(64, 8, 0, "LAPS"); + V_DrawCenteredString(64, 16, hilicol, va("%d", numlaps)); } // Right hand side @@ -2548,13 +2545,12 @@ static void HU_DrawRankings(void) if ((gametyperules & GTR_CIRCUIT)) { - if (circuitmap) - tab[scorelines].count = players[i].laps; - else - tab[scorelines].count = players[i].realtime; + tab[scorelines].count = players[i].laps; } else + { tab[scorelines].count = players[i].roundscore; + } scorelines++; diff --git a/src/k_hud.c b/src/k_hud.c index e80135f17..f8b6bc387 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2304,7 +2304,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); else if (players[tab[i].num].pflags & PF_NOCONTEST) V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST."); - else if (circuitmap) + else V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count)); } else @@ -2313,7 +2313,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime)); else if (players[tab[i].num].pflags & PF_NOCONTEST) V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST."); - else if (circuitmap) + else V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count)); } #undef timestring diff --git a/src/lua_script.c b/src/lua_script.c index 4f9eab969..cde3e2f46 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -162,9 +162,6 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"maptol")) { lua_pushinteger(L, maptol); return 1; - } else if (fastcmp(word,"circuitmap")) { - lua_pushboolean(L, circuitmap); - return 1; } else if (fastcmp(word,"stoppedclock")) { lua_pushboolean(L, stoppedclock); return 1; diff --git a/src/p_setup.c b/src/p_setup.c index 36a589f22..d890868f8 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -6822,7 +6822,6 @@ static void P_InitLevelSettings(void) rflagpoint = bflagpoint = NULL; // circuit, race and competition stuff - circuitmap = false; numstarposts = 0; timeinmap = 0; diff --git a/src/p_spec.c b/src/p_spec.c index 208e6c04f..48d38a125 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -6671,12 +6671,6 @@ void P_SpawnSpecials(boolean fromnetsave) break; } - // SRB2Kart - case 2001: // Finish Line - if ((gametyperules & GTR_CIRCUIT)) - circuitmap = true; - break; - default: break; } diff --git a/src/p_user.c b/src/p_user.c index 7de98c92b..be70fd0e0 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2639,7 +2639,7 @@ static void P_DeathThink(player_t *player) player->realtime = leveltime - starttime; if (player == &players[consoleplayer]) { - if (player->spectator || !circuitmap) + if (player->spectator) curlap = 0; else if (curlap != UINT32_MAX) curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V @@ -3998,7 +3998,7 @@ void P_PlayerThink(player_t *player) player->realtime = leveltime - starttime; if (player == &players[consoleplayer]) { - if (player->spectator || !circuitmap) + if (player->spectator) curlap = 0; else if (curlap != UINT32_MAX) curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V From d5cd4f6798be7e9569cb22038f3400f85b0e4672 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 1 Jan 2023 14:54:58 +0000 Subject: [PATCH 59/76] Make placeholder for vote screen gametype color yellowmap[0] instead of 255 cyan --- src/y_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/y_inter.c b/src/y_inter.c index 9d7a62f3e..509f40451 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1550,7 +1550,7 @@ void Y_StartVote(void) levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0'; // set up the gtc and gts - levelinfo[i].gtc = 255; // TODO rewrite vote screen + levelinfo[i].gtc = yellowmap[0]; // TODO rewrite vote screen if (i == 2 && votelevels[i][1] != votelevels[0][1]) levelinfo[i].gts = gametypes[votelevels[i][1]]->name; else From 4bbe3d71778e30aa4718a57d7fb8316bca2ea9c4 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 1 Jan 2023 14:55:58 +0000 Subject: [PATCH 60/76] Draw Rings/Lives HUD even when not GTR_CIRCUIT --- src/k_hud.c | 141 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 93 insertions(+), 48 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index f8b6bc387..742d0506b 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2330,40 +2330,13 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN } } -#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) - -static void K_drawKartLapsAndRings(void) +static void K_drawKartLaps(void) { - const boolean uselives = G_GametypeUsesLives(); - SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; - UINT8 rn[2]; - INT32 ringflip = 0; - UINT8 *ringmap = NULL; - boolean colorring = false; - INT32 ringx = 0; - - rn[0] = ((abs(stplyr->rings) / 10) % 10); - rn[1] = (abs(stplyr->rings) % 10); - - if (stplyr->rings <= 0 && (leveltime/5 & 1)) // In debt - { - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); - colorring = true; - } - else if (stplyr->rings >= 20) // Maxed out - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); - - if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) - { - ringflip = V_FLIP; - ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; - ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); - } if (r_splitscreen > 1) { - INT32 fx = 0, fy = 0, fr = 0; + INT32 fx = 0, fy = 0; INT32 flipflag = 0; // pain and suffering defined below @@ -2389,8 +2362,6 @@ static void K_drawKartLapsAndRings(void) } } - fr = fx; - // Laps V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]); @@ -2417,6 +2388,75 @@ static void K_drawKartLapsAndRings(void) V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->laps) % 10]); V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(numlaps) % 10]); } + } + else + { + // Laps + V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker); + V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, numlaps), numlaps)); + } +} + +#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) + +static void K_drawRingCounter(void) +{ + const boolean uselives = G_GametypeUsesLives(); + SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; + INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; + UINT8 rn[2]; + INT32 ringflip = 0; + UINT8 *ringmap = NULL; + boolean colorring = false; + INT32 ringx = 0, fy = 0; + + rn[0] = ((abs(stplyr->rings) / 10) % 10); + rn[1] = (abs(stplyr->rings) % 10); + + if (stplyr->rings <= 0 && (leveltime/5 & 1)) // In debt + { + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); + colorring = true; + } + else if (stplyr->rings >= 20) // Maxed out + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); + + if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) + { + ringflip = V_FLIP; + ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; + ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); + } + + if (r_splitscreen > 1) + { + INT32 fx = 0, fr = 0; + INT32 flipflag = 0; + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = LAPS_X; + fy = LAPS_Y; + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = LAPS_X; + fy = LAPS_Y; + splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN; + } + else // else, that means we're P2 or P4. + { + fx = LAPS2_X; + fy = LAPS2_Y; + splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN; + flipflag = V_FLIP; // make the string right aligned and other shit + } + } + + fr = fx; // Rings if (!uselives) @@ -2451,41 +2491,42 @@ static void K_drawKartLapsAndRings(void) } else { - // Laps - V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker); - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, numlaps), numlaps)); + fy = LAPS_Y-11; + + if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS) + fy -= 4; // Rings if (!uselives) - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[1]); + V_DrawScaledPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[1]); else - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[0]); + V_DrawScaledPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[0]); - V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); + V_DrawMappedPatch(LAPS_X+ringx+7, fy-5, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); if (stplyr->rings < 0) // Draw the minus for ring debt { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminus, ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap); + V_DrawMappedPatch(LAPS_X+23, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminus, ringmap); + V_DrawMappedPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+35, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap); } else { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap); + V_DrawMappedPatch(LAPS_X+23, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap); } // SPB ring lock if (stplyr->pflags & PF_RINGLOCK) - V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); + V_DrawScaledPatch(LAPS_X-5, fy-17, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); // Lives if (uselives) { UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_RANK], colormap); + V_DrawMappedPatch(LAPS_X+46, fy-5, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_RANK], colormap); if (stplyr->lives >= 0) - V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow OR underflow + V_DrawScaledPatch(LAPS_X+63, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow OR underflow } } } @@ -2502,7 +2543,7 @@ static void K_drawKartAccessibilityIcons(INT32 fx) if (r_splitscreen < 2) // adjust to speedometer height { - if (gametyperules & (GTR_BUMPERS|GTR_SPHERES)) + if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS) fy -= 4; } else @@ -2592,7 +2633,7 @@ static void K_drawKartSpeedometer(void) numbers[1] = ((convSpeed / 10) % 10); numbers[2] = (convSpeed % 10); - if (gametyperules & (GTR_BUMPERS|GTR_SPHERES)) + if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS) battleoffset = -4; V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometersticker); @@ -5121,7 +5162,7 @@ void K_drawKartHUD(void) { if (gametyperules & GTR_CIRCUIT) { - K_drawKartLapsAndRings(); + K_drawKartLaps(); } else if (gametyperules & GTR_BUMPERS) { @@ -5143,6 +5184,10 @@ void K_drawKartHUD(void) { K_drawBlueSphereMeter(); } + else + { + K_drawRingCounter(); + } if (modeattacking && !bossinfo.valid) { From 8f78b37e57670b4f6f9d95b9fbe15f9c4791c72d Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 1 Jan 2023 16:02:55 +0000 Subject: [PATCH 61/76] Fix `yellowmap` not being included Was going to `#include "console.h"` but not worth it for literally one placeholder index --- src/y_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/y_inter.c b/src/y_inter.c index 509f40451..902966ca4 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1550,7 +1550,7 @@ void Y_StartVote(void) levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0'; // set up the gtc and gts - levelinfo[i].gtc = yellowmap[0]; // TODO rewrite vote screen + levelinfo[i].gtc = 73; // yellowmap[0] -- TODO rewrite vote screen if (i == 2 && votelevels[i][1] != votelevels[0][1]) levelinfo[i].gts = gametypes[votelevels[i][1]]->name; else From c10bd068de734d493a8666fbe44eadf51f7c8287 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 1 Jan 2023 23:39:28 +0000 Subject: [PATCH 62/76] Fix loading a map in a seperate file before loading a file with the relevant header --- src/p_setup.c | 2 +- src/w_wad.c | 4 ++-- src/w_wad.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 61c2a9ec3..0aff1ef4e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7771,7 +7771,7 @@ UINT8 P_InitMapData(boolean existingmapheaders) for (i = 0; i < nummapheaders; ++i) { name = mapheaderinfo[i]->lumpname; - maplump = W_CheckNumForMap(name); + maplump = W_CheckNumForMap(name, (mapheaderinfo[i]->lumpnum == LUMPERROR)); // Always check for cup cache reassociations. // (The core assumption is that cups < headers.) diff --git a/src/w_wad.c b/src/w_wad.c index cef5615cc..ad630a6fb 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1309,12 +1309,12 @@ lumpnum_t W_CheckNumForLongName(const char *name) // Look for valid map data through all added files in descendant order. // Get a map marker for WADs, and a standalone WAD file lump inside PK3s. -lumpnum_t W_CheckNumForMap(const char *name) +lumpnum_t W_CheckNumForMap(const char *name, boolean checktofirst) { lumpnum_t check = INT16_MAX; UINT32 uhash, hash = quickncasehash(name, LUMPNUMCACHENAME); INT32 i; - UINT16 firstfile = (partadd_earliestfile == UINT16_MAX) ? 0 : partadd_earliestfile; + UINT16 firstfile = (checktofirst || (partadd_earliestfile == UINT16_MAX)) ? 0 : partadd_earliestfile; // Check the lumpnumcache first. Loop backwards so that we check // most recent entries first diff --git a/src/w_wad.h b/src/w_wad.h index 24c0ac74b..31ca26e69 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -171,7 +171,7 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump); -lumpnum_t W_CheckNumForMap(const char *name); +lumpnum_t W_CheckNumForMap(const char *name, boolean checktofirst); lumpnum_t W_CheckNumForName(const char *name); lumpnum_t W_CheckNumForLongName(const char *name); lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR From 151dccf32fa61e2ee7f9217c16e95431d1a65891 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Jan 2023 15:39:42 -0800 Subject: [PATCH 63/76] Parity between title screen and menu version info --- src/f_finale.c | 71 ++++++++++++++++++++++++++---------------------- src/f_finale.h | 2 ++ src/k_menudraw.c | 7 +---- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index e20deae1f..bbac13838 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1889,6 +1889,43 @@ void F_StartTitleScreen(void) F_CacheTitleScreen(); } +void F_VersionDrawer(void) +{ + // An adapted thing from old menus - most games have version info on the title screen now... + INT32 texty = vid.height - 10*vid.dupy; + +#define addtext(f, str) {\ + V_DrawThinString(vid.dupx, texty, V_NOSCALESTART|f, str);\ + texty -= 10*vid.dupy;\ +} + if (customversionstring[0] != '\0') + { + addtext(V_ALLOWLOWERCASE, customversionstring); + addtext(0, "Mod version:"); + } + else + { +// Development -- show revision / branch info +#if defined(TESTERS) + addtext(V_ALLOWLOWERCASE|V_SKYMAP, "Tester client"); + addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate)); +#elif defined(HOSTTESTERS) + addtext(V_ALLOWLOWERCASE|V_REDMAP, "Netgame host for testers"); + addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate)); +#elif defined(DEVELOP) + addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, comprevision); + addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, compbranch); +#else // Regular build + addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING)); +#endif + if (compuncommitted) + { + addtext(V_REDMAP|V_STRINGDANCE, "! UNCOMMITTED CHANGES !"); + } + } +#undef addtext +} + // (no longer) De-Demo'd Title Screen void F_TitleScreenDrawer(void) { @@ -1959,39 +1996,7 @@ void F_TitleScreenDrawer(void) V_DrawFixedPatch(0, 0, FRACUNIT, 0, kts_copyright, NULL); - // An adapted thing from old menus - most games have version info on the title screen now... - { - INT32 texty = vid.height - 10*vid.dupy; -#define addtext(f, str) {\ - V_DrawThinString(vid.dupx, texty, V_NOSCALESTART|f, str);\ - texty -= 10*vid.dupy;\ -} - if (customversionstring[0] != '\0') - { - addtext(V_ALLOWLOWERCASE, customversionstring); - addtext(0, "Mod version:"); - } - else - { -// Development -- show revision / branch info -#if defined(TESTERS) - addtext(V_ALLOWLOWERCASE|V_SKYMAP, "Tester client"); - addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate)); -#elif defined(HOSTTESTERS) - addtext(V_ALLOWLOWERCASE|V_REDMAP, "Netgame host for testers"); - addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate)); -#elif defined(DEVELOP) - addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, comprevision); - addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, compbranch); -#else // Regular build - addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING)); -#endif - if (compuncommitted) - addtext(V_REDMAP|V_STRINGDANCE, "! UNCOMMITTED CHANGES !"); - } -#undef addtext - } - + F_VersionDrawer(); break; } diff --git a/src/f_finale.h b/src/f_finale.h index a42228625..ca1108214 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -60,6 +60,8 @@ void F_EndingDrawer(void); void F_CreditTicker(void); void F_CreditDrawer(void); +void F_VersionDrawer(void); + void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean resetplayer); void F_CutsceneDrawer(void); void F_EndCutScene(void); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9071907bc..7c20a0f04 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -541,12 +541,7 @@ void M_Drawer(void) } else { -#ifdef DEVELOP // Development -- show revision / branch info - V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, compbranch); - V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, comprevision); -#else // Regular build - V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING)); -#endif + F_VersionDrawer(); } From 718eb8b11f848605c16ca67bf2cb8000c3c29b67 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 2 Jan 2023 00:54:24 +0000 Subject: [PATCH 64/76] Permit losing a life when exited if the player has PF_NOCONTEST --- src/k_grandprix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index d26787100..6d879054b 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -675,7 +675,7 @@ void K_PlayerLoseLife(player_t *player) return; } - if (player->spectator || player->exiting || player->bot || player->lives <= 0 || (player->pflags & PF_LOSTLIFE)) + if (player->spectator || (player->exiting && !(player->pflags & PF_NOCONTEST)) || player->bot || player->lives <= 0 || (player->pflags & PF_LOSTLIFE)) { return; } From 2e4b5bd061657642cee3678ad8c0a4fa0ef4c9a2 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 29 Nov 2022 19:23:05 -0800 Subject: [PATCH 65/76] Clean up P_FuseThink flicker condition --- src/p_mobj.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 26b38d631..a1544b802 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9528,13 +9528,38 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s P_RemoveMobj(mobj); // make sure they disappear } +static boolean P_CanFlickerFuse(mobj_t *mobj) +{ + switch (mobj->type) + { + case MT_SNAPPER_HEAD: + case MT_SNAPPER_LEG: + case MT_MINECARTSEG: + return true; + + case MT_RANDOMITEM: + case MT_EGGMANITEM: + case MT_FALLINGROCK: + if (mobj->fuse <= TICRATE) + { + return true; + } + break; + + default: + break; + } + + return false; + +} + static boolean P_FuseThink(mobj_t *mobj) { - if (mobj->type == MT_SNAPPER_HEAD || mobj->type == MT_SNAPPER_LEG || mobj->type == MT_MINECARTSEG) - mobj->renderflags ^= RF_DONTDRAW; - - if (mobj->fuse <= TICRATE && (mobj->type == MT_RANDOMITEM || mobj->type == MT_EGGMANITEM || mobj->type == MT_FALLINGROCK)) + if (P_CanFlickerFuse(mobj)) + { mobj->renderflags ^= RF_DONTDRAW; + } mobj->fuse--; From 43aa162fcba052fe491e4f07514d0358d531cdff Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 10 Dec 2022 01:12:36 -0800 Subject: [PATCH 66/76] Add K_GetChaosEmeraldColor --- src/k_battle.c | 37 ++++++++++++++++++++++++------------- src/k_battle.h | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 15b81f81c..7079bf3a4 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -221,6 +221,29 @@ void K_CheckEmeralds(player_t *player) K_CheckBumpers(); } +UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType) +{ + switch (emeraldType) + { + case EMERALD_CHAOS1: + return SKINCOLOR_CHAOSEMERALD1; + case EMERALD_CHAOS2: + return SKINCOLOR_CHAOSEMERALD2; + case EMERALD_CHAOS3: + return SKINCOLOR_CHAOSEMERALD3; + case EMERALD_CHAOS4: + return SKINCOLOR_CHAOSEMERALD4; + case EMERALD_CHAOS5: + return SKINCOLOR_CHAOSEMERALD5; + case EMERALD_CHAOS6: + return SKINCOLOR_CHAOSEMERALD6; + case EMERALD_CHAOS7: + return SKINCOLOR_CHAOSEMERALD7; + default: + return SKINCOLOR_NONE; + } +} + mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType) { boolean validEmerald = true; @@ -240,25 +263,13 @@ mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT switch (emeraldType) { case EMERALD_CHAOS1: - emerald->color = SKINCOLOR_CHAOSEMERALD1; - break; case EMERALD_CHAOS2: - emerald->color = SKINCOLOR_CHAOSEMERALD2; - break; case EMERALD_CHAOS3: - emerald->color = SKINCOLOR_CHAOSEMERALD3; - break; case EMERALD_CHAOS4: - emerald->color = SKINCOLOR_CHAOSEMERALD4; - break; case EMERALD_CHAOS5: - emerald->color = SKINCOLOR_CHAOSEMERALD5; - break; case EMERALD_CHAOS6: - emerald->color = SKINCOLOR_CHAOSEMERALD6; - break; case EMERALD_CHAOS7: - emerald->color = SKINCOLOR_CHAOSEMERALD7; + emerald->color = K_GetChaosEmeraldColor(emeraldType); break; default: CONS_Printf("Invalid emerald type %d\n", emeraldType); diff --git a/src/k_battle.h b/src/k_battle.h index 42d92cf33..12ad78004 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -25,6 +25,7 @@ boolean K_IsPlayerWanted(player_t *player); void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount); void K_CheckBumpers(void); void K_CheckEmeralds(player_t *player); +UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType); mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType); mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount); void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType); From a94e18c277f324f8e6b2199d58ffa3e93927069e Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Dec 2022 22:32:57 -0800 Subject: [PATCH 67/76] Add K_UpdateMobjItemOverlay --- src/k_kart.c | 23 +++++++++++++++++++++++ src/k_kart.h | 2 ++ src/p_mobj.c | 20 +------------------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f1c0b8be2..97f3361df 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11164,4 +11164,27 @@ void K_HandleDirectionalInfluence(player_t *player) player->mo->momy = FixedMul(speed, finalY); } +void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount) +{ + switch (itemType) + { + case KITEM_ORBINAUT: + part->sprite = SPR_ITMO; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(itemCount); + break; + case KITEM_INVINCIBILITY: + part->sprite = SPR_ITMI; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); + break; + case KITEM_SAD: + part->sprite = SPR_ITEM; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; + break; + default: + part->sprite = SPR_ITEM; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType); + break; + } +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index f74f14e12..b297b2a57 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -197,6 +197,8 @@ fixed_t K_ItemScaleForPlayer(player_t *player); void K_SetItemOut(player_t *player); void K_UnsetItemOut(player_t *player); +void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_mobj.c b/src/p_mobj.c index a1544b802..8bdd50fd4 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4346,25 +4346,7 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj) part->threshold = mobj->threshold; part->movecount = mobj->movecount; - switch (itemType) - { - case KITEM_ORBINAUT: - part->sprite = SPR_ITMO; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount); - break; - case KITEM_INVINCIBILITY: - part->sprite = SPR_ITMI; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); - break; - case KITEM_SAD: - part->sprite = SPR_ITEM; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; - break; - default: - part->sprite = SPR_ITEM; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType); - break; - } + K_UpdateMobjItemOverlay(part, itemType, mobj->movecount); // update number frame if (K_GetShieldFromItem(itemType) != KSHIELD_NONE) // shields don't stack, so don't show a number From 6eb8da1a567e579d5984f486047cf59faf5bd6d2 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Dec 2022 22:36:26 -0800 Subject: [PATCH 68/76] Add K_GetTotallyRandomResult, for battle item spawners --- src/k_kart.c | 100 +++++++++++++++++++++++++-------------------------- src/k_kart.h | 1 + 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 97f3361df..bb33f7493 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6183,17 +6183,48 @@ void K_DropHnextList(player_t *player, boolean keepshields) } } +SINT8 K_GetTotallyRandomResult(UINT8 useodds) +{ + itemroulette_t rouletteData = {0}; + INT32 spawnchance[NUMKARTRESULTS]; + INT32 totalspawnchance = 0; + INT32 i; + + memset(spawnchance, 0, sizeof (spawnchance)); + + K_FillItemRouletteData(NULL, &rouletteData); + + for (i = 1; i < NUMKARTRESULTS; i++) + { + spawnchance[i] = ( + totalspawnchance += K_KartGetItemOdds(NULL, &rouletteData, useodds, i) + ); + } + + if (totalspawnchance > 0) + { + totalspawnchance = P_RandomKey(PR_ITEM_ROULETTE, totalspawnchance); + for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); + } + else + { + i = KITEM_SAD; + } + + return i; +} + mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount) { mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM); mobj_t *backdrop = P_SpawnMobjFromMobj(drop, 0, 0, 0, MT_OVERLAY); - + P_SetTarget(&backdrop->target, drop); P_SetMobjState(backdrop, S_ITEMBACKDROP); P_SetScale(drop, drop->scale>>4); drop->destscale = (3*drop->destscale)/2; - + drop->angle = angle; P_Thrust(drop, FixedAngle(P_RandomFixed(PR_ITEM_ROULETTE) * 180) + angle, @@ -6205,62 +6236,31 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 if (type == 0) { - itemroulette_t rouletteData = {0}; - UINT8 useodds = 0; - INT32 spawnchance[NUMKARTRESULTS]; - INT32 totalspawnchance = 0; - INT32 i; + const SINT8 i = K_GetTotallyRandomResult(amount); - memset(spawnchance, 0, sizeof (spawnchance)); + // TODO: this is bad! + // K_KartGetItemResult requires a player + // but item roulette will need rewritten to change this - useodds = amount; + const SINT8 newType = K_ItemResultToType(i); + const UINT8 newAmount = K_ItemResultToAmount(i); - K_FillItemRouletteData(NULL, &rouletteData); - - for (i = 1; i < NUMKARTRESULTS; i++) + if (newAmount > 1) { - spawnchance[i] = ( - totalspawnchance += K_KartGetItemOdds(NULL, &rouletteData, useodds, i) - ); - } + UINT8 j; - if (totalspawnchance > 0) - { - UINT8 newType; - UINT8 newAmount; - - totalspawnchance = P_RandomKey(PR_ITEM_ROULETTE, totalspawnchance); - for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); - - // TODO: this is bad! - // K_KartGetItemResult requires a player - // but item roulette will need rewritten to change this - - newType = K_ItemResultToType(i); - newAmount = K_ItemResultToAmount(i); - - if (newAmount > 1) + for (j = 0; j < newAmount-1; j++) { - UINT8 j; - - for (j = 0; j < newAmount-1; j++) - { - K_CreatePaperItem( - x, y, z, - angle, flip, - newType, 1 - ); - } + K_CreatePaperItem( + x, y, z, + angle, flip, + newType, 1 + ); } + } - drop->threshold = newType; - drop->movecount = 1; - } - else - { - drop->threshold = 1; - drop->movecount = 1; - } + drop->threshold = newType; + drop->movecount = 1; } else { diff --git a/src/k_kart.h b/src/k_kart.h index b297b2a57..76043919b 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -138,6 +138,7 @@ INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage); void K_SpawnDriftBoostExplosion(player_t *player, int stage); void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave); void K_KartUpdatePosition(player_t *player); +SINT8 K_GetTotallyRandomResult(UINT8 useodds); mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount); void K_DropItems(player_t *player); void K_DropRocketSneaker(player_t *player); From 3965f7776300bde638cf99b519e285f5f75e9836 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Dec 2022 22:40:29 -0800 Subject: [PATCH 69/76] Add BATTLE_SPAWN_INTERVAL --- src/k_battle.c | 2 +- src/k_battle.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_battle.c b/src/k_battle.c index 7079bf3a4..6612b9548 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -350,7 +350,7 @@ UINT8 K_NumEmeralds(player_t *player) void K_RunPaperItemSpawners(void) { const boolean overtime = (battleovertime.enabled >= 10*TICRATE); - tic_t interval = 8*TICRATE; + tic_t interval = BATTLE_SPAWN_INTERVAL; const boolean canmakeemeralds = true; //(!(battlecapsules || bossinfo.boss)); diff --git a/src/k_battle.h b/src/k_battle.h index 12ad78004..d42d0ee09 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -8,6 +8,8 @@ extern "C" { #endif +#define BATTLE_SPAWN_INTERVAL (8*TICRATE) + extern struct battleovertime { UINT16 enabled; ///< Has this been initalized yet? From 71004d4b46d5596b91c372495d77da438be8c637 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Dec 2022 22:55:46 -0800 Subject: [PATCH 70/76] Halve spawn time for Battle items --- src/k_battle.c | 17 ++++++++--------- src/k_battle.h | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 6612b9548..d0bc69c97 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -347,10 +347,15 @@ UINT8 K_NumEmeralds(player_t *player) return num; } +static inline boolean IsOnInterval(tic_t interval) +{ + return ((leveltime - starttime) % interval) == 0; +} + void K_RunPaperItemSpawners(void) { const boolean overtime = (battleovertime.enabled >= 10*TICRATE); - tic_t interval = BATTLE_SPAWN_INTERVAL; + const tic_t interval = BATTLE_SPAWN_INTERVAL; const boolean canmakeemeralds = true; //(!(battlecapsules || bossinfo.boss)); @@ -375,13 +380,7 @@ void K_RunPaperItemSpawners(void) return; } - if (overtime == true) - { - // Double frequency of items - interval /= 2; - } - - if (((leveltime - starttime) % interval) != 0) + if (!IsOnInterval(interval)) { return; } @@ -557,7 +556,7 @@ void K_RunPaperItemSpawners(void) } else { - if (gametyperules & GTR_SPHERES) + if ((gametyperules & GTR_SPHERES) && IsOnInterval(2 * interval)) { drop = K_SpawnSphereBox( spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), diff --git a/src/k_battle.h b/src/k_battle.h index d42d0ee09..68f3c1432 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -8,7 +8,7 @@ extern "C" { #endif -#define BATTLE_SPAWN_INTERVAL (8*TICRATE) +#define BATTLE_SPAWN_INTERVAL (4*TICRATE) extern struct battleovertime { From 8a92f03f7b969bef684acbff25ff37a99274c50e Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Dec 2022 22:57:01 -0800 Subject: [PATCH 71/76] Set fuse for Battle items --- src/k_battle.h | 1 + src/k_kart.c | 5 +++++ src/p_mobj.c | 1 + 3 files changed, 7 insertions(+) diff --git a/src/k_battle.h b/src/k_battle.h index 68f3c1432..f64cfa967 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -9,6 +9,7 @@ extern "C" { #endif #define BATTLE_SPAWN_INTERVAL (4*TICRATE) +#define BATTLE_DESPAWN_TIME (15*TICRATE) extern struct battleovertime { diff --git a/src/k_kart.c b/src/k_kart.c index bb33f7493..660878d4c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6273,6 +6273,11 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 P_SetTarget(&backdrop->tracer, drop); backdrop->flags2 |= MF2_LINKDRAW; + if (gametyperules & GTR_BUMPERS) + { + drop->fuse = BATTLE_DESPAWN_TIME; + } + return drop; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 8bdd50fd4..f965d32c8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9522,6 +9522,7 @@ static boolean P_CanFlickerFuse(mobj_t *mobj) case MT_RANDOMITEM: case MT_EGGMANITEM: case MT_FALLINGROCK: + case MT_FLOATINGITEM: if (mobj->fuse <= TICRATE) { return true; From 5887c361118763eb84c0f443d1ea93a7acf09c7a Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Jan 2023 15:55:47 -0800 Subject: [PATCH 72/76] Show CMAKE_BUILD_TYPE on title screen Includes a hint if optimizations may have been turned off. --- cmake/Comptime.cmake | 16 ++++++++++++++++ src/CMakeLists.txt | 2 +- src/comptime.c | 2 ++ src/config.h.in | 3 +++ src/d_netcmd.c | 7 ++----- src/doomdef.h | 3 ++- src/f_finale.c | 9 +++++++++ 7 files changed, 35 insertions(+), 7 deletions(-) diff --git a/cmake/Comptime.cmake b/cmake/Comptime.cmake index 3859c2aad..ddd79536e 100644 --- a/cmake/Comptime.cmake +++ b/cmake/Comptime.cmake @@ -1,3 +1,5 @@ +cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + set(CMAKE_BINARY_DIR "${BINARY_DIR}") set(CMAKE_CURRENT_BINARY_DIR "${BINARY_DIR}") @@ -10,4 +12,18 @@ git_current_branch(SRB2_COMP_BRANCH) git_summary(SRB2_COMP_REVISION) git_working_tree_dirty(SRB2_COMP_UNCOMMITTED) +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE None) +endif() + +# These build types enable optimizations of some kind by default. +set(optimized_build_types "MINSIZEREL;RELEASE;RELWITHDEBINFO") + +string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type) +if("${build_type}" IN_LIST optimized_build_types) + set(SRB2_COMP_OPTIMIZED TRUE) +else() + set(SRB2_COMP_OPTIMIZED FALSE) +endif() + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/src/config.h") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 148d12ff4..7293393cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -154,7 +154,7 @@ add_custom_command( # build time for accurate git information and before anything # that needs it, obviously. add_custom_target(_SRB2_reconf ALL - COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/.. -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Comptime.cmake + COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/.. -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Comptime.cmake WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.." ) add_dependencies(SRB2SDL2 _SRB2_reconf) diff --git a/src/comptime.c b/src/comptime.c index 2baef79d6..74b810062 100644 --- a/src/comptime.c +++ b/src/comptime.c @@ -11,6 +11,8 @@ #include "config.h" const char *compbranch = SRB2_COMP_BRANCH; const char *comprevision = SRB2_COMP_REVISION; +const char *comptype = CMAKE_BUILD_TYPE; +const int compoptimized = SRB2_COMP_OPTIMIZED; #elif (defined(COMPVERSION)) #include "comptime.h" diff --git a/src/config.h.in b/src/config.h.in index d7b67cdce..77a205a74 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -20,6 +20,9 @@ #define COMPVERSION_UNCOMMITTED #endif +#define CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" +#cmakedefine01 SRB2_COMP_OPTIMIZED + #endif /* Manually defined asset hashes for non-CMake builds diff --git a/src/d_netcmd.c b/src/d_netcmd.c index df3b86bcf..5d3e00830 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4774,16 +4774,13 @@ static void Command_Version_f(void) else // 16-bit? 128-bit? CONS_Printf("Bits Unknown "); + CONS_Printf("%s ", comptype); + // No ASM? #ifdef NOASM CONS_Printf("\x85" "NOASM " "\x80"); #endif - // Debug build -#ifdef _DEBUG - CONS_Printf("\x85" "DEBUG " "\x80"); -#endif - // DEVELOP build #if defined(TESTERS) CONS_Printf("\x88" "TESTERS " "\x80"); diff --git a/src/doomdef.h b/src/doomdef.h index 09a5efa4a..72ba17a84 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -664,7 +664,8 @@ UINT32 quickncasehash (const char *p, size_t n) // Compile date and time and revision. extern const char *compdate, *comptime, *comprevision, *compbranch; -extern int compuncommitted; +extern int compuncommitted, compoptimized; +extern const char *comptype; // Disabled code and code under testing // None of these that are disabled in the normal build are guaranteed to work perfectly diff --git a/src/f_finale.c b/src/f_finale.c index bbac13838..36ca452f9 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1918,6 +1918,15 @@ void F_VersionDrawer(void) #else // Regular build addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING)); #endif + if (compoptimized) + { + addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s build", comptype)); + } + else + { + addtext(V_ALLOWLOWERCASE|V_ORANGEMAP, va("%s build (no optimizations)", comptype)); + } + if (compuncommitted) { addtext(V_REDMAP|V_STRINGDANCE, "! UNCOMMITTED CHANGES !"); From 4b9797fe2631e2e1f04cb92f14f2d6bee9f705a7 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 2 Jan 2023 13:08:11 +0000 Subject: [PATCH 73/76] Correct for recursive iteration of multiple mines in succession clobbering filescope `minehitlag` --- src/k_collide.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_collide.c b/src/k_collide.c index 06d6900e6..e9e832b3d 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -324,6 +324,11 @@ tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin) if (!spin) { + if (minehitlag == 0) + { + minehitlag = actor->hitlag; + } + Obj_SpawnBrolyKi(actor, minehitlag); return minehitlag; From 26f3490ba85011164a89531a8c4e4ac5622ebb0a Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 2 Jan 2023 13:32:12 +0000 Subject: [PATCH 74/76] Increased memory safety with cupgrid memory allocation Also clearer variable name for length of cupgrid page in memory --- src/k_menufunc.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 73fab9aaa..191542ca7 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3568,6 +3568,12 @@ static boolean M_LevelListFromGametype(INT16 gt) if (first || gt != levellist.newgametype || levellist.guessgt != MAXGAMETYPES) { + if (first) + { + cupgrid.cappages = 0; + cupgrid.builtgrid = NULL; + } + levellist.newgametype = gt; levellist.levelsearch.typeoflevel = G_TOLFlag(gt); @@ -3595,7 +3601,7 @@ static boolean M_LevelListFromGametype(INT16 gt) { levelsearch_t templevelsearch = levellist.levelsearch; // full copy size_t currentid = 0, highestunlockedid = 0; - const size_t unitlen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS); + const size_t pagelen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS); boolean foundany = false; templevelsearch.cup = kartcupheaders; @@ -3606,20 +3612,20 @@ static boolean M_LevelListFromGametype(INT16 gt) I_Error("Can you really call this a racing game, I didn't recieve any Cups on my pillow or anything"); #endif - if (!cupgrid.builtgrid) + if (cupgrid.cappages == 0) { cupgrid.cappages = 2; cupgrid.builtgrid = Z_Calloc( - cupgrid.cappages * unitlen, + cupgrid.cappages * pagelen, PU_STATIC, - cupgrid.builtgrid); + NULL); if (!cupgrid.builtgrid) { I_Error("M_LevelListFromGametype: Not enough memory to allocate builtgrid"); } } - memset(cupgrid.builtgrid, 0, cupgrid.cappages * unitlen); + memset(cupgrid.builtgrid, 0, cupgrid.cappages * pagelen); while (templevelsearch.cup) { @@ -3633,10 +3639,10 @@ static boolean M_LevelListFromGametype(INT16 gt) foundany = true; - if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * unitlen) + if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * pagelen) { // Double the size of the buffer, and clear the other stuff. - const size_t firstlen = cupgrid.cappages * unitlen; + const size_t firstlen = cupgrid.cappages * pagelen; cupgrid.builtgrid = Z_Realloc(cupgrid.builtgrid, firstlen * 2, PU_STATIC, NULL); From e73e0bd835f08c752e8114da08eb5391c55388a9 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 2 Jan 2023 20:20:47 +0000 Subject: [PATCH 75/76] Don't treat players with 0 lives that are exiting (definitely just completed a race) as irrelevant in P_CheckRacers counts Fixes special stages not booting you back to the title screen after running out of lives on them --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index 9caf37531..9887b8bb7 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -861,7 +861,7 @@ boolean P_CheckRacers(void) // Check if all the players in the race have finished. If so, end the level. for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) + if (!playeringame[i] || players[i].spectator || (players[i].lives <= 0 && !players[i].exiting)) { // Y'all aren't even playing continue; From 5bdbbd1695e55b231ab2f080ff9cc4b927d05ebf Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 2 Jan 2023 20:04:01 -0800 Subject: [PATCH 76/76] Comptime.cmake: escape \backslashes and "quotes" in git subject line --- cmake/Comptime.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/Comptime.cmake b/cmake/Comptime.cmake index ddd79536e..f50faf933 100644 --- a/cmake/Comptime.cmake +++ b/cmake/Comptime.cmake @@ -9,9 +9,11 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Mo include(GitUtilities) git_current_branch(SRB2_COMP_BRANCH) -git_summary(SRB2_COMP_REVISION) git_working_tree_dirty(SRB2_COMP_UNCOMMITTED) +git_summary(revision) +string(REGEX REPLACE "([\"\\])" "\\\\\\1" SRB2_COMP_REVISION "${revision}") + if("${CMAKE_BUILD_TYPE}" STREQUAL "") set(CMAKE_BUILD_TYPE None) endif()