feat: 360-degree omni marking option

- omni_mark: full-sphere sweep (linear_sphere_sweep capsule covering max_range
  around the player) replaces the forward-only ping raycast, so enemies behind
  you / off-screen become mark candidates
- omni_mark_ignore_los: optional sub-toggle, skips line-of-sight checks entirely
- prefer nearest unit on score ties (sweep hit order is arbitrary)
- bypass FOV dot gate in is_target_pingable when omni_mark is on
- LOS check budget 10 -> 16 per frame
This commit is contained in:
2026-10-03 06:09:31 -04:00
parent a2de1dfd1d
commit 5862b9194f
4 changed files with 59 additions and 7 deletions
@@ -72,6 +72,8 @@ local mod_settings = {
focus_target_switch_override_manual = mod:get("focus_target_switch_override_manual") or false,
focus_target_switch_melee = mod:get("focus_target_switch_melee") or false,
focus_target_switch_range = mod:get("focus_target_switch_range") or false,
omni_mark = mod:get("omni_mark") or false,
omni_mark_ignore_los = mod:get("omni_mark_ignore_los") or false,
}
mod.settings = mod_settings
if mod:get("capacitance_retention_elite_threshold_negative_zero") then
@@ -169,6 +169,18 @@ local widgets = {
type = "checkbox",
default_value = false
},
{
setting_id = "omni_mark",
type = "checkbox",
default_value = false,
sub_widgets = {
{
setting_id = "omni_mark_ignore_los",
type = "checkbox",
default_value = false,
},
},
},
}
},
{
@@ -57,6 +57,26 @@ local localization = {
["zh-cn"] = "调试模式",
["zh-tw"] = "除錯模式",
},
omni_mark = {
en = "360° Marking",
["zh-cn"] = "全向标记",
["zh-tw"] = "全向標記",
},
omni_mark_tooltip = {
en = "Find mark targets in all directions, including off-screen enemies behind you, instead of only under the crosshair.",
["zh-cn"] = "向所有方向寻找标记目标,包括屏幕外和身后的敌人,而不再仅限准星下方。",
["zh-tw"] = "向所有方向尋找標記目標,包括螢幕外和身後的敵人,而不再僅限準星下方。",
},
omni_mark_ignore_los = {
en = "Ignore Line of Sight",
["zh-cn"] = "忽略视线",
["zh-tw"] = "忽略視線",
},
omni_mark_ignore_los_tooltip = {
en = "Mark enemies even without a clear line of sight (through walls).",
["zh-cn"] = "即使没有清晰视线也能标记敌人(穿墙)。",
["zh-tw"] = "即使沒有清晰視線也能標記敵人(穿牆)。",
},
-- arbites settings
adamant_settings = {
en = "Arbites Settings",
@@ -53,13 +53,14 @@ local INDEX_NORMAL = 3
local INDEX_ACTOR = 4
local COLLISION_FILTER = "filter_player_ping_target_selection"
local EMPTY_TABLE = {}
local MAX_VISIBILITY_CHECKS_PER_FRAME = 10
local MAX_VISIBILITY_CHECKS_PER_FRAME = 16
local VIEW_CHECK_MIN_DISTANCE = 3
local VIEW_CHECK_MIN_DOT = 0.75
local AIM_CHECK_FILTER = "filter_player_ping_target_selection"
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
-- Params
local visibility_raycast_object = nil
@@ -524,7 +525,7 @@ function mod:is_target_pingable(target_unit)
local forward = Vector3_flat(Quaternion_forward(first_person_component.rotation))
local dot = Vector3_dot(Vector3_normalize(forward), Vector3_normalize(to_target))
if dot < VIEW_CHECK_MIN_DOT then
return false
return mod_settings.omni_mark == true
end
local first_person_extension = ScriptUnit_extension(player_unit, "first_person_system")
@@ -578,9 +579,16 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
return
end
-- raycast for hit unit list
-- hit unit list: forward raycast, or full-sphere sweep when omni mark is enabled
local omni_mark = mod_settings.omni_mark
local ray_origin, forward, right, up = smart_targeting_extension:_targeting_parameters()
local hits, num_hits = PhysicsWorld_raycast(smart_targeting_extension._physics_world, ray_origin, forward, max_range, "all", "collision_filter", COLLISION_FILTER)
local hits, num_hits, hits_use_actor
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
else
hits, num_hits = PhysicsWorld_raycast(smart_targeting_extension._physics_world, ray_origin, forward, max_range, "all", "collision_filter", COLLISION_FILTER)
end
if not hits or num_hits <= 0 then
return
@@ -624,9 +632,11 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
local best_unit_is_dormant_daemonhost = false
local best_unit_marked_by_execution_order = false
local best_unit_is_sticky_targeting = false
local best_unit_distance = math.huge
local marked_unit = marked_tag and marked_tag._target_unit
if marked_unit then
best_unit = marked_unit
best_unit_distance = Vector3_length(Unit_world_position(best_unit, 1) - ray_origin)
local best_unit_data_extension = ScriptUnit_extension(best_unit, "unit_data_system")
local best_unit_breed_data = best_unit_data_extension and best_unit_data_extension._breed
best_unit_breed_name = best_unit_breed_data and best_unit_breed_data.name
@@ -640,7 +650,7 @@ 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 = hit[INDEX_ACTOR]
local hit_actor = hits_use_actor and hit.actor or hit[INDEX_ACTOR]
if not hit_actor then
goto continue
end
@@ -695,7 +705,7 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
best_unit_score = best_unit_score + (best_unit_is_attacking_player and 100 or 0)
end
if hit_unit_score <= best_unit_score then
if hit_unit_score < best_unit_score then
goto continue
end
@@ -707,6 +717,11 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
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
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
@@ -733,7 +748,9 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
end
local visible
if tag_name == TAG_NAMES.SERVO_SKULL_TAG then
if omni_mark and mod_settings.omni_mark_ignore_los then
visible = true
elseif tag_name == TAG_NAMES.SERVO_SKULL_TAG then
visible = is_servo_skull_target_visible(companion_position, hit_unit, fixed_frame)
elseif tag_name == TAG_NAMES.COMPANION_TAG then
local half_height = Breed_height(hit_unit, hit_unit_breed_data) * 0.5
@@ -755,6 +772,7 @@ function mod:find_auto_mark_target_unit(min_range, max_range, tag_name, tag_cont
best_unit_is_dormant_daemonhost = hit_unit_is_dormant_daemonhost
best_unit_marked_by_execution_order = hit_unit_marked_by_execution_order
best_unit_is_sticky_targeting = false
best_unit_distance = distance
::continue::
end