From f38f568b2d6af47b9e131529065d6aba07488aec Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Sat, 5 Aug 2023 02:40:49 -0300 Subject: [PATCH] Make gametype data available to Lua --- src/d_main.c | 2 + src/deh_soc.c | 4 +- src/doomstat.h | 7 ++- src/g_game.c | 58 ++++++++++++----- src/g_game.h | 1 + src/lua_baselib.c | 5 +- src/lua_infolib.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++ src/lua_libs.h | 1 + src/lua_script.c | 12 ++++ 9 files changed, 223 insertions(+), 24 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 22a676255..77070012e 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1374,6 +1374,8 @@ void D_SRB2Main(void) CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n"); Z_Init(); + G_InitGametypes(); + clientGamedata = M_NewGameDataStruct(); serverGamedata = M_NewGameDataStruct(); diff --git a/src/deh_soc.c b/src/deh_soc.c index df44b3caa..76a5759a6 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1324,12 +1324,12 @@ void readgametype(MYFILE *f, char *gtname) gametypes[newgtidx].timelimit = newgttimelimit; // Write the new gametype name. - gametypes[newgtidx].name = Z_StrDup((const char *)gtname); + gametypes[newgtidx].name = Z_StrDup(gtname); // Write the constant name. if (gtconst[0] == '\0') strncpy(gtconst, gtname, MAXLINELEN); - G_AddGametypeConstant(newgtidx, (const char *)gtconst); + G_AddGametypeConstant(newgtidx, gtconst); // Update gametype_cons_t accordingly. G_UpdateGametypeSelections(); diff --git a/src/doomstat.h b/src/doomstat.h index 30f29799e..4fdcefde5 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -388,8 +388,9 @@ typedef struct extern mapheader_t* mapheaderinfo[NUMMAPS]; -// Gametypes #define NUMGAMETYPEFREESLOTS 128 + +// Gametypes enum GameType { GT_COOP = 0, // also used in single player @@ -456,8 +457,8 @@ enum typedef struct { - const char *name; - const char *constant_name; + char *name; + char *constant_name; UINT32 rules; UINT32 typeoflevel; UINT8 intermission_type; diff --git a/src/g_game.c b/src/g_game.c index bf2d5ae1b..713aca263 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3404,11 +3404,40 @@ void G_ExitLevel(void) } } +// See also the enum GameType in doomstat.h +static const char *Gametype_Names[NUMGAMETYPES] = +{ + "Co-op", // GT_COOP + "Competition", // GT_COMPETITION + "Race", // GT_RACE + + "Match", // GT_MATCH + "Team Match", // GT_TEAMMATCH + + "Tag", // GT_TAG + "Hide & Seek", // GT_HIDEANDSEEK + + "CTF" // GT_CTF +}; + +static const char *Gametype_ConstantNames[NUMGAMETYPES] = +{ + "GT_COOP", // GT_COOP + "GT_COMPETITION", // GT_COMPETITION + "GT_RACE", // GT_RACE + + "GT_MATCH", // GT_MATCH + "GT_TEAMMATCH", // GT_TEAMMATCH + + "GT_TAG", // GT_TAG + "GT_HIDEANDSEEK", // GT_HIDEANDSEEK + + "GT_CTF" // GT_CTF +}; + gametype_t gametypes[NUMGAMETYPES] = { // GT_COOP { - .name = "Co-op", - .constant_name = "GT_COOP", .rules = GTR_CAMPAIGN|GTR_LIVES|GTR_FRIENDLY|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES|GTR_CUTSCENES, .typeoflevel = TOL_COOP, .intermission_type = int_coop, @@ -3416,8 +3445,6 @@ gametype_t gametypes[NUMGAMETYPES] = { }, // GT_COMPETITION { - .name = "Competition", - .constant_name = "GT_COMPETITION", .rules = GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT, .typeoflevel = TOL_COMPETITION, .intermission_type = int_comp, @@ -3425,8 +3452,6 @@ gametype_t gametypes[NUMGAMETYPES] = { }, // GT_RACE { - .name = "Race", - .constant_name = "GT_RACE", .rules = GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT, .typeoflevel = TOL_RACE, .intermission_type = int_race, @@ -3434,8 +3459,6 @@ gametype_t gametypes[NUMGAMETYPES] = { }, // GT_MATCH { - .name = "Match", - .constant_name = "GT_MATCH", .rules = GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY, .typeoflevel = TOL_MATCH, .intermission_type = int_match, @@ -3446,8 +3469,6 @@ gametype_t gametypes[NUMGAMETYPES] = { }, // GT_TEAMMATCH { - .name = "Team Match", - .constant_name = "GT_TEAMMATCH", .rules = GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, .typeoflevel = TOL_MATCH, .intermission_type = int_teammatch, @@ -3458,8 +3479,6 @@ gametype_t gametypes[NUMGAMETYPES] = { }, // GT_TAG { - .name = "Tag", - .constant_name = "GT_TAG", .rules = GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, .typeoflevel = TOL_TAG, .intermission_type = int_match, @@ -3471,8 +3490,6 @@ gametype_t gametypes[NUMGAMETYPES] = { }, // GT_HIDEANDSEEK { - .name = "Hide & Seek", - .constant_name = "GT_HIDEANDSEEK", .rules = GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_HIDEFROZEN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, .typeoflevel = TOL_TAG, .intermission_type = int_match, @@ -3484,8 +3501,6 @@ gametype_t gametypes[NUMGAMETYPES] = { }, // GT_CTF { - .name = "CTF", - .constant_name = "GT_CTF", .rules = GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, .typeoflevel = TOL_CTF, .intermission_type = int_ctf, @@ -3496,6 +3511,15 @@ gametype_t gametypes[NUMGAMETYPES] = { }, }; +void G_InitGametypes(void) +{ + for (unsigned i = 0; i <= GT_CTF; i++) + { + gametypes[i].name = Z_StrDup(Gametype_Names[i]); + gametypes[i].constant_name = Z_StrDup(Gametype_ConstantNames[i]); + } +} + // // Sets a new gametype, also setting gametype rules accordingly. // @@ -3513,7 +3537,7 @@ INT16 G_AddGametype(UINT32 rules) INT16 newgtype = gametypecount; gametypecount++; - gametypes[newgtype].name = "???"; + gametypes[newgtype].name = Z_StrDup("???"); gametypes[newgtype].rules = rules; // Update gametype_cons_t accordingly. diff --git a/src/g_game.h b/src/g_game.h index 8bcbbfdad..510b27b62 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -191,6 +191,7 @@ void G_SaveGame(UINT32 slot, INT16 mapnum); void G_SaveGameOver(UINT32 slot, boolean modifylives); void G_SetGametype(INT16 gametype); +void G_InitGametypes(void); INT16 G_AddGametype(UINT32 rules); void G_AddGametypeConstant(INT16 gtype, const char *newgtconst); void G_UpdateGametypeSelections(void); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index a6f8cf9a0..cac518084 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -155,6 +155,7 @@ static const struct { {META_SFXINFO, "sfxinfo_t"}, {META_SKINCOLOR, "skincolor_t"}, {META_COLORRAMP, "skincolor_t.ramp"}, + {META_GAMETYPE, "gametype_t"}, {META_SPRITEINFO, "spriteinfo_t"}, {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, @@ -3407,8 +3408,8 @@ static int lib_gAddGametype(lua_State *L) const char *k; lua_Integer i; - const char *gtname = NULL; - const char *gtconst = NULL; + char *gtname = NULL; + char *gtconst = NULL; const char *gtdescription = NULL; INT16 newgtidx = 0; UINT32 newgtrules = 0; diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 3764acf6a..c475aefae 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1899,6 +1899,140 @@ static int colorramp_len(lua_State *L) return 1; } +/////////////// +// GAMETYPES // +/////////////// + +static int lib_getGametypes(lua_State *L) +{ + INT16 i; + lua_remove(L, 1); + + i = luaL_checkinteger(L, 1); + if (i < 0 || i >= gametypecount) + return luaL_error(L, "gametypes[] index %d out of range (0 - %d)", i, gametypecount-1); + LUA_PushUserdata(L, &gametypes[i], META_GAMETYPE); + return 1; +} + +// #gametypes -> gametypecount +static int lib_gametypeslen(lua_State *L) +{ + lua_pushinteger(L, gametypecount); + return 1; +} + +enum gametype_e +{ + gametype_name, + gametype_rules, + gametype_typeoflevel, + gametype_intermission_type, + gametype_rankings_type, + gametype_pointlimit, + gametype_timelimit +}; + +const char *const gametype_opt[] = { + "name", + "rules", + "type_of_level", + "intermission_type", + "rankings_type", + "point_limit", + "time_limit", + NULL, +}; + +static int gametype_fields_ref = LUA_NOREF; + +static int gametype_get(lua_State *L) +{ + gametype_t *gt = *((gametype_t **)luaL_checkudata(L, 1, META_GAMETYPE)); + enum gametype_e field = Lua_optoption(L, 2, gametype_name, gametype_fields_ref); + + I_Assert(gt != NULL); + I_Assert(gt >= gametypes); + + switch (field) + { + case gametype_name: + lua_pushstring(L, gt->name); + break; + case gametype_rules: + lua_pushinteger(L, gt->rules); + break; + case gametype_typeoflevel: + lua_pushinteger(L, gt->typeoflevel); + break; + case gametype_intermission_type: + lua_pushinteger(L, gt->intermission_type); + break; + case gametype_rankings_type: + lua_pushinteger(L, gt->rankings_type); + break; + case gametype_pointlimit: + lua_pushinteger(L, gt->pointlimit); + break; + case gametype_timelimit: + lua_pushinteger(L, gt->timelimit); + break; + } + return 1; +} + +static int gametype_set(lua_State *L) +{ + gametype_t *gt = *((gametype_t **)luaL_checkudata(L, 1, META_GAMETYPE)); + enum gametype_e field = Lua_optoption(L, 2, -1, gametype_fields_ref); + + if (hud_running) + return luaL_error(L, "Do not alter gametype data in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter gametype data in CMD building code!"); + + I_Assert(gt != NULL); + I_Assert(gt >= gametypes); + + switch (field) + { + case gametype_name: + Z_Free(gt->name); + gt->name = Z_StrDup(luaL_checkstring(L, 3)); + break; + case gametype_rules: + gt->rules = luaL_checkinteger(L, 3); + break; + case gametype_typeoflevel: + gt->typeoflevel = luaL_checkinteger(L, 3); + break; + case gametype_intermission_type: + gt->intermission_type = luaL_checkinteger(L, 3); + break; + case gametype_rankings_type: + gt->rankings_type = luaL_checkinteger(L, 3); + break; + case gametype_pointlimit: + gt->pointlimit = luaL_checkinteger(L, 3); + break; + case gametype_timelimit: + gt->timelimit = luaL_checkinteger(L, 3); + break; + } + return 0; +} + +static int gametype_num(lua_State *L) +{ + gametype_t *gt = *((gametype_t **)luaL_checkudata(L, 1, META_GAMETYPE)); + + I_Assert(gt != NULL); + I_Assert(gt >= gametypes); + + lua_pushinteger(L, gt-gametypes); + return 1; +} + ////////////////////////////// // // Now push all these functions into the Lua state! @@ -1938,6 +2072,19 @@ int LUA_InfoLib(lua_State *L) mobjinfo_fields_ref = Lua_CreateFieldTable(L, mobjinfo_opt); + luaL_newmetatable(L, META_GAMETYPE); + lua_pushcfunction(L, gametype_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, gametype_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, gametype_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + gametype_fields_ref = Lua_CreateFieldTable(L, gametype_opt); + luaL_newmetatable(L, META_SKINCOLOR); lua_pushcfunction(L, skincolor_get); lua_setfield(L, -2, "__index"); @@ -2104,6 +2251,16 @@ int LUA_InfoLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "spriteinfo"); + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getGametypes); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_gametypeslen); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "gametypes"); + luaL_newmetatable(L, META_LUABANKS); lua_pushcfunction(L, lib_getluabanks); lua_setfield(L, -2, "__index"); diff --git a/src/lua_libs.h b/src/lua_libs.h index 7f8d21f38..df1898597 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -28,6 +28,7 @@ extern boolean mousegrabbedbylua; #define META_SKINCOLOR "SKINCOLOR_T*" #define META_COLORRAMP "SKINCOLOR_T*RAMP" #define META_SPRITEINFO "SPRITEINFO_T*" +#define META_GAMETYPE "GAMETYPE_T*" #define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]" #define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*" diff --git a/src/lua_script.c b/src/lua_script.c index 6a5982006..20b3b0af8 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -990,6 +990,7 @@ enum ARCH_SKINCOLOR, ARCH_MOUSE, ARCH_SKIN, + ARCH_GAMETYPE, ARCH_TEND=0xFF, }; @@ -1019,6 +1020,7 @@ static const struct { {META_SKINCOLOR, ARCH_SKINCOLOR}, {META_MOUSE, ARCH_MOUSE}, {META_SKIN, ARCH_SKIN}, + {META_GAMETYPE, ARCH_GAMETYPE}, {NULL, ARCH_NULL} }; @@ -1347,6 +1349,13 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) WRITEUINT8(save_p, skin - skins); // UINT8 because MAXSKINS is only 32 break; } + case ARCH_GAMETYPE: + { + gametype_t *gt = *((gametype_t **)lua_touserdata(gL, myindex)); + WRITEUINT8(save_p, ARCH_GAMETYPE); + WRITEUINT8(save_p, gt - gametypes); + break; + } default: WRITEUINT8(save_p, ARCH_NULL); return 2; @@ -1596,6 +1605,9 @@ static UINT8 UnArchiveValue(int TABLESINDEX) case ARCH_SKIN: LUA_PushUserdata(gL, &skins[READUINT8(save_p)], META_SKIN); break; + case ARCH_GAMETYPE: + LUA_PushUserdata(gL, &gametypes[READUINT8(save_p)], META_GAMETYPE); + break; case ARCH_TEND: return 1; }