diff --git a/code/__DEFINES/ai/ai.dm b/code/__DEFINES/ai/ai.dm index aafd3feb203..f8285b075e4 100644 --- a/code/__DEFINES/ai/ai.dm +++ b/code/__DEFINES/ai/ai.dm @@ -1,5 +1,6 @@ #define GET_AI_BEHAVIOR(behavior_type) SSai_behaviors.ai_behaviors[behavior_type] #define GET_TARGETING_STRATEGY(targeting_type) SSai_behaviors.targeting_strategies[targeting_type] +#define GET_TARGET_PRIORITY_STRATEGY(targeting_type) SSai_behaviors.target_priority_strategies[targeting_type] #define HAS_AI_CONTROLLER_TYPE(thing, type) istype(thing?.ai_controller, type) //AI controller flags diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 3e9a65027af..660a4e3c82c 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -83,6 +83,7 @@ #define BB_BASIC_MOB_CURRENT_TARGET "BB_basic_current_target" #define BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION "BB_basic_current_target_hiding_location" #define BB_TARGETING_STRATEGY "targeting_strategy" +#define BB_TARGET_PRIORITY_STRATEGY "target_priority_strategy" #define BB_HUNT_TARGETING_STRATEGY "hunt_targeting_strategy" ///some behaviors that check current_target also set this on deep crit mobs #define BB_BASIC_MOB_EXECUTION_TARGET "BB_basic_execution_target" @@ -96,6 +97,7 @@ #define BB_TARGET_HELD_ITEM "BB_target_held_item" /// How likely is this mob to move when idle per tick? #define BB_BASIC_MOB_IDLE_WALK_CHANCE "BB_basic_idle_walk_chance" +#define BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN "BB_basic_mob_target_refresh_cooldown" /// Minimum range to keep target within #define BB_RANGED_SKIRMISH_MIN_DISTANCE "BB_ranged_skirmish_min_distance" @@ -140,7 +142,7 @@ /// How often can we get spooked by a target? #define BB_STATIONARY_COOLDOWN "BB_stationary_cooldown" -///List of mobs who have damaged us +/// Assoc list of mobs who have damaged us -> Moment at which they damaged us #define BB_BASIC_MOB_RETALIATE_LIST "BB_basic_mob_shitlist" /// Chance to randomly acquire a new target @@ -197,6 +199,8 @@ #define BB_BASIC_MOB_REINFORCEMENT_TARGET "BB_basic_mob_reinforcement_target" /// The next time at which this mob can call for reinforcements #define BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN "BB_basic_mob_reinforcements_cooldown" +/// Soft-reinforcements requests which will cause the mob to prioritize the attacker +#define BB_MINING_MOB_REINFORCEMENTS_REQUESTS "BB_mining_mob_reinforcements_requests" /// the direction we started when executing stare at things #define BB_STARTING_DIRECTION "BB_startdir" diff --git a/code/controllers/subsystem/processing/ai_behaviors.dm b/code/controllers/subsystem/processing/ai_behaviors.dm index 7304c5ed362..aa5ef4eb55c 100644 --- a/code/controllers/subsystem/processing/ai_behaviors.dm +++ b/code/controllers/subsystem/processing/ai_behaviors.dm @@ -8,24 +8,33 @@ PROCESSING_SUBSYSTEM_DEF(ai_behaviors) /datum/controller/subsystem/movement/ai_movement, ) wait = 1 - ///List of all ai_behavior singletons, key is the typepath while assigned value is a newly created instance of the typepath. See SetupAIBehaviors() + /// List of all ai_behavior singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_ai_behaviors() var/list/ai_behaviors - ///List of all targeting_strategy singletons, key is the typepath while assigned value is a newly created instance of the typepath. See SetupAIBehaviors() + /// List of all targeting_strategy singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_targeting_strats() var/list/targeting_strategies + /// List of all target_priority_strategy singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_target_priority_strats() + var/list/target_priority_strategies /datum/controller/subsystem/processing/ai_behaviors/Initialize() - SetupAIBehaviors() - SetupTargetingStrats() + setup_ai_behaviors() + setup_targeting_strats() + setup_target_priority_strats() return SS_INIT_SUCCESS -/datum/controller/subsystem/processing/ai_behaviors/proc/SetupAIBehaviors() +/datum/controller/subsystem/processing/ai_behaviors/proc/setup_ai_behaviors() ai_behaviors = list() for(var/behavior_type in subtypesof(/datum/ai_behavior)) var/datum/ai_behavior/ai_behavior = new behavior_type ai_behaviors[behavior_type] = ai_behavior -/datum/controller/subsystem/processing/ai_behaviors/proc/SetupTargetingStrats() +/datum/controller/subsystem/processing/ai_behaviors/proc/setup_targeting_strats() targeting_strategies = list() for(var/target_type in subtypesof(/datum/targeting_strategy)) var/datum/targeting_strategy/target_start = new target_type targeting_strategies[target_type] = target_start + +/datum/controller/subsystem/processing/ai_behaviors/proc/setup_target_priority_strats() + target_priority_strategies = list() + for(var/target_type in subtypesof(/datum/target_priority_strategy)) + var/datum/target_priority_strategy/target_start = new target_type + target_priority_strategies[target_type] = target_start diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm index 9305c398611..de9697902d2 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm @@ -12,6 +12,10 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ var/vision_range = 9 /// Blackboard key for aggro range, uses vision range if not specified var/aggro_range_key = BB_AGGRO_RANGE + /// Blackboard key for the target priority strategy + var/priority_strategy_key = BB_TARGET_PRIORITY_STRATEGY + /// If we have a priority strategy set, how often do we refresh our target search? + var/priority_refresh_cooldown = 6 SECONDS /datum/ai_behavior/find_potential_targets/get_cooldown(datum/ai_controller/cooldown_for) if(cooldown_for.blackboard[BB_FIND_TARGETS_FIELD(type)]) @@ -26,7 +30,8 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ CRASH("No target datum was supplied in the blackboard for [controller.pawn]") var/atom/current_target = controller.blackboard[target_key] - if (targeting_strategy.can_attack(living_mob, current_target, vision_range)) + var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[priority_strategy_key]) + if((!priority_strategy || controller.blackboard[BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN] > world.time) && current_target && targeting_strategy.can_attack(living_mob, current_target, vision_range)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/aggro_range = controller.blackboard[aggro_range_key] || vision_range @@ -46,22 +51,30 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ potential_targets += hostile_machine if(!potential_targets.len) - failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) + if(!current_target) + failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/list/filtered_targets = list() + var/current_priority = 0 + if(priority_strategy) + current_priority = priority_strategy.get_target_priority(controller, current_target) for(var/atom/pot_target in potential_targets) - if(targeting_strategy.can_attack(living_mob, pot_target))//Can we attack it? - filtered_targets += pot_target + if(!targeting_strategy.can_attack(living_mob, pot_target)) continue + if (priority_strategy && priority_strategy.get_target_priority(controller, pot_target) < current_priority) + continue + filtered_targets += pot_target if(!filtered_targets.len) - failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) + if(!current_target) + failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/atom/target = pick_final_target(controller, filtered_targets) controller.set_blackboard_key(target_key, target) + controller.set_blackboard_key(BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN, world.time + priority_refresh_cooldown) var/atom/potential_hiding_location = targeting_strategy.find_hidden_mobs(living_mob, target) @@ -153,7 +166,10 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ /// Returns the desired final target from the filtered list of targets /datum/ai_behavior/find_potential_targets/proc/pick_final_target(datum/ai_controller/controller, list/filtered_targets) - return pick(filtered_targets) + var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[priority_strategy_key]) + if (!priority_strategy) + return pick(filtered_targets) + return priority_strategy.select_target(controller, filtered_targets) /// Targets with the trait specified by the BB_TARGET_PRIORITY_TRAIT blackboard key will be prioritized over the rest. /datum/ai_behavior/find_potential_targets/prioritize_trait @@ -164,7 +180,7 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ if(HAS_TRAIT(target, controller.blackboard[BB_TARGET_PRIORITY_TRAIT])) priority_targets += target if(length(priority_targets)) - return pick(priority_targets) + return ..(controller, priority_targets) return ..() /datum/ai_behavior/find_potential_targets/bigger_range diff --git a/code/datums/ai/basic_mobs/basic_subtrees/call_reinforcements.dm b/code/datums/ai/basic_mobs/basic_subtrees/call_reinforcements.dm index f78697b2b81..46886680d52 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/call_reinforcements.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/call_reinforcements.dm @@ -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 diff --git a/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm b/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm index 52b19036b9a..211e01be1f2 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm @@ -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 diff --git a/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm b/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm index 6893bad6f6a..f1c04e1d8a7 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm @@ -43,6 +43,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 +59,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 +79,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) . = ..() diff --git a/code/datums/ai/basic_mobs/target_priority_strategies/_target_priority_strategy.dm b/code/datums/ai/basic_mobs/target_priority_strategies/_target_priority_strategy.dm new file mode 100644 index 00000000000..3cb58ba086c --- /dev/null +++ b/code/datums/ai/basic_mobs/target_priority_strategies/_target_priority_strategy.dm @@ -0,0 +1,13 @@ +/// Target priority datum, exists to allow mobs to have custom targeting priorities. Singleton. +/datum/target_priority_strategy + +/// Returns a number representing the priority of a target, higher -> more likely to attack +/datum/target_priority_strategy/proc/get_target_priority(datum/ai_controller/controller, atom/target) + return 1 + +/// Returns a single atom from the list of passed targets +/datum/target_priority_strategy/proc/select_target(datum/ai_controller/controller, list/atom/targets) + var/list/target_priorities = list() + for (var/atom/target as anything in targets) + target_priorities[target] = get_target_priority(controller, target) + return pick_weight(target_priorities) diff --git a/code/datums/ai/basic_mobs/target_priority_strategies/mining_strategies.dm b/code/datums/ai/basic_mobs/target_priority_strategies/mining_strategies.dm new file mode 100644 index 00000000000..e69ee59ff10 --- /dev/null +++ b/code/datums/ai/basic_mobs/target_priority_strategies/mining_strategies.dm @@ -0,0 +1,84 @@ +/// Ashies get lower priorities than humans +#define AGGRO_PRIORITY_ASHWALKER 1 +/// Everyone by default +#define AGGRO_PRIORITY_HUMAN 2 +/// Mobs who have attacked someone in our view recently +#define AGGRO_PRIORITY_MINER 8 +/// NODE drone priority +#define AGGRO_PRIORITY_NODE 10 +/// NODE drone priority for legion broods and brimdemons +#define AGGRO_PRIORITY_NODE_LOW_PRIO 3 +/// Priority for mobs we're retaliating against +#define AGGRO_PRIORITY_RETALIATE 15 + +/// Prioritizes NODE drones until attacked, then swaps back onto miners +/datum/target_priority_strategy/mining + /// For how long do we keep aggro on mobs over NODE drones? + var/retaliate_aggro_memory = 25 SECONDS + /// Priority for NODE drones + var/node_priority = AGGRO_PRIORITY_NODE + +/datum/target_priority_strategy/mining/get_target_priority(datum/ai_controller/controller, mob/living/target) + if (!isliving(target)) + return ..() + + // If a mob recently attacked us, it has higher priority than NODE drones, otherwise we care about NODE drones more + var/list/shitlist = controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST] + if (shitlist?[target] && (shitlist[target] + retaliate_aggro_memory > world.time)) + // Does not decay until it passes the retaliate memory threshold to avoid constant target swapping + return AGGRO_PRIORITY_RETALIATE + + if (istype(target, /mob/living/basic/node_drone)) + return node_priority + + // Check if they've recently attacked any of our friends, if so - increase their priority by 1 for each attack in the past [retaliate_aggro_memory] seconds + var/list/allies_shitlist = controller.blackboard[BB_MINING_MOB_REINFORCEMENTS_REQUESTS] + var/list/target_requests = allies_shitlist?[target] + if (!target_requests) + return target.has_faction(FACTION_ASHWALKER) ? AGGRO_PRIORITY_ASHWALKER : AGGRO_PRIORITY_HUMAN + + var/aggro_boost = 0 + var/total_requests = length(target_requests) + // Goes end-to-start because we do not care about requests older than [retaliate_aggro_memory] + for (var/i in 1 to total_requests) + var/request_time = target_requests[total_requests - i + 1] + if (request_time + retaliate_aggro_memory < world.time) + break + aggro_boost += 1 + + if (aggro_boost) + return AGGRO_PRIORITY_MINER + aggro_boost + return target.has_faction(FACTION_ASHWALKER) ? AGGRO_PRIORITY_ASHWALKER : AGGRO_PRIORITY_HUMAN + +/datum/target_priority_strategy/mining/select_target(datum/ai_controller/controller, list/atom/targets) + var/max_priority = 0 + var/min_distance = INFINITY + var/lucky_fella = null + + // Selects highest priority targets, then picks the closest + for (var/atom/target as anything in targets) + var/target_prio = get_target_priority(controller, target) + if (target_prio < max_priority) + continue + + if (target_prio > max_priority) + max_priority = target_prio + min_distance = INFINITY + + var/target_dist = get_dist(controller.pawn, target) + if (target_dist < min_distance) + lucky_fella = target + min_distance = target_dist + + return pick(lucky_fella) + +/datum/target_priority_strategy/mining/low_node_priority + // Higher than normal humans but will instantly pivot towards anyone who attacks or attacked mobs in our vicinity + node_priority = AGGRO_PRIORITY_NODE_LOW_PRIO + +#undef AGGRO_PRIORITY_ASHWALKER +#undef AGGRO_PRIORITY_HUMAN +#undef AGGRO_PRIORITY_MINER +#undef AGGRO_PRIORITY_NODE +#undef AGGRO_PRIORITY_NODE_LOW_PRIO +#undef AGGRO_PRIORITY_RETALIATE diff --git a/code/datums/components/ai_retaliate_advanced.dm b/code/datums/components/ai_retaliate_advanced.dm index d734fa92b3c..12e22c3d395 100644 --- a/code/datums/components/ai_retaliate_advanced.dm +++ b/code/datums/components/ai_retaliate_advanced.dm @@ -33,5 +33,5 @@ if (!victim.ai_controller) return - victim.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker) + victim.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker, world.time) post_retaliate_callback?.InvokeAsync(attacker) diff --git a/code/datums/elements/ai_retaliate.dm b/code/datums/elements/ai_retaliate.dm index 03465d3c246..4fcb5158143 100644 --- a/code/datums/elements/ai_retaliate.dm +++ b/code/datums/elements/ai_retaliate.dm @@ -24,4 +24,4 @@ if (victim == attacker) return - victim.ai_controller?.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker) + victim.ai_controller?.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker, world.time) diff --git a/code/modules/mob/living/basic/clown/clown.dm b/code/modules/mob/living/basic/clown/clown.dm index 7808c45f72f..9c8ec1e8bb7 100644 --- a/code/modules/mob/living/basic/clown/clown.dm +++ b/code/modules/mob/living/basic/clown/clown.dm @@ -57,7 +57,7 @@ if (!istype(attacker)) return for (var/mob/living/basic/clown/harbringer in oview(src, 7)) - harbringer.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker) + harbringer.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker, world.time) /mob/living/basic/clown/melee_attack(atom/target, list/modifiers, ignore_cooldown = FALSE) if(!istype(target, /obj/item/food/grown/banana/bunch)) diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm index e18af7ed2ce..01c4a564673 100644 --- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm +++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm @@ -1,10 +1,12 @@ /datum/ai_controller/basic_controller/bileworm blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/bileworm, + BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, ) planning_subtrees = list( /datum/ai_planning_subtree/escape_captivity, + /datum/ai_planning_subtree/call_reinforcements/mining, /datum/ai_planning_subtree/simple_find_target, /datum/ai_planning_subtree/bileworm_attack, /datum/ai_planning_subtree/bileworm_execute, diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm index eb6f6f671ed..b60d7ab4a90 100644 --- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm @@ -4,6 +4,7 @@ /datum/ai_controller/basic_controller/brimdemon blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining/low_node_priority, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) @@ -11,6 +12,7 @@ idle_behavior = /datum/idle_behavior/idle_random_walk/no_target planning_subtrees = list( /datum/ai_planning_subtree/escape_captivity, + /datum/ai_planning_subtree/call_reinforcements/mining, /datum/ai_planning_subtree/simple_find_target, /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, /datum/ai_planning_subtree/move_to_cardinal/brimdemon, diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm index 63842f05ef5..5fe6e9d891d 100644 --- a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm @@ -4,6 +4,7 @@ /datum/ai_controller/basic_controller/goliath blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) @@ -11,6 +12,7 @@ idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( /datum/ai_planning_subtree/escape_captivity, + /datum/ai_planning_subtree/call_reinforcements/mining, /datum/ai_planning_subtree/target_retaliate/check_faction, /datum/ai_planning_subtree/simple_find_target, /datum/ai_planning_subtree/find_food, diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm index 96cde9e5ac5..ff81f32ffc0 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm @@ -2,6 +2,7 @@ /datum/ai_controller/basic_controller/legion blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/legion, + BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_AGGRO_RANGE = 5, // Unobservant BB_BASIC_MOB_FLEE_DISTANCE = 6, @@ -11,6 +12,7 @@ idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( /datum/ai_planning_subtree/escape_captivity, + /datum/ai_planning_subtree/call_reinforcements/mining, /datum/ai_planning_subtree/random_speech/legion, /datum/ai_planning_subtree/simple_find_target, /datum/ai_planning_subtree/targeted_mob_ability, @@ -21,6 +23,7 @@ /datum/ai_controller/basic_controller/legion_brood blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/legion, + BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining/low_node_priority, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm index 1fc6a7d02be..dd2bbe18de2 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm @@ -103,8 +103,17 @@ else add_ally(creator) created_by = WEAKREF(creator) - ai_controller?.set_blackboard_key(BB_LEGION_BROOD_CREATOR, creator) RegisterSignal(creator, COMSIG_QDELETING, PROC_REF(creator_destroyed)) + if (!ai_controller) + return + + ai_controller.set_blackboard_key(BB_LEGION_BROOD_CREATOR, creator) + if (!creator.ai_controller) + return + + // Inherit our creator's target and reinforcement requests + ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, creator.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) + ai_controller.set_blackboard_key(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, creator.ai_controller.blackboard[BB_MINING_MOB_REINFORCEMENTS_REQUESTS]) /// Reference handling /mob/living/basic/mining/legion_brood/proc/creator_destroyed() diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm index c634f1238d1..6906121b4b8 100644 --- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm @@ -8,6 +8,7 @@ /datum/ai_controller/basic_controller/lobstrosity blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_LOBSTROSITY_EXPLOIT_TRAITS = list(TRAIT_INCAPACITATED, TRAIT_FLOORED, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT), @@ -24,6 +25,7 @@ idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( /datum/ai_planning_subtree/escape_captivity, + /datum/ai_planning_subtree/call_reinforcements/mining, /datum/ai_planning_subtree/random_speech/insect, /datum/ai_planning_subtree/hoard_fingers, /datum/ai_planning_subtree/pet_planning, diff --git a/code/modules/mob/living/basic/lavaland/mook/mook.dm b/code/modules/mob/living/basic/lavaland/mook/mook.dm index 80d398f23d0..966c1bfba18 100644 --- a/code/modules/mob/living/basic/lavaland/mook/mook.dm +++ b/code/modules/mob/living/basic/lavaland/mook/mook.dm @@ -187,7 +187,7 @@ if(istype(intruder, /mob/living/basic/mining/mook)) return for(var/mob/living/basic/mining/mook/villager in oview(src, 9)) - villager.ai_controller?.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, intruder) + villager.ai_controller?.set_blackboard_key_assoc(BB_BASIC_MOB_RETALIATE_LIST, intruder, world.time) /mob/living/basic/mining/mook/worker diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm index d31f76eaee6..15df9f7eba1 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm @@ -1,12 +1,14 @@ /datum/ai_controller/basic_controller/watcher blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, ) ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( /datum/ai_planning_subtree/escape_captivity, + /datum/ai_planning_subtree/call_reinforcements/mining, /datum/ai_planning_subtree/target_retaliate/check_faction, /datum/ai_planning_subtree/simple_find_target, /datum/ai_planning_subtree/maintain_distance, diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index 6f1ff9e864f..d114b6d42a1 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -320,4 +320,4 @@ /// If someone slaps one of the school, scatter /mob/living/basic/carp/passive/proc/on_attacked(mob/living/attacker) for(var/mob/living/basic/carp/passive/schoolmate in oview(src, 9)) - schoolmate.ai_controller?.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker) + schoolmate.ai_controller?.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker, world.time) diff --git a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm index d2ab4e691b5..f6926b2ea51 100644 --- a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm +++ b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm @@ -121,7 +121,7 @@ if (!istype(attacker)) return for (var/mob/living/basic/garden_gnome/potential_gnome in oview(src, 7)) - potential_gnome.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker) + potential_gnome.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker, world.time) /datum/ai_controller/basic_controller/garden_gnome blackboard = list( diff --git a/code/modules/mob/living/basic/trooper/nanotrasen.dm b/code/modules/mob/living/basic/trooper/nanotrasen.dm index 80f0f348624..8798467969b 100644 --- a/code/modules/mob/living/basic/trooper/nanotrasen.dm +++ b/code/modules/mob/living/basic/trooper/nanotrasen.dm @@ -98,4 +98,4 @@ if (!istype(attacker)) return for (var/mob/living/basic/trooper/nanotrasen/potential_trooper in oview(src, 7)) - potential_trooper.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker) + potential_trooper.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker, world.time) diff --git a/tgstation.dme b/tgstation.dme index 36f5422e607..8604a7465cb 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1034,6 +1034,8 @@ #include "code\datums\ai\basic_mobs\pet_commands\pet_follow_friend.dm" #include "code\datums\ai\basic_mobs\pet_commands\pet_use_targeted_ability.dm" #include "code\datums\ai\basic_mobs\pet_commands\play_dead.dm" +#include "code\datums\ai\basic_mobs\target_priority_strategies\_target_priority_strategy.dm" +#include "code\datums\ai\basic_mobs\target_priority_strategies\mining_strategies.dm" #include "code\datums\ai\basic_mobs\targeting_strategies\_targeting_strategy.dm" #include "code\datums\ai\basic_mobs\targeting_strategies\basic_targeting_strategy.dm" #include "code\datums\ai\basic_mobs\targeting_strategies\dont_target_friends.dm"