diff --git a/src/command.h b/src/command.h
index f0dc62418a8815abfa9e7c9b84d9357aac406b73..deb30e28bfacae8d554257703b7fe940a13053f6 100644
--- a/src/command.h
+++ b/src/command.h
@@ -121,6 +121,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 c7c7c604068cd4761ea3944a92cd4efecd9a0ddb..20f6fc65a26e8a87def621e1978a67fd4d6e5057 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -5617,6 +5617,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 aaa676526d1eac6adbad4ac5d8ba34e31388d2cd..00e8d036d46098d622cef26b73a8e49e0ad4758a 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!");
@@ -335,6 +340,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)))
 
@@ -382,7 +390,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
@@ -397,9 +405,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);
@@ -412,7 +420,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
 
@@ -421,25 +429,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];
@@ -482,7 +490,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))
 			{
@@ -496,6 +504,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);
 	}
 
@@ -532,6 +553,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 = strdup(wadfiles[numwadfiles - 1]->filename);
+			nameonly(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 30dc94a861dd328d13d8355599581466a206d7f9..ae390d56e0a2e1fe7903fd4fa1b41bde171012f7 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"
@@ -317,6 +318,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);
@@ -350,6 +352,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)
@@ -1039,6 +1042,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[] =
@@ -1650,6 +1654,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
 // ==========================================================================
@@ -2226,6 +2233,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);
@@ -6967,6 +6991,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 a791d56f40984c41bfd30c92006d5ad3e767d250..44ba9dfc3f581ee88aecf7dc9b8e8b00f23a0da1 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -485,6 +485,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)\