Files
SmArtKar 119c1e9ccc Adds a priority targeting framework, and priority targeting to mining mobs (#95720)
## About The Pull Request

Introduces a new targeting priority strategy system for basicmob AIs,
which allows controllers to decide which mob to prioritize over others.

Mining mobs will now focus on the NODE drone unless hit, and will pursue
the attacker for 25 seconds before dropping the aggro. They also get
increased aggro if you've attacked other mobs in their view recently,
and after a few hits will have enough aggro to swap to you from the NODE
drone. Ashwalkers get a reduction in aggro because they live there.
Legion broods and brimdemons will immediately target anyone who attacks
their allies rather than waiting for multiple hits. Broods also now
inherit their parent's targets and retaliation/reinforcements lists.


https://github.com/user-attachments/assets/6baaba8a-8b3c-4b2f-ae8b-842f0b1f2b6d

#### This is a bounty for ArcaneMusic

## Why It's Good For The Game

Makes vent defense mob behavior more predictable and easier for players
to manipulate, allowing them to draw aggro from the NODE drone should
make vents more engaging and less of an AI rng fest

## Changelog
🆑
add: Mining mobs now use priority when choosing their target,
prioritizing NODE drones over miners who haven't attacked them or their
allies
/🆑
2026-05-08 20:40:01 +01:00

66 lines
3.2 KiB
Plaintext

/// Add or remove people to our retaliation shitlist just on an arbitrary whim
/datum/ai_planning_subtree/capricious_retaliate
/// Blackboard key which tells us how to select valid targets
var/targeting_strategy_key = BB_TARGETING_STRATEGY
/// Whether we should skip checking faction for our decision
var/ignore_faction = TRUE
/datum/ai_planning_subtree/capricious_retaliate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
controller.queue_behavior(/datum/ai_behavior/capricious_retaliate, targeting_strategy_key, ignore_faction)
/// Add or remove people to our retaliation shitlist just on an arbitrary whim
/datum/ai_behavior/capricious_retaliate
action_cooldown = 1 SECONDS
/datum/ai_behavior/capricious_retaliate/perform(seconds_per_tick, datum/ai_controller/controller, targeting_strategy_key, ignore_faction)
var/atom/pawn = controller.pawn
if (controller.blackboard_key_exists(BB_BASIC_MOB_RETALIATE_LIST))
var/deaggro_chance = controller.blackboard[BB_RANDOM_DEAGGRO_CHANCE] || 10
if (!SPT_PROB(deaggro_chance, seconds_per_tick))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
pawn.visible_message(span_notice("[pawn] calms down.")) // We can blackboard key this if anyone else actually wants to customise it
controller.clear_blackboard_key(BB_BASIC_MOB_RETALIATE_LIST)
controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
controller.CancelActions() // Otherwise they will try and get one last kick in
return AI_BEHAVIOR_DELAY
var/aggro_chance = controller.blackboard[BB_RANDOM_AGGRO_CHANCE] || 0.5
if (!SPT_PROB(aggro_chance, seconds_per_tick))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/aggro_range = controller.blackboard[BB_AGGRO_RANGE] || 9
var/list/potential_targets = hearers(aggro_range, get_turf(pawn)) - pawn
if (!length(potential_targets))
failed_targeting(pawn)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/datum/targeting_strategy/target_helper = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key])
var/mob/living/final_target = null
if (ignore_faction)
controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, TRUE)
while (isnull(final_target) && length(potential_targets))
var/mob/living/test_target = pick_n_take(potential_targets)
if (target_helper.can_attack(pawn, test_target, vision_range = aggro_range))
final_target = test_target
if (isnull(final_target))
failed_targeting(pawn)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, final_target, world.time)
pawn.visible_message(span_warning("[pawn] glares grumpily at [final_target]!"))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/// Called if we try but fail to target something
/datum/ai_behavior/capricious_retaliate/proc/failed_targeting(atom/pawn)
pawn.visible_message(span_notice("[pawn] grumbles.")) // We're pissed off but with no outlet to vent our frustration upon
/datum/ai_behavior/capricious_retaliate/finish_action(datum/ai_controller/controller, succeeded, ignore_faction)
. = ..()
if (succeeded || !ignore_faction)
return
var/usually_ignores_faction = controller.blackboard[BB_ALWAYS_IGNORE_FACTION] || FALSE
controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, usually_ignores_faction)