diff --git a/src/blua/ldblib.c b/src/blua/ldblib.c
new file mode 100644
index 0000000000000000000000000000000000000000..f3a4787028aa46cdc0114d28c48417907b595205
--- /dev/null
+++ b/src/blua/ldblib.c
@@ -0,0 +1,310 @@
+/*
+** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ldblib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+static void settabss(lua_State* L, const char* i, const char* v) {
+    lua_pushstring(L, v);
+    lua_setfield(L, -2, i);
+}
+
+
+static void settabsi(lua_State* L, const char* i, int v) {
+    lua_pushinteger(L, v);
+    lua_setfield(L, -2, i);
+}
+
+
+static lua_State* getthread(lua_State* L, int* arg) {
+    if (lua_isthread(L, 1)) {
+        *arg = 1;
+        return lua_tothread(L, 1);
+    }
+    else {
+        *arg = 0;
+        return L;
+    }
+}
+
+
+static void treatstackoption(lua_State* L, lua_State* L1, const char* fname) {
+    if (L == L1) {
+        lua_pushvalue(L, -2);
+        lua_remove(L, -3);
+    }
+    else
+        lua_xmove(L1, L, 1);
+    lua_setfield(L, -2, fname);
+}
+
+
+static int db_getinfo(lua_State* L) {
+    lua_Debug ar;
+    int arg;
+    lua_State* L1 = getthread(L, &arg);
+    const char* options = luaL_optstring(L, arg + 2, "flnSu");
+    if (lua_isnumber(L, arg + 1)) {
+        if (!lua_getstack(L1, (int)lua_tointeger(L, arg + 1), &ar)) {
+            lua_pushnil(L);  /* level out of range */
+            return 1;
+        }
+    }
+    else if (lua_isfunction(L, arg + 1)) {
+        lua_pushfstring(L, ">%s", options);
+        options = lua_tostring(L, -1);
+        lua_pushvalue(L, arg + 1);
+        lua_xmove(L, L1, 1);
+    }
+    else
+        return luaL_argerror(L, arg + 1, "function or level expected");
+    if (!lua_getinfo(L1, options, &ar))
+        return luaL_argerror(L, arg + 2, "invalid option");
+    lua_createtable(L, 0, 2);
+    if (strchr(options, 'S')) {
+        settabss(L, "source", ar.source);
+        settabss(L, "short_src", ar.short_src);
+        settabsi(L, "linedefined", ar.linedefined);
+        settabsi(L, "lastlinedefined", ar.lastlinedefined);
+        settabss(L, "what", ar.what);
+    }
+    if (strchr(options, 'l'))
+        settabsi(L, "currentline", ar.currentline);
+    if (strchr(options, 'u'))
+        settabsi(L, "nups", ar.nups);
+    if (strchr(options, 'n')) {
+        settabss(L, "name", ar.name);
+        settabss(L, "namewhat", ar.namewhat);
+    }
+    if (strchr(options, 'L'))
+        treatstackoption(L, L1, "activelines");
+    if (strchr(options, 'f'))
+        treatstackoption(L, L1, "func");
+    return 1;  /* return table */
+}
+
+
+static int db_getlocal(lua_State* L) {
+    int arg;
+    lua_State* L1 = getthread(L, &arg);
+    lua_Debug ar;
+    const char* name;
+    if (!lua_getstack(L1, luaL_checkint(L, arg + 1), &ar))  /* out of range? */
+        return luaL_argerror(L, arg + 1, "level out of range");
+    name = lua_getlocal(L1, &ar, luaL_checkint(L, arg + 2));
+    if (name) {
+        lua_xmove(L1, L, 1);
+        lua_pushstring(L, name);
+        lua_pushvalue(L, -2);
+        return 2;
+    }
+    else {
+        lua_pushnil(L);
+        return 1;
+    }
+}
+
+
+static int auxupvalue(lua_State* L, int get) {
+    const char* name;
+    int n = luaL_checkint(L, 2);
+    luaL_checktype(L, 1, LUA_TFUNCTION);
+    if (lua_iscfunction(L, 1)) return 0;  /* cannot touch C upvalues from Lua */
+    name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
+    if (name == NULL) return 0;
+    lua_pushstring(L, name);
+    lua_insert(L, -(get + 1));
+    return get + 1;
+}
+
+
+static int db_getupvalue(lua_State* L) {
+    return auxupvalue(L, 1);
+}
+
+
+
+static char KEY_HOOK = 'h';
+
+
+static void hookf(lua_State* L, lua_Debug* ar) {
+    static const char* const hooknames[] =
+    { "call", "return", "line", "count", "tail return" };
+    lua_pushlightuserdata(L, (void*)&KEY_HOOK);
+    lua_rawget(L, LUA_REGISTRYINDEX);
+    lua_pushlightuserdata(L, L);
+    lua_rawget(L, -2);
+    if (lua_isfunction(L, -1)) {
+        lua_pushstring(L, hooknames[(int)ar->event]);
+        if (ar->currentline >= 0)
+            lua_pushinteger(L, ar->currentline);
+        else lua_pushnil(L);
+        lua_assert(lua_getinfo(L, "lS", ar));
+        lua_call(L, 2, 0);
+    }
+}
+
+
+static int makemask(const char* smask, int count) {
+    int mask = 0;
+    if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
+    if (strchr(smask, 'r')) mask |= LUA_MASKRET;
+    if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
+    if (count > 0) mask |= LUA_MASKCOUNT;
+    return mask;
+}
+
+
+static char* unmakemask(int mask, char* smask) {
+    int i = 0;
+    if (mask & LUA_MASKCALL) smask[i++] = 'c';
+    if (mask & LUA_MASKRET) smask[i++] = 'r';
+    if (mask & LUA_MASKLINE) smask[i++] = 'l';
+    smask[i] = '\0';
+    return smask;
+}
+
+
+static void gethooktable(lua_State* L) {
+    lua_pushlightuserdata(L, (void*)&KEY_HOOK);
+    lua_rawget(L, LUA_REGISTRYINDEX);
+    if (!lua_istable(L, -1)) {
+        lua_pop(L, 1);
+        lua_createtable(L, 0, 1);
+        lua_pushlightuserdata(L, (void*)&KEY_HOOK);
+        lua_pushvalue(L, -2);
+        lua_rawset(L, LUA_REGISTRYINDEX);
+    }
+}
+
+
+static int db_sethook(lua_State* L) {
+    int arg, mask, count;
+    lua_Hook func;
+    lua_State* L1 = getthread(L, &arg);
+    if (lua_isnoneornil(L, arg + 1)) {
+        lua_settop(L, arg + 1);
+        func = NULL; mask = 0; count = 0;  /* turn off hooks */
+    }
+    else {
+        const char* smask = luaL_checkstring(L, arg + 2);
+        luaL_checktype(L, arg + 1, LUA_TFUNCTION);
+        count = luaL_optint(L, arg + 3, 0);
+        func = hookf; mask = makemask(smask, count);
+    }
+    gethooktable(L);
+    lua_pushlightuserdata(L, L1);
+    lua_pushvalue(L, arg + 1);
+    lua_rawset(L, -3);  /* set new hook */
+    lua_pop(L, 1);  /* remove hook table */
+    lua_sethook(L1, func, mask, count);  /* set hooks */
+    return 0;
+}
+
+
+static int db_gethook(lua_State* L) {
+    int arg;
+    lua_State* L1 = getthread(L, &arg);
+    char buff[5];
+    int mask = lua_gethookmask(L1);
+    lua_Hook hook = lua_gethook(L1);
+    if (hook != NULL && hook != hookf)  /* external hook? */
+        lua_pushliteral(L, "external hook");
+    else {
+        gethooktable(L);
+        lua_pushlightuserdata(L, L1);
+        lua_rawget(L, -2);   /* get hook */
+        lua_remove(L, -2);  /* remove hook table */
+    }
+    lua_pushstring(L, unmakemask(mask, buff));
+    lua_pushinteger(L, lua_gethookcount(L1));
+    return 3;
+}
+
+
+#define LEVELS1	12	/* size of the first part of the stack */
+#define LEVELS2	10	/* size of the second part of the stack */
+
+static int db_errorfb(lua_State* L) {
+    int level;
+    int firstpart = 1;  /* still before eventual `...' */
+    int arg;
+    lua_State* L1 = getthread(L, &arg);
+    lua_Debug ar;
+    if (lua_isnumber(L, arg + 2)) {
+        level = (int)lua_tointeger(L, arg + 2);
+        lua_pop(L, 1);
+    }
+    else
+        level = (L == L1) ? 1 : 0;  /* level 0 may be this own function */
+    if (lua_gettop(L) == arg)
+        lua_pushliteral(L, "");
+    else if (!lua_isstring(L, arg + 1)) return 1;  /* message is not a string */
+    else lua_pushliteral(L, "\n");
+    lua_pushliteral(L, "stack traceback:");
+    while (lua_getstack(L1, level++, &ar)) {
+        if (level > LEVELS1 && firstpart) {
+            /* no more than `LEVELS2' more levels? */
+            if (!lua_getstack(L1, level + LEVELS2, &ar))
+                level--;  /* keep going */
+            else {
+                lua_pushliteral(L, "\n\t...");  /* too many levels */
+                while (lua_getstack(L1, level + LEVELS2, &ar))  /* find last levels */
+                    level++;
+            }
+            firstpart = 0;
+            continue;
+        }
+        lua_pushliteral(L, "\n\t");
+        lua_getinfo(L1, "Snl", &ar);
+        lua_pushfstring(L, "%s:", ar.short_src);
+        if (ar.currentline > 0)
+            lua_pushfstring(L, "%d:", ar.currentline);
+        if (*ar.namewhat != '\0')  /* is there a name? */
+            lua_pushfstring(L, " in function " LUA_QS, ar.name);
+        else {
+            if (*ar.what == 'm')  /* main? */
+                lua_pushfstring(L, " in main chunk");
+            else if (*ar.what == 'C' || *ar.what == 't')
+                lua_pushliteral(L, " ?");  /* C function or tail call */
+            else
+                lua_pushfstring(L, " in function <%s:%d>",
+                    ar.short_src, ar.linedefined);
+        }
+        lua_concat(L, lua_gettop(L) - arg);
+    }
+    lua_concat(L, lua_gettop(L) - arg);
+    return 1;
+}
+
+
+static const luaL_Reg dblib[] = {
+  {"gethook", db_gethook},
+  {"getinfo", db_getinfo},
+  {"getlocal", db_getlocal},
+  {"getupvalue", db_getupvalue},
+  {"sethook", db_sethook},
+  {"traceback", db_errorfb},
+  {NULL, NULL}
+};
+
+
+LUALIB_API int luaopen_debug(lua_State* L) {
+    luaL_register(L, LUA_DBLIBNAME, dblib);
+    return 1;
+}
\ No newline at end of file
diff --git a/src/blua/linit.c b/src/blua/linit.c
index dcf05d9f2c925e49457514c57f99afe7bf66c1d8..392d45dce47adf40ce753e400dcf1df2e2f46d91 100644
--- a/src/blua/linit.c
+++ b/src/blua/linit.c
@@ -20,6 +20,7 @@ static const luaL_Reg lualibs[] = {
   {LUA_IOLIBNAME, luaopen_io},
   {LUA_OSLIBNAME, luaopen_os},
   {LUA_STRLIBNAME, luaopen_string},
+  {LUA_DBLIBNAME, luaopen_debug},
   {NULL, NULL}
 };
 
diff --git a/src/blua/lualib.h b/src/blua/lualib.h
index 7127e4d77e56c1d2aee9c7079b59c5a3adc959a2..ccc9c66479e567c1be4848328363512d446ec5f6 100644
--- a/src/blua/lualib.h
+++ b/src/blua/lualib.h
@@ -30,6 +30,8 @@ LUALIB_API int (luaopen_os) (lua_State *L);
 #define LUA_STRLIBNAME	"string"
 LUALIB_API int (luaopen_string) (lua_State *L);
 
+#define LUA_DBLIBNAME	"debug"
+LUALIB_API int (luaopen_debug)(lua_State* L);
 
 /* open all previous libraries */
 LUALIB_API void (luaL_openlibs) (lua_State *L);
diff --git a/src/d_main.c b/src/d_main.c
index c139650d1eb039a057da10d063f392f33c94fac2..0e87cbb32768684877a55600c9e414b0da4461a7 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -73,6 +73,7 @@
 #include "m_perfstats.h"
 #include "m_random.h"
 #include "command.h"
+#include "m_automod.h"
 
 #ifdef CMAKECONFIG
 #include "config.h"
@@ -1187,6 +1188,13 @@ static void IdentifyVersion(void)
 	D_AddFile(&startupwadfiles, va(pandf,srb2waddir, "patch.pk3"));
 #endif
 
+	char *pipekingdom = va(pandf, srb2waddir, "pipekingdom.pk3");
+	if (FIL_ReadFileOK(pipekingdom))
+	{
+		togglepipekingdom = true;
+		D_AddFile(&startupwadfiles, pipekingdom);
+	}
+
 #if !defined (HAVE_SDL) || defined (HAVE_MIXER)
 	{
 #define MUSICTEST(str) \
diff --git a/src/deh_tables.c b/src/deh_tables.c
index 20f6fc65a26e8a87def621e1978a67fd4d6e5057..33906f9a6aab549b9f6e68b5b24adb616a945fd2 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -25,6 +25,7 @@
 #include "g_game.h" // Joystick axes (for lua)
 #include "i_joy.h"
 #include "g_input.h" // Game controls (for lua)
+#include "m_menu.h" // Menu constants (for lua)
 
 #include "deh_tables.h"
 
@@ -5706,6 +5707,54 @@ struct int_const_s const INT_CONST[] = {
 	{"MA_NOCUTSCENES",MA_NOCUTSCENES},
 	{"MA_INGAME",MA_INGAME},
 
+	// Menuitem constants
+	{ "IT_TYPE",IT_TYPE },
+	{ "IT_CALL",IT_CALL },
+	{ "IT_SPACE",IT_SPACE },
+	{ "IT_ARROWS",IT_ARROWS },
+	{ "IT_KEYHANDLER",IT_KEYHANDLER },
+	{ "IT_SUBMENU",IT_SUBMENU },
+	{ "IT_CVAR",IT_CVAR },
+	{ "IT_CVAR",IT_PAIR },
+	{ "IT_MSGHANDLER",IT_MSGHANDLER },
+	{ "IT_DISPLAY",IT_DISPLAY },
+	{ "IT_NOTHING",IT_NOTHING },
+	{ "IT_PATCH",IT_PATCH },
+	{ "IT_STRING",IT_STRING },
+	{ "IT_WHITESTRING",IT_WHITESTRING },
+	{ "IT_DYBIGSPACE",IT_DYBIGSPACE },
+	{ "IT_DYLITLSPACE",IT_DYLITLSPACE },
+	{ "IT_STRING2",IT_STRING2 },
+	{ "IT_GRAYPATCH",IT_GRAYPATCH },
+	{ "IT_BIGSLIDER",IT_BIGSLIDER },
+	{ "IT_TRANSTEXT",IT_TRANSTEXT },
+	{ "IT_TRANSTEXT2",IT_TRANSTEXT2 },
+	{ "IT_HEADERTEXT",IT_HEADERTEXT },
+	{ "IT_QUESTIONMARKS",IT_QUESTIONMARKS },
+	{ "IT_QUESTIONMARKS",IT_CENTER },
+	{ "IT_CVARTYPE",IT_CVARTYPE },
+	{ "IT_CV_NORMAL",IT_CV_NORMAL },
+	{ "IT_CV_SLIDER",IT_CV_SLIDER },
+	{ "IT_CV_STRING",IT_CV_STRING },
+	{ "IT_CV_NOPRINT",IT_CV_NOPRINT },
+	{ "IT_CV_NOMOD",IT_CV_NOMOD },
+	{ "IT_CV_INVISSLIDER",IT_CV_INVISSLIDER },
+	{ "IT_CV_INTEGERSTEP",IT_CV_INTEGERSTEP },
+	{ "IT_CV_FLOATSLIDER",IT_CV_FLOATSLIDER },
+	{ "IT_CALLTYPE",IT_CALLTYPE },
+	{ "IT_CALL_NORMAL",IT_CALL_NORMAL },
+	{ "IT_CALL_NOTMODIFIED",IT_CALL_NOTMODIFIED },
+	{ "IT_BIGSPACE",IT_BIGSPACE },
+	{ "IT_LITLSPACE",IT_LITLSPACE },
+	{ "IT_CONTROL",IT_CONTROL },
+	{ "IT_CVARMAX",IT_CVARMAX },
+	{ "IT_DISABLED",IT_DISABLED },
+	{ "IT_GRAYEDOUT",IT_GRAYEDOUT },
+	{ "IT_GRAYEDOUT2",IT_GRAYEDOUT2 },
+	{ "IT_HEADER",IT_HEADER },
+	{ "IT_SECRET",IT_SECRET },
+
+
 	// music types
 	{"MU_NONE", MU_NONE},
 	{"MU_WAV", MU_WAV},
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index be054d1ba9dbb67b1498a52d7a202885f9fd05c7..c0022608c44e482ea9ab0b416d85c6516a2e821d 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -237,6 +237,8 @@ static const struct {
 
 	{META_KEYEVENT,     "keyevent_t"},
 	{META_MOUSE,        "mouse_t"},
+
+	{META_MENUITEM,		"menuitem_t"},
 	{NULL,              NULL}
 };
 
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 5208c56ab312a71076d914855d028f4dbeab9633..656015e9751764876db56bdd3c71731177659291 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -74,6 +74,7 @@ automatically.
 	X (PlayerHeight),/* override player height */\
 	X (PlayerCanEnterSpinGaps),\
 	X (AddonLoaded),\
+	X (MenuChange),\
 	X (KeyDown),\
 	X (KeyUp),\
 
@@ -91,6 +92,7 @@ automatically.
 	X (intermission),\
 	X (continue),\
 	X (playersetup),\
+	X (menu),\
 
 /*
 I chose to access the hook enums through a macro as well. This could provide
@@ -146,6 +148,7 @@ int  LUA_HookKey(event_t *event, int hook); // Hooks for key events
 void LUA_HookPreThinkFrame(void);
 void LUA_HookThinkFrame(void);
 void LUA_HookPostThinkFrame(void);
+void LUA_HookMenuChange(int currentmenu, int previousmenu);
 int  LUA_HookMobjLineCollide(mobj_t *, line_t *);
 int  LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher);
 int  LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype);
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index a4d55d62433e1a89ef7e55c9f07c9c9c8218094b..ddc19e9625e3dc40a5052229b93bbbe8aa3a7ba1 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -1076,6 +1076,18 @@ void LUA_HookPlayerQuit(player_t *plr, kickreason_t reason)
 	}
 }
 
+void LUA_HookMenuChange(int currentmenu, int previousmenu)
+{
+	Hook_State hook;
+	if (prepare_hook(&hook, 0, HOOK(MenuChange)))
+	{
+		lua_pushinteger(gL, currentmenu);
+		lua_pushinteger(gL, previousmenu);
+		call_hooks(&hook, 1, res_force);
+	}
+	return 1;
+}
+
 int LUA_HookIntermissionThinker(boolean pstagefailed, INT32 intertic, INT32 tallydonetic, INT32 endtic)
 {
 	Hook_State hook;
diff --git a/src/lua_hud.h b/src/lua_hud.h
index 36ce230ed2a384a73e1f988c41ef79d6d91986a5..157598a638dc71aed50760abcaf654d1b7419beb 100644
--- a/src/lua_hud.h
+++ b/src/lua_hud.h
@@ -47,6 +47,8 @@ enum hud {
 	hud_intermissiontitletext,
 	hud_intermissionmessages,
 	hud_intermissionemeralds,
+	// Menu
+	hud_menu,
 	hud_MAX
 };
 
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 797dc4d86785f812172a09095ef04cce99a3606c..b658a463c58af4fa09b99ddfed96524daf066ce6 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -25,6 +25,7 @@
 #include "w_wad.h"
 #include "z_zone.h"
 #include "y_inter.h"
+#include "m_menu.h"
 
 #include "lua_script.h"
 #include "lua_libs.h"
@@ -69,6 +70,8 @@ static const char *const hud_disable_options[] = {
 	"intermissiontitletext",
 	"intermissionmessages",
 	"intermissionemeralds",
+
+	"menu",
 	NULL};
 
 // you know, let's actually make sure that the table is synced.
@@ -209,6 +212,22 @@ static const char *const camera_opt[] = {
 
 static int camera_fields_ref = LUA_NOREF;
 
+enum menuitemf {
+	menuitem_status = 0,
+	menuitem_patch,
+	menuitem_text,
+	menuitem_alphaKey,
+};
+
+static const char* const menuitem_opt[] = {
+	"status",
+	"patch",
+	"text",
+	"alphaKey",
+	NULL };
+
+static int menuitem_fields_ref = LUA_NOREF;
+
 static int lib_getHudInfo(lua_State *L)
 {
 	UINT32 i;
@@ -455,6 +474,37 @@ static int camera_set(lua_State *L)
 	return 0;
 }
 
+static int menuitem_get(lua_State* L)
+{
+	menuitem_t* menuitem = *((menuitem_t**)luaL_checkudata(L, 1, META_MENUITEM));
+	enum menuitemf field = Lua_optoption(L, 2, -1, menuitem_fields_ref);
+
+	// cameras should always be valid unless I'm a nutter
+	I_Assert(menuitem != NULL);
+
+	switch (field)
+	{
+	case menuitem_status:
+		lua_pushinteger(L, menuitem->status);
+		break;
+	case menuitem_patch:
+		lua_pushstring(L, menuitem->patch);
+		break;
+	case menuitem_text:
+		lua_pushstring(L, menuitem->text);
+		break;
+	case menuitem_alphaKey:
+		lua_pushinteger(L, menuitem->alphaKey);
+		break;
+	}
+	return 1;
+}
+
+static int menuitem_set(lua_State* L)
+{
+	return luaL_error(L, LUA_QL("menuitem_t") " struct cannot be edited by Lua.");
+}
+
 //
 // lib_draw
 //
@@ -1467,9 +1517,11 @@ int LUA_HudLib(lua_State *L)
 	LUA_RegisterUserdataMetatable(L, META_COLORMAP, colormap_get, NULL, NULL);
 	LUA_RegisterUserdataMetatable(L, META_PATCH, patch_get, patch_set, NULL);
 	LUA_RegisterUserdataMetatable(L, META_CAMERA, camera_get, camera_set, NULL);
+	LUA_RegisterUserdataMetatable(L, META_MENUITEM, menuitem_get, menuitem_set, NULL);
 
 	patch_fields_ref = Lua_CreateFieldTable(L, patch_opt);
 	camera_fields_ref = Lua_CreateFieldTable(L, camera_opt);
+	menuitem_fields_ref = Lua_CreateFieldTable(L, menuitem_opt);
 
 	LUA_RegisterGlobalUserdata(L, "hudinfo", lib_getHudInfo, NULL, lib_hudinfolen);
 
@@ -1508,6 +1560,21 @@ void LUA_SetHudHook(int hook, huddrawlist_h list)
 			lua_pushinteger(gL, (lt_endtime + TICRATE));
 			break;
 
+		case HUD_HOOK(menu):
+			//lua_createtable(gL, 0, 0);
+			lua_pushinteger(gL, currentMenu->menuid);
+			lua_pushinteger(gL, M_ReturnCurrentItemID() + 1);
+
+			INT16 i;
+
+			lua_createtable(gL, currentMenu->numitems, 0);
+			for (i = 0; i < currentMenu->numitems; i++) {
+				menuitem_t* item = &currentMenu->menuitems[i];
+				LUA_PushUserdata(gL, item, META_MENUITEM);
+				lua_rawseti(gL, -2, i + 1);
+			}
+			break;
+
 		case HUD_HOOK(intermission):
 			lua_pushboolean(gL, stagefailed);
 			lua_pushinteger(gL, interlasttic);
diff --git a/src/lua_libs.h b/src/lua_libs.h
index a90d8ac7fb800c426e7bef94538803366a835b8b..0f5b4ba78969c472c96e7c76ab8d081dd9ad6951 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -95,6 +95,8 @@ extern boolean ignoregameinputs;
 #define META_KEYEVENT "KEYEVENT_T*"
 #define META_MOUSE "MOUSE_T*"
 
+#define META_MENUITEM "MENUITEM_T*"
+
 boolean luaL_checkboolean(lua_State *L, int narg);
 
 int LUA_EnumLib(lua_State *L);
diff --git a/src/lua_script.c b/src/lua_script.c
index d34ab2b5fdeabfd7f02c5fc4c130c678772d8f65..604e66b3a780d4276fe8e712d7afb6913b937b5e 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -403,6 +403,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
 	} else if (fastcmp(word,"VERSION")) {
 		lua_pushinteger(L, VERSION);
 		return 1;
+	} else if (fastcmp(word, "TBSCOMMUNITYBUILD")) {
+		lua_pushboolean(L, modifiedgame);
+		return 1;
 	} else if (fastcmp(word,"SUBVERSION")) {
 		lua_pushinteger(L, SUBVERSION);
 		return 1;
diff --git a/src/m_automod.c b/src/m_automod.c
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/m_automod.h b/src/m_automod.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4ef0d7332626b2eed7ea0bd2ef42fbcfdcde0e0
--- /dev/null
+++ b/src/m_automod.h
@@ -0,0 +1,3 @@
+#include "doomtype.h"
+
+boolean togglepipekingdom;
\ No newline at end of file
diff --git a/src/m_menu.c b/src/m_menu.c
index ae390d56e0a2e1fe7903fd4fa1b41bde171012f7..3e8753ac862af01c296c4de4bcc57e25bf7bacde 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -28,6 +28,7 @@
 #include "g_game.h"
 #include "g_input.h"
 #include "m_argv.h"
+#include "m_automod.h"
 
 // Data.
 #include "sounds.h"
@@ -75,6 +76,10 @@
 // And just some randomness for the exits.
 #include "m_random.h"
 
+// Lua 
+#include "lua_hud.h"
+#include "lua_hudlib_drawlist.h"
+
 #if defined(HAVE_SDL)
 #include "SDL.h"
 #if SDL_VERSION_ATLEAST(2,0,0)
@@ -505,7 +510,7 @@ consvar_t cv_dummyloadless = CVAR_INIT ("dummyloadless", "In-game", CV_HIDEN, lo
 static menuitem_t MainMenu[] =
 {
 	{IT_STRING|IT_CALL,    NULL, "1  Player",   M_SinglePlayerMenu,      76},
-	{IT_STRING|IT_SUBMENU, NULL, "Multiplayer", &MP_MainDef,             84},
+	//{IT_STRING|IT_SUBMENU, NULL, "Multiplayer", &MP_MainDef,             84},
 	{IT_STRING|IT_CALL,    NULL, "Extras",      M_SecretsMenu,           92},
 	{IT_CALL   |IT_STRING, NULL, "Addons",      M_Addons,               100},
 	{IT_STRING|IT_CALL,    NULL, "Options",     M_Options,              108},
@@ -729,7 +734,7 @@ static menuitem_t SR_EmblemHintMenu[] =
 static menuitem_t SP_MainMenu[] =
 {
 	// Note: If changing the positions here, also change them in M_SinglePlayerMenu()
-	{IT_CALL | IT_STRING,	NULL, "Start Game",    M_LoadGame,                 76},
+	{IT_CALL | IT_STRING,	NULL, "SRB2 Campaign", M_LoadGame,                 76},
 	{IT_SECRET,				NULL, "Record Attack", M_TimeAttack,               84},
 	{IT_SECRET,				NULL, "NiGHTS Mode",   M_NightsAttack,             92},
 	{IT_SECRET,				NULL, "Marathon Run",  M_Marathon,                100},
@@ -3024,6 +3029,11 @@ static void M_HandleMenuPresState(menu_t *newMenu)
 	}
 }
 
+int M_ReturnCurrentItemID(void)
+{
+	return itemOn;
+}
+
 // =========================================================================
 // BASIC MENU HANDLING
 // =========================================================================
@@ -3618,6 +3628,10 @@ void M_Drawer(void)
 
 	if (menuactive)
 	{
+
+		if (!LUA_HudEnabled(hud_menu))
+			goto luahook;
+
 		// now that's more readable with a faded background (yeah like Quake...)
 		if (!wipe && (curfadevalue || (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK)))
 			V_DrawFadeScreen(0xFF00, (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK) ? 16 : curfadevalue);
@@ -3644,6 +3658,14 @@ void M_Drawer(void)
 #endif
 			}
 		}
+
+
+		luahook:
+		{
+			LUA_HUD_ClearDrawList(luahuddrawlist_menu);
+			LUA_HUDHOOK(menu, luahuddrawlist_menu);
+		}
+		LUA_HUD_DrawList(luahuddrawlist_menu);
 	}
 
 	// focus lost notification goes on top of everything, even the former everything
@@ -3676,6 +3698,7 @@ void M_StartControlPanel(void)
 		return;
 	}
 
+	INT16 prev = currentMenu->menuid;
 	menuactive = true;
 
 	if (!Playing())
@@ -3786,6 +3809,7 @@ void M_StartControlPanel(void)
 		M_UpdateItemOn();
 	}
 
+	LUA_HookMenuChange(currentMenu->menuid, prev);
 	CON_ToggleOff(); // move away console
 }
 
@@ -3831,6 +3855,7 @@ void M_ClearMenus(boolean callexitmenufunc)
 void M_SetupNextMenu(menu_t *menudef)
 {
 	INT16 i;
+	INT16 prev = currentMenu->menuid;
 
 #if defined (MASTERSERVER) && defined (HAVE_THREADS)
 	if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef)
@@ -3872,6 +3897,8 @@ void M_SetupNextMenu(menu_t *menudef)
 	if (itemOn >= currentMenu->numitems)
 		itemOn = currentMenu->numitems - 1;
 
+	LUA_HookMenuChange(currentMenu->menuid, prev);
+
 	// the curent item can be disabled,
 	// this code go up until an enabled item found
 	if (( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE ))
@@ -4000,6 +4027,8 @@ void M_Init(void)
 	}
 
 	CV_RegisterVar(&cv_serversort);
+
+	luahuddrawlist_menu = LUA_HUD_CreateDrawList();
 }
 
 static void M_InitCharacterDescription(INT32 i)
diff --git a/src/m_menu.h b/src/m_menu.h
index 44ba9dfc3f581ee88aecf7dc9b8e8b00f23a0da1..8e7673fa07eae7ba0e0ae533c0515e2163a80f42 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -22,6 +22,8 @@
 #include "i_threads.h"
 #include "netcode/mserv.h"
 #include "r_things.h" // for SKINNAMESIZE
+#include "lua_hud.h"
+#include "lua_hudlib_drawlist.h"
 
 // Compatibility with old-style named NiGHTS replay files.
 #define OLDNREPLAYNAME
@@ -348,6 +350,7 @@ typedef struct menu_s
 
 void M_SetupNextMenu(menu_t *menudef);
 void M_ClearMenus(boolean callexitmenufunc);
+extern int M_ReturnCurrentItemID(void);
 
 // Maybe this goes here????? Who knows.
 boolean M_MouseNeeded(void);
@@ -487,6 +490,8 @@ void M_InitPlayerSetupColors(void);
 void M_FreePlayerSetupColors(void);
 void M_FreeslotIntoCustomMenu(consvar_t* cvar, const char* category, const char* name);
 
+static huddrawlist_h luahuddrawlist_menu;
+
 // These defines make it a little easier to make menus
 #define DEFAULTMENUSTYLE(id, header, source, prev, x, y)\
 {\
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 86ffa7082ac3210321a06c3b477ba3b6239947b9..fb1e5c1f03f8f4449b8f1eaf1e76ff5e6790cfa6 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -318,6 +318,7 @@
     <ClInclude Include="..\hardware\hw_md2load.h" />
     <ClInclude Include="..\hardware\hw_md3load.h" />
     <ClInclude Include="..\hardware\hw_model.h" />
+    <ClInclude Include="..\m_automod.h" />
     <ClInclude Include="..\u_list.h" />
     <ClInclude Include="..\hu_stuff.h" />
     <ClInclude Include="..\info.h" />
@@ -447,6 +448,7 @@
     <ClCompile Include="..\blua\lauxlib.c" />
     <ClCompile Include="..\blua\lbaselib.c" />
     <ClCompile Include="..\blua\lcode.c" />
+    <ClCompile Include="..\blua\ldblib.c" />
     <ClCompile Include="..\blua\ldebug.c" />
     <ClCompile Include="..\blua\ldo.c" />
     <ClCompile Include="..\blua\ldump.c" />
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index d2f9d018f2b3ef44e59def85634aa46ef24acd95..7b452dc5b63e7bf97b1b1deeb8123d278c941ed1 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -562,6 +562,9 @@
     <ClInclude Include="..\u_list.h" />
     <ClInclude Include="..\mserv.h" />
     <ClInclude Include="..\http-mserv.h" />
+    <ClInclude Include="..\m_automod.h">
+      <Filter>M_Misc</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\tmap.nas">
@@ -754,9 +757,6 @@
     <ClCompile Include="..\hardware\hw_shaders.c">
       <Filter>Hw_Hardware</Filter>
     </ClCompile>
-    <ClCompile Include="..\hardware\u_list.c">
-      <Filter>Hw_Hardware</Filter>
-    </ClCompile>
     <ClCompile Include="..\filesrch.c">
       <Filter>I_Interface</Filter>
     </ClCompile>
@@ -1119,6 +1119,9 @@
     <ClCompile Include="..\r_bbox.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
+    <ClCompile Include="..\blua\ldblib.c">
+      <Filter>BLUA</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <Image Include="Srb2SDL.ico">
diff --git a/src/sdl/ldblib.c b/src/sdl/ldblib.c
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/version.h b/src/version.h
index 8d8f8978e7ea3cf593247a73a9460ed139193c10..f5c96e3cc1ed0804497b17d2c35b0d2700601b56 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,4 +1,4 @@
-#define SRB2VERSION "2.2.14"/* this must be the first line, for cmake !! */
+#define SRB2VERSION "2.2.14 (TBS-custom)"/* this must be the first line, for cmake !! */
 
 // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/members/?key=ms_admin ).
 // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server.