diff --git a/src/command.h b/src/command.h index 70342a785..e0724a9e1 100644 --- a/src/command.h +++ b/src/command.h @@ -125,6 +125,7 @@ typedef enum // used on menus CV_CHEAT = 2048, // Don't let this be used in multiplayer unless cheats are on. CV_ALLOWLUA = 4096,/* Let this be called from Lua */ + CV_NOMENU = 8192, // Lua exclusive flag, to give choice to modders regarding custom options menu. } cvflags_t; typedef struct CV_PossibleValue_s diff --git a/src/deh_tables.c b/src/deh_tables.c index a96adf8ae..d983bab54 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5642,6 +5642,7 @@ struct int_const_s const INT_CONST[] = { {"CV_HIDDEN",CV_HIDEN}, {"CV_CHEAT",CV_CHEAT}, {"CV_ALLOWLUA",CV_ALLOWLUA}, + {"CV_NOMENU",CV_NOMENU}, // v_video flags {"V_NOSCALEPATCH",V_NOSCALEPATCH}, diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index b9b24307b..34f1affc5 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -22,6 +22,11 @@ #include "lua_libs.h" #include "lua_hud.h" // hud_running errors +// Included for the custom options menu +#include "netcode/d_netfil.h" +#include "m_menu.h" +#include "w_wad.h" + // for functions not allowed in hud.add hooks #define NOHUD if (hud_running)\ return luaL_error(L, "HUD rendering code should not call this function!"); @@ -361,6 +366,9 @@ static int lib_cvRegisterVar(lua_State *L) cvar = ZZ_Calloc(sizeof(consvar_t)); LUA_PushUserdata(L, cvar, META_CVAR); + const char* category = NULL; + const char* menu_name = NULL; + #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to " LUA_QL("CV_RegisterVar") " (%s)", e); #define TYPEERROR(f, t) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t), luaL_typename(L, -1))) @@ -408,7 +416,7 @@ static int lib_cvRegisterVar(lua_State *L) { if (lua_islightuserdata(L, 4)) { - CV_PossibleValue_t *pv = lua_touserdata(L, 4); + CV_PossibleValue_t* pv = lua_touserdata(L, 4); if (pv == CV_OnOff || pv == CV_YesNo || pv == CV_Unsigned || pv == CV_Natural || pv == CV_TrueFalse) cvar->PossibleValue = pv; else @@ -423,9 +431,9 @@ static int lib_cvRegisterVar(lua_State *L) // being used for multiple cvars will be converted and stored multiple times. // So maybe instead it should be a seperate function which must be run beforehand or something. size_t count = 0; - CV_PossibleValue_t *cvpv; + CV_PossibleValue_t* cvpv; - const char * const MINMAX[2] = {"MIN", "MAX"}; + const char* const MINMAX[2] = { "MIN", "MAX" }; int minmax_unset = 3; lua_pushnil(L); @@ -438,7 +446,7 @@ static int lib_cvRegisterVar(lua_State *L) lua_getfield(L, LUA_REGISTRYINDEX, "CV_PossibleValue"); I_Assert(lua_istable(L, 5)); lua_pushlightuserdata(L, cvar); - cvpv = lua_newuserdata(L, sizeof(CV_PossibleValue_t) * (count+1)); + cvpv = lua_newuserdata(L, sizeof(CV_PossibleValue_t) * (count + 1)); lua_rawset(L, 5); lua_pop(L, 1); // pop CV_PossibleValue registry table @@ -447,25 +455,25 @@ static int lib_cvRegisterVar(lua_State *L) while (lua_next(L, 4)) { INT32 n; - const char * strval; + const char* strval; // stack: [...] PossibleValue table, index, value // 4 5 6 if (lua_type(L, 5) != LUA_TSTRING - || lua_type(L, 6) != LUA_TNUMBER) + || lua_type(L, 6) != LUA_TNUMBER) FIELDERROR("PossibleValue", "custom PossibleValue table requires a format of string=integer, i.e. {MIN=0, MAX=9999}"); strval = lua_tostring(L, 5); if ( - stricmp(strval, MINMAX[n=0]) == 0 || - stricmp(strval, MINMAX[n=1]) == 0 - ){ + stricmp(strval, MINMAX[n = 0]) == 0 || + stricmp(strval, MINMAX[n = 1]) == 0 + ) { /* need to shift forward */ if (minmax_unset == 3) { memmove(&cvpv[2], &cvpv[0], - i * sizeof *cvpv); + i * sizeof * cvpv); i += 2; } cvpv[n].strvalue = MINMAX[n]; @@ -508,7 +516,7 @@ static int lib_cvRegisterVar(lua_State *L) lua_pop(L, 1); cvar->func = Lua_OnChange; } - else if (cvar->flags & CV_CALL && (k && fasticmp(k, "can_change"))) + else if (cvar->flags & CV_CALL && (i == 6 || (k && fasticmp(k, "can_change")))) { if (!lua_isfunction(L, 4)) { @@ -522,6 +530,19 @@ static int lib_cvRegisterVar(lua_State *L) lua_pop(L, 1); cvar->can_change = Lua_CanChange; } + else if (((i == 5 && !(cvar->flags & CV_CALL)) + || (cvar->flags & CV_CALL && i == 7)) + || (k && fasticmp(k, "category"))) + { + category = lua_isnoneornil(L, 4) ? NULL : lua_tostring(L, 4); + } + else if (((i == 6 && !(cvar->flags & CV_CALL)) + || (cvar->flags & CV_CALL && i == 8)) + || (k && fasticmp(k, "displayname"))) + { + menu_name = lua_isnoneornil(L, 4) ? NULL : lua_tostring(L, 4); + } + lua_pop(L, 1); } @@ -558,6 +579,24 @@ static int lib_cvRegisterVar(lua_State *L) return luaL_error(L, "failed to register cvar (probable conflict with internal variable/command names)"); } + if (!((cvar->flags & CV_NOMENU) + || (cvar->flags & CV_HIDEN)) + || (cvar->flags & CV_NOSHOWHELP)) + { + if (!category) + { + char* temp = wadfiles[numwadfiles - 1]->filename; + temp += strlen(temp) - nameonlylength(temp); + + category = temp; + } + + if (menu_name && menu_name[0] != '\0') + M_FreeslotIntoCustomMenu(cvar, category, menu_name); + else + M_FreeslotIntoCustomMenu(cvar, category, cvar->name); + } + // return cvar userdata return 1; } diff --git a/src/m_menu.c b/src/m_menu.c index be1b421f7..f5e3e3671 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -48,6 +48,7 @@ #include "p_setup.h" #include "f_finale.h" #include "lua_hook.h" +#include "lua_libs.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -316,6 +317,7 @@ menu_t OP_P1ControlsDef, OP_P2ControlsDef, OP_MouseOptionsDef; menu_t OP_Mouse2OptionsDef, OP_Joystick1Def, OP_Joystick2Def; menu_t OP_CameraOptionsDef, OP_Camera2OptionsDef; menu_t OP_PlaystyleDef; +menu_t OP_AddonCustomOptionsDef; static void M_VideoModeMenu(INT32 choice); static void M_Setup1PControlsMenu(INT32 choice); static void M_Setup2PControlsMenu(INT32 choice); @@ -349,6 +351,7 @@ static void M_EraseData(INT32 choice); static void M_Addons(INT32 choice); static void M_AddonsOptions(INT32 choice); +static void M_AddonsCvarOptions(INT32 choice); static patch_t *addonsp[NUM_EXT+5]; #define addonmenusize 9 // number of items actually displayed in the addons menu view, formerly (2*numaddonsshown + 1) @@ -1037,6 +1040,7 @@ static menuitem_t OP_MainMenu[] = {IT_CALL | IT_STRING, NULL, "Server Options...", M_ServerOptions, 80}, {IT_SUBMENU | IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 100}, + {IT_CALL | IT_STRING, NULL, "Custom Options...", M_AddonsCvarOptions,110}, }; static menuitem_t OP_P1ControlsMenu[] = @@ -1643,6 +1647,9 @@ static menuitem_t OP_MonitorToggleMenu[] = {IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Eggman Box", &cv_eggmanbox, 140}, }; +#define MAXADDONOPTIONS 999 +menuitem_t OP_AddonOptionsSlots[MAXADDONOPTIONS]; + // ========================================================================== // ALL MENU DEFINITIONS GO HERE // ========================================================================== @@ -2219,6 +2226,23 @@ menu_t OP_ScreenshotOptionsDef = NULL }; +INT16 addoncvarpos = 0; + +static void M_AddonsCvarOptions(INT32 choice) +{ + (void)choice; + + if (addoncvarpos) + M_SetupNextMenu(&OP_AddonCustomOptionsDef); + else + M_StartMessage(M_GetText("No Custom Option was found.\nTry to load any Addon!\n(Press a key)\n"), NULL, MM_NOTHING); +} + +menu_t OP_AddonCustomOptionsDef = DEFAULTSCROLLMENUSTYLE( + MTREE3(MN_OP_MAIN, MN_OP_DATA, MN_OP_ADDONS), + "M_ADDONS", OP_AddonOptionsSlots, &OP_MainDef, 30, 30); + + menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE( MTREE3(MN_OP_MAIN, MN_OP_DATA, MN_OP_ADDONS), "M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); @@ -6940,6 +6964,58 @@ static void M_SelectableClearMenus(INT32 choice) M_ClearMenus(true); } +#define CCVHEIGHT 5 +#define CCVHEIGHTHEADER 1 +#define CCVHEIGHTHEADERAFTER 6 + +UINT16 addonvaralphakey = 4; +INT16 addonlastheaderpos = 0; + +INT32 CVARSETUP; + +void M_FreeslotIntoCustomMenu(consvar_t* cvar, const char* category, const char* name) +{ + if (addoncvarpos == INT16_MAX) + return; + + if (addoncvarpos >= MAXADDONOPTIONS - 2) + { + CONS_Printf("Failed to register the console variable '%s' into the menu. Custom Options menu most likely reached the hard limit.\n", name); + addoncvarpos = INT16_MAX; + return; + } + + if (!CVARSETUP) + { + CONS_Printf("Custom Options menu initiation.\n"); + for (CVARSETUP = 0; CVARSETUP < MAXADDONOPTIONS; ++CVARSETUP) + OP_AddonOptionsSlots[CVARSETUP] = (menuitem_t){ IT_DISABLED, NULL, "", 0, INT16_MAX }; + } + + if (category && ((addoncvarpos == 0 && category[0] != '\0') || !fasticmp(category, OP_AddonOptionsSlots[addonlastheaderpos].text))) + { + addonlastheaderpos = addoncvarpos; + addonvaralphakey += CCVHEIGHTHEADER; + + OP_AddonOptionsSlots[addoncvarpos] = (menuitem_t){ IT_HEADER, NULL, Z_StrDup(category), NULL, addonvaralphakey }; + addonvaralphakey += CCVHEIGHTHEADERAFTER; + + + ++addoncvarpos; + } + + if (cvar->PossibleValue && fasticmp(cvar->PossibleValue[0].strvalue, "MIN")) + OP_AddonOptionsSlots[addoncvarpos] = (menuitem_t){ IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, Z_StrDup(name), cvar, addonvaralphakey }; + else if (cvar->flags & CV_FLOAT) + OP_AddonOptionsSlots[addoncvarpos] = (menuitem_t){ IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, Z_StrDup(name), cvar, addonvaralphakey }; + else + OP_AddonOptionsSlots[addoncvarpos] = (menuitem_t){ IT_STRING | IT_CVAR, NULL, Z_StrDup(name), cvar, addonvaralphakey }; + + addonvaralphakey += CCVHEIGHT; + ++addoncvarpos; +} + + // ====== // CHEATS // ====== diff --git a/src/m_menu.h b/src/m_menu.h index dc8bef8b1..f14a2a308 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -486,6 +486,7 @@ UINT16 M_GetColorIndex(UINT16 color); menucolor_t* M_GetColorFromIndex(UINT16 index); void M_InitPlayerSetupColors(void); void M_FreePlayerSetupColors(void); +void M_FreeslotIntoCustomMenu(consvar_t* cvar, const char* category, const char* name); // These defines make it a little easier to make menus #define DEFAULTMENUSTYLE(id, header, source, prev, x, y)\