feat: Debug HUD overlay — live left-side text panel (own HUD element injected via UIManager.create_player_hud hook, plain-text widget, game's own HudElementBase pipeline): per-tag target/cooldown/priority-switch, skull state + hacking flag, last omni pick + reject counts, last server accept verdict; toggled by 'Debug HUD Overlay' (takes effect at mission start), fed at 5Hz

This commit is contained in:
2026-10-04 09:49:47 -04:00
parent 3dcb228387
commit 2535c1aaaf
7 changed files with 235 additions and 0 deletions
@@ -330,6 +330,7 @@ mod:io_dofile("AutoMark/scripts/mods/AutoMark/mark/base_mark")
mod:io_dofile("AutoMark/scripts/mods/AutoMark/mark/companion_mark") mod:io_dofile("AutoMark/scripts/mods/AutoMark/mark/companion_mark")
mod:io_dofile("AutoMark/scripts/mods/AutoMark/mark/focus_target_mark") mod:io_dofile("AutoMark/scripts/mods/AutoMark/mark/focus_target_mark")
mod:io_dofile("AutoMark/scripts/mods/AutoMark/mark/servo_skull_mark") mod:io_dofile("AutoMark/scripts/mods/AutoMark/mark/servo_skull_mark")
mod:io_dofile("AutoMark/scripts/mods/AutoMark/ui/debug_hud")
-- Mod Enabled -- Mod Enabled
mod.on_enabled = function(initial_call) mod.on_enabled = function(initial_call)
@@ -174,6 +174,11 @@ local widgets = {
type = "checkbox", type = "checkbox",
default_value = false, default_value = false,
}, },
{
setting_id = "debug_hud",
type = "checkbox",
default_value = false,
},
}, },
}, },
{ {
@@ -67,6 +67,16 @@ local localization = {
["zh-cn"] = "在聊天窗口中打印调试消息。关闭可保持游戏界面安静;后台诊断不受影响。", ["zh-cn"] = "在聊天窗口中打印调试消息。关闭可保持游戏界面安静;后台诊断不受影响。",
["zh-tw"] = "在聊天視窗中打印除錯訊息。關閉可保持遊戲介面安靜;背景診斷不受影響。", ["zh-tw"] = "在聊天視窗中打印除錯訊息。關閉可保持遊戲介面安靜;背景診斷不受影響。",
}, },
debug_hud = {
en = "Debug HUD Overlay",
["zh-cn"] = "调试HUD面板",
["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"] = "在螢幕左側顯示即時文字面板:目前標記目標、冷卻、骷髏/狗狀態及伺服器接受結果。下個任務開始時生效。",
},
omni_mark = { omni_mark = {
en = "360° Marking", en = "360° Marking",
["zh-cn"] = "全向标记", ["zh-cn"] = "全向标记",
@@ -449,6 +449,7 @@ function mod:auto_hack(dt, t, fixed_frame)
local smart_tag_system = context.smart_tag_system local smart_tag_system = context.smart_tag_system
local tag = smart_tag_system and smart_tag_system:unit_tag(skull_mark_check.unit) local tag = smart_tag_system and smart_tag_system:unit_tag(skull_mark_check.unit)
local accepted = tag and tag._template and tag._template.name == TAG_NAMES.SERVO_SKULL_TAG local accepted = tag and tag._template and tag._template.name == TAG_NAMES.SERVO_SKULL_TAG
mod.last_skull_confirm = { n = skull_mark_check.n, accepted = accepted == true }
mod:diag(string.format("OMNI-CONFIRM skull-mark %s accepted=%s alive=%s state=%s", skull_mark_check.n, tostring(accepted == true), tostring(HEALTH_ALIVE[skull_mark_check.unit] == true), mod:debug_servo_skull_state())) mod:diag(string.format("OMNI-CONFIRM skull-mark %s accepted=%s alive=%s state=%s", skull_mark_check.n, tostring(accepted == true), tostring(HEALTH_ALIVE[skull_mark_check.unit] == true), mod:debug_servo_skull_state()))
skull_mark_check = nil skull_mark_check = nil
end end
@@ -864,9 +864,12 @@ elseif tag_name == TAG_NAMES.SERVO_SKULL_TAG then
end end
table.sort(parts) table.sort(parts)
mod:diag(string.format("OMNI-DIAG pass[%s] best=%s rejects=[%s]", tostring(tag_name), tostring(best_unit_breed_name), table.concat(parts, ","))) mod:diag(string.format("OMNI-DIAG pass[%s] best=%s rejects=[%s]", tostring(tag_name), tostring(best_unit_breed_name), table.concat(parts, ",")))
mod.omni_last_rejects = table.concat(parts, ",")
omni_rejects = nil omni_rejects = nil
end end
mod.omni_last = { tag_name = tag_name, best = best_unit_breed_name, cands = num_hits }
if best_unit ~= marked_unit then if best_unit ~= marked_unit then
return best_unit, best_unit_tag, best_unit_is_dormant_daemonhost return best_unit, best_unit_tag, best_unit_is_dormant_daemonhost
end end
@@ -0,0 +1,123 @@
-- 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.
local mod = get_mod("AutoMark")
local mod_settings = mod.settings
local TAG_NAMES = mod.TAG_NAMES
local Managers = Managers
local ScriptUnit_extension = ScriptUnit.extension
mod.debug_hud_last = {}
local ELEMENT_CLASS_NAME = "HudElementAutoMarkDebug"
mod:hook(CLASS.UIManager, "create_player_hud",
function(func, self, peer_id, local_player_id, elements, visibility_groups)
if mod_settings.debug_hud and elements then
local already = false
for i = 1, #elements do
if elements[i].class_name == ELEMENT_CLASS_NAME then
already = true
break
end
end
if not already then
elements[#elements + 1] = {
class_name = ELEMENT_CLASS_NAME,
filename = "AutoMark/scripts/mods/AutoMark/ui/hud_element_debug",
use_hud_scale = true,
visibility_groups = { "alive", "dead" },
}
end
end
return func(self, peer_id, local_player_id, elements, visibility_groups)
end)
local function unit_breed_name(unit)
if not unit then
return "-"
end
local ok, name = pcall(function()
local unit_data_extension = ScriptUnit_extension(unit, "unit_data_system")
local breed = unit_data_extension and unit_data_extension._breed
return breed and breed.name or "?"
end)
return ok and name or "?"
end
local function tag_line(label, tag_name)
local mark_context = mod.mark_context
local tag_context = mark_context and mark_context[tag_name]
local tag = tag_context and tag_context.tag
local target = tag and tag._target_unit
local class_settings = mod:get_class_settings(tag_name)
return string.format("%-5s | %-22s | cd %4.1f sw %4.1f | %s",
label,
unit_breed_name(target),
tag_context and tag_context.cooldown or 0,
tag_context and tag_context.priority_switch_cooldown or 0,
class_settings and class_settings.toggle_class and "ON" or "OFF")
end
function mod:build_debug_hud_lines()
local buf = mod.debug_hud_last
for i = 1, #buf do
buf[i] = nil
end
if not mod_settings.debug_hud or not mod_settings.toggle_mod then
return buf
end
local context = mod.context
local game_mode_manager = Managers.state.game_mode
buf[#buf + 1] = "AUTOMARK " .. tostring(context.class_name) .. " " .. (game_mode_manager and tostring(game_mode_manager:game_mode_name()) or "?")
local has_skull = context.class_name == "cryptic" and context.has_servo_skull
local has_dog = context.class_name == "adamant" and context.has_companion
if has_skull then
buf[#buf + 1] = tag_line("SKULL", TAG_NAMES.SERVO_SKULL_TAG)
buf[#buf + 1] = "skull state: " .. tostring(mod:debug_servo_skull_state()) .. (mod:is_servo_skull_hacking() and " HACKING" or "")
end
if has_dog then
buf[#buf + 1] = tag_line("DOG", TAG_NAMES.COMPANION_TAG)
end
buf[#buf + 1] = tag_line("PING", TAG_NAMES.ENEMY_TAG)
local last = mod.omni_last
if last then
buf[#buf + 1] = string.format("PICK[%s] best=%s cands=%s", tostring(last.tag_name), tostring(last.best), tostring(last.cands))
if mod.omni_last_rejects then
buf[#buf + 1] = " rejects: " .. mod.omni_last_rejects
end
end
local confirm = mod.last_skull_confirm
if confirm then
buf[#buf + 1] = "SERVER last " .. tostring(confirm.n) .. ": " .. (confirm.accepted and "ACCEPTED" or "REJECTED")
end
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
end
if self._player.viewport_name ~= "player1" then
return
end
hud_build_timer = hud_build_timer - dt
if hud_build_timer <= 0 then
hud_build_timer = 0.2
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
mod:diag("OMNI-HUD builder error:", tostring(err))
end
end
end)
@@ -0,0 +1,92 @@
-- 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 = {
240,
235,
180,
255,
},
},
},
}, "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)
self._rebuild_time = self._rebuild_time - dt
if self._rebuild_time > 0 then
return
end
self._rebuild_time = 0.2
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