Skip to content

Commit

Permalink
[MIRROR] [NO GBP] Fixes even more AI related CI runtimes [MDB IGNORE]…
Browse files Browse the repository at this point in the history
… (#25682) (#1157)

* [NO GBP] Fixes even more AI related CI runtimes (#80262)

## About The Pull Request

Consider this a continuation of
tgstation/tgstation#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 <[email protected]>
Co-authored-by: Bloop <[email protected]>
  • Loading branch information
3 people authored Dec 17, 2023
1 parent a4cb907 commit 23a5712
Show file tree
Hide file tree
Showing 22 changed files with 59 additions and 46 deletions.
18 changes: 16 additions & 2 deletions code/datums/ai/_ai_controller.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions code/datums/ai/babies/babies_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))
2 changes: 2 additions & 0 deletions code/datums/ai/bane/bane_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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 ..()
2 changes: 2 additions & 0 deletions code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
6 changes: 3 additions & 3 deletions code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/ai/basic_mobs/pet_commands/play_dead.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions code/datums/ai/generic/generic_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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 ..()
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 0 additions & 2 deletions code/datums/ai/generic/generic_subtrees.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/datums/ai/hunting_behavior/hunting_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
4 changes: 3 additions & 1 deletion code/datums/ai/monkey/monkey_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 0 additions & 2 deletions code/datums/ai/monkey/monkey_controller.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions code/datums/ai/monkey/monkey_subtrees.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/datums/ai/robot_customer/robot_customer_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions code/modules/mob/living/basic/bots/bot_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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 ..()
Expand All @@ -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)
Expand Down
13 changes: 8 additions & 5 deletions code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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 ..()

Expand All @@ -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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -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 ..()

Expand All @@ -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)
Expand Down
13 changes: 6 additions & 7 deletions code/modules/mob/living/basic/bots/medbot/medbot_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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 ..()

Expand All @@ -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
Expand Down Expand Up @@ -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]))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ..()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
9 changes: 2 additions & 7 deletions code/modules/mob/living/basic/pets/cat/cat_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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!"
Expand Down
Loading

0 comments on commit 23a5712

Please sign in to comment.