diff --git a/AutoMark/scripts/mods/AutoMark/targeting/custom_targeting.lua b/AutoMark/scripts/mods/AutoMark/targeting/custom_targeting.lua index e964137..1caebd3 100644 --- a/AutoMark/scripts/mods/AutoMark/targeting/custom_targeting.lua +++ b/AutoMark/scripts/mods/AutoMark/targeting/custom_targeting.lua @@ -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