diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 8a658f3913d..4836625d919 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -133,3 +133,11 @@ }\ /datum/controller/subsystem/verb_manager/##X/fire() {..() /*just so it shows up on the profiler*/} \ /datum/controller/subsystem/verb_manager/##X + +#define AI_CONTROLLER_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/ai_controllers/##X);\ +/datum/controller/subsystem/ai_controllers/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ + PreInit();\ +}\ +/datum/controller/subsystem/ai_controllers/##X/fire() {..() /*just so it shows up on the profiler*/} \ +/datum/controller/subsystem/ai_controllers/##X diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index fba8a97d713..a08d454315d 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -156,6 +156,7 @@ #define INIT_ORDER_TICKER 55 #define INIT_ORDER_TCG 55 #define INIT_ORDER_MAPPING 50 +#define INIT_ORDER_AI_IDLE_CONTROLLERS 50 #define INIT_ORDER_EARLY_ASSETS 48 #define INIT_ORDER_RESEARCH 47 #define INIT_ORDER_TIMETRACK 46 @@ -192,7 +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_IDLE_NPC 5 #define FIRE_PRIORITY_PING 10 #define FIRE_PRIORITY_SERVER_MAINT 10 #define FIRE_PRIORITY_RESEARCH 10 diff --git a/code/_globalvars/lists/basic_ai.dm b/code/_globalvars/lists/basic_ai.dm new file mode 100644 index 00000000000..8d79c9bfafe --- /dev/null +++ b/code/_globalvars/lists/basic_ai.dm @@ -0,0 +1,12 @@ +///all basic ai subtrees +GLOBAL_LIST_EMPTY(ai_subtrees) + +///basic ai controllers based on status +GLOBAL_LIST_INIT(ai_controllers_by_status, list( + AI_STATUS_ON = list(), + AI_STATUS_OFF = list(), + AI_STATUS_IDLE = list(), +)) + +///basic ai controllers based on their z level +GLOBAL_LIST_EMPTY(ai_controllers_by_zlevel) diff --git a/code/controllers/subsystem/ai_controllers.dm b/code/controllers/subsystem/ai_controllers.dm index a6badb44a3f..30d3c4986f2 100644 --- a/code/controllers/subsystem/ai_controllers.dm +++ b/code/controllers/subsystem/ai_controllers.dm @@ -6,40 +6,23 @@ SUBSYSTEM_DEF(ai_controllers) init_order = INIT_ORDER_AI_CONTROLLERS wait = 0.5 SECONDS //Plan every half second if required, not great not terrible. runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - - ///List of all ai_subtree singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_subtrees() - var/list/datum/ai_planning_subtree/ai_subtrees = list() - ///Assoc List of all AI statuses and all AI controllers with that status. - var/list/ai_controllers_by_status = list( - AI_STATUS_ON = list(), - AI_STATUS_OFF = list(), - AI_STATUS_IDLE = list(), - ) - ///Assoc List of all AI controllers and the Z level they are on, which we check when someone enters/leaves a Z level to turn them on/off. - var/list/ai_controllers_by_zlevel = list() + ///type of status we are interested in running + var/planning_status = AI_STATUS_ON /// The tick cost of all active AI, calculated on fire. - var/cost_on - /// The tick cost of all idle AI, calculated on fire. - var/cost_idle - + var/our_cost /datum/controller/subsystem/ai_controllers/Initialize() setup_subtrees() return SS_INIT_SUCCESS /datum/controller/subsystem/ai_controllers/stat_entry(msg) - var/list/active_list = ai_controllers_by_status[AI_STATUS_ON] - var/list/inactive_list = ai_controllers_by_status[AI_STATUS_OFF] - var/list/idle_list = ai_controllers_by_status[AI_STATUS_IDLE] - msg = "Active AIs:[length(active_list)]/[round(cost_on,1)]%|Inactive:[length(inactive_list)]|Idle:[length(idle_list)]/[round(cost_idle,1)]%" + var/list/planning_list = GLOB.ai_controllers_by_status[planning_status] + msg = "Planning AIs:[length(planning_list)]/[round(our_cost,1)]%" return ..() /datum/controller/subsystem/ai_controllers/fire(resumed) var/timer = TICK_USAGE_REAL - cost_idle = MC_AVERAGE(cost_idle, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) - - timer = TICK_USAGE_REAL - for(var/datum/ai_controller/ai_controller as anything in ai_controllers_by_status[AI_STATUS_ON]) + for(var/datum/ai_controller/ai_controller as anything in GLOB.ai_controllers_by_status[planning_status]) if(!COOLDOWN_FINISHED(ai_controller, failed_planning_cooldown)) continue @@ -49,18 +32,20 @@ SUBSYSTEM_DEF(ai_controllers) if(!LAZYLEN(ai_controller.current_behaviors)) //Still no plan COOLDOWN_START(ai_controller, failed_planning_cooldown, AI_FAILED_PLANNING_COOLDOWN) - cost_on = MC_AVERAGE(cost_on, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + our_cost = MC_AVERAGE(our_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) ///Creates all instances of ai_subtrees and assigns them to the ai_subtrees list. /datum/controller/subsystem/ai_controllers/proc/setup_subtrees() + if(length(GLOB.ai_subtrees)) + return for(var/subtree_type in subtypesof(/datum/ai_planning_subtree)) var/datum/ai_planning_subtree/subtree = new subtree_type - ai_subtrees[subtree_type] = subtree + GLOB.ai_subtrees[subtree_type] = subtree ///Called when the max Z level was changed, updating our coverage. /datum/controller/subsystem/ai_controllers/proc/on_max_z_changed() - if (!islist(ai_controllers_by_zlevel)) - ai_controllers_by_zlevel = new /list(world.maxz,0) - while (SSai_controllers.ai_controllers_by_zlevel.len < world.maxz) - SSai_controllers.ai_controllers_by_zlevel.len++ - SSai_controllers.ai_controllers_by_zlevel[ai_controllers_by_zlevel.len] = list() + if(!length(GLOB.ai_controllers_by_zlevel)) + GLOB.ai_controllers_by_zlevel = new /list(world.maxz,0) + while (GLOB.ai_controllers_by_zlevel.len < world.maxz) + GLOB.ai_controllers_by_zlevel.len++ + GLOB.ai_controllers_by_zlevel[GLOB.ai_controllers_by_zlevel.len] = list() diff --git a/code/controllers/subsystem/ai_idle_controllers.dm b/code/controllers/subsystem/ai_idle_controllers.dm new file mode 100644 index 00000000000..367a2c82ffc --- /dev/null +++ b/code/controllers/subsystem/ai_idle_controllers.dm @@ -0,0 +1,8 @@ +AI_CONTROLLER_SUBSYSTEM_DEF(ai_idle_controllers) + name = "AI Idle Controllers" + flags = SS_POST_FIRE_TIMING | SS_BACKGROUND + priority = FIRE_PRIORITY_IDLE_NPC + init_order = INIT_ORDER_AI_IDLE_CONTROLLERS + wait = 5 SECONDS + runlevels = RUNLEVEL_GAME + planning_status = AI_STATUS_IDLE diff --git a/code/controllers/subsystem/processing/ai_idle_behaviors.dm b/code/controllers/subsystem/processing/ai_idle_behaviors.dm new file mode 100644 index 00000000000..cda3d354882 --- /dev/null +++ b/code/controllers/subsystem/processing/ai_idle_behaviors.dm @@ -0,0 +1,6 @@ +PROCESSING_SUBSYSTEM_DEF(idle_ai_behaviors) + name = "idle_ai_behaviors" + flags = SS_NO_INIT | SS_BACKGROUND + wait = 1.5 SECONDS + priority = FIRE_PRIORITY_IDLE_NPC + init_order = INIT_ORDER_AI_IDLE_CONTROLLERS //must execute only after ai behaviors are initialized diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 33b63f09a01..964de81b62a 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -103,7 +103,7 @@ multiple modular subtrees with behaviors return var/list/temp_subtree_list = list() for(var/subtree in planning_subtrees) - var/subtree_instance = SSai_controllers.ai_subtrees[subtree] + var/subtree_instance = GLOB.ai_subtrees[subtree] temp_subtree_list += subtree_instance planning_subtrees = temp_subtree_list @@ -124,7 +124,7 @@ multiple modular subtrees with behaviors var/turf/pawn_turf = get_turf(pawn) if(pawn_turf) - SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] += src + GLOB.ai_controllers_by_zlevel[pawn_turf.z] += src SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_POSSESSED_PAWN) @@ -248,9 +248,9 @@ multiple modular subtrees with behaviors if((mob_pawn?.client && !continue_processing_when_client)) return if(old_turf) - SSai_controllers.ai_controllers_by_zlevel[old_turf.z] -= src + GLOB.ai_controllers_by_zlevel[old_turf.z] -= src if(new_turf) - SSai_controllers.ai_controllers_by_zlevel[new_turf.z] += src + GLOB.ai_controllers_by_zlevel[new_turf.z] += src var/new_level_clients = SSmobs.clients_by_zlevel[new_turf.z].len if(new_level_clients) set_ai_status(AI_STATUS_IDLE) @@ -272,9 +272,9 @@ multiple modular subtrees with behaviors ai_movement.stop_moving_towards(src) var/turf/pawn_turf = get_turf(pawn) if(pawn_turf) - SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] -= src + GLOB.ai_controllers_by_zlevel[pawn_turf.z] -= src if(ai_status) - SSai_controllers.ai_controllers_by_status[ai_status] -= src + GLOB.ai_controllers_by_status[ai_status] -= src pawn.ai_controller = null pawn = null if(destroy) @@ -386,15 +386,25 @@ multiple modular subtrees with behaviors //remove old status, if we've got one if(ai_status) - SSai_controllers.ai_controllers_by_status[ai_status] -= src + GLOB.ai_controllers_by_status[ai_status] -= src + stop_previous_processing() ai_status = new_ai_status - SSai_controllers.ai_controllers_by_status[new_ai_status] += src + GLOB.ai_controllers_by_status[new_ai_status] += src switch(ai_status) if(AI_STATUS_ON) START_PROCESSING(SSai_behaviors, src) - if(AI_STATUS_OFF, AI_STATUS_IDLE) - STOP_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) + if(AI_STATUS_ON) + STOP_PROCESSING(SSai_behaviors, src) + if(AI_STATUS_IDLE) + STOP_PROCESSING(SSidle_ai_behaviors, src) /datum/ai_controller/proc/PauseAi(time) paused_until = world.time + time diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 0596c851a5f..e157840c58a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1841,7 +1841,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) var/old_level_new_clients = (registered_z ? SSmobs.clients_by_zlevel[registered_z].len : null) //No one is left after we're gone, shut off inactive ones if(registered_z && old_level_new_clients == 0) - for(var/datum/ai_controller/controller as anything in SSai_controllers.ai_controllers_by_zlevel[registered_z]) + for(var/datum/ai_controller/controller as anything in GLOB.ai_controllers_by_zlevel[registered_z]) controller.set_ai_status(AI_STATUS_OFF) if(new_z) @@ -1852,7 +1852,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) SSmobs.clients_by_zlevel[new_z] += src if(new_level_old_clients == 0) //No one was here before, wake up all the AIs. - for (var/datum/ai_controller/controller as anything in SSai_controllers.ai_controllers_by_zlevel[new_z]) + for (var/datum/ai_controller/controller as anything in GLOB.ai_controllers_by_zlevel[new_z]) //We don't set them directly on, for instances like AIs acting while dead and other cases that may exist in the future. //This isn't a problem for AIs with a client since the client will prevent this from being called anyway. controller.set_ai_status(controller.get_expected_ai_status()) diff --git a/tgstation.dme b/tgstation.dme index bcc589c45ee..833a784378d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -531,6 +531,7 @@ #include "code\_globalvars\time_vars.dm" #include "code\_globalvars\lists\achievements.dm" #include "code\_globalvars\lists\ambience.dm" +#include "code\_globalvars\lists\basic_ai.dm" #include "code\_globalvars\lists\canisters.dm" #include "code\_globalvars\lists\cargo.dm" #include "code\_globalvars\lists\client.dm" @@ -627,6 +628,7 @@ #include "code\controllers\subsystem\addiction.dm" #include "code\controllers\subsystem\admin_verbs.dm" #include "code\controllers\subsystem\ai_controllers.dm" +#include "code\controllers\subsystem\ai_idle_controllers.dm" #include "code\controllers\subsystem\air.dm" #include "code\controllers\subsystem\ambience.dm" #include "code\controllers\subsystem\area_contents.dm" @@ -747,6 +749,7 @@ #include "code\controllers\subsystem\processing\acid.dm" #include "code\controllers\subsystem\processing\ai_basic_avoidance.dm" #include "code\controllers\subsystem\processing\ai_behaviors.dm" +#include "code\controllers\subsystem\processing\ai_idle_behaviors.dm" #include "code\controllers\subsystem\processing\antag_hud.dm" #include "code\controllers\subsystem\processing\aura.dm" #include "code\controllers\subsystem\processing\clock_component.dm"