From d8b499d221a3a4fece38e01d4ba966698946c635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dexter=20Castor=20D=C3=B6pping?= Date: Sun, 8 Sep 2024 16:04:33 +0200 Subject: [PATCH] Add mechanism for mods to extend CE's 'View' menu --- component-explorer/load_menu_extensions.lua | 51 +++++++++ component-explorer/main.lua | 48 ++++++--- component-explorer/menu_extensions.lua | 108 ++++++++++++++++++++ 3 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 component-explorer/load_menu_extensions.lua create mode 100644 component-explorer/menu_extensions.lua diff --git a/component-explorer/load_menu_extensions.lua b/component-explorer/load_menu_extensions.lua new file mode 100644 index 0000000..e18f22f --- /dev/null +++ b/component-explorer/load_menu_extensions.lua @@ -0,0 +1,51 @@ +-- See mods/component-explorer/menu_extensions.lua if you want to add your mod +-- to Component Explorer's 'View' menu. Don't mess with this file. + +---@module 'component-explorer.utils.copy' +local copy = dofile_once("mods/component-explorer/utils/copy.lua") + +---@module 'component-explorer.menu_extensions' +local menu_extensions = dofile_once("mods/component-explorer/menu_extensions.lua") + +---@class LoadedMenuExtension : MenuExtension +---@field original_order integer + +---@type LoadedMenuExtension[] +local loaded_extensions = {} + +local string_or_nil = {["string"]=1, ["nil"]=1} +local function_or_nil = {["function"]=1, ["nil"]=1} + +for index, extension in ipairs(menu_extensions.list) do + ---@type LoadedMenuExtension + local loaded = copy.shallow_copy(extension) + + -- Makes no sense to set one but not the other + if not loaded.shortcut or not loaded.check_shortcut then + loaded.shortcut = nil + loaded.check_shortcut = nil + end + + loaded.original_order = index + + if type(loaded.category) == "string" + and type(loaded.name) == "string" + and type(loaded.is_enabled) == "function" + and type(loaded.set_enabled) == "function" + and string_or_nil[type(loaded.shortcut)] + and function_or_nil[type(loaded.check_shortcut)] + then + loaded_extensions[#loaded_extensions+1] = loaded + else + print("Couldn't load extension " .. tostring(loaded.name) + .. " because of a type mismatch") + end +end + +table.sort(loaded_extensions, function(a, b) + if a.category < b.category then return true end + if a.category > b.category then return false end + return a.original_order < b.original_order +end) + +return loaded_extensions diff --git a/component-explorer/main.lua b/component-explorer/main.lua index 4b61b82..faa9086 100644 --- a/component-explorer/main.lua +++ b/component-explorer/main.lua @@ -62,7 +62,8 @@ local repeat_scripts = dofile_once("mods/component-explorer/repeat_scripts.lua") ---@module 'component-explorer.utils.file_util' dofile_once("mods/component-explorer/utils/file_util.lua") -local HAS_KAE_WAYPOINT = ModIsEnabled("kae_waypoint") +---@module 'component-explorer.load_menu_extensions' +local loaded_extensions = dofile_once("mods/component-explorer/load_menu_extensions.lua") local last_frame_run = -1 @@ -149,19 +150,35 @@ function show_view_menu_items() _, run_flags.open = imgui.MenuItem("Run Flags", "", run_flags.open) _, mod_settings.open = imgui.MenuItem("Mod Settings", "", mod_settings.open) - if HAS_KAE_WAYPOINT then - imgui.Separator() - local enabled = ModSettingGet("kae_waypoint.enable") --[[@as boolean]] + imgui.Separator() + _, windows_hidden_entity = imgui.MenuItem("Hide entity windows", "", windows_hidden_entity) + _, windows_hidden_component = imgui.MenuItem("Hide component windows", "", windows_hidden_component) + + local current_category = "" + for _, extension in ipairs(loaded_extensions) do + if extension.category ~= current_category then + current_category = extension.category + if imgui.SeparatorText then + imgui.SeparatorText(current_category) + else + imgui.Separator() + end + end + + local name = extension.name + if not imgui.SeparatorText then + name = extension.category .. ": " .. extension.name + end + + local shortcut = extension.shortcut and sct(extension.shortcut) or "" + + local is_open = extension.is_enabled() local changed - changed, enabled = imgui.MenuItem("Kaedenn Teleport", sct("CTRL+SHIFT+T"), enabled) + changed, is_open = imgui.MenuItem(name, shortcut, is_open) if changed then - ModSettingSetNextValue("kae_waypoint.enable", enabled, false) + extension.set_enabled(is_open) end end - - imgui.Separator() - _, windows_hidden_entity = imgui.MenuItem("Hide entity windows", "", windows_hidden_entity) - _, windows_hidden_component = imgui.MenuItem("Hide component windows", "", windows_hidden_component) end -- Can't know the width before creating the window.. Just an initial value, it's updated @@ -396,17 +413,18 @@ function keyboard_shortcut_items() spawn_stuff.open = not spawn_stuff.open end - if HAS_KAE_WAYPOINT and imgui.IsKeyPressed(imgui.Key.T) then - local toggle = not ModSettingGet("kae_waypoint.enable") - ModSettingSetNextValue("kae_waypoint.enable", toggle, false) - end - if imgui.IsKeyPressed(imgui.Key.W) then local world_entity = GameGetWorldStateEntity() local world_component = EntityGetFirstComponent(world_entity, "WorldStateComponent") toggle_watch_component(world_entity, world_component) end + for _, extension in ipairs(loaded_extensions) do + if extension.check_shortcut and extension.check_shortcut() then + extension.set_enabled(not extension.is_enabled()) + end + end + -- Mouse shortcuts if imgui.IsMouseClicked(imgui.MouseButton.Left) then diff --git a/component-explorer/menu_extensions.lua b/component-explorer/menu_extensions.lua new file mode 100644 index 0000000..e8d6f48 --- /dev/null +++ b/component-explorer/menu_extensions.lua @@ -0,0 +1,108 @@ +local menu_extensions = {} + +--[[ + +# Guide for modders + +Have you written a Noita mod that uses imgui and can be useful for modding or science purposes? +In that case you have my permission to add your mod into Component Explorer's 'View' menu. +This makes it easy for users to find your tool in a well known and convenient place. + +## How-to + +For the purposes of this guide, we're adding a menu entry for an imaginary mod named 'mymod'. + +Inside 'mods/mymod/init.lua' add a line like this: + +ModLuaFileAppend( + "mods/component-explorer/menu_extensions.lua", + "mods/mymod/ce_menu_entry.lua" +) + +Note that you don't need to check if Component Explorer is enabled to do the append. +It'll not have any effect if you run the append when the mod isn't active or installed. + +Next, inside 'mods/mymod/ce_menu_entry.lua' add the following: + +local menu_extensions = dofile_once("mods/component-explorer/menu_extensions.lua") +menu_extensions.list[#menu_extensions.list+1] = { + category = "My Name", + name = "My Mod", + is_enabled = function() + -- Tells CE whether the mod's main window is currently visible. + -- Need to use a method that's visible across mod contexts. + -- For instance: globals, mod settings, run flags, or persistent flags. + return GlobalsGetValue("MYMOD_MAIN_WINDOW") == "1" + end, + set_enabled = function(enabled) + -- CE is telling you whether the user enabled or disabled your entry. + GlobalsSetValue("MYMOD_MAIN_WINDOW", enabled and "1" or "0") + end, + -- These are optional. Please be careful with adding new shortcuts since + -- it's easy to step on someone else's toes with this. + shortcut = "CTRL+SHIFT+N", + check_shortcut = function() + return imgui.IsKeyPressed(imgui.Key.N) + end +} + +And the should be all you need. When you start the game with mymod and Component +Explorer active, mymod should show up in the 'View' menu. + +Please only add entries to menu_extensions.list and don't mess with entries +added by other people. + +## Contact + +Hopefully the above works for you but if it doesn't you can contact me on GitHub +and Discord. + +GitHub: https://github.com/dextercd/Noita-Component-Explorer/issues + +Discord: dextercd or the CE forum post on the Noita Discord + : https://discord.com/channels/453998283174576133/1021520048495542322 + +]] + +---@class MenuExtension +---@field category string +---@field name string +---@field is_enabled fun(): boolean +---@field set_enabled fun(enabled: boolean) +---@field shortcut string? +---@field check_shortcut (fun(): boolean)? + +---@type MenuExtension[] +menu_extensions.list = {} + +if ModIsEnabled("kae_waypoint") then + menu_extensions.list[#menu_extensions.list+1] = { + category = "Kaedenn", + name = "Teleport to Anything", + is_enabled = function() + return not not ModSettingGet("kae_waypoint.enable") + end, + set_enabled = function(enabled) + ModSettingSetNextValue("kae_waypoint.enable", enabled, false) + end, + shortcut = "CTRL+SHIFT+T", + check_shortcut = function() + return imgui.IsKeyPressed(imgui.Key.T) + end + } +end + +if ModIsEnabled("world_radar") then + menu_extensions.list[#menu_extensions.list+1] = { + category = "Kaedenn", + name = "World Information Scanner", + is_enabled = function() + return not not ModSettingGet("world_radar.enable") + end, + set_enabled = function(enabled) + ModSettingSetNextValue("world_radar.enable", enabled, false) + end + } +end + +return menu_extensions