From 2630a8af8a134ae4b6a0a2be475ebbbf96f06d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dexter=20Castor=20D=C3=B6pping?= Date: Sun, 31 Dec 2023 04:15:23 +0100 Subject: [PATCH] Add cursor feature --- component-explorer/cursor.lua | 251 +++++++++++++++++++++++ component-explorer/lua_console.lua | 1 + component-explorer/main.lua | 44 +++- component-explorer/settings.lua | 28 ++- component-explorer/ui/cursor.png | Bin 0 -> 613 bytes component-explorer/utils/player_util.lua | 9 +- incr_version.sh | 4 +- 7 files changed, 327 insertions(+), 10 deletions(-) create mode 100644 component-explorer/cursor.lua create mode 100644 component-explorer/ui/cursor.png diff --git a/component-explorer/cursor.lua b/component-explorer/cursor.lua new file mode 100644 index 0000000..0ba5638 --- /dev/null +++ b/component-explorer/cursor.lua @@ -0,0 +1,251 @@ +---@module 'component-explorer.help' +local help = dofile_once("mods/component-explorer/help.lua") + +---@module 'component-explorer.utils.player_util' +local player_util = dofile_once("mods/component-explorer/utils/player_util.lua") + +---@module 'component-explorer.utils.ce_settings' +local ce_settings = dofile_once("mods/component-explorer/utils/ce_settings.lua") + +dofile_once("data/scripts/lib/utilities.lua") + +local TEMPORARY_VISIBLE = 30 + +local cursor = {} + +---@enum CURSOR_PARENT +cursor.CURSOR_PARENT = { + camera = "camera", + world = "world", + player = "player", +} + +---Get name of enum value +---@param parent CURSOR_PARENT +---@return string +function cursor.parent_name(parent) + if parent == cursor.CURSOR_PARENT.world then return "World" end + if parent == cursor.CURSOR_PARENT.camera then return "Camera" end + if parent == cursor.CURSOR_PARENT.player then return "Player" end + error("Bad enum value") +end + +---@type CURSOR_PARENT[] +local cursor_options = {} +for _, v in pairs(cursor.CURSOR_PARENT) do + table.insert(cursor_options, v) +end +table.sort(cursor_options, + function(a, b) return cursor.parent_name(a) < cursor.parent_name(b) end) + +cursor.current_parent = ce_settings.get("parent") --[[@as CURSOR_PARENT]] +cursor.rx, cursor.ry = 0, 0 +cursor.keep_visible = ce_settings.get("keep_visible") --[[@as boolean]] +---@type integer +cursor.visible_frames = 0 + +---Get current position of the cursor. +---@return number X coordinate +---@return number Y coordinate +function cursor.pos() + local px, py = cursor.parent_pos() + return px + cursor.rx, py + cursor.ry +end + +---Get coordinate of the thing the cursor is parented to. +---@return number X coordinate +---@return number Y coordinate +function cursor.parent_pos() + if cursor.current_parent == cursor.CURSOR_PARENT.world then + return 0, 0 + end + + if cursor.current_parent == cursor.CURSOR_PARENT.camera then + local cx, cy, cw, ch = GameGetCameraBounds() + return cx + cw * 0.5, cy + ch * 0.5 + end + + if cursor.current_parent == cursor.CURSOR_PARENT.player then + local p = player_util.get_player() + if p then + local x, y = EntityGetTransform(p) + return x, y + end + end + + return 0, 0 +end + +---Set absolute position of cursor +---@param x number +---@param y number +---@param make_visible boolean? Make visible for a short moment +function cursor.set_pos(x, y, make_visible) + if make_visible then + cursor.visible_frames = TEMPORARY_VISIBLE + end + local px, py = cursor.parent_pos() + cursor.set_relative_pos(x - px, y - py) +end + +---Set cursor position relative to parent +---@param rx number +---@param ry number +function cursor.set_relative_pos(rx, ry) + cursor.rx = rx + cursor.ry = ry +end + +function cursor.set_parent(new_parent) + local cx, cy = cursor.pos() + cursor.current_parent = new_parent + + -- Update position to be the same as before + + -- Except if it's now camera relative we force it to be on screen + if cursor.current_parent == cursor.CURSOR_PARENT.camera then + local cam_x, cam_y, cam_w, cam_h = GameGetCameraBounds() + if cx < cam_x or cx > cam_x + cam_w or cy < cam_y or cy > cam_y + cam_h then + -- Would be off screen, recenter + cursor.set_relative_pos(0, 0) + return + end + end + + cursor.set_pos(cx, cy) +end + +local gui = GuiCreate() + +local function world2screen(x, y) + local virt_x = MagicNumbersGetValue("VIRTUAL_RESOLUTION_X") + local virt_y = MagicNumbersGetValue("VIRTUAL_RESOLUTION_Y") + local cx, cy = GameGetCameraBounds() + local screen_width, screen_height = GuiGetScreenDimensions(gui) + + -- Bunch of weird constants in here but it seems to improve the accuracy of + -- the conversion. + return + (x - cx - 2.8) / (virt_x - 0) * screen_width, + (y - cy - 0.5) / (virt_y * 0.99) * screen_height +end + +function cursor.update() + GuiStartFrame(gui) + + local show = false + local alpha = 1 + + if cursor.config_open or cursor.keep_visible then + show = true + elseif cursor.visible_frames > 0 then + show = true + alpha = cursor.visible_frames / TEMPORARY_VISIBLE + end + + if show then + if cursor.visible_frames > 0 then + cursor.visible_frames = cursor.visible_frames - 1 + end + + local w, h = GuiGetImageDimensions(gui, "mods/component-explorer/ui/cursor.png") + local x, y = world2screen(cursor.pos()) + GuiOptionsAddForNextWidget(gui, GUI_OPTION.NonInteractive) + GuiImage(gui, 1, x - w * 0.5, y - h * 0.5, "mods/component-explorer/ui/cursor.png", alpha, 1, 1) + end + +end + +local function config_content() + local _ + _, cursor.keep_visible = imgui.Checkbox("Keep visible", cursor.keep_visible) + + imgui.SameLine() + help.marker("Keep the cursor visible even after closing the config window.") + + if cursor.current_parent ~= cursor.CURSOR_PARENT.world then imgui.BeginDisabled() end + + local cx, cy = cursor.pos() + local poschange + poschange, cx, cy = imgui.InputFloat2("Position", cx, cy) + if poschange then + cursor.set_pos(cx, cy) + end + + if cursor.current_parent ~= cursor.CURSOR_PARENT.world then imgui.EndDisabled() end + + if imgui.BeginCombo("Parent", cursor.parent_name(cursor.current_parent)) then + for _, cp in ipairs(cursor_options) do + if imgui.Selectable(cursor.parent_name(cp), cursor.current_parent == cp) then + cursor.set_parent(cp) + end + end + imgui.EndCombo() + end + + if cursor.current_parent ~= cursor.CURSOR_PARENT.world then + imgui.SetNextItemWidth(250) + local relchange, rx, ry = imgui.InputFloat2("Offset", cursor.rx, cursor.ry) + if relchange then + cursor.set_relative_pos(rx, ry) + end + + imgui.SameLine() + if imgui.Button("Zero") then + cursor.set_relative_pos(0, 0) + end + end +end + +local function about_content() + local example = "local x, y = cursor.pos()" + imgui.TextWrapped(table.concat({ + "Component Explorer defines its own cursor that you can place in the world. ", + "CE will use this in the future when it needs a location, for instance, when spawning in items.\n\n", + "You can use the cursors yourself from the Lua console and user scripts.\n", + "Do `" .. example .. "` to get the position of the cursor in the world and use it in the console! :)\n\n", + })) + + imgui.BeginDisabled() + imgui.InputText("##ex", example) + imgui.EndDisabled() + imgui.SameLine() + if imgui.Button("Copy") then + imgui.SetClipboardText(example) + end + + imgui.Dummy(0, 20) + imgui.TextWrapped(table.concat({ + "You can use this window to configure the cursor. ", + "To change the default values, go into the mod settings.\n\n", + "Use CTRL+SHIFT+Click to position the cursor, this works best when using ImGui version 1.15.1 or above." + })) +end + +cursor.config_open = false + +function cursor.config_show() + imgui.SetNextWindowSize(480, 200, imgui.Cond.FirstUseEver) + local show + show, cursor.config_open = imgui.Begin("Cursor Config", cursor.config_open) + + if not show then + return + end + + if imgui.BeginTabBar("##cursor_config") then + if imgui.BeginTabItem("Config") then + config_content() + imgui.EndTabItem() + end + if imgui.BeginTabItem("About") then + about_content() + imgui.EndTabItem() + end + imgui.EndTabBar() + end + + imgui.End() +end + +return cursor diff --git a/component-explorer/lua_console.lua b/component-explorer/lua_console.lua index 9f5501e..6331c62 100644 --- a/component-explorer/lua_console.lua +++ b/component-explorer/lua_console.lua @@ -24,6 +24,7 @@ local console_tools = { ModTextFileGetContent = ModTextFileGetContent, ModTextFileWhoSetContent = ModTextFileWhoSetContent, EZWand = dofile_once("mods/component-explorer/deps/EZWand.lua"), + cursor = dofile_once("mods/component-explorer/cursor.lua"), watch_global = globals.watch, unwatch_global = globals.unwatch, } diff --git a/component-explorer/main.lua b/component-explorer/main.lua index 363b96f..2925db9 100644 --- a/component-explorer/main.lua +++ b/component-explorer/main.lua @@ -45,6 +45,9 @@ local run_flags = dofile_once("mods/component-explorer/run_flags.lua") ---@module 'component-explorer.herd_relation' local herd_relation = dofile_once("mods/component-explorer/herd_relation.lua") +---@module 'component-explorer.cursor' +local cursor = dofile_once("mods/component-explorer/cursor.lua") + local last_frame_run = -1 local is_escape_paused = false @@ -106,17 +109,14 @@ function show_view_menu_items() local _ _, console.open = imgui.MenuItem("Lua Console", sct("CTRL+SHIFT+L"), console.open) _, entity_list.open = imgui.MenuItem("Entity List", sct("CTRL+SHIFT+K"), entity_list.open) - _, herd_relation.open = imgui.MenuItem("Herd Relation", "", herd_relation.open) + _, herd_relation.open = imgui.MenuItem("Herd Relation", "", herd_relation.open) _, wiki_wands.open = imgui.MenuItem("Wiki Wands", "", wiki_wands.open) _, file_viewer.open = imgui.MenuItem("File Viewer", sct("CTRL+SHIFT+F"), file_viewer.open) _, translations.open = imgui.MenuItem("Translations", "", translations.open) local clicked clicked, entity_picker.open = imgui.MenuItem("Entity Picker...", sct("CTRL+SHIFT+E"), entity_picker.open) - if clicked then - imgui.SetWindowFocus(nil) - end - + if clicked then imgui.SetWindowFocus(nil) end if imgui.IsItemHovered() then help.tooltip(table.concat({ "Allows you to move your mouse over an entity to open a window for it. ", @@ -125,6 +125,8 @@ function show_view_menu_items() })) end + _, cursor.config_open = imgui.MenuItem("Cursor Config", "", cursor.config_open) + _, globals.open = imgui.MenuItem("Globals", "", globals.open) _, run_flags.open = imgui.MenuItem("Run Flags", "", run_flags.open) _, mod_settings.open = imgui.MenuItem("Mod Settings", "", mod_settings.open) @@ -241,6 +243,7 @@ function update_ui(paused, current_frame_run) keyboard_shortcuts() main_window() + cursor.update() if window_open_about then show_about_window() @@ -282,6 +285,10 @@ function update_ui(paused, current_frame_run) translations.show() end + if cursor.config_open then + cursor.config_show() + end + if run_flags.open then run_flags.show() end @@ -295,6 +302,24 @@ function update_ui(paused, current_frame_run) end end +local function is_imgui_version(major, minor, patch) + if not imgui.version_info then + return false + end + + local parts = imgui.version_info.ndi.parts + if parts[1] > major then return true end + if parts[1] == major then + if parts[2] > minor then return true end + if parts[2] == minor then + return parts[3] >= patch + end + end + return false +end + +local good_mouse_handling = is_imgui_version(1, 15, 1) + ---Handles the keyboard shortcuts. function keyboard_shortcut_items() if imgui.IsKeyPressed(imgui.Key.E) then @@ -329,4 +354,13 @@ function keyboard_shortcut_items() if imgui.IsKeyPressed(imgui.Key.U) then console.user_scripts_open = not console.user_scripts_open end + + if good_mouse_handling then + imgui.SetNextFrameWantCaptureMouse(true) + end + + if imgui.IsMouseClicked(imgui.MouseButton.Left) then + local cx, cy = DEBUG_GetMouseWorld() + cursor.set_pos(cx, cy, true) + end end diff --git a/component-explorer/settings.lua b/component-explorer/settings.lua index 2b71e51..ccf01a8 100644 --- a/component-explorer/settings.lua +++ b/component-explorer/settings.lua @@ -140,7 +140,33 @@ mod_settings = { scope = MOD_SETTING_SCOPE_RUNTIME, } } - } + }, + { + category_id = "cursor", + ui_name = "Cursor settings", + ui_description = "Default settings for the cursor at launch.", + settings = { + { + id = "keep_visible", + ui_name = "Visible outside config", + ui_description = "Keep the cursor visible even when the cursor config is closed.", + value_default = false, + scope = MOD_SETTING_SCOPE_RUNTIME, + }, + { + id = "parent", + ui_name = "Cursor parent", + ui_description = "Relative to what should the cursor be positioned?", + value_default = "player", + values = { + {"camera", "Camera"}, + {"world", "World"}, + {"player", "Player"}, + }, + scope = MOD_SETTING_SCOPE_RUNTIME, + } + }, + }, } function ModSettingsUpdate(init_scope) diff --git a/component-explorer/ui/cursor.png b/component-explorer/ui/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..181317fc35f0c4460f8e25862eff64b7f123b13d GIT binary patch literal 613 zcmV-r0-F7aP)EX>4Tx04R}tkv&MmP!xqvQ$>-AgB?T!GgK#wpduY>6^c+H)C#RSn7s54nlvOS zE{=k0!NH%!s)LKOt`4q(Aov5~>f)s6A|>9J6k5c1;qgAsyXWxUeSpxYFwN?U1DbA| z>10C8=2pd?SA;N%0AiSwnPtpLQVPEHbx)mCcTt|@-}h(rt9gq70g*V)4AUmwAfDZ{ z4bJ<-VOEk=;&b8&lP*a7$aTfzH_kz@3Dp}fAb%yn8LNMaF7kRU=q4P{hdBTBnYiiI?tCw%<_Qpd2CnqBzuEv|pQP8@ zTI2{A*aj}H+nTZmT00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru=L{bKIyT|X3WWdw02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{002EnL_t(2&tp`-@%ujmxd5MLLUK&_48Ezs%7{;%iGczz zlr`fDRf6K=#xudF8qT3jPTFQ*U|?XRBv%0dV67q3UY8Nn00000NkvXXu0mjf9LWVV literal 0 HcmV?d00001 diff --git a/component-explorer/utils/player_util.lua b/component-explorer/utils/player_util.lua index 625fdea..8fc9664 100644 --- a/component-explorer/utils/player_util.lua +++ b/component-explorer/utils/player_util.lua @@ -1,8 +1,13 @@ local player_util = {} - +---@return integer? function player_util.get_player() - return EntityGetWithTag("player_unit")[1] + local p = EntityGetWithTag("player_unit")[1] + if p then + return p + end + + return EntityGetWithTag("polymorphed_player")[1] end return player_util diff --git a/incr_version.sh b/incr_version.sh index 061263b..ff6d902 100755 --- a/incr_version.sh +++ b/incr_version.sh @@ -66,7 +66,7 @@ git add CMakeLists.txt git commit -m "Release $new_version" git tag "release-$new_version" +./do.sh + echo echo git push origin HEAD --tags - -./do.sh