From 23a5712c48e24d8f12854e13216d5aa693c1af2c Mon Sep 17 00:00:00 2001 From: Iajret Creature <122297233+Steals-The-PRs@users.noreply.github.com> Date: Sun, 17 Dec 2023 20:08:19 +0300 Subject: [PATCH] [MIRROR] [NO GBP] Fixes even more AI related CI runtimes [MDB IGNORE] (#25682) (#1157) * [NO GBP] Fixes even more AI related CI runtimes (#80262) ## About The Pull Request Consider this a continuation of https://github.com/tgstation/tgstation/pull/80202 ![firefox_P62DdMv946](https://github.com/tgstation/tgstation/assets/13398309/1a784a27-e5c9-42d1-b160-7eb9251b3997) ~~It seems I missed a few.~~ Edit: Modified per request to handle this more broadly. If a pawn gets `qdel`'d, the ai controller should be set to off and get removed from the list of active controllers, and all their actions should be canceled. Also adds some qdeleted checks to `finish_action()`, which can still run after the pawn gets qdeleted as part of the `CancelActions()` chain. ## Why It's Good For The Game Less spurious CI failures. ## Changelog Nothing player facing really. * [NO GBP] Fixes even more AI related CI runtimes * Update _ai_controller.dm --------- Co-authored-by: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com> --- code/datums/ai/_ai_controller.dm | 18 ++++++++++++++++-- code/datums/ai/babies/babies_behaviors.dm | 2 ++ code/datums/ai/bane/bane_behaviors.dm | 2 ++ .../basic_ai_behaviors/climb_tree.dm | 2 ++ .../basic_ai_behaviors/unbuckle_mob.dm | 6 +++--- .../opportunistic_ventcrawler.dm | 2 +- .../pet_commands/pet_use_targeted_ability.dm | 2 +- .../ai/basic_mobs/pet_commands/play_dead.dm | 2 +- code/datums/ai/generic/generic_behaviors.dm | 4 ++-- code/datums/ai/generic/generic_subtrees.dm | 2 -- .../ai/hunting_behavior/hunting_behaviors.dm | 2 +- code/datums/ai/monkey/monkey_behaviors.dm | 4 +++- code/datums/ai/monkey/monkey_controller.dm | 2 -- code/datums/ai/monkey/monkey_subtrees.dm | 3 --- .../robot_customer/robot_customer_behaviors.dm | 2 +- code/modules/mob/living/basic/bots/bot_ai.dm | 6 ++++-- .../living/basic/bots/cleanbot/cleanbot_ai.dm | 13 ++++++++----- .../mob/living/basic/bots/medbot/medbot_ai.dm | 13 ++++++------- .../basic/lavaland/brimdemon/brimdemon_ai.dm | 4 ++-- .../mob/living/basic/pets/cat/cat_ai.dm | 9 ++------- .../parrot/parrot_ai/_parrot_controller.dm | 2 ++ .../pets/parrot/parrot_ai/parrot_perching.dm | 3 --- 22 files changed, 59 insertions(+), 46 deletions(-) diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 88a24f50516..06a4aac8358 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -70,6 +70,9 @@ multiple modular subtrees with behaviors /datum/ai_controller/Destroy(force, ...) set_ai_status(AI_STATUS_OFF) UnpossessPawn(FALSE) + set_movement_target(type, null) + if(ai_movement.moving_controllers[src]) + ai_movement.stop_moving_towards(src) return ..() ///Sets the current movement target, with an optional param to override the movement behavior @@ -118,6 +121,7 @@ multiple modular subtrees with behaviors reset_ai_status() RegisterSignal(pawn, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_changed)) RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained)) + RegisterSignal(pawn, COMSIG_QDELETING, PROC_REF(on_pawn_qdeleted)) /// Sets the AI on or off based on current conditions, call to reset after you've manually disabled it somewhere /datum/ai_controller/proc/reset_ai_status() @@ -152,7 +156,7 @@ multiple modular subtrees with behaviors if(isnull(pawn)) return // instantiated without an applicable pawn, fine - UnregisterSignal(pawn, list(COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT, COMSIG_MOB_STATCHANGE)) + UnregisterSignal(pawn, list(COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT, COMSIG_MOB_STATCHANGE, COMSIG_QDELETING)) if(ai_movement.moving_controllers[src]) ai_movement.stop_moving_towards(src) pawn.ai_controller = null @@ -230,6 +234,8 @@ multiple modular subtrees with behaviors ///Determines whether the AI can currently make a new plan /datum/ai_controller/proc/able_to_plan() . = TRUE + if(QDELETED(pawn)) + return FALSE for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) if(!(current_behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //We have a behavior that blocks planning . = FALSE @@ -296,7 +302,7 @@ multiple modular subtrees with behaviors if(length(arguments)) behavior_args[behavior_type] = arguments else - behavior_args[behavior_type] = null + behavior_args -= behavior_type /datum/ai_controller/proc/ProcessBehavior(seconds_per_tick, datum/ai_behavior/behavior) var/list/arguments = list(seconds_per_tick, src) @@ -334,6 +340,14 @@ multiple modular subtrees with behaviors set_ai_status(AI_STATUS_ON) //Can't do anything while player is connected RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained)) +// Turn the controller off the controller if the pawn has been qdeleted +/datum/ai_controller/proc/on_pawn_qdeleted() + SIGNAL_HANDLER + set_ai_status(AI_STATUS_OFF) + set_movement_target(type, null) + if(ai_movement.moving_controllers[src]) + ai_movement.stop_moving_towards(src) + /// Use this proc to define how your controller defines what access the pawn has for the sake of pathfinding. Return the access list you want to use /datum/ai_controller/proc/get_access() return diff --git a/code/datums/ai/babies/babies_behaviors.dm b/code/datums/ai/babies/babies_behaviors.dm index 5941bb818f4..8cc03d72f6b 100644 --- a/code/datums/ai/babies/babies_behaviors.dm +++ b/code/datums/ai/babies/babies_behaviors.dm @@ -75,4 +75,6 @@ if(!succeeded) return var/mob/living/living_pawn = controller.pawn + if(QDELETED(living_pawn)) // pawn can be null at this point + return living_pawn.set_combat_mode(initial(living_pawn.combat_mode)) diff --git a/code/datums/ai/bane/bane_behaviors.dm b/code/datums/ai/bane/bane_behaviors.dm index d36c110f0c3..c47c455f7f9 100644 --- a/code/datums/ai/bane/bane_behaviors.dm +++ b/code/datums/ai/bane/bane_behaviors.dm @@ -3,5 +3,7 @@ if(succeeded) var/list/bane_quotes = strings("bane.json", "bane") var/mob/living/bane = controller.pawn + if(QDELETED(bane)) // pawn can be null at this point + return ..() bane.say(pick(bane_quotes)) return ..() diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm index bb901066025..c8c18072a4a 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm @@ -25,6 +25,8 @@ . = ..() var/obj/structure/flora/target_tree = controller.blackboard[target_key] var/mob/living/basic/living_pawn = controller.pawn + if(QDELETED(living_pawn)) // pawn can be null at this point + return SEND_SIGNAL(living_pawn, COMSIG_LIVING_CLIMB_TREE, target_tree) finish_action(controller, TRUE, target_key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm index d0fb7e3e8c7..655b335d3b6 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm @@ -4,11 +4,11 @@ . = ..() var/mob/living/living_pawn = controller.pawn - var/atom/movable/buckled_too = living_pawn.buckled + var/atom/movable/buckled_to = living_pawn.buckled - if(isnull(buckled_too)) + if(isnull(buckled_to)) finish_action(controller, FALSE) return - buckled_too.unbuckle_mob(living_pawn) + buckled_to.unbuckle_mob(living_pawn) finish_action(controller, TRUE) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/opportunistic_ventcrawler.dm b/code/datums/ai/basic_mobs/basic_subtrees/opportunistic_ventcrawler.dm index 8d2dd2443de..240272d1ef4 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/opportunistic_ventcrawler.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/opportunistic_ventcrawler.dm @@ -2,7 +2,7 @@ /datum/ai_planning_subtree/opportunistic_ventcrawler /datum/ai_planning_subtree/opportunistic_ventcrawler/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(QDELETED(controller.pawn) || HAS_TRAIT(controller.pawn, TRAIT_MOVE_VENTCRAWLING)) + if(HAS_TRAIT(controller.pawn, TRAIT_MOVE_VENTCRAWLING)) return SUBTREE_RETURN_FINISH_PLANNING // hold on let me cook var/obj/machinery/atmospherics/components/unary/vent_pump/target = controller.blackboard[BB_ENTRY_VENT_TARGET] diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm b/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm index 584fb984839..c7153b0c12f 100644 --- a/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm +++ b/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm @@ -16,7 +16,7 @@ finish_action(controller, FALSE, ability_key, target_key) return var/mob/pawn = controller.pawn - if (ability.InterceptClickOn(pawn, null, target)) + if(QDELETED(pawn) || ability.InterceptClickOn(pawn, null, target)) finish_action(controller, TRUE, ability_key, target_key) /datum/ai_behavior/pet_use_ability/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) diff --git a/code/datums/ai/basic_mobs/pet_commands/play_dead.dm b/code/datums/ai/basic_mobs/pet_commands/play_dead.dm index 0520898c4a5..c4b99cf6f42 100644 --- a/code/datums/ai/basic_mobs/pet_commands/play_dead.dm +++ b/code/datums/ai/basic_mobs/pet_commands/play_dead.dm @@ -18,7 +18,7 @@ /datum/ai_behavior/play_dead/finish_action(datum/ai_controller/controller, succeeded) . = ..() var/mob/living/basic/basic_pawn = controller.pawn - if(!istype(basic_pawn) || basic_pawn.stat) // imagine actually dying while playing dead. hell, imagine being the kid waiting for your pup to get back up :( + if(QDELETED(basic_pawn) || basic_pawn.stat) // imagine actually dying while playing dead. hell, imagine being the kid waiting for your pup to get back up :( return basic_pawn.visible_message(span_notice("[basic_pawn] miraculously springs back to life!")) REMOVE_TRAIT(basic_pawn, TRAIT_FAKEDEATH, BASIC_MOB_DEATH_TRAIT) diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm index 962c8d141cd..9a7ec19cd5e 100644 --- a/code/datums/ai/generic/generic_behaviors.dm +++ b/code/datums/ai/generic/generic_behaviors.dm @@ -67,6 +67,8 @@ /datum/ai_behavior/break_spine/finish_action(datum/ai_controller/controller, succeeded, target_key) if(succeeded) var/mob/living/bane = controller.pawn + if(QDELETED(bane)) // pawn can be null at this point + return ..() bane.stop_pulling() controller.clear_blackboard_key(target_key) return ..() @@ -283,8 +285,6 @@ . = ..() controller.clear_blackboard_key(BB_FOLLOW_TARGET) - - /datum/ai_behavior/perform_emote /datum/ai_behavior/perform_emote/perform(seconds_per_tick, datum/ai_controller/controller, emote, speech_sound) diff --git a/code/datums/ai/generic/generic_subtrees.dm b/code/datums/ai/generic/generic_subtrees.dm index c33091a64d4..12007a26124 100644 --- a/code/datums/ai/generic/generic_subtrees.dm +++ b/code/datums/ai/generic/generic_subtrees.dm @@ -32,8 +32,6 @@ */ /datum/ai_planning_subtree/generic_resist/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn - if(QDELETED(living_pawn)) - return if(SHOULD_RESIST(living_pawn) && SPT_PROB(RESIST_SUBTREE_PROB, seconds_per_tick)) controller.queue_behavior(/datum/ai_behavior/resist) //BRO IM ON FUCKING FIRE BRO diff --git a/code/datums/ai/hunting_behavior/hunting_behaviors.dm b/code/datums/ai/hunting_behavior/hunting_behaviors.dm index f3ffa8c130b..3e747640be3 100644 --- a/code/datums/ai/hunting_behavior/hunting_behaviors.dm +++ b/code/datums/ai/hunting_behavior/hunting_behaviors.dm @@ -30,7 +30,7 @@ return var/mob/living/living_pawn = controller.pawn // We can't hunt if we're indisposed - if(QDELETED(living_pawn) || HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED) || living_pawn.stat != CONSCIOUS) + if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED) || living_pawn.stat != CONSCIOUS) return var/atom/hunted = controller.blackboard[target_key] diff --git a/code/datums/ai/monkey/monkey_behaviors.dm b/code/datums/ai/monkey/monkey_behaviors.dm index 13ae6817005..b041ec763a8 100644 --- a/code/datums/ai/monkey/monkey_behaviors.dm +++ b/code/datums/ai/monkey/monkey_behaviors.dm @@ -166,8 +166,10 @@ /datum/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded, target_key) . = ..() var/mob/living/living_pawn = controller.pawn - SSmove_manager.stop_looping(living_pawn) controller.clear_blackboard_key(target_key) + if(QDELETED(living_pawn)) // pawn can be null at this point + return + SSmove_manager.stop_looping(living_pawn) /// attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little /datum/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, seconds_per_tick, disarm) diff --git a/code/datums/ai/monkey/monkey_controller.dm b/code/datums/ai/monkey/monkey_controller.dm index 4e2f0a90b1e..951147007f9 100644 --- a/code/datums/ai/monkey/monkey_controller.dm +++ b/code/datums/ai/monkey/monkey_controller.dm @@ -97,8 +97,6 @@ have ways of interacting with a specific mob and control it. /datum/ai_controller/monkey/proc/set_trip_mode(mode = TRUE) var/mob/living/carbon/regressed_monkey = pawn - if(QDELETED(regressed_monkey)) - return var/brain = regressed_monkey.get_organ_slot(ORGAN_SLOT_BRAIN) if(istype(brain, /obj/item/organ/internal/brain/primate)) // In case we are a monkey AI in a human brain by who was previously controlled by a client but it now not by some marvel var/obj/item/organ/internal/brain/primate/monkeybrain = brain diff --git a/code/datums/ai/monkey/monkey_subtrees.dm b/code/datums/ai/monkey/monkey_subtrees.dm index 75d2a3eb3a5..572eeb6b56e 100644 --- a/code/datums/ai/monkey/monkey_subtrees.dm +++ b/code/datums/ai/monkey/monkey_subtrees.dm @@ -26,9 +26,6 @@ ///monkey combat subtree. /datum/ai_planning_subtree/monkey_combat/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn - if(QDELETED(living_pawn)) - return - var/list/enemies = controller.blackboard[BB_MONKEY_ENEMIES] if((HAS_TRAIT(controller.pawn, TRAIT_PACIFISM)) || (!length(enemies) && !controller.blackboard[BB_MONKEY_AGGRESSIVE])) //Pacifist, or we have no enemies and we're not pissed diff --git a/code/datums/ai/robot_customer/robot_customer_behaviors.dm b/code/datums/ai/robot_customer/robot_customer_behaviors.dm index 95260b8e68d..35fd26d76f6 100644 --- a/code/datums/ai/robot_customer/robot_customer_behaviors.dm +++ b/code/datums/ai/robot_customer/robot_customer_behaviors.dm @@ -103,7 +103,7 @@ var/mob/living/greytider = controller.blackboard[BB_CUSTOMER_CURRENT_TARGET] //usually if we stop waiting, it's because we're done with the venue. but here we're either beating some dude up //or are being qdeleted and don't want runtime errors, so don't switch to leaving - if(greytider || QDELETED(src)) + if(greytider || QDELETED(src) || QDELETED(customer_pawn)) return controller.set_blackboard_key(BB_CUSTOMER_LEAVING, TRUE) customer_pawn.update_icon() //They might have a special leaving accesoiry (french flag) diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm index 39c4875b15f..bb8d77eb64b 100644 --- a/code/modules/mob/living/basic/bots/bot_ai.dm +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -97,7 +97,7 @@ /datum/ai_planning_subtree/find_patrol_beacon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/basic/bot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn) || !(bot_pawn.bot_mode_flags & BOT_MODE_AUTOPATROL) || bot_pawn.mode == BOT_SUMMON) + if(!(bot_pawn.bot_mode_flags & BOT_MODE_AUTOPATROL) || bot_pawn.mode == BOT_SUMMON) return if(controller.blackboard_key_exists(BB_BEACON_TARGET)) @@ -179,6 +179,8 @@ /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 ..() @@ -188,7 +190,7 @@ /datum/ai_planning_subtree/salute_authority/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/basic/bot/bot_pawn = controller.pawn //we are criminals, dont salute the dirty pigs - if(QDELETED(bot_pawn) || bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) + if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) return if(controller.blackboard_key_exists(BB_SALUTE_TARGET)) controller.queue_behavior(/datum/ai_behavior/salute_authority, BB_SALUTE_TARGET, BB_SALUTE_MESSAGES) 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 5ca7cdae3a6..9d1367ee075 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm @@ -41,7 +41,7 @@ /datum/ai_planning_subtree/pet_planning/cleanbot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) var/mob/living/basic/bot/bot_pawn = controller.pawn //we are DONE listening to orders - if(QDELETED(bot_pawn) || bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) + if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) return return ..() @@ -51,7 +51,7 @@ /datum/ai_planning_subtree/cleaning_subtree/SelectBehaviors(datum/ai_controller/basic_controller/bot/cleanbot/controller, seconds_per_tick) var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn) || LAZYLEN(bot_pawn.do_afters)) + if(LAZYLEN(bot_pawn.do_afters)) return SUBTREE_RETURN_FINISH_PLANNING if(controller.reachable_key(BB_CLEAN_TARGET, BOT_CLEAN_PATH_LIMIT)) @@ -84,7 +84,7 @@ /datum/ai_planning_subtree/acid_spray/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn) || !(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) + if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) return if(controller.reachable_key(BB_ACID_SPRAY_TARGET, BOT_CLEAN_PATH_LIMIT)) controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_ACID_SPRAY_TARGET) @@ -140,6 +140,9 @@ if(!length(speech_list)) return var/mob/living/living_pawn = controller.pawn + if(QDELETED(living_pawn)) // pawn can be null at this point + controller.clear_blackboard_key(target_key) + return living_pawn.say(pick(controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES]), forced = "ai controller") controller.clear_blackboard_key(target_key) @@ -149,7 +152,7 @@ /datum/ai_planning_subtree/use_mob_ability/foam_area/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) var/mob/living/basic/bot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn) || !(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) + if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) return return ..() @@ -158,7 +161,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) 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 4c31567f9e0..54315deda72 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm @@ -23,7 +23,7 @@ /datum/ai_movement/jps/bot/medbot/allowed_to_move(datum/move_loop/source) var/datum/ai_controller/controller = source.extra_info var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn) || bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) + if(bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) return FALSE return ..() @@ -32,7 +32,7 @@ /datum/ai_planning_subtree/treat_wounded_target/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn) || bot_pawn.medical_mode_flags & MEDBOT_TIPPED_MODE) + 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 @@ -69,7 +69,7 @@ /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])) @@ -98,8 +98,7 @@ if(patient.stat >= HARD_CRIT && prob(5)) var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_NEAR_DEATH_SPEECH])) - if(!QDELETED(bot_pawn)) - bot_pawn.melee_attack(patient) + bot_pawn.melee_attack(patient) finish_action(controller, TRUE, target_key) // only clear the target if they get healed @@ -127,7 +126,7 @@ /datum/ai_planning_subtree/handle_medbot_speech/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn //we cant speak! - if(QDELETED(bot_pawn) || !(bot_pawn.medical_mode_flags & MEDBOT_SPEAK_MODE)) + if(!(bot_pawn.medical_mode_flags & MEDBOT_SPEAK_MODE)) return var/currently_tipped = bot_pawn.medical_mode_flags & MEDBOT_TIPPED_MODE @@ -174,7 +173,7 @@ /datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn) || !(bot_pawn.medical_mode_flags & MEDBOT_DECLARE_CRIT)) + if(!(bot_pawn.medical_mode_flags & MEDBOT_DECLARE_CRIT)) return return ..() 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 fe209387e00..0012aff294d 100644 --- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm @@ -25,11 +25,11 @@ /datum/ai_behavior/move_to_cardinal/brimdemon/finish_action(datum/ai_controller/controller, succeeded, target_key) . = ..() - if (!succeeded) + if(!succeeded) return var/mob/living/target = controller.blackboard[target_key] var/datum/action/cooldown/ability = controller.blackboard[BB_TARGETED_ACTION] - if(!ability?.IsAvailable()) + if(QDELETED(target) || QDELETED(controller.pawn) || !ability?.IsAvailable()) return ability.InterceptClickOn(caller = controller.pawn, target = target) diff --git a/code/modules/mob/living/basic/pets/cat/cat_ai.dm b/code/modules/mob/living/basic/pets/cat/cat_ai.dm index c3660d87f05..6cd01eb5453 100644 --- a/code/modules/mob/living/basic/pets/cat/cat_ai.dm +++ b/code/modules/mob/living/basic/pets/cat/cat_ai.dm @@ -27,8 +27,6 @@ /datum/ai_planning_subtree/reside_in_home/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn - if(QDELETED(living_pawn)) - return if(controller.blackboard_key_exists(BB_CAT_HOME)) controller.queue_behavior(/datum/ai_behavior/enter_cat_home, BB_CAT_HOME) @@ -75,7 +73,6 @@ . = ..() controller.clear_blackboard_key(target_key) - /datum/ai_planning_subtree/flee_target/from_flee_key/cat_struggle flee_behaviour = /datum/ai_behavior/run_away_from_target/cat_struggle @@ -88,7 +85,7 @@ /datum/ai_planning_subtree/territorial_struggle/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn - if(QDELETED(living_pawn) || living_pawn.gender != MALE || !SPT_PROB(hostility_chance, seconds_per_tick)) + if(living_pawn.gender != MALE || !SPT_PROB(hostility_chance, seconds_per_tick)) return if(controller.blackboard_key_exists(BB_TRESSPASSER_TARGET)) controller.queue_behavior(/datum/ai_behavior/territorial_struggle, BB_TRESSPASSER_TARGET, BB_HOSTILE_MEOWS) @@ -167,8 +164,6 @@ /datum/ai_planning_subtree/find_and_hunt_target/hunt_mice/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn - if(QDELETED(living_pawn)) - return var/list/items_we_carry = typecache_filter_list(living_pawn, controller.blackboard[BB_HUNTABLE_PREY]) if(length(items_we_carry)) return @@ -214,7 +209,7 @@ var/mob/living/living_pawn = controller.pawn var/atom/target = controller.blackboard[target_key] controller.clear_blackboard_key(target_key) - if(isnull(target)) + if(isnull(target) || QDELETED(living_pawn)) return var/manual_emote = "attempts to hunt [target]..." var/end_result = success ? "and succeeds!" : "but fails!" diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm index 9b7ecd4af1d..8a0fe5e157d 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm @@ -30,6 +30,8 @@ /datum/ai_behavior/travel_towards/and_drop/finish_action(datum/ai_controller/controller, succeeded, target_key) . = ..() var/mob/living/living_mob = controller.pawn + if(QDELETED(living_mob)) // pawn can be null at this point + return var/obj/drop_item = locate(/obj/item) in (living_mob.contents - typecache_filter_list(living_mob.contents, controller.blackboard[BB_IGNORE_ITEMS])) drop_item?.forceMove(get_turf(living_mob)) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm index 847d123f767..ccc3ef92f6e 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm @@ -8,9 +8,6 @@ /datum/ai_planning_subtree/perch_on_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn - if(QDELETED(living_pawn)) - return - var/atom/buckled_to = living_pawn.buckled //do we have a current target or is chance to unbuckle has passed? then unbuckle!