From fe59d3f55fd2ef540142157721ab2c94502b9fe7 Mon Sep 17 00:00:00 2001 From: Iajret Creature <122297233+Steals-The-PRs@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:35:19 +0300 Subject: [PATCH] [MIRROR] ai controllers use cell trackers to know when to idle (#2034) (#2938) * ai controllers use cell trackers to know when to idle (#82691) ## About The Pull Request this makes ai controllers use cell trackers and signals to determine when to idle ## Why It's Good For The Game might be better than looping over all clients for every controller ## Changelog :cl: code: The way mobs idle has been refactored, please report any issues with non-reactive mobs /:cl: * ai controllers use cell trackers to know when to idle --------- Co-authored-by: NovaBot <154629622+NovaBot13@users.noreply.github.com> Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> --- code/__DEFINES/ai/ai.dm | 2 +- code/controllers/subsystem/ai_controllers.dm | 14 +---- code/datums/ai/_ai_controller.dm | 59 ++++++++++++++++++++ 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/code/__DEFINES/ai/ai.dm b/code/__DEFINES/ai/ai.dm index fc8d5f3491c..5427fc8f64c 100644 --- a/code/__DEFINES/ai/ai.dm +++ b/code/__DEFINES/ai/ai.dm @@ -16,7 +16,7 @@ #define AI_BOT_PATH_LENGTH 150 // How far should we, by default, be looking for interesting things to de-idle? -#define AI_DEFAULT_INTERESTING_DIST 14 +#define AI_DEFAULT_INTERESTING_DIST 10 ///Cooldown on planning if planning failed last time diff --git a/code/controllers/subsystem/ai_controllers.dm b/code/controllers/subsystem/ai_controllers.dm index 933a96dcaed..a6badb44a3f 100644 --- a/code/controllers/subsystem/ai_controllers.dm +++ b/code/controllers/subsystem/ai_controllers.dm @@ -36,11 +36,6 @@ SUBSYSTEM_DEF(ai_controllers) /datum/controller/subsystem/ai_controllers/fire(resumed) var/timer = TICK_USAGE_REAL - for(var/datum/ai_controller/ai_controller as anything in ai_controllers_by_status[AI_STATUS_IDLE]) - for(var/client/client_found in GLOB.clients) - if(get_dist(get_turf(client_found.mob), get_turf(ai_controller.pawn)) <= ai_controller.interesting_dist) - ai_controller.set_ai_status(AI_STATUS_ON) - break cost_idle = MC_AVERAGE(cost_idle, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) timer = TICK_USAGE_REAL @@ -53,14 +48,7 @@ SUBSYSTEM_DEF(ai_controllers) ai_controller.SelectBehaviors(wait * 0.1) if(!LAZYLEN(ai_controller.current_behaviors)) //Still no plan COOLDOWN_START(ai_controller, failed_planning_cooldown, AI_FAILED_PLANNING_COOLDOWN) - if(ai_controller.can_idle) - var/found_interesting = FALSE - for(var/client/client_found in GLOB.clients) - if(get_dist(get_turf(client_found.mob), get_turf(ai_controller.pawn)) <= ai_controller.interesting_dist) - found_interesting = TRUE - break - if(!found_interesting) - ai_controller.set_ai_status(AI_STATUS_IDLE) + cost_on = MC_AVERAGE(cost_on, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) ///Creates all instances of ai_subtrees and assigns them to the ai_subtrees list. diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 195117f69ed..436b4912b18 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -46,6 +46,8 @@ multiple modular subtrees with behaviors ///The idle behavior this AI performs when it has no actions. var/datum/idle_behavior/idle_behavior = null + ///our current cell grid + var/datum/cell_tracker/our_cells // Movement related things here ///Reference to the movement datum we use. Is a type on initialize but becomes a ref afterwards. @@ -73,6 +75,7 @@ multiple modular subtrees with behaviors /datum/ai_controller/Destroy(force) UnpossessPawn(FALSE) + our_cells = null set_movement_target(type, null) if(ai_movement.moving_controllers[src]) ai_movement.stop_moving_towards(src) @@ -131,6 +134,59 @@ multiple modular subtrees with behaviors RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained)) RegisterSignal(pawn, COMSIG_QDELETING, PROC_REF(on_pawn_qdeleted)) + our_cells = new(interesting_dist, interesting_dist, 1) + set_new_cells() + + RegisterSignal(pawn, COMSIG_MOVABLE_MOVED, PROC_REF(update_grid)) + +/datum/ai_controller/proc/update_grid(datum/source, datum/spatial_grid_cell/new_cell) + SIGNAL_HANDLER + + set_new_cells() + +/datum/ai_controller/proc/set_new_cells() + + var/turf/our_turf = get_turf(pawn) + + if(isnull(our_turf)) + return + + var/list/cell_collections = our_cells.recalculate_cells(our_turf) + + for(var/datum/old_grid as anything in cell_collections[2]) + UnregisterSignal(old_grid, list(SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), SPATIAL_GRID_CELL_EXITED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS))) + + for(var/datum/spatial_grid_cell/new_grid as anything in cell_collections[1]) + RegisterSignal(new_grid, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), PROC_REF(on_client_enter)) + RegisterSignal(new_grid, SPATIAL_GRID_CELL_EXITED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), PROC_REF(on_client_exit)) + + recalculate_idle() + +/datum/ai_controller/proc/should_idle() + if(!can_idle) + return FALSE + for(var/datum/spatial_grid_cell/grid as anything in our_cells.member_cells) + if(length(grid.client_contents)) + return FALSE + return TRUE + +/datum/ai_controller/proc/recalculate_idle() + if(ai_status == AI_STATUS_OFF) + return + if(should_idle()) + set_ai_status(AI_STATUS_IDLE) + +/datum/ai_controller/proc/on_client_enter(datum/source, atom/target) + SIGNAL_HANDLER + + if(ai_status == AI_STATUS_IDLE) + set_ai_status(AI_STATUS_ON) + +/datum/ai_controller/proc/on_client_exit(datum/source, datum/exited) + SIGNAL_HANDLER + + recalculate_idle() + /// 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()) @@ -141,6 +197,7 @@ multiple modular subtrees with behaviors * Returns AI_STATUS_ON otherwise. */ /datum/ai_controller/proc/get_expected_ai_status() + if (!ismob(pawn)) return AI_STATUS_ON @@ -160,6 +217,8 @@ multiple modular subtrees with behaviors #endif if(!length(SSmobs.clients_by_zlevel[pawn_turf.z])) return AI_STATUS_OFF + if(should_idle()) + return AI_STATUS_IDLE return AI_STATUS_ON /datum/ai_controller/proc/get_current_turf()