diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm index 5cf2e4263d42..3c9a8551f501 100644 --- a/code/__DEFINES/ai/bot_keys.dm +++ b/code/__DEFINES/ai/bot_keys.dm @@ -35,6 +35,10 @@ #define BB_NEAR_DEATH_SPEECH "near_death_speech" ///in crit patient we must alert medbay about #define BB_PATIENT_IN_CRIT "patient_in_crit" +///how much time interval before we clear list +#define BB_UNREACHABLE_LIST_COOLDOWN "unreachable_list_cooldown" +///can we clear the list now +#define BB_CLEAR_LIST_READY "clear_list_ready" // cleanbots ///key that holds the foaming ability diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/travel_towards.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/travel_towards.dm index 55f6ef4c4c00..6eb7c36dadd6 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/travel_towards.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/travel_towards.dm @@ -8,13 +8,15 @@ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION /// If true we will get rid of our target on completion var/clear_target = FALSE + ///should we use a different movement type? + var/new_movement_type /datum/ai_behavior/travel_towards/setup(datum/ai_controller/controller, target_key) . = ..() var/atom/target = controller.blackboard[target_key] if(QDELETED(target)) return FALSE - set_movement_target(controller, target) + set_movement_target(controller, target, new_movement_type) /datum/ai_behavior/travel_towards/perform(seconds_per_tick, datum/ai_controller/controller, target_key) . = ..() @@ -24,6 +26,8 @@ . = ..() if (clear_target) controller.clear_blackboard_key(target_key) + if(new_movement_type) + controller.change_ai_movement_type(initial(controller.ai_movement)) /datum/ai_behavior/travel_towards/stop_on_arrival clear_target = TRUE diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm index b0d34f68c224..625f87104122 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -12,11 +12,11 @@ finish_action(controller, TRUE) return var/find_this_thing = search_tactic(controller, locate_path, search_range) - if(find_this_thing) - controller.set_blackboard_key(set_key, find_this_thing) - finish_action(controller, TRUE) - else + if(QDELETED(controller.pawn) || isnull(find_this_thing)) finish_action(controller, FALSE) + return + controller.set_blackboard_key(set_key, find_this_thing) + finish_action(controller, TRUE) /datum/ai_behavior/find_and_set/proc/search_tactic(datum/ai_controller/controller, locate_path, search_range) return locate(locate_path) in oview(search_range, controller.pawn) diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm index 3644869140d7..6024b7e7562c 100644 --- a/code/datums/ai/movement/ai_movement_jps.dm +++ b/code/datums/ai/movement/ai_movement_jps.dm @@ -39,7 +39,7 @@ /datum/ai_movement/jps/bot max_pathing_attempts = 25 - maximum_length = AI_BOT_PATH_LENGTH + maximum_length = 25 diagonal_flags = DIAGONAL_REMOVE_ALL /datum/ai_movement/jps/bot/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) @@ -48,3 +48,6 @@ if(isnull(our_pawn)) return our_pawn.RegisterSignal(loop, COMSIG_MOVELOOP_JPS_FINISHED_PATHING, TYPE_PROC_REF(/mob/living/basic/bot, generate_bot_path)) + +/datum/ai_movement/jps/bot/travel_to_beacon + maximum_length = AI_BOT_PATH_LENGTH diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm index d07d4e13f71d..b4080462c24e 100644 --- a/code/modules/mob/living/basic/bots/bot_ai.dm +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -5,7 +5,8 @@ "salutes", "nods in appreciation towards", "fist bumps", - ) + ), + BB_UNREACHABLE_LIST_COOLDOWN = 45 SECONDS, ) ai_movement = /datum/ai_movement/jps/bot @@ -54,10 +55,12 @@ clear_blackboard_key(key) ///set the target if we can reach them -/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, distance = 10) +/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, distance = 10, bypass_add_to_blacklist = FALSE) if(can_reach_target(target, distance)) set_blackboard_key(key, target) return TRUE + if(!bypass_add_to_blacklist) + set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, REF(target), TRUE) return FALSE /datum/ai_controller/basic_controller/bot/proc/can_reach_target(target, distance = 10) @@ -70,33 +73,16 @@ return FALSE return TRUE -///check if the target is too far away, and delete them if so and add them to the unreachables list -/datum/ai_controller/basic_controller/bot/proc/reachable_key(key, distance = 10) - var/datum/target = blackboard[key] - if(QDELETED(target)) - return FALSE - var/datum/last_attempt = blackboard[BB_LAST_ATTEMPTED_PATHING] - if(last_attempt != target) - current_pathing_attempts = 0 - set_blackboard_key(BB_LAST_ATTEMPTED_PATHING, target) - else - current_pathing_attempts++ - if(current_pathing_attempts >= max_pathing_attempts || !can_reach_target(target, distance)) - clear_blackboard_key(key) - clear_blackboard_key(BB_LAST_ATTEMPTED_PATHING) - set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) - return FALSE - return TRUE - /// subtree to manage our list of unreachables, we reset it every 15 seconds /datum/ai_planning_subtree/manage_unreachable_list /datum/ai_planning_subtree/manage_unreachable_list/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(isnull(controller.blackboard[BB_UNREACHABLE_LIST_COOLDOWN]) || controller.blackboard[BB_CLEAR_LIST_READY] > world.time) + return controller.queue_behavior(/datum/ai_behavior/manage_unreachable_list, BB_TEMPORARY_IGNORE_LIST) /datum/ai_behavior/manage_unreachable_list behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = 45 SECONDS /datum/ai_behavior/manage_unreachable_list/perform(seconds_per_tick, datum/ai_controller/controller, list_key) . = ..() @@ -104,8 +90,13 @@ controller.clear_blackboard_key(list_key) finish_action(controller, TRUE) +/datum/ai_behavior/manage_unreachable_list/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.set_blackboard_key(BB_CLEAR_LIST_READY, controller.blackboard[BB_UNREACHABLE_LIST_COOLDOWN] + world.time) /datum/ai_planning_subtree/find_patrol_beacon + ///travel towards beacon behavior + var/travel_behavior = /datum/ai_behavior/travel_towards/beacon /datum/ai_planning_subtree/find_patrol_beacon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/basic/bot/bot_pawn = controller.pawn @@ -114,7 +105,7 @@ if(controller.blackboard_key_exists(BB_BEACON_TARGET)) bot_pawn.update_bot_mode(new_mode = BOT_PATROL) - controller.queue_behavior(/datum/ai_behavior/travel_towards/beacon, BB_BEACON_TARGET) + controller.queue_behavior(travel_behavior, BB_BEACON_TARGET) return if(controller.blackboard_key_exists(BB_PREVIOUS_BEACON_TARGET)) @@ -132,9 +123,9 @@ var/atom/final_target var/atom/previous_target = controller.blackboard[BB_PREVIOUS_BEACON_TARGET] for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"]) - if(beacon == previous_target) - continue var/dist = get_dist(bot_pawn, beacon) + if(beacon == previous_target || dist <= 1) + continue if(dist > closest_distance) continue closest_distance = dist @@ -170,6 +161,7 @@ /datum/ai_behavior/travel_towards/beacon clear_target = TRUE + new_movement_type = /datum/ai_movement/jps/bot/travel_to_beacon /datum/ai_behavior/travel_towards/beacon/finish_action(datum/ai_controller/controller, succeeded, target_key) var/atom/target = controller.blackboard[target_key] @@ -188,9 +180,12 @@ /datum/ai_behavior/travel_towards/bot_summon clear_target = TRUE + new_movement_type = /datum/ai_movement/jps/bot/travel_to_beacon /datum/ai_behavior/travel_towards/bot_summon/finish_action(datum/ai_controller/controller, succeeded, target_key) var/mob/living/basic/bot/bot_pawn = controller.pawn + if(QDELETED(bot_pawn)) // pawn can be null at this point + return ..() bot_pawn.calling_ai_ref = null bot_pawn.update_bot_mode(new_mode = BOT_IDLE) return ..() diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm index 2f16f88799db..c03806143925 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm @@ -6,6 +6,7 @@ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_UNREACHABLE_LIST_COOLDOWN = 3 MINUTES, BB_SALUTE_MESSAGES = list( "salutes", "nods in appreciation towards", @@ -51,7 +52,7 @@ /datum/ai_planning_subtree/cleaning_subtree /datum/ai_planning_subtree/cleaning_subtree/SelectBehaviors(datum/ai_controller/basic_controller/bot/cleanbot/controller, seconds_per_tick) - if(controller.reachable_key(BB_CLEAN_TARGET, BOT_CLEAN_PATH_LIMIT)) + if(controller.blackboard_key_exists(BB_CLEAN_TARGET)) controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CLEAN_TARGET) return SUBTREE_RETURN_FINISH_PLANNING @@ -68,13 +69,17 @@ controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets, BB_CLEAN_TARGET, final_hunt_list) /datum/ai_behavior/find_and_set/in_list/clean_targets - action_cooldown = 2 SECONDS //Monkestation edit + action_cooldown = 3 SECONDS /datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/controller, locate_paths, search_range) var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] for(var/atom/found_item in found) - if(LAZYACCESS(ignore_list, found_item)) + if(LAZYACCESS(ignore_list, REF(found_item))) + continue + var/list/path = get_path_to(controller.pawn, found_item, max_distance = BOT_CLEAN_PATH_LIMIT, access = controller.get_access()) + if(!length(path)) + controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, REF(found_item), TRUE) continue return found_item @@ -84,7 +89,7 @@ var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) return - if(controller.reachable_key(BB_ACID_SPRAY_TARGET, BOT_CLEAN_PATH_LIMIT)) + if(controller.blackboard_key_exists(BB_ACID_SPRAY_TARGET)) controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_ACID_SPRAY_TARGET) return SUBTREE_RETURN_FINISH_PLANNING @@ -97,7 +102,7 @@ /datum/ai_behavior/find_and_set/spray_target/search_tactic(datum/ai_controller/controller, locate_path, search_range) var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] for(var/mob/living/carbon/human/human_target in oview(search_range, controller.pawn)) - if(LAZYACCESS(ignore_list, human_target)) + if(LAZYACCESS(ignore_list, REF(human_target))) continue if(human_target.stat != CONSCIOUS || isnull(human_target.mind)) continue @@ -128,16 +133,20 @@ . = ..() controller.set_blackboard_key(BB_POST_CLEAN_COOLDOWN, POST_CLEAN_COOLDOWN + world.time) var/atom/target = controller.blackboard[target_key] + if(!succeeded && !isnull(target)) + controller.clear_blackboard_key(target_key) + controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, REF(target), TRUE) + return if(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH])) return if(!iscarbon(target)) controller.clear_blackboard_key(target_key) return var/list/speech_list = controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES] - if(!length(speech_list)) - return - var/mob/living/living_pawn = controller.pawn - living_pawn.say(pick(controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES]), forced = "ai controller") + if(length(speech_list)) + var/mob/living/living_pawn = controller.pawn + if(!QDELETED(living_pawn)) // pawn can be null at this point + living_pawn.say(pick(speech_list), forced = "ai controller") controller.clear_blackboard_key(target_key) /datum/ai_planning_subtree/use_mob_ability/foam_area @@ -155,7 +164,7 @@ /datum/ai_planning_subtree/befriend_janitors/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) var/mob/living/basic/bot/bot_pawn = controller.pawn //we are now evil. dont befriend the janitors - if((bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) + if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) return if(controller.blackboard_key_exists(BB_FRIENDLY_JANITOR)) controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_FRIENDLY_JANITOR, BB_FRIENDLY_MESSAGE) @@ -198,12 +207,12 @@ return if(isnull(parent.ai_controller)) return - if(LAZYACCESS(parent.ai_controller.blackboard[BB_TEMPORARY_IGNORE_LIST], target)) + if(LAZYACCESS(parent.ai_controller.blackboard[BB_TEMPORARY_IGNORE_LIST], REF(target))) return return ..() /datum/pet_command/point_targeting/clean/execute_action(datum/ai_controller/basic_controller/bot/controller) - if(controller.reachable_key(BB_CURRENT_PET_TARGET)) + if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET)) controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CURRENT_PET_TARGET) return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm index 2d2bc27079fd..d982869595bc 100644 --- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm @@ -1,4 +1,5 @@ #define BOT_FRUSTRATION_LIMIT 8 +#define BOT_ANGER_THRESHOLD 5 /datum/ai_controller/basic_controller/bot/hygienebot blackboard = list( @@ -128,7 +129,7 @@ var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] if(succeeded) - if(controller.blackboard[BB_WASH_FRUSTRATION] > 0) + if(controller.blackboard[BB_WASH_FRUSTRATION] > BOT_ANGER_THRESHOLD) announcement.announce(pick(controller.blackboard[BB_WASH_DONE])) controller.clear_blackboard_key(target_key) return @@ -139,4 +140,5 @@ announcement.announce(pick(controller.blackboard[BB_WASH_THREATS])) controller.set_blackboard_key(BB_WASH_FRUSTRATION, 0) +#undef BOT_ANGER_THRESHOLD #undef BOT_FRUSTRATION_LIMIT diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm index 100366e0b12c..21f9ab29d1b3 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm @@ -7,7 +7,7 @@ /datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit, /datum/ai_planning_subtree/treat_wounded_target, /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/find_patrol_beacon, + /datum/ai_planning_subtree/find_patrol_beacon/medbot, ) ai_movement = /datum/ai_movement/jps/bot/medbot reset_keys = list( @@ -19,6 +19,8 @@ ai_traits = PAUSE_DURING_DO_AFTER /datum/ai_movement/jps/bot/medbot + maximum_length = BOT_PATIENT_PATH_LIMIT + max_pathing_attempts = 20 // only AI isnt allowed to move when this flag is set, sentient players can /datum/ai_movement/jps/bot/medbot/allowed_to_move(datum/move_loop/source) @@ -28,6 +30,9 @@ return FALSE return ..() +/datum/ai_movement/jps/bot/medbot/travel_to_beacon + maximum_length = AI_BOT_PATH_LENGTH + /datum/ai_planning_subtree/treat_wounded_target @@ -36,9 +41,9 @@ if(bot_pawn.medical_mode_flags & MEDBOT_TIPPED_MODE) controller.clear_blackboard_key(BB_PATIENT_TARGET) return - var/reach_distance = (bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) ? 1 : BOT_PATIENT_PATH_LIMIT - if(controller.reachable_key(BB_PATIENT_TARGET, reach_distance)) - controller.queue_behavior(/datum/ai_behavior/tend_to_patient, BB_PATIENT_TARGET, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.bot_access_flags) + var/is_stationary = bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE + if(controller.blackboard_key_exists(BB_PATIENT_TARGET)) + controller.queue_behavior(/datum/ai_behavior/tend_to_patient, BB_PATIENT_TARGET, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.bot_access_flags, is_stationary) return SUBTREE_RETURN_FINISH_PLANNING controller.queue_behavior(/datum/ai_behavior/find_suitable_patient, BB_PATIENT_TARGET, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.medical_mode_flags, bot_pawn.bot_access_flags) @@ -52,25 +57,25 @@ search_range = (mode_flags & MEDBOT_STATIONARY_MODE) ? 1 : initial(search_range) var/list/ignore_keys = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] for(var/mob/living/carbon/human/treatable_target in oview(search_range, controller.pawn)) - if(LAZYACCESS(ignore_keys, treatable_target) || treatable_target.stat == DEAD) + if(LAZYACCESS(ignore_keys, REF(treatable_target)) || treatable_target.stat == DEAD) continue if((access_flags & BOT_COVER_EMAGGED) && treatable_target.stat == CONSCIOUS) - controller.set_blackboard_key(BB_PATIENT_TARGET, treatable_target) + controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance =BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) break if((heal_type == HEAL_ALL_DAMAGE)) if(treatable_target.get_total_damage() > threshold) - controller.set_blackboard_key(BB_PATIENT_TARGET, treatable_target) + controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) break continue if(treatable_target.get_current_damage_of_type(damagetype = heal_type) > threshold) - controller.set_blackboard_key(BB_PATIENT_TARGET, treatable_target) + controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) break finish_action(controller, controller.blackboard_key_exists(BB_PATIENT_TARGET)) /datum/ai_behavior/find_suitable_patient/finish_action(datum/ai_controller/controller, succeeded, target_key) . = ..() - if(!succeeded || get_dist(controller.pawn, controller.blackboard[target_key]) <= 1) + if(!succeeded || QDELETED(controller.pawn) ||get_dist(controller.pawn, controller.blackboard[target_key]) <= 1) return var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_WAIT_SPEECH])) @@ -85,14 +90,14 @@ return FALSE set_movement_target(controller, target) -/datum/ai_behavior/tend_to_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, threshold, damage_type_healer, access_flags) +/datum/ai_behavior/tend_to_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, threshold, damage_type_healer, access_flags, is_stationary) . = ..() var/mob/living/carbon/human/patient = controller.blackboard[target_key] if(QDELETED(patient) || patient.stat == DEAD) - finish_action(controller, FALSE, target_key) + finish_action(controller, FALSE, target_key, is_stationary) return if(check_if_healed(patient, threshold, damage_type_healer, access_flags)) - finish_action(controller, TRUE, target_key, healed_target = TRUE) + finish_action(controller, TRUE, target_key, is_stationary, healed_target = TRUE) return var/mob/living/basic/bot/bot_pawn = controller.pawn @@ -100,16 +105,23 @@ var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_NEAR_DEATH_SPEECH])) bot_pawn.melee_attack(patient) - finish_action(controller, TRUE, target_key) + finish_action(controller, TRUE, target_key, is_stationary) // only clear the target if they get healed -/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/controller, succeeded, target_key, healed_target = FALSE) +/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/controller, succeeded, target_key, is_stationary, healed_target = FALSE) . = ..() + var/atom/target = controller.blackboard[target_key] if(!succeeded) + + if(!isnull(target) && !is_stationary) + controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, REF(target), TRUE) + + controller.clear_blackboard_key(target_key) return - var/atom/target = controller.blackboard[target_key] + if(QDELETED(target) || !healed_target) return + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_AFTERHEAL_SPEECH])) controller.clear_blackboard_key(target_key) @@ -207,4 +219,11 @@ . = ..() controller.clear_blackboard_key(target_key) +/datum/ai_planning_subtree/find_patrol_beacon/medbot + ///travel towards beacon behavior + travel_behavior = /datum/ai_behavior/travel_towards/beacon/medbot + +/datum/ai_behavior/travel_towards/beacon/medbot + new_movement_type = /datum/ai_movement/jps/bot/medbot/travel_to_beacon + #undef BOT_PATIENT_PATH_LIMIT diff --git a/monkestation/code/modules/slimecore/mobs/_base_slime.dm b/monkestation/code/modules/slimecore/mobs/_base_slime.dm index 07902700889f..d3472f0778cd 100644 --- a/monkestation/code/modules/slimecore/mobs/_base_slime.dm +++ b/monkestation/code/modules/slimecore/mobs/_base_slime.dm @@ -232,6 +232,7 @@ if(slime_flags & CLEANER_SLIME) ai_controller.clear_blackboard_key(BB_CLEAN_TARGET) + new_planning_subtree |= add_or_replace_tree(/datum/ai_planning_subtree/manage_unreachable_list) new_planning_subtree |= add_or_replace_tree(/datum/ai_planning_subtree/cleaning_subtree_slime) if(!(slime_flags & PASSIVE_SLIME)) diff --git a/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm b/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm index 98efcd5f8d30..1d596ae2a597 100644 --- a/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm +++ b/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm @@ -1,63 +1,4 @@ -/datum/ai_behavior/execute_clean_slime - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH +/datum/ai_behavior/execute_clean/slime -/datum/ai_behavior/execute_clean_slime/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(isnull(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/execute_clean_slime/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - . = ..() - var/mob/living/basic/living_pawn = controller.pawn - var/atom/target = controller.blackboard[target_key] - - if(QDELETED(target)) - finish_action(controller, FALSE, target_key) - return - - living_pawn.balloon_alert_to_viewers("cleaned") - living_pawn.visible_message(span_notice("[living_pawn] dissolves \the [target].")) - SEND_SIGNAL(living_pawn, COMSIG_MOB_FEED, target, 20) - qdel(target) // Sent to the shadow realm to never be seen again - finish_action(controller, TRUE, target_key) - -/datum/ai_behavior/execute_clean_slime/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH])) - return - if(!iscarbon(target)) - controller.clear_blackboard_key(target_key) - return - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/find_and_set/in_list/clean_targets_slime +/datum/ai_behavior/find_and_set/in_list/clean_targets/slime action_cooldown = 1.2 SECONDS - -/datum/ai_behavior/find_and_set/in_list/clean_targets_slime/search_tactic(datum/ai_controller/controller, locate_paths, search_range) - var/obj/closest - var/closest_dist - var/closest_path - for(var/obj/trash as anything in view(search_range, controller.pawn)) - if(QDELETED(trash)) - continue - if(!is_type_in_typecache(trash, locate_paths) && !HAS_TRAIT(trash, TRAIT_TRASH_ITEM)) - continue - if(trash.loc == controller.pawn.loc) - return trash - var/dist = get_dist(get_turf(controller.pawn), get_turf(trash)) - var/path_length - if(!QDELETED(closest)) - if(dist > (closest_dist + 2)) // leeway to try to avoid "shorter dist but longer path" targets - continue - path_length = length(get_path_to(controller.pawn, trash)) - if(closest_path <= path_length) - continue - else - path_length = length(get_path_to(controller.pawn, trash)) - closest = trash - closest_dist = dist - closest_path = path_length - return closest diff --git a/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm b/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm index 6ed10c756cd7..8fe8d215e37a 100644 --- a/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm +++ b/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm @@ -6,6 +6,7 @@ BB_BASIC_MOB_SCARED_ITEM = /obj/item/extinguisher, BB_BASIC_MOB_STOP_FLEEING = TRUE, BB_WONT_TARGET_CLIENTS = FALSE, //specifically to stop targetting clients + BB_UNREACHABLE_LIST_COOLDOWN = 45 SECONDS, // how often we want to clear our unreachable list ) ai_movement = /datum/ai_movement/basic_avoidance diff --git a/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm b/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm index 1f768cc92696..a78684189905 100644 --- a/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm +++ b/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm @@ -3,7 +3,7 @@ /datum/ai_planning_subtree/cleaning_subtree_slime/SelectBehaviors(datum/ai_controller/basic_controller/controller, seconds_per_tick) if(controller.blackboard_key_exists(BB_CLEAN_TARGET)) - controller.queue_behavior(/datum/ai_behavior/execute_clean_slime, BB_CLEAN_TARGET) + controller.queue_behavior(/datum/ai_behavior/execute_clean/slime, BB_CLEAN_TARGET) return SUBTREE_RETURN_FINISH_PLANNING var/list/final_hunt_list = list() @@ -13,5 +13,5 @@ final_hunt_list += controller.blackboard[BB_HUNTABLE_PESTS] final_hunt_list += controller.blackboard[BB_HUNTABLE_TRASH] - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets_slime, BB_CLEAN_TARGET, final_hunt_list) + controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets/slime, BB_CLEAN_TARGET, final_hunt_list) // Add a final parameter here if you want a search range diff --git a/monkestation/code/modules/slimecore/slime_traits/cleaner.dm b/monkestation/code/modules/slimecore/slime_traits/cleaner.dm index 87501d1a0e17..0098dbec0a3e 100644 --- a/monkestation/code/modules/slimecore/slime_traits/cleaner.dm +++ b/monkestation/code/modules/slimecore/slime_traits/cleaner.dm @@ -74,15 +74,32 @@ ADD_TRAIT(parent, TRAIT_SLIME_DUST_IMMUNE, "trait") parent.recompile_ai_tree() RegisterSignals(parent, reset_signals, PROC_REF(reset_target)) + RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_unarmed_attack)) /datum/slime_trait/cleaner/on_remove(mob/living/basic/slime/parent) . = ..() + UnregisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK) UnregisterSignal(parent, reset_signals) parent.slime_flags &= ~(CLEANER_SLIME | PASSIVE_SLIME) parent.recompile_ai_tree() qdel(parent.GetComponent(/datum/component/pollution_scrubber)) REMOVE_TRAIT(parent, TRAIT_SLIME_DUST_IMMUNE, "trait") +/datum/slime_trait/cleaner/proc/on_unarmed_attack(mob/living/parent, atom/target, proximity, modifiers) + var/target_is_dissolvable = \ + is_type_in_typecache(target, cleanable_decals) \ + || is_type_in_typecache(target, cleanable_blood) \ + || is_type_in_typecache(target, huntable_pests) \ + || is_type_in_typecache(target, huntable_trash) + + if(target_is_dissolvable) + parent.balloon_alert_to_viewers("cleaned") + parent.visible_message(span_notice("[parent] dissolves \the [target].")) + SEND_SIGNAL(parent, COMSIG_MOB_FEED, target, 20) + qdel(target) // Sent to the shadow realm to never be seen again + return COMPONENT_CANCEL_ATTACK_CHAIN + // Otherwise, allow the attack to proceed as normal. + /datum/slime_trait/cleaner/proc/reset_target(datum/source) SIGNAL_HANDLER host.ai_controller.clear_blackboard_key(BB_CLEAN_TARGET)