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)