diff --git a/code/datums/ai/ai_controller.dm b/code/datums/ai/ai_controller.dm index 7b5e4ef7b49..fc23d17a333 100644 --- a/code/datums/ai/ai_controller.dm +++ b/code/datums/ai/ai_controller.dm @@ -99,8 +99,17 @@ RESTRICT_TYPE(/datum/ai_controller) /// Sets the current movement target, with an optional param to override the movement behavior /datum/ai_controller/proc/set_movement_target(source, atom/target, datum/ai_movement/new_movement) + if(current_movement_target) + UnregisterSignal(current_movement_target, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_PREQDELETED)) + if(!isnull(target) && !isatom(target)) + stack_trace("[pawn]'s current movement target is [target.type], not an atom") + cancel_actions() + return movement_target_source = source current_movement_target = target + if(!isnull(current_movement_target)) + RegisterSignal(current_movement_target, COMSIG_MOVABLE_MOVED, PROC_REF(on_movement_target_move)) + RegisterSignal(current_movement_target, COMSIG_PARENT_PREQDELETED, PROC_REF(on_movement_target_delete)) if(new_movement) change_ai_movement_type(new_movement) @@ -158,6 +167,18 @@ RESTRICT_TYPE(/datum/ai_controller) RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained)) RegisterSignal(pawn, COMSIG_PARENT_QDELETING, PROC_REF(on_pawn_qdeleted)) +/datum/ai_controller/proc/on_movement_target_move(datum/source) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + check_target_max_distance() + +/datum/ai_controller/proc/on_movement_target_delete(atom/source) + SIGNAL_HANDLER // COMSIG_PARENT_PREQDELETED + set_movement_target(source = type, target = null) + +/datum/ai_controller/proc/check_target_max_distance() + if(get_dist(current_movement_target, pawn) > max_target_distance) + cancel_actions() + /// 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() set_ai_status(get_expected_ai_status()) @@ -282,16 +303,6 @@ RESTRICT_TYPE(/datum/ai_controller) idle_behavior.perform_idle_behavior(seconds_per_tick, src) //Do some stupid shit while we have nothing to do return - if(current_movement_target) - if(!isatom(current_movement_target)) - stack_trace("[pawn]'s current movement target is [current_movement_target], not an atom!") - cancel_actions() - return - - if(get_dist(pawn, current_movement_target) > max_target_distance) //The distance is out of range - cancel_actions() - return - for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) // Convert the current behaviour action cooldown to realtime seconds from deciseconds.current_behavior // Then pick the max of this and the seconds_per_tick passed to ai_controller.process() @@ -299,9 +310,9 @@ RESTRICT_TYPE(/datum/ai_controller) var/action_seconds_per_tick = max(current_behavior.get_cooldown(src) * 0.1, seconds_per_tick) if(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT) //Might need to move closer - if(!current_movement_target) - stack_trace("[pawn] wants to perform action type [current_behavior.type] which requires movement, but has no current movement target!") - return // This can cause issues, so don't let these slide. + if(isnull(current_movement_target)) + fail_behavior(current_behavior) + return // Stops pawns from performing such actions that should require the target to be adjacent. var/atom/movable/moving_pawn = pawn @@ -432,11 +443,14 @@ RESTRICT_TYPE(/datum/ai_controller) if(!LAZYLEN(current_behaviors)) return for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) - var/list/arguments = list(src, FALSE) - var/list/stored_arguments = behavior_args[current_behavior.type] - if(stored_arguments) - arguments += stored_arguments - current_behavior.finish_action(arglist(arguments)) + fail_behavior(current_behavior) + +/datum/ai_controller/proc/fail_behavior(datum/ai_behavior/current_behavior) + var/list/arguments = list(src, FALSE) + var/list/stored_arguments = behavior_args[current_behavior.type] + if(stored_arguments) + arguments += stored_arguments + current_behavior.finish_action(arglist(arguments)) /// Turn the controller on or off based on if you're alive. /// We only register to this if the flag is present so don't need to check again.