refactor: debug overlay abandons the UIHud element pipeline entirely — draws direct to level_world screen gui (Gui.rect dark panel + Gui.slug_text lines) per FpsDoctor's proven pattern in the same mods dir: per-world gui handle validation, pcall+backoff, 5Hz rebuild, RESOLUTION_LOOKUP scaling; X/Y now anchor top-left; deletes hud_element_debug.lua + require-path/injection/recreate machinery; works hub/MeatGrinder/mission, applies live
This commit is contained in:
@@ -73,9 +73,9 @@ local localization = {
|
||||
["zh-tw"] = "除錯HUD面板",
|
||||
},
|
||||
debug_hud_tooltip = {
|
||||
en = "Shows a live text panel (left side of the screen) with current mark targets, cooldowns, skull/dog state and server accept results. Applied at the next mission start.",
|
||||
["zh-cn"] = "在屏幕左侧显示实时文本面板:当前标记目标、冷却、骷髅/狗状态及服务器接受结果。下个任务开始时生效。",
|
||||
["zh-tw"] = "在螢幕左側顯示即時文字面板:目前標記目標、冷卻、骷髏/狗狀態及伺服器接受結果。下個任務開始時生效。",
|
||||
en = "Shows a live overlay panel (dark box, top-left by default) with current mark targets, cooldowns, skull/dog state and server accept results. Applies immediately, works in hub, Meat Grinder and missions.",
|
||||
["zh-cn"] = "显示实时悬浮面板(深色底,默认左上):当前标记目标、冷却、骷髅/狗状态及服务器接受结果。立即生效,大厅/训练场/任务均可用。",
|
||||
["zh-tw"] = "顯示即時浮動面板(深色底,預設左上):目前標記目標、冷卻、骷髏/狗狀態及伺服器接受結果。立即生效,大廳/訓練場/任務均可用。",
|
||||
},
|
||||
debug_hud_x = {
|
||||
en = "Overlay X",
|
||||
@@ -93,9 +93,9 @@ local localization = {
|
||||
["zh-tw"] = "距螢幕左緣的距離(UI像素),立即生效。",
|
||||
},
|
||||
debug_hud_y_tooltip = {
|
||||
en = "Vertical offset from screen middle (positive = down), in UI pixels. Applies immediately.",
|
||||
["zh-cn"] = "相对屏幕垂直中线的偏移(正值向下),单位UI像素,立即生效。",
|
||||
["zh-tw"] = "相對螢幕垂直中線的偏移(正值向下),單位UI像素,立即生效。",
|
||||
en = "Distance from the top edge of the screen, in UI pixels. Applies immediately.",
|
||||
["zh-cn"] = "距屏幕顶部的距离(UI像素),立即生效。",
|
||||
["zh-tw"] = "距螢幕頂緣的距離(UI像素),立即生效。",
|
||||
},
|
||||
omni_mark = {
|
||||
en = "360° Marking",
|
||||
|
||||
@@ -1,104 +1,66 @@
|
||||
-- AutoMark debug HUD (mod side): injects the HUD element definition at mission
|
||||
-- HUD creation and feeds it a rebuilt status line buffer at 5 Hz.
|
||||
-- AutoMark debug HUD (mod side): a live status overlay drawn straight onto the
|
||||
-- level world's screen gui (the pattern FpsDoctor uses in this very mods folder).
|
||||
-- Deliberately avoids the UIHud element pipeline: direct Gui.rect + Gui.slug_text
|
||||
-- with per-world gui handle validation, pcall-guarded with backoff on failure.
|
||||
local mod = get_mod("AutoMark")
|
||||
local mod_settings = mod.settings
|
||||
local TAG_NAMES = mod.TAG_NAMES
|
||||
local Managers = Managers
|
||||
local World = World
|
||||
local Gui = Gui
|
||||
local Vector3 = Vector3
|
||||
local Vector2 = Vector2
|
||||
local Color = Color
|
||||
local RESOLUTION_LOOKUP = RESOLUTION_LOOKUP
|
||||
local ScriptUnit_extension = ScriptUnit.extension
|
||||
|
||||
mod.debug_hud_last = {}
|
||||
|
||||
mod:diag("OMNI-HUD load: settings debug_hud=", tostring(mod_settings.debug_hud), " chat=", tostring(mod_settings.debug_chat), " debug=", tostring(mod_settings.debug_mode))
|
||||
|
||||
local ELEMENT_CLASS_NAME = "HudElementAutoMarkDebug"
|
||||
local ELEMENT_FILENAME = "AutoMark/scripts/mods/AutoMark/ui/hud_element_debug"
|
||||
local FONT_TYPE = "arial"
|
||||
local REBUILD_INTERVAL = 0.2
|
||||
local DRAW_FAIL_BACKOFF = 120
|
||||
|
||||
-- the game HUD requires element files through the game require system; without
|
||||
-- registering here, require(ELEMENT_FILENAME) from UIHud._add_element never finds
|
||||
-- the mod file (DMF loads mod scripts through its own file loader, not package.path)
|
||||
mod:add_require_path(ELEMENT_FILENAME)
|
||||
local gui, gui_world, font_path
|
||||
local fail_cooldown = 0
|
||||
local rebuild_acc = REBUILD_INTERVAL
|
||||
local first_draw_logged = false
|
||||
|
||||
mod:hook("UIHud", "init",
|
||||
function(func, self, elements, visibility_groups, params)
|
||||
if elements then
|
||||
if not mod.omni_hud_init_count then
|
||||
mod.omni_hud_init_count = 0
|
||||
local function level_world()
|
||||
local world_manager = Managers.world
|
||||
if world_manager and world_manager:has_world("level_world") then
|
||||
return world_manager:world("level_world")
|
||||
end
|
||||
mod.omni_hud_init_count = mod.omni_hud_init_count + 1
|
||||
mod:diag("OMNI-HUD init#", mod.omni_hud_init_count, "debug_hud=", tostring(mod_settings.debug_hud), " elements=", tostring(#elements))
|
||||
local idx = nil
|
||||
for i = 1, #elements do
|
||||
if elements[i].class_name == ELEMENT_CLASS_NAME then
|
||||
idx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if mod_settings.debug_hud and not idx then
|
||||
elements[#elements + 1] = {
|
||||
class_name = ELEMENT_CLASS_NAME,
|
||||
filename = ELEMENT_FILENAME,
|
||||
use_hud_scale = true,
|
||||
visibility_groups = { "alive", "dead" },
|
||||
}
|
||||
if not mod.debug_hud_inject_logged then
|
||||
mod.debug_hud_inject_logged = true
|
||||
mod:diag("OMNI-HUD inject: ADDED")
|
||||
end
|
||||
elseif not mod_settings.debug_hud and idx then
|
||||
table.remove(elements, idx)
|
||||
end
|
||||
end
|
||||
return func(self, elements, visibility_groups, params)
|
||||
end)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- live toggling: UIHud init only runs on HUD creation, so when the setting flips we
|
||||
-- rebuild the HUD on the spot (pattern borrowed from NumericUI's recreate_hud)
|
||||
local recreate_timer = 0
|
||||
local last_debug_hud = nil
|
||||
|
||||
mod.update = function (dt)
|
||||
local want = mod_settings.debug_hud and true or false
|
||||
if last_debug_hud == nil then
|
||||
last_debug_hud = want
|
||||
return
|
||||
local function ensure_gui()
|
||||
local world = level_world()
|
||||
if not world then
|
||||
gui = nil
|
||||
gui_world = nil
|
||||
font_path = nil
|
||||
return nil
|
||||
end
|
||||
|
||||
if want ~= last_debug_hud then
|
||||
last_debug_hud = want
|
||||
recreate_timer = 0.5
|
||||
return
|
||||
if gui and gui_world == world then
|
||||
return gui
|
||||
end
|
||||
gui = World.create_screen_gui(world, "immediate")
|
||||
gui_world = world
|
||||
font_path = nil
|
||||
return gui
|
||||
end
|
||||
|
||||
if recreate_timer <= 0 then
|
||||
return
|
||||
local function ensure_font()
|
||||
if font_path then
|
||||
return font_path
|
||||
end
|
||||
|
||||
recreate_timer = recreate_timer - dt
|
||||
if recreate_timer > 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local ui_manager = Managers.ui
|
||||
local hud = ui_manager and ui_manager._hud
|
||||
if not hud or not hud._element_definitions or not hud._visibility_groups then
|
||||
return
|
||||
end
|
||||
|
||||
local player = Managers.player and Managers.player:local_player(1)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
local elements = hud._element_definitions
|
||||
local visibility_groups = hud._visibility_groups
|
||||
hud:destroy()
|
||||
ui_manager:create_player_hud(player:peer_id(), player:local_player_id(), elements, visibility_groups)
|
||||
mod:diag("OMNI-HUD: live recreate, debug_hud ->", tostring(want))
|
||||
end)
|
||||
if not ok then
|
||||
mod:diag("OMNI-HUD: live recreate FAILED:", tostring(err))
|
||||
local font_data = Managers.font and Managers.font:data_by_type(FONT_TYPE)
|
||||
if font_data then
|
||||
font_path = font_data.path
|
||||
end
|
||||
return font_path
|
||||
end
|
||||
|
||||
local function unit_breed_name(unit)
|
||||
@@ -170,23 +132,108 @@ function mod:build_debug_hud_lines()
|
||||
return buf
|
||||
end
|
||||
|
||||
local hud_build_timer = 0
|
||||
mod:hook_safe(CLASS.PlayerUnitSmartTargetingExtension, "fixed_update",
|
||||
function(self, unit, dt, t, fixed_frame)
|
||||
if not mod_settings.debug_hud or not mod_settings.toggle_mod then
|
||||
return
|
||||
local cached = { x = 0, top_y = 0, line_h = 0, font_px = 0, panel_w = 0, panel_h = 0, pad = 0 }
|
||||
|
||||
local function text_width(gui_handle, font, font_px, text)
|
||||
local ok, min, max = pcall(Gui.slug_text_extents, gui_handle, text, font, font_px)
|
||||
if ok and min and max then
|
||||
return max[1] - min[1]
|
||||
end
|
||||
if self._player.viewport_name ~= "player1" then
|
||||
return #text * font_px * 0.55
|
||||
end
|
||||
|
||||
local function draw_inner(dt)
|
||||
local gui_handle = ensure_gui()
|
||||
if not gui_handle then
|
||||
return
|
||||
end
|
||||
|
||||
hud_build_timer = hud_build_timer - dt
|
||||
if hud_build_timer <= 0 then
|
||||
hud_build_timer = 0.2
|
||||
local font = ensure_font()
|
||||
if not font then
|
||||
return
|
||||
end
|
||||
|
||||
local buf = mod.debug_hud_last
|
||||
local n = #buf
|
||||
if n == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local r = RESOLUTION_LOOKUP
|
||||
local scale = (r and r.scale) or 1.0
|
||||
local sw = (r and r.width) or 1920
|
||||
|
||||
local font_px = math.max(8, math.floor(16 * scale + 0.5))
|
||||
local line_h = math.floor(20 * scale + 0.5)
|
||||
local pad = math.floor(6 * scale + 0.5)
|
||||
|
||||
local margin_x = math.floor((mod_settings.debug_hud_x or 25) * scale + 0.5)
|
||||
local margin_y = math.floor((mod_settings.debug_hud_y or 24) * scale + 0.5)
|
||||
|
||||
local maxw = 0
|
||||
for i = 1, n do
|
||||
local w = text_width(gui_handle, font, font_px, buf[i])
|
||||
if w > maxw then
|
||||
maxw = w
|
||||
end
|
||||
end
|
||||
|
||||
cached.x = math.min(margin_x, math.max(0, sw - maxw - pad * 2 - 8))
|
||||
cached.top_y = margin_y
|
||||
cached.line_h = line_h
|
||||
cached.font_px = font_px
|
||||
cached.panel_w = maxw + pad * 2
|
||||
cached.panel_h = n * line_h + pad * 2
|
||||
cached.pad = pad
|
||||
|
||||
Gui.rect(gui_handle,
|
||||
Vector3(cached.x - pad, cached.top_y - pad, 899),
|
||||
Vector2(cached.panel_w, cached.panel_h),
|
||||
Color(165, 10, 10, 10))
|
||||
|
||||
for i = 1, n do
|
||||
local line = buf[i]
|
||||
local col = Color(235, 235, 220, 150)
|
||||
if line:find("REJECTED") or line:find("OFF") or line:find("FAIL") then
|
||||
col = Color(235, 255, 110, 100)
|
||||
elseif i == 1 then
|
||||
col = Color(235, 140, 235, 255)
|
||||
end
|
||||
Gui.slug_text(gui_handle, line, font, font_px, Vector3(cached.x, cached.top_y + line_h * (i - 1), 900), nil, col)
|
||||
end
|
||||
|
||||
if not first_draw_logged then
|
||||
first_draw_logged = true
|
||||
mod:diag("OMNI-HUD first draw OK, lines=", n)
|
||||
end
|
||||
end
|
||||
|
||||
local function draw(dt)
|
||||
if fail_cooldown > 0 then
|
||||
fail_cooldown = fail_cooldown - 1
|
||||
return
|
||||
end
|
||||
local ok, err = pcall(draw_inner, dt)
|
||||
if not ok then
|
||||
gui, gui_world, font_path = nil, nil, nil
|
||||
fail_cooldown = DRAW_FAIL_BACKOFF
|
||||
mod:diag("OMNI-HUD draw failed, backing off:", tostring(err))
|
||||
end
|
||||
end
|
||||
|
||||
mod.update = function (dt)
|
||||
if not mod_settings.debug_hud or not mod_settings.toggle_mod then
|
||||
return
|
||||
end
|
||||
|
||||
rebuild_acc = rebuild_acc + dt
|
||||
if rebuild_acc >= REBUILD_INTERVAL then
|
||||
rebuild_acc = 0
|
||||
local ok, err = pcall(mod.build_debug_hud_lines, mod)
|
||||
if not ok and mod_settings.debug_mode and not mod.debug_hud_err_logged then
|
||||
mod.debug_hud_err_logged = true
|
||||
if not ok then
|
||||
mod:diag("OMNI-HUD builder error:", tostring(err))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
draw(dt)
|
||||
end
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
-- AutoMark debug HUD element: plain-text live status panel (fork feature).
|
||||
-- Instantiated by the game HUD pipeline (UIHud._add_element requires this file
|
||||
-- by definition.filename). Content is produced by AutoMark mod side
|
||||
-- (ui/debug_hud.lua -> mod.debug_hud_last) so this file stays dumb.
|
||||
local UIWorkspaceSettings = require("scripts/settings/ui/ui_workspace_settings")
|
||||
local UIWidget = require("scripts/managers/ui/ui_widget")
|
||||
|
||||
require("scripts/foundation/utilities/color")
|
||||
|
||||
local mod = get_mod("AutoMark")
|
||||
|
||||
local HudElementAutoMarkDebug = class("HudElementAutoMarkDebug", "HudElementBase")
|
||||
|
||||
local panel_size = { 860, 800 }
|
||||
|
||||
local scenegraph_definition = {
|
||||
screen = UIWorkspaceSettings.screen,
|
||||
panel = {
|
||||
horizontal_alignment = "left",
|
||||
parent = "screen",
|
||||
vertical_alignment = "middle",
|
||||
size = panel_size,
|
||||
position = {
|
||||
25,
|
||||
0,
|
||||
10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local widget_definitions = {
|
||||
automark_debug = UIWidget.create_definition({
|
||||
{
|
||||
pass_type = "text",
|
||||
style_id = "debug_text",
|
||||
value_id = "debug_text",
|
||||
style = {
|
||||
drop_shadow = true,
|
||||
font_size = 19,
|
||||
font_type = "proxima_nova_bold",
|
||||
horizontal_alignment = "left",
|
||||
vertical_alignment = "top",
|
||||
text_horizontal_alignment = "left",
|
||||
text_vertical_alignment = "top",
|
||||
line_spacing = 1.2,
|
||||
size = panel_size,
|
||||
offset = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
text_color = Color.terminal_text_header(255, true),
|
||||
},
|
||||
},
|
||||
}, "panel"),
|
||||
}
|
||||
|
||||
HudElementAutoMarkDebug.init = function (self, parent, draw_layer, start_scale)
|
||||
HudElementAutoMarkDebug.super.init(self, parent, draw_layer, start_scale, {
|
||||
scenegraph_definition = scenegraph_definition,
|
||||
widget_definitions = widget_definitions,
|
||||
})
|
||||
self._rebuild_time = 0
|
||||
self._last_text = nil
|
||||
end
|
||||
|
||||
HudElementAutoMarkDebug.update = function (self, dt, t, ui_renderer, render_settings, input_service)
|
||||
HudElementAutoMarkDebug.super.update(self, dt, t, ui_renderer, render_settings, input_service)
|
||||
|
||||
self._rebuild_time = self._rebuild_time - dt
|
||||
if self._rebuild_time > 0 then
|
||||
return
|
||||
end
|
||||
self._rebuild_time = 0.2
|
||||
|
||||
local panel = self._ui_scenegraph and self._ui_scenegraph.panel
|
||||
if panel and panel.local_position then
|
||||
local lp = panel.local_position
|
||||
local x = mod.settings.debug_hud_x or 25
|
||||
local y = mod.settings.debug_hud_y or 0
|
||||
if lp[1] ~= x or lp[2] ~= y then
|
||||
lp[1] = x
|
||||
lp[2] = y
|
||||
end
|
||||
end
|
||||
|
||||
local buf = mod.debug_hud_last
|
||||
local text = table.concat(buf, "\n")
|
||||
|
||||
if text ~= self._last_text then
|
||||
self._last_text = text
|
||||
local widget = self._widgets_by_name.automark_debug
|
||||
if widget then
|
||||
widget.content.debug_text = text
|
||||
widget.dirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return HudElementAutoMarkDebug
|
||||
Reference in New Issue
Block a user