feat: spoof _contextual_tag_template_name for the flagged auto-hack target — tagcheck proved the game silently rejects off-LOS set_tag (27/29 tag_applied=false); the game re-validates tags through this forward-LOS check, so flagging the chosen unit's extension makes its own validation accept the order

This commit is contained in:
2026-10-03 15:13:50 -04:00
parent e98d5e166e
commit e62d6dc6e8
@@ -29,6 +29,29 @@ local Vector3_distance = Vector3.distance
local HACK_TAG_NAME = "hacking_over_here_companion"
local CANCEL_INTERVAL = 0.5
-- set_tag() for off-LOS units is silently rejected because the game re-validates the tag
-- through the target extension's forward-LOS _contextual_tag_template_name(); flag the single
-- auto-hack target on its extension so the game's own validation accepts the order.
if not mod.omni_hack_spoof_hooked then
local spoof_class, spoof_class_name
for name, cls in pairs(CLASS) do
if type(cls) == "table" and rawget(cls, "_contextual_tag_template_name") ~= nil then
spoof_class, spoof_class_name = cls, name
break
end
end
if spoof_class then
mod.omni_hack_spoof_hooked = true
mod.omni_hack_spoof_class = spoof_class_name
mod:hook(spoof_class, "_contextual_tag_template_name", function(previous_hook, self, player_unit, tag_name)
if tag_name == "companion_order" and self._automark_hack_order then
return HACK_TAG_NAME
end
return previous_hook(self, player_unit, tag_name)
end)
end
end
local function servo_skull_mark_callback()
local smart_tag_system = context.smart_tag_system
if not smart_tag_system then
@@ -235,7 +258,7 @@ local function find_hackable_target_unit_omni()
end
local player_position = Unit_world_position(player_unit, 1)
local selected_unit, selected_distance, selected_name
local selected_unit, selected_distance, selected_name, selected_extension
local diag_total
if mod_settings.debug_mode then
diag_total = 0
@@ -255,6 +278,7 @@ local function find_hackable_target_unit_omni()
end
if distance <= OMNI_HACK_RANGE and (not selected_distance or distance < selected_distance) then
selected_unit, selected_distance = unit, distance
selected_extension = target_extension
if diag_samples then
selected_name = breed_data.name
end
@@ -273,7 +297,10 @@ local function find_hackable_target_unit_omni()
end
if selected_unit then
-- don't retarget the same unit forever if the game refuses to accept the tag
mod._hack_blacklist[selected_unit] = { t = HACK_RETRY_DELAY, n = selected_name }
mod._hack_blacklist[selected_unit] = { t = HACK_RETRY_DELAY, n = selected_name, ext = selected_extension }
if selected_extension then
selected_extension._automark_hack_order = true
end
end
return selected_unit, selected_distance
end
@@ -288,6 +315,9 @@ function mod:auto_hack(dt, t, fixed_frame)
for unit, entry in pairs(mod._hack_blacklist) do
entry.t = entry.t - dt
if entry.t <= 0 then
if entry.ext then
entry.ext._automark_hack_order = nil
end
mod._hack_blacklist[unit] = nil
end
end
@@ -297,7 +327,7 @@ function mod:auto_hack(dt, t, fixed_frame)
if hack_check.ttl <= 0 then
if mod_settings.debug_mode then
local smart_tag_system = context.smart_tag_system
mod:diag(string.format("OMNI-HACK-DIAG tagcheck %s alive=%s tag_applied=%s skull_hacking=%s", tostring(hack_check.n), tostring(HEALTH_ALIVE[hack_check.unit] == true), tostring(smart_tag_system and smart_tag_system:unit_tag(hack_check.unit) ~= nil), tostring(mod:is_servo_skull_hacking())))
mod:diag(string.format("OMNI-HACK-DIAG tagcheck %s alive=%s tag_applied=%s skull_hacking=%s spoof=%s", tostring(hack_check.n), tostring(HEALTH_ALIVE[hack_check.unit] == true), tostring(smart_tag_system and smart_tag_system:unit_tag(hack_check.unit) ~= nil), tostring(mod:is_servo_skull_hacking()), tostring(mod.omni_hack_spoof_class)))
end
hack_check = nil
end