if lmenu then error("Multiple versions of Lugent's Menus are loaded, the loaded addon was disabled to prevent problems.", 0) return end -- Flags creator rawset(_G, "createFlags", function(tname, t) for i = 1,#t do rawset(_G, t[i], 2 ^ (i - 1)) table.insert(tname, {string = t[i], value = 2 ^ (i - 1)} ) end end) -- Key constants local KEY_UP = 230 local KEY_DOWN = 238 local KEY_LEFT = 233 local KEY_RIGHT = 235 local KEY_ESC = 27 local KEY_ENTER = 13 local KEY_CONSOLE = 96 -- Menu types rawset(_G, "MMT_NORMAL", 0) rawset(_G, "MMT_SCROLL", 1) rawset(_G, "MMT_CUSTOM", 2) -- Menu item types rawset(_G, "MIT_STRING", 0) rawset(_G, "MIT_HEADER", 1) -- Menu item flags rawset(_G, "MENUITEMFLAGS", {}) createFlags(MENUITEMFLAGS, { "MIF_DISABLED", "MIF_FUNCTION", "MIF_CVAR_STRING", "MIF_CVAR_NUMBER", "MIF_CVAR_SLIDER" -- Sadly, Lua can't read CVar's Possible Values. So this is useless }) rawset(_G, "lmenu", {}) lmenu.version = "1.0" -- version, just to display on load lmenu.active = false -- if active lmenu.current = -1 -- current menu table lmenu.cursor = -1 -- the actual cursor item position lmenu.page = -1 -- the actual page on the menu local function LM_ActiveMenu() return lmenu.active and (lmenu.current ~= -1) and (gamestate == GS_LEVEL) end local function LM_Reset() lmenu.active = false lmenu.current = -1 lmenu.cursor = -1 lmenu.page = -1 end local function LM_CloseMenu() if not LM_ActiveMenu() then return end input.setMouseGrab(true) LM_Reset() end local function LM_OpenMenu(menu, page) if (page < 1) and (page > #menu) then CONS_Printf("LM_OpenMenu(): Tried to open a menu with specified page out of bounds.") return end lmenu.active = true lmenu.page = page lmenu.current = menu lmenu.cursor = lmenu.current[lmenu.page].attributes.start.item end local function LM_PrevPage() if not LM_ActiveMenu() then return end if (lmenu.current[lmenu.page].attributes.previous.page < 1) then LM_CloseMenu() return end lmenu.cursor = lmenu.current[lmenu.page].attributes.previous.item lmenu.page = lmenu.current[lmenu.page].attributes.previous.page end local function LM_GoToPage(page) if not LM_ActiveMenu() then return end if (page < 1) and (page > #lmenu.current) then CONS_Printf("LM_GoToPage(): Tried to go out of bounds.") return end lmenu.page = page lmenu.cursor = lmenu.current[lmenu.page].attributes.start.item end local function LM_ExecuteThinker() if not LM_ActiveMenu() then return end if lmenu.current[lmenu.page].functions.ticker then lmenu.current[lmenu.page].functions.ticker(lmenu.current) end end local function LM_PressedKey(keyevent, code) return (keyevent.num == code) end local function test_function(menu) LM_GoToPage(2) end local function test_function2(menu) LM_PrevPage() end local test_menu = { { attributes = { pos = {30, 30}, header = {text = "Test Menu", color = V_YELLOWMAP}, start = {item = 2}, previous = {page = -1, item = -1}, style = MMT_NORMAL }, functions = {handler = nil, ticker = nil, drawer = nil}, content = { {id = MIT_HEADER, flags = 0, string = "Test Header", color = 0, func = nil, cvar = nil, pos_y = 0}, {id = MIT_STRING, flags = 0, string = "Test String", color = 0, func = nil, cvar = nil, pos_y = 12}, {id = MIT_STRING, flags = MIF_FUNCTION, string = "Test Function...", color = 0, func = test_function, cvar = nil, pos_y = 22}, {id = MIT_STRING, flags = MIF_CVAR_STRING, string = "Test CVar String", color = 0, func = nil, cvar = CV_FindVar("cooplives"), pos_y = 42}, {id = MIT_STRING, flags = MIF_CVAR_NUMBER, string = "Test CVar Number", color = 0, func = nil, cvar = CV_FindVar("inttime"), pos_y = 52}, {id = MIT_STRING, flags = MIF_DISABLED, string = "Test Disabled", color = 0, func = nil, cvar = nil, pos_y = 72}, } }, { attributes = { pos = {30, 30}, header = {text = "Top Secret Cheats", color = V_YELLOWMAP}, start = {item = 2}, previous = {page = 1, item = 3}, style = MMT_NORMAL }, functions = {handler = nil, ticker = nil, drawer = nil}, content = { {id = MIT_HEADER, flags = 0, string = "Use with caution", color = 0, func = nil, cvar = nil, pos_y = 0}, {id = MIT_STRING, flags = 0, string = "Teleport to Area 51...", color = 0, func = nil, cvar = nil, pos_y = 12}, {id = MIT_STRING, flags = 0, string = "Remove Tails...", color = 0, func = nil, cvar = nil, pos_y = 22}, {id = MIT_STRING, flags = MIF_FUNCTION, string = "Restart the universe...", color = V_REDMAP, func = test_function2, cvar = nil, pos_y = 102}, } } } addHook("PreThinkFrame", do if LM_ActiveMenu() then input.setMouseGrab(false) LM_ExecuteThinker() end end) addHook("KeyDown", function (keyevent) if LM_ActiveMenu() then if LM_PressedKey(keyevent, KEY_CONSOLE) then return end if LM_PressedKey(keyevent, KEY_ESC) then LM_PrevPage() return true end if LM_PressedKey(keyevent, KEY_UP) then repeat lmenu.cursor = $ - 1 if not lmenu.cursor then lmenu.cursor = #lmenu.current[lmenu.page].content end until (lmenu.current[lmenu.page].content[lmenu.cursor].id ~= MIT_HEADER) and not (lmenu.current[lmenu.page].content[lmenu.cursor].flags & MIF_DISABLED) S_StartSound(nil, sfx_menu1, consoleplayer) return true end if LM_PressedKey(keyevent, KEY_DOWN) then repeat lmenu.cursor = $ + 1 if lmenu.cursor > #lmenu.current[lmenu.page].content then lmenu.cursor = 1 end until (lmenu.current[lmenu.page].content[lmenu.cursor].id ~= MIT_HEADER) and not (lmenu.current[lmenu.page].content[lmenu.cursor].flags & MIF_DISABLED) S_StartSound(nil, sfx_menu1, consoleplayer) return true end if LM_PressedKey(keyevent, KEY_LEFT) then if (lmenu.current[lmenu.page].content[lmenu.cursor].flags & (MIF_CVAR_STRING|MIF_CVAR_NUMBER)) then COM_BufInsertText(consoleplayer, "add " .. lmenu.current[lmenu.page].content[lmenu.cursor].cvar.name .. " -1") S_StartSound(nil, sfx_menu1, consoleplayer) end return true end if LM_PressedKey(keyevent, KEY_RIGHT) then if (lmenu.current[lmenu.page].content[lmenu.cursor].flags & (MIF_CVAR_STRING|MIF_CVAR_NUMBER)) then COM_BufInsertText(consoleplayer, "add " .. lmenu.current[lmenu.page].content[lmenu.cursor].cvar.name .. " 1") S_StartSound(nil, sfx_menu1, consoleplayer) end return true end if LM_PressedKey(keyevent, KEY_ENTER) then if (lmenu.current[lmenu.page].content[lmenu.cursor].flags & MIF_FUNCTION) then if lmenu.current[lmenu.page].content[lmenu.cursor].func lmenu.current[lmenu.page].content[lmenu.cursor].func(lmenu.current) S_StartSound(nil, sfx_menu1, consoleplayer) end end return true end if lmenu.current[lmenu.page].functions.handler then lmenu.current[lmenu.page].functions.handler(keyevent) return true end return true end return end) local function LM_DrawStandardMenu(v, menu) if menu.attributes.header then local color_flag = (menu.attributes.header.color and menu.attributes.header.color or 0) v.drawString(160, 10, menu.attributes.header.text, V_ALLOWLOWERCASE|color_flag, "center") end local cursor_y = 0 for item_index, item in ipairs(menu.content) do if (item_index == lmenu.cursor) then cursor_y = item.pos_y end if (item.id == MIT_STRING) then local selected_flag = (item_index == lmenu.cursor) and V_YELLOWMAP or (item.color and item.color or 0) local disabled_flag = (item.flags & MIF_DISABLED) and V_TRANSLUCENT or 0 local string_flags = disabled_flag|selected_flag v.drawString(menu.attributes.pos[1], menu.attributes.pos[2] + item.pos_y, item.string, V_ALLOWLOWERCASE|string_flags, "left") if (item.flags & MIF_CVAR_STRING) then v.drawString(320 - menu.attributes.pos[1], (menu.attributes.pos[2] + item.pos_y), item.cvar.string, V_ALLOWLOWERCASE|string_flags, "right") if (item_index == lmenu.cursor) then v.drawString((((320 - menu.attributes.pos[1]) - 10) - v.stringWidth(item.cvar.string)) - ((leveltime % 9) / 5), (menu.attributes.pos[2] + item.pos_y), "\x1C", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) v.drawString(((320 - menu.attributes.pos[1]) + 2) + ((leveltime % 9) / 5), (menu.attributes.pos[2] + item.pos_y), "\x1D", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) end elseif (item.flags & MIF_CVAR_NUMBER) then v.drawString(320 - menu.attributes.pos[1], (menu.attributes.pos[2] + item.pos_y), item.cvar.value, V_ALLOWLOWERCASE|string_flags, "right") if (item_index == lmenu.cursor) then v.drawString((((320 - menu.attributes.pos[1]) - 10) - v.stringWidth(item.cvar.value)) - ((leveltime % 9) / 5), (menu.attributes.pos[2] + item.pos_y), "\x1C", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) v.drawString(((320 - menu.attributes.pos[1]) + 2) + ((leveltime % 9) / 5), (menu.attributes.pos[2] + item.pos_y), "\x1D", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) end elseif (item.flags & MIF_CVAR_SLIDER) then v.drawScaled(((320 - menu.attributes.pos[1]) - 78) * FRACUNIT, (menu.attributes.pos[2] + item.pos_y) * FRACUNIT, FRACUNIT, v.cachePatch("M_SLIDEL"), string_flags) for i = 1, 9 do v.drawScaled((((320 - menu.attributes.pos[1]) - 78) + (i * 8)) * FRACUNIT, (menu.attributes.pos[2] + item.pos_y) * FRACUNIT, FRACUNIT, v.cachePatch("M_SLIDEM"), string_flags) end v.drawScaled((((320 - menu.attributes.pos[1]) - 78) + 80) * FRACUNIT, (menu.attributes.pos[2] + item.pos_y) * FRACUNIT, FRACUNIT, v.cachePatch("M_SLIDER"), string_flags) end elseif (item.id == MIT_HEADER) then local color_flag = (item.color and item.color or V_YELLOWMAP) v.drawString(19, menu.attributes.pos[2] + item.pos_y, item.string, V_ALLOWLOWERCASE|color_flag, "left") v.drawFill(19, (menu.attributes.pos[2] + item.pos_y) + 9, 281, 1, 73); v.drawFill(300, (menu.attributes.pos[2] + item.pos_y) + 9, 1, 1, 26); v.drawFill(19, (menu.attributes.pos[2] + item.pos_y) + 10, 281, 1, 26); end end v.drawScaled((menu.attributes.pos[1] - 24) * FRACUNIT, (menu.attributes.pos[2] + cursor_y) * FRACUNIT, FRACUNIT, v.cachePatch("M_CURSOR")) end local scrollareaheight = 88 local function LM_DrawScrollMenu(v, menu) if menu.attributes.header then local color_flag = (menu.attributes.header.color and menu.attributes.header.color or 0) v.drawString(160, 10, menu.attributes.header.text, V_ALLOWLOWERCASE|color_flag, "center") end local arrowup, arrowdown = false, false if (menu.content[#menu.content].pos_y > scrollareaheight) then arrowup, arrowdown = false, true end local offset_y = 0 if (menu.content[lmenu.cursor].pos_y >= scrollareaheight) then arrowup, arrowdown = true, true offset_y = menu.content[lmenu.cursor].pos_y - scrollareaheight if (((menu.content[#menu.content].pos_y + menu.attributes.pos[2]) - offset_y) <= (scrollareaheight * 2)) then arrowup, arrowdown = true, false offset_y = (menu.content[#menu.content].pos_y + menu.attributes.pos[2]) - (scrollareaheight * 2) end end if arrowup then v.drawString(menu.attributes.pos[1] - 20, menu.attributes.pos[2] - ((leveltime % 9) / 5), "\x1A", V_YELLOWMAP) end if arrowdown then v.drawString(menu.attributes.pos[1] - 20, ((scrollareaheight * 2)) + ((leveltime % 9) / 5), "\x1B", V_YELLOWMAP) end local cursor_y = 0 for item_index, item in ipairs(menu.content) do if (item_index == lmenu.cursor) then cursor_y = item.pos_y - offset_y end if (((item.pos_y + menu.attributes.pos[2]) - offset_y) < menu.attributes.pos[2]) or (((item.pos_y + menu.attributes.pos[2]) - offset_y) > (scrollareaheight * 2)) then continue end local string_position = (menu.attributes.pos[2] + item.pos_y) - offset_y if (item.id == MIT_STRING) then local selected_flag = (item_index == lmenu.cursor) and V_YELLOWMAP or (item.color and item.color or 0) local disabled_flag = (item.flags & MIF_DISABLED) and V_TRANSLUCENT or 0 local string_flags = disabled_flag|selected_flag v.drawString(menu.attributes.pos[1], string_position, item.string, V_ALLOWLOWERCASE|string_flags, "left") if (item.flags & MIF_CVAR_STRING) then v.drawString(320 - menu.attributes.pos[1], string_position, item.cvar.string, V_ALLOWLOWERCASE|string_flags, "right") if (item_index == lmenu.cursor) then v.drawString((((320 - menu.attributes.pos[1]) - 10) - v.stringWidth(item.cvar.string)) - ((leveltime % 9) / 5), string_position, "\x1C", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) v.drawString(((320 - menu.attributes.pos[1]) + 2) + ((leveltime % 9) / 5), string_position, "\x1D", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) end elseif (item.flags & MIF_CVAR_NUMBER) then v.drawString(320 - menu.attributes.pos[1], string_position, item.cvar.value, V_ALLOWLOWERCASE|string_flags, "right") if (item_index == lmenu.cursor) then v.drawString((((320 - menu.attributes.pos[1]) - 10) - v.stringWidth(item.cvar.value)) - ((leveltime % 9) / 5), string_position, "\x1C", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) v.drawString(((320 - menu.attributes.pos[1]) + 2) + ((leveltime % 9) / 5), string_position, "\x1D", V_YELLOWMAP|V_ALLOWLOWERCASE|string_flags) end elseif (item.flags & MIF_CVAR_SLIDER) then v.drawScaled(((320 - menu.attributes.pos[1]) - 78) * FRACUNIT, string_position * FRACUNIT, FRACUNIT, v.cachePatch("M_SLIDEL"), string_flags) for i = 1, 9 do v.drawScaled((((320 - menu.attributes.pos[1]) - 78) + (i * 8)) * FRACUNIT, string_position * FRACUNIT, FRACUNIT, v.cachePatch("M_SLIDEM"), string_flags) end v.drawScaled((((320 - menu.attributes.pos[1]) - 78) + 80) * FRACUNIT, string_position * FRACUNIT, FRACUNIT, v.cachePatch("M_SLIDER"), string_flags) end elseif (item.id == MIT_HEADER) then local color_flag = (item.color and item.color or V_YELLOWMAP) v.drawString(19, string_position, item.string, V_ALLOWLOWERCASE|color_flag, "left") v.drawFill(19, string_position + 9, 281, 1, 73); v.drawFill(300, string_position + 9, 1, 1, 26); v.drawFill(19, string_position + 10, 281, 1, 26); end end v.drawScaled((menu.attributes.pos[1] - 24) * FRACUNIT, (menu.attributes.pos[2] + cursor_y) * FRACUNIT, FRACUNIT, v.cachePatch("M_CURSOR")) end hud.add(function (v, _, _) if LM_ActiveMenu() then v.fadeScreen(31, 5) for menu_index, menu in ipairs(lmenu.current) do if (menu_index == lmenu.page) then if (menu.attributes.style == MMT_NORMAL) then LM_DrawStandardMenu(v, menu) elseif (menu.attributes.style == MMT_SCROLL) then LM_DrawScrollMenu(v, menu) elseif (menu.attributes.style == MMT_CUSTOM) then if menu.functions.drawer then menu.functions.drawer(v, menu) end end end end end end, "game") rawset(_G, "KEY_UP", KEY_UP) rawset(_G, "KEY_DOWN", KEY_DOWN) rawset(_G, "KEY_LEFT", KEY_LEFT) rawset(_G, "KEY_RIGHT", KEY_RIGHT) rawset(_G, "KEY_ESC", KEY_ESC) rawset(_G, "KEY_ENTER", KEY_ENTER) rawset(_G, "KEY_CONSOLE", KEY_CONSOLE) rawset(_G, "LM_PressedKey", LM_PressedKey) rawset(_G, "LM_GoToPage", LM_GoToPage) rawset(_G, "LM_PrevPage", LM_PrevPage) rawset(_G, "LM_OpenMenu", LM_OpenMenu) rawset(_G, "LM_CloseMenu", LM_CloseMenu) rawset(_G, "LM_ActiveMenu", LM_ActiveMenu) rawset(_G, "LM_DrawScrollMenu", LM_DrawScrollMenu) rawset(_G, "LM_DrawStandardMenu", LM_DrawStandardMenu) print("Lugent's Menus version " .. lmenu.version .. ", made by Lugent") print("This addon is not reusable")