diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 2de9e7140ad..115748a9a82 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -141,3 +141,11 @@ }\ /datum/controller/subsystem/ai_controllers/##X/fire() {..() /*just so it shows up on the profiler*/} \ /datum/controller/subsystem/ai_controllers/##X + +#define UNPLANNED_CONTROLLER_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/unplanned_controllers/##X);\ +/datum/controller/subsystem/unplanned_controllers/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ + PreInit();\ +}\ +/datum/controller/subsystem/unplanned_controllers/##X/fire() {..() /*just so it shows up on the profiler*/} \ +/datum/controller/subsystem/unplanned_controllers/##X diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index c5e3172533a..ea707e33a1f 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -193,6 +193,7 @@ // Subsystem fire priority, from lowest to highest priority // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) +#define FIRE_PRIORITY_UNPLANNED_NPC 3 #define FIRE_PRIORITY_IDLE_NPC 5 #define FIRE_PRIORITY_PING 10 #define FIRE_PRIORITY_SERVER_MAINT 10 diff --git a/code/_globalvars/lists/basic_ai.dm b/code/_globalvars/lists/basic_ai.dm index 8d79c9bfafe..a8646bb8d7f 100644 --- a/code/_globalvars/lists/basic_ai.dm +++ b/code/_globalvars/lists/basic_ai.dm @@ -10,3 +10,10 @@ GLOBAL_LIST_INIT(ai_controllers_by_status, list( ///basic ai controllers based on their z level GLOBAL_LIST_EMPTY(ai_controllers_by_zlevel) + +///basic ai controllers that are currently performing idled behaviors +GLOBAL_LIST_INIT(unplanned_controllers, list( + AI_STATUS_ON = list(), + AI_STATUS_IDLE = list(), +)) + diff --git a/code/controllers/subsystem/unplanned_ai_idle_controllers.dm b/code/controllers/subsystem/unplanned_ai_idle_controllers.dm new file mode 100644 index 00000000000..6385239e18c --- /dev/null +++ b/code/controllers/subsystem/unplanned_ai_idle_controllers.dm @@ -0,0 +1,4 @@ +UNPLANNED_CONTROLLER_SUBSYSTEM_DEF(idle_unplanned_controllers) + name = "Unplanned AI Idle Controllers" + wait = 2.5 SECONDS + target_status = AI_STATUS_IDLE diff --git a/code/controllers/subsystem/unplanned_controllers.dm b/code/controllers/subsystem/unplanned_controllers.dm new file mode 100644 index 00000000000..3fb5f46dd06 --- /dev/null +++ b/code/controllers/subsystem/unplanned_controllers.dm @@ -0,0 +1,18 @@ +/// Handles making mobs perform lightweight "idle" behaviors such as wandering around when they have nothing planned +SUBSYSTEM_DEF(unplanned_controllers) + name = "Unplanned AI Controllers" + flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT + priority = FIRE_PRIORITY_UNPLANNED_NPC + init_order = INIT_ORDER_AI_CONTROLLERS + wait = 0.25 SECONDS + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + ///what ai status are we interested in + var/target_status = AI_STATUS_ON + +/datum/controller/subsystem/unplanned_controllers/stat_entry(msg) + msg = "Planning AIs:[length(GLOB.unplanned_controllers[target_status])]" + return ..() + +/datum/controller/subsystem/unplanned_controllers/fire(resumed) + for(var/datum/ai_controller/ai_controller as anything in GLOB.unplanned_controllers[target_status]) + ai_controller.idle_behavior.perform_idle_behavior(wait * 0.1, ai_controller) diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 239f02b05b8..76e87bc14f3 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -284,6 +284,7 @@ multiple modular subtrees with behaviors GLOB.ai_controllers_by_zlevel[pawn_turf.z] -= src if(ai_status) GLOB.ai_controllers_by_status[ai_status] -= src + remove_from_unplanned_controllers() pawn.ai_controller = null pawn = null if(destroy) @@ -340,10 +341,6 @@ multiple modular subtrees with behaviors ///Runs any actions that are currently running /datum/ai_controller/process(seconds_per_tick) - if(!length(current_behaviors) && idle_behavior) - 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 not an atom, rather a [current_movement_target.type]! Did you accidentally set it to a weakref?") @@ -354,7 +351,6 @@ multiple modular subtrees with behaviors CancelActions() 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 @@ -417,17 +413,24 @@ multiple modular subtrees with behaviors //remove old status, if we've got one if(ai_status) GLOB.ai_controllers_by_status[ai_status] -= src + remove_from_unplanned_controllers() stop_previous_processing() ai_status = new_ai_status GLOB.ai_controllers_by_status[new_ai_status] += src + if(ai_status == AI_STATUS_OFF) + CancelActions() + return + if(!length(current_behaviors)) + add_to_unplanned_controllers() + return + start_ai_processing() + +/datum/ai_controller/proc/start_ai_processing() switch(ai_status) if(AI_STATUS_ON) START_PROCESSING(SSai_behaviors, src) if(AI_STATUS_IDLE) START_PROCESSING(SSidle_ai_behaviors, src) - CancelActions() - if(AI_STATUS_OFF) - CancelActions() /datum/ai_controller/proc/stop_previous_processing() switch(ai_status) @@ -441,6 +444,16 @@ multiple modular subtrees with behaviors update_able_to_run() addtimer(CALLBACK(src, PROC_REF(update_able_to_run)), time) +/datum/ai_controller/proc/add_to_unplanned_controllers() + if(isnull(ai_status) || ai_status == AI_STATUS_OFF || isnull(idle_behavior)) + return + GLOB.unplanned_controllers[ai_status][src] = TRUE + +/datum/ai_controller/proc/remove_from_unplanned_controllers() + if(isnull(ai_status) || ai_status == AI_STATUS_OFF) + return + GLOB.unplanned_controllers[ai_status] -= src + /datum/ai_controller/proc/modify_cooldown(datum/ai_behavior/behavior, new_cooldown) behavior_cooldowns[behavior] = new_cooldown @@ -459,6 +472,7 @@ multiple modular subtrees with behaviors if(!behavior.setup(arglist(arguments))) return + var/should_exit_unplanned = !length(current_behaviors) planned_behaviors[behavior] = TRUE current_behaviors[behavior] = TRUE @@ -471,6 +485,9 @@ multiple modular subtrees with behaviors if(!(behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //this one blocks planning! able_to_plan = FALSE + if(should_exit_unplanned) + exit_unplanned_mode() + SEND_SIGNAL(src, AI_CONTROLLER_BEHAVIOR_QUEUED(behavior_type), arguments) /datum/ai_controller/proc/check_able_to_plan() @@ -482,6 +499,16 @@ multiple modular subtrees with behaviors /datum/ai_controller/proc/dequeue_behavior(datum/ai_behavior/behavior) current_behaviors -= behavior able_to_plan = check_able_to_plan() + if(!length(current_behaviors)) + enter_unplanned_mode() + +/datum/ai_controller/proc/exit_unplanned_mode() + remove_from_unplanned_controllers() + start_ai_processing() + +/datum/ai_controller/proc/enter_unplanned_mode() + add_to_unplanned_controllers() + stop_previous_processing() /datum/ai_controller/proc/ProcessBehavior(seconds_per_tick, datum/ai_behavior/behavior) var/list/arguments = list(seconds_per_tick, src) diff --git a/code/datums/ai/idle_behaviors/_idle_behavior.dm b/code/datums/ai/idle_behaviors/_idle_behavior.dm index 315233bb71d..bacb8e7cdf3 100644 --- a/code/datums/ai/idle_behaviors/_idle_behavior.dm +++ b/code/datums/ai/idle_behaviors/_idle_behavior.dm @@ -1,4 +1,5 @@ /datum/idle_behavior /datum/idle_behavior/proc/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - return + set waitfor = FALSE + SHOULD_CALL_PARENT(TRUE) diff --git a/code/datums/ai/idle_behaviors/idle_dog.dm b/code/datums/ai/idle_behaviors/idle_dog.dm index 46e0d040c9d..4d036e9a7a5 100644 --- a/code/datums/ai/idle_behaviors/idle_dog.dm +++ b/code/datums/ai/idle_behaviors/idle_dog.dm @@ -1,5 +1,6 @@ ///Dog specific idle behavior. /datum/idle_behavior/idle_dog/perform_idle_behavior(seconds_per_tick, datum/ai_controller/basic_controller/dog/controller) + . = ..() var/mob/living/living_pawn = controller.pawn if(!isturf(living_pawn.loc) || living_pawn.pulledby) return diff --git a/code/datums/ai/idle_behaviors/idle_haunted.dm b/code/datums/ai/idle_behaviors/idle_haunted.dm index a67b5d6cbe0..5784b5104f6 100644 --- a/code/datums/ai/idle_behaviors/idle_haunted.dm +++ b/code/datums/ai/idle_behaviors/idle_haunted.dm @@ -4,6 +4,7 @@ var/teleport_chance = 4 /datum/idle_behavior/idle_ghost_item/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) + . = ..() var/obj/item/item_pawn = controller.pawn if(ismob(item_pawn.loc)) //Being held. dont teleport return diff --git a/code/datums/ai/idle_behaviors/idle_monkey.dm b/code/datums/ai/idle_behaviors/idle_monkey.dm index 5b5e189435d..c32534dce52 100644 --- a/code/datums/ai/idle_behaviors/idle_monkey.dm +++ b/code/datums/ai/idle_behaviors/idle_monkey.dm @@ -13,6 +13,7 @@ ) /datum/idle_behavior/idle_monkey/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) + . = ..() var/mob/living/living_pawn = controller.pawn if(SPT_PROB(25, seconds_per_tick) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) diff --git a/tgstation.dme b/tgstation.dme index 30c3aa6177f..780c17d90de 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -712,6 +712,8 @@ #include "code\controllers\subsystem\transport.dm" #include "code\controllers\subsystem\tts.dm" #include "code\controllers\subsystem\tutorials.dm" +#include "code\controllers\subsystem\unplanned_ai_idle_controllers.dm" +#include "code\controllers\subsystem\unplanned_controllers.dm" #include "code\controllers\subsystem\verb_manager.dm" #include "code\controllers\subsystem\vis_overlays.dm" #include "code\controllers\subsystem\vote.dm"