Skip to content

Commit

Permalink
Add mechanism for mods to extend CE's 'View' menu
Browse files Browse the repository at this point in the history
  • Loading branch information
dextercd committed Sep 8, 2024
1 parent 4bbe70c commit d8b499d
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 15 deletions.
51 changes: 51 additions & 0 deletions component-explorer/load_menu_extensions.lua
Original file line number Diff line number Diff line change
@@ -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
48 changes: 33 additions & 15 deletions component-explorer/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
108 changes: 108 additions & 0 deletions component-explorer/menu_extensions.lua
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit d8b499d

Please sign in to comment.