fix: 360 omni marking now enumerates smart-tag registry instead of physics sweep
linear_sphere_sweep returns only a capped hit table (no count) and its slots flood with world-geometry actors, starving real enemies behind the player (crash: nil num_hits compare; then silent no-marks). Candidates now come from smart_tag_system._unit_extension_data (alive, breed-typed, in range) — geometry can't interfere, LOS gates still apply unless omni_mark_ignore_los. OMNI-DIAG candidate/reject logging kept, gated on debug_mode.
This commit is contained in:
@@ -26,7 +26,7 @@ Only the exception tables need manual updates. All in `scripts/mods/AutoMark/tar
|
||||
- `ENEMY_EXTENTS` — melee-aim half-extents for big units (fallback = breed's own values, then 0.3)
|
||||
- `IGNORE_EXECUTION_ORDER_FORCE_MARK_BREED_NAMES` / `IGNORE_EXECUTION_ORDER_PRIORITY_BREED_NAMES` / `IGNORE_THREAT_PRIORITY_BREED_NAMES` — special-mechanic exceptions (netgunner, ritualists, grenadiers, bomber)
|
||||
- Hard-coded name/behaviour checks: `renegade_netgunner` (sticky targeting), `cultist_ritualist` / `chaos_mutator_ritualist` (ignore-unaggroed exemption), `chaos_poxwalker_bomber` (priority-0 default), dormant-daemonhost logic keyed off the `witch` breed tag
|
||||
- `360° marking` lives in `find_auto_mark_target_unit`: a full-sphere `linear_sphere_sweep` replaces the forward raycast when `omni_mark` is on (settings: `omni_mark`, `omni_mark_ignore_los`).
|
||||
- `360° marking` lives in `find_auto_mark_target_unit`: when `omni_mark` is on, candidates come from enumerating `smart_tag_system._unit_extension_data` (alive, `_target_type == "breed"`, in range) instead of the forward raycast — do NOT use `PhysicsWorld.linear_sphere_sweep` for this: it returns only a capped table (no count), floods slots with world-geometry actors, and starves real enemies. LOS gates still apply unless `omni_mark_ignore_los` (settings: `omni_mark`, `omni_mark_ignore_los`). With `debug_mode` on, logs `OMNI-DIAG` lines: candidate breeds per sweep + per-pass reject reasons.
|
||||
|
||||
## Procedure after a game update
|
||||
|
||||
|
||||
@@ -60,7 +60,12 @@ local AIM_CHECK_FILTER = "filter_player_ping_target_selectio
|
||||
local AIM_CHECK_MAX_HITS = 64
|
||||
local AIM_CHECK_MIN_RADIUS = 1
|
||||
local AIM_CHECK_RADIUS_PER_METER = 0.052
|
||||
local OMNI_MAX_HITS = 128
|
||||
local omni_rejects = nil
|
||||
local function omni_reject(reason)
|
||||
if omni_rejects then
|
||||
omni_rejects[reason] = (omni_rejects[reason] or 0) + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Params
|
||||
local visibility_raycast_object = nil
|
||||
@@ -581,11 +586,50 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
|
||||
|
||||
-- hit unit list: forward raycast, or full-sphere sweep when omni mark is enabled
|
||||
local omni_mark = mod_settings.omni_mark
|
||||
if mod_settings.debug_mode and not mod.omni_diag_settings_logged then
|
||||
mod.omni_diag_settings_logged = true
|
||||
mod:info(string.format("OMNI-DIAG settings: omni_mark=%s ignore_los=%s debug=%s", tostring(omni_mark), tostring(mod_settings.omni_mark_ignore_los), tostring(mod_settings.debug_mode)))
|
||||
end
|
||||
local ray_origin, forward, right, up = smart_targeting_extension:_targeting_parameters()
|
||||
local hits, num_hits, hits_use_actor
|
||||
local hits, num_hits, hits_are_units
|
||||
if omni_mark then
|
||||
hits, num_hits = PhysicsWorld_linear_sphere_sweep(smart_targeting_extension._physics_world, ray_origin, ray_origin + forward * max_range, max_range, OMNI_MAX_HITS, "types", "both", "collision_filter", COLLISION_FILTER, "report_initial_overlap", true)
|
||||
hits_use_actor = true
|
||||
-- enumerate smart-taggable units directly; physics sphere sweeps get swamped by world geometry actors
|
||||
hits = {}
|
||||
num_hits = 0
|
||||
local unit_extension_data = smart_tag_system._unit_extension_data
|
||||
local max_range_squared = max_range * max_range
|
||||
if unit_extension_data then
|
||||
for unit, smart_tag_extension in pairs(unit_extension_data) do
|
||||
if HEALTH_ALIVE[unit] and smart_tag_extension._target_type == "breed" then
|
||||
local position = Unit_world_position(unit, 1)
|
||||
if Vector3_distance_squared(position, ray_origin) <= max_range_squared then
|
||||
num_hits = num_hits + 1
|
||||
hits[num_hits] = { unit = unit, position = position }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
hits_are_units = true
|
||||
omni_diag_counter = (omni_diag_counter or 0) + 1
|
||||
omni_rejects = mod_settings.debug_mode and omni_diag_counter % 10 == 1 and {} or nil
|
||||
if omni_rejects then
|
||||
local counts, ordered_names = {}, {}
|
||||
for i = 1, num_hits do
|
||||
local unit_data_extension = ScriptUnit_extension(hits[i].unit, "unit_data_system")
|
||||
local breed = unit_data_extension and unit_data_extension._breed
|
||||
local key = breed and breed.name or "?"
|
||||
if counts[key] == nil then
|
||||
counts[key] = 0
|
||||
ordered_names[#ordered_names + 1] = key
|
||||
end
|
||||
counts[key] = counts[key] + 1
|
||||
end
|
||||
local name_parts = {}
|
||||
for _, n in ipairs(ordered_names) do
|
||||
name_parts[#name_parts + 1] = n .. "x" .. counts[n]
|
||||
end
|
||||
mod:info(string.format("OMNI-DIAG candidates=%d breeds=[%s]", num_hits, table.concat(name_parts, ",")))
|
||||
end
|
||||
else
|
||||
hits, num_hits = PhysicsWorld_raycast(smart_targeting_extension._physics_world, ray_origin, forward, max_range, "all", "collision_filter", COLLISION_FILTER)
|
||||
end
|
||||
@@ -650,14 +694,21 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
|
||||
|
||||
for i = 1, num_hits do
|
||||
local hit = hits[i]
|
||||
local hit_actor = hits_use_actor and hit.actor or hit[INDEX_ACTOR]
|
||||
if not hit_actor then
|
||||
goto continue
|
||||
local hit_unit, hit_unit_center_pos
|
||||
if hits_are_units then
|
||||
hit_unit = hit.unit
|
||||
hit_unit_center_pos = hit.position
|
||||
else
|
||||
local hit_actor = hit[INDEX_ACTOR]
|
||||
if not hit_actor then
|
||||
omni_reject("no_actor")
|
||||
goto continue
|
||||
end
|
||||
hit_unit = Actor_unit(hit_actor)
|
||||
end
|
||||
|
||||
local hit_unit = Actor_unit(hit_actor)
|
||||
-- ignore player unit, already marked unit and dead unit
|
||||
if hit_unit == player_unit or hit_unit == marked_unit or canceled_units[hit_unit] or not HEALTH_ALIVE[hit_unit] then
|
||||
omni_reject("skip_player_dead")
|
||||
goto continue
|
||||
end
|
||||
|
||||
@@ -665,11 +716,13 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
|
||||
local hit_unit_breed_data = hit_unit_data_extension and hit_unit_data_extension._breed
|
||||
-- ignore untaggable unit
|
||||
if not hit_unit_breed_data or hit_unit_breed_data.smart_tag_target_type ~= "breed" or hit_unit_breed_data.faction_name == "imperium" or hit_unit_breed_data.unit_template_name ~= "minion" then
|
||||
omni_reject("untaggable")
|
||||
goto continue
|
||||
end
|
||||
|
||||
local hit_unit_breed_name = hit_unit_breed_data.name
|
||||
if best_unit_is_sticky_targeting and hit_unit_breed_name ~= "renegade_netgunner" then
|
||||
omni_reject("sticky1")
|
||||
goto continue
|
||||
end
|
||||
|
||||
@@ -684,12 +737,14 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
|
||||
or IGNORE_EXECUTION_ORDER_FORCE_MARK_BREED_NAMES[hit_unit_breed_name]
|
||||
or hit_unit_is_dormant_daemonhost
|
||||
then
|
||||
omni_reject("priority_or_group")
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
|
||||
local hit_unit_is_attacking_player = hit_unit_attacking_unit_id == player_unit_id
|
||||
if best_unit_is_sticky_targeting and not hit_unit_is_attacking_player then
|
||||
omni_reject("sticky2")
|
||||
goto continue
|
||||
end
|
||||
|
||||
@@ -706,43 +761,53 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
|
||||
end
|
||||
|
||||
if hit_unit_score < best_unit_score then
|
||||
omni_reject("lower_score")
|
||||
goto continue
|
||||
end
|
||||
|
||||
local hit_unit_center_pos = Actor_world_bounds(hit_actor)
|
||||
if not hit_unit_center_pos then
|
||||
hit_unit_center_pos = Actor_world_bounds(hit[INDEX_ACTOR])
|
||||
end
|
||||
local to_hit_unit_center = hit_unit_center_pos - ray_origin
|
||||
local distance = Vector3_length(to_hit_unit_center)
|
||||
-- filter unit by range
|
||||
if distance < min_range or distance > max_range then
|
||||
omni_reject("out_of_range")
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- prefer the closer unit on score ties (hit order is arbitrary for the omni sweep)
|
||||
if hit_unit_score == best_unit_score and distance >= best_unit_distance then
|
||||
omni_reject("tie_distance")
|
||||
goto continue
|
||||
end
|
||||
|
||||
local hit_unit_tag = smart_tag_system:unit_tag(hit_unit)
|
||||
if tag_name == TAG_NAMES.ENEMY_TAG then
|
||||
if hit_unit_tag then
|
||||
omni_reject("already_tagged")
|
||||
goto continue
|
||||
end
|
||||
elseif tag_name == TAG_NAMES.VETERAN_TAG then
|
||||
if hit_unit_is_dormant_daemonhost then
|
||||
if hit_unit_tag then
|
||||
omni_reject("vet_tagged")
|
||||
goto continue
|
||||
end
|
||||
else
|
||||
if not is_focus_target_tag_target_valid(hit_unit, hit_unit_tag, hit_unit_breed_name, hit_unit_attacking_unit_id, removed_units) then
|
||||
omni_reject("vet_invalid")
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
elseif tag_name == TAG_NAMES.COMPANION_TAG then
|
||||
if not is_cyber_mastiff_tag_target_valid(hit_unit, hit_unit_tag, hit_unit_breed_name, hit_unit_breed_data, hit_unit_attacking_unit_id, game_session, removed_units, distance, companion_position, hit_unit_center_pos) then
|
||||
omni_reject("companion_invalid")
|
||||
goto continue
|
||||
end
|
||||
elseif tag_name == TAG_NAMES.SERVO_SKULL_TAG then
|
||||
if not is_servo_skull_tag_target_valid(hit_unit, hit_unit_tag, hit_unit_breed_name, hit_unit_breed_data, hit_unit_attacking_unit_id, game_session, removed_units, companion_position, hit_unit_center_pos, max_ability_charges, remaining_capacitance) then
|
||||
omni_reject("skull_invalid")
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
@@ -761,6 +826,7 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
|
||||
end
|
||||
|
||||
if not visible then
|
||||
omni_reject("not_visible")
|
||||
goto continue
|
||||
end
|
||||
|
||||
@@ -777,6 +843,16 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
|
||||
::continue::
|
||||
end
|
||||
|
||||
if omni_rejects then
|
||||
local parts = {}
|
||||
for k, v in pairs(omni_rejects) do
|
||||
parts[#parts + 1] = k .. "=" .. v
|
||||
end
|
||||
table.sort(parts)
|
||||
mod:info(string.format("OMNI-DIAG pass[%s] best=%s rejects=[%s]", tostring(tag_name), tostring(best_unit_breed_name), table.concat(parts, ",")))
|
||||
omni_rejects = nil
|
||||
end
|
||||
|
||||
if best_unit ~= marked_unit then
|
||||
return best_unit, best_unit_tag, best_unit_is_dormant_daemonhost
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user