Skip to content

Commit

Permalink
Add cursor feature
Browse files Browse the repository at this point in the history
  • Loading branch information
dextercd committed Dec 31, 2023
1 parent 9abff74 commit 2630a8a
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 10 deletions.
251 changes: 251 additions & 0 deletions component-explorer/cursor.lua
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions component-explorer/lua_console.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
44 changes: 39 additions & 5 deletions component-explorer/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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. ",
Expand All @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
28 changes: 27 additions & 1 deletion component-explorer/settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Binary file added component-explorer/ui/cursor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2630a8a

Please sign in to comment.