From 185b36bd27de557116deb3af5e9cb3499d911b30 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Dec 2022 01:07:57 +0000 Subject: [PATCH] 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];