Merge remote-tracking branch 'tgstation/master' into upstream-6-2-2026

This commit is contained in:
shayoki
2026-06-03 01:23:54 -05:00
1259 changed files with 50763 additions and 28726 deletions
@@ -1,5 +1,3 @@
#define REINFORCEMENTS_COOLDOWN (30 SECONDS)
/// Calls all nearby mobs that share a faction to give backup in combat
/datum/ai_planning_subtree/call_reinforcements
/// Blackboard key containing something to say when calling reinforcements (takes precedence over emotes)
@@ -21,8 +19,6 @@
controller.queue_behavior(/datum/ai_behavior/perform_speech, call_say)
else if(!isnull(call_emote))
controller.queue_behavior(/datum/ai_behavior/perform_emote, call_emote)
else
controller.queue_behavior(/datum/ai_behavior/perform_emote, "cries for help!")
controller.queue_behavior(call_type)
@@ -30,20 +26,43 @@
/datum/ai_planning_subtree/call_reinforcements/proc/decide_to_call(datum/ai_controller/controller)
return controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) && istype(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET], /mob)
/datum/ai_planning_subtree/call_reinforcements/mining
call_type = /datum/ai_behavior/call_reinforcements/mining
/// Call out to all mobs in the specified range for help
/datum/ai_behavior/call_reinforcements
/// How frequently can we call for reinforcements?
var/cooldown = 30 SECONDS
/// Range to call reinforcements from
var/reinforcements_range = 15
/datum/ai_behavior/call_reinforcements/perform(seconds_per_tick, datum/ai_controller/controller)
var/mob/pawn_mob = controller.pawn
for(var/mob/other_mob in oview(reinforcements_range, pawn_mob))
if(pawn_mob.faction_check_atom(other_mob) && !isnull(other_mob.ai_controller))
// Add our current target to their retaliate list so that they'll attack our aggressor
other_mob.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET])
other_mob.ai_controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENT_TARGET, pawn_mob)
if(!pawn_mob.faction_check_atom(other_mob) || isnull(other_mob.ai_controller))
continue
// Add our current target to their retaliate list so that they'll attack our aggressor
other_mob.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET], world.time)
other_mob.ai_controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENT_TARGET, pawn_mob)
controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN, world.time + REINFORCEMENTS_COOLDOWN)
controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN, world.time + cooldown)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
#undef REINFORCEMENTS_COOLDOWN
/// Does not force retaliation, but increases targeting priority instead
/datum/ai_behavior/call_reinforcements/mining
cooldown = 1 SECONDS
reinforcements_range = 7
/datum/ai_behavior/call_reinforcements/mining/perform(seconds_per_tick, datum/ai_controller/controller)
var/mob/pawn_mob = controller.pawn
var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
for(var/mob/other_mob in oview(reinforcements_range, pawn_mob))
if(!pawn_mob.faction_check_atom(other_mob) || isnull(other_mob.ai_controller))
continue
var/list/existing_requests = other_mob.ai_controller.blackboard[BB_MINING_MOB_REINFORCEMENTS_REQUESTS]
if (!existing_requests || !existing_requests[target])
other_mob.ai_controller.set_blackboard_key_assoc_lazylist(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, target, list())
other_mob.ai_controller.add_blackboard_key_assoc(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, target, world.time)
controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN, world.time + cooldown)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
@@ -49,7 +49,7 @@
failed_targeting(pawn)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, final_target)
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
@@ -21,6 +21,7 @@
set_movement_target(controller, target)
/datum/ai_behavior/mine_wall/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/basic/living_pawn = controller.pawn
var/turf/closed/mineral/target = controller.blackboard[target_key]
var/is_gibtonite_turf = istype(target, /turf/closed/mineral/gibtonite)
@@ -3,10 +3,12 @@
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
/// Targeting strategy key to use
var/strategy_key = BB_TARGETING_STRATEGY
/// Behavior to use to find targets
var/target_behavior = /datum/ai_behavior/find_potential_targets
/datum/ai_planning_subtree/simple_find_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
controller.queue_behavior(/datum/ai_behavior/find_potential_targets, target_key, strategy_key, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
controller.queue_behavior(target_behavior, target_key, strategy_key, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
// Prevents finding a target if a human is nearby
/datum/ai_planning_subtree/simple_find_target/not_while_observed
@@ -21,9 +23,8 @@
target_key = BB_BASIC_MOB_FLEE_TARGET
/datum/ai_planning_subtree/simple_find_target/increased_range
/datum/ai_planning_subtree/simple_find_target/increased_range/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
controller.queue_behavior(/datum/ai_behavior/find_potential_targets/bigger_range, target_key, strategy_key, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
target_behavior = /datum/ai_behavior/find_potential_targets/bigger_range
/datum/ai_planning_subtree/simple_find_target/hunt
strategy_key = BB_HUNT_TARGETING_STRATEGY
@@ -9,9 +9,11 @@
var/hiding_place_key = BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION
/// do we check for faction?
var/check_faction = FALSE
/// Behavior to use to select our target
var/target_behavior = /datum/ai_behavior/target_from_retaliate_list
/datum/ai_planning_subtree/target_retaliate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
controller.queue_behavior(/datum/ai_behavior/target_from_retaliate_list, BB_BASIC_MOB_RETALIATE_LIST, target_key, targeting_strategy_key, hiding_place_key, check_faction)
controller.queue_behavior(target_behavior, BB_BASIC_MOB_RETALIATE_LIST, target_key, targeting_strategy_key, hiding_place_key, check_faction)
/datum/ai_planning_subtree/target_retaliate/check_faction
check_faction = TRUE
@@ -43,6 +45,12 @@
var/list/shitlist = controller.blackboard[shitlist_key]
var/atom/existing_target = controller.blackboard[target_key]
var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[BB_TARGET_PRIORITY_STRATEGY])
var/existing_priority = 0
// If we have an existing target and its priority is higher than our new target's, don't switch focus
if (priority_strategy && existing_target)
existing_priority = priority_strategy.get_target_priority(controller, existing_target)
if (!check_faction)
controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, TRUE)
@@ -53,13 +61,12 @@
for(var/mob/living/potential_target as anything in shitlist)
if(!targeting_strategy.can_attack(living_mob, potential_target, vision_range))
continue
// Strict comparasion because priority strategies might not care about retaliation, so this makes existing targets not override potential retaliates
if (priority_strategy && priority_strategy.get_target_priority(controller, potential_target) < existing_priority)
continue
enemies_list += potential_target
if(!length(enemies_list))
if(existing_target)
controller.clear_blackboard_key(target_key)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/atom/new_target = pick_final_target(controller, enemies_list)
@@ -74,7 +81,10 @@
/// Returns the desired final target from the filtered list of enemies
/datum/ai_behavior/target_from_retaliate_list/proc/pick_final_target(datum/ai_controller/controller, list/enemies_list)
return pick(enemies_list)
var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[BB_TARGET_PRIORITY_STRATEGY])
if (!priority_strategy)
return pick(enemies_list)
return priority_strategy.select_target(controller, enemies_list)
/datum/ai_behavior/target_from_retaliate_list/finish_action(datum/ai_controller/controller, succeeded, shitlist_key, target_key, targeting_strategy_key, hiding_location_key, check_faction)
. = ..()