wire up ai idling to spatial grid (#30711)

* wire up ai idling to spatial grid

* lint

* Apply suggestions from code review

Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Signed-off-by: warriorstar-orion <orion@snowfrost.garden>

* stupid loadbearing src

* some spatial grid cleanup

* make sure to remove controllers in the GLOB

* immediately set targets and state for hiveloord brood

* properly fail behaviors when movement targets are lost

* Apply suggestions from code review

Comment formatting

Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/ai/ai_controller.dm

Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/ai/ai_controller.dm

Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Apply suggestions from code review

Comment formatting

Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/ai/ai_controller.dm

Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com>

---------

Signed-off-by: warriorstar-orion <orion@snowfrost.garden>
Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com>
Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
This commit is contained in:
warriorstar-orion
2025-12-09 23:46:38 +00:00
committed by GitHub
co-authored by DGamerL PollardTheDragon Burzah
parent 9e87289bd8
commit be48d7598f
17 changed files with 351 additions and 127 deletions
+29 -34
View File
@@ -7,59 +7,54 @@ SUBSYSTEM_DEF(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
/// The tick cost of all currently processed AI, being summed together
var/summing_cost
/datum/controller/subsystem/ai_controllers/Initialize()
setup_subtrees()
/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)
if(!resumed)
summing_cost = 0
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])
if(!ai_controller.able_to_plan())
for(var/datum/ai_controller/ai_controller as anything in GLOB.ai_controllers_by_status[planning_status])
if(!ai_controller.able_to_plan)
continue
ai_controller.select_behaviors(wait / (1 SECONDS))
if(!LAZYLEN(ai_controller.current_behaviors)) //Still no plan
ai_controller.select_behaviors(wait * 0.1)
if(!length(ai_controller.current_behaviors)) // Still no plan
ai_controller.planning_failed()
if(MC_TICK_CHECK)
break
cost_on = MC_AVERAGE(cost_on, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
summing_cost += TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)
if(MC_TICK_CHECK)
return
///Creates all instances of ai_subtrees and assigns them to the ai_subtrees list.
our_cost = MC_AVERAGE(our_cost, summing_cost)
/// 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.
/// 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()
@@ -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
@@ -0,0 +1,34 @@
GLOBAL_LIST_EMPTY(unplanned_controller_subsystems)
/// Handles making mobs perform lightweight "idle" behaviors such as wandering around when they have nothing planned
SUBSYSTEM_DEF(unplanned_controllers)
name = "AI Unplanned Controllers"
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
priority = FIRE_PRIORITY_UNPLANNED_NPC
wait = 0.25 SECONDS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
/// What ai status are we interested in
var/target_status = AI_STATUS_ON
var/list/current_run = list()
/datum/controller/subsystem/unplanned_controllers/Initialize()
GLOB.unplanned_controller_subsystems += src
/datum/controller/subsystem/unplanned_controllers/Destroy()
GLOB.unplanned_controller_subsystems -= src
return ..()
/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)
if(!resumed)
src.current_run = GLOB.unplanned_controllers[target_status].Copy()
var/list/current_run = src.current_run // cache for sonic speed
while(length(current_run))
var/datum/ai_controller/unplanned = current_run[current_run.len]
current_run.len--
if(!QDELETED(unplanned))
unplanned.idle_behavior.perform_idle_behavior(wait * 0.1, unplanned)
if(MC_TICK_CHECK)
return
@@ -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
@@ -0,0 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(idle_ai_behaviors)
name = "AI Idle 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