From 1b8bd51ec2fa2fa2b24c0e43c8a2d1973188bdee Mon Sep 17 00:00:00 2001 From: CapybaraExtravagante <110635252+CapybaraExtravagante@users.noreply.github.com> Date: Tue, 29 Nov 2022 23:31:53 +0100 Subject: [PATCH] Allows datum AI to create new plans while a plan is still executing (#71596) ## About The Pull Request In some cases, you need to perform behaviors that can occur ontop of different behaviors. E.g. "I need to continiously spit out foam while moving to a point". If these behaviors are put separetely, it is difficult to determine that the behavior for spitting out foam needs to end. And in the current code, aslong as it has not ended, the plan will never end. So once the AI reaches the point it would stand still at the end and spit out foam unendingly. To work around this I've made it so behaviors can be set to allow planning while they run if they have the AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION flag. If all remaining behaviors on a controller have this flag, a new plan is made. If this plan is the exact same as the plan that was currently being performed, nothing happens. But if the plan is different, the current one is ended and the new plan is executed. This means situations like this are handled gracefully now. This will be required for basic bots. ## Why It's Good For The Game More graceful handling of "continous" behaviors! :) ## Changelog :cl: Capybara Holly refactor: Allows datum AI to create new plans while a plan is still executing /:cl: Co-authored-by: Capybara --- code/__DEFINES/ai.dm | 2 ++ code/controllers/subsystem/ai_controllers.dm | 9 +++++---- code/datums/ai/_ai_controller.dm | 21 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm index fcb595cdbd3..622cc84134e 100644 --- a/code/__DEFINES/ai.dm +++ b/code/__DEFINES/ai.dm @@ -22,6 +22,8 @@ #define AI_BEHAVIOR_KEEP_MOVE_TARGET_ON_FINISH (1<<2) ///Does finishing this task make the AI stop moving towards the target? #define AI_BEHAVIOR_KEEP_MOVING_TOWARDS_TARGET_ON_FINISH (1<<3) +///Does this behavior NOT block planning? +#define AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION (1<<4) ///AI flags #define STOP_MOVING_WHEN_PULLED (1<<0) diff --git a/code/controllers/subsystem/ai_controllers.dm b/code/controllers/subsystem/ai_controllers.dm index 82c22869e32..3d8d2653149 100644 --- a/code/controllers/subsystem/ai_controllers.dm +++ b/code/controllers/subsystem/ai_controllers.dm @@ -27,7 +27,8 @@ SUBSYSTEM_DEF(ai_controllers) if(!COOLDOWN_FINISHED(ai_controller, failed_planning_cooldown)) continue - if(!LAZYLEN(ai_controller.current_behaviors)) - 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.able_to_plan()) + continue + 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) diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 2c1d733baf8..25da255284a 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -9,6 +9,8 @@ multiple modular subtrees with behaviors var/atom/pawn ///Bitfield of traits for this AI to handle extra behavior var/ai_traits + ///Current actions planned to be performed by the AI in the upcoming plan + var/list/planned_behaviors ///Current actions being performed by the AI. var/list/current_behaviors ///Current actions and their respective last time ran as an assoc list. @@ -190,6 +192,14 @@ multiple modular subtrees with behaviors ProcessBehavior(action_delta_time, current_behavior) return +///Determines whether the AI can currently make a new plan +/datum/ai_controller/proc/able_to_plan() + . = TRUE + for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) + if(!(current_behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //We have a behavior that blocks planning + . = FALSE + break + ///This is where you decide what actions are taken by the AI. /datum/ai_controller/proc/SelectBehaviors(delta_time) SHOULD_NOT_SLEEP(TRUE) //Fuck you don't sleep in procs like this. @@ -197,12 +207,22 @@ multiple modular subtrees with behaviors return FALSE LAZYINITLIST(current_behaviors) + LAZYCLEARLIST(planned_behaviors) if(LAZYLEN(planning_subtrees)) for(var/datum/ai_planning_subtree/subtree as anything in planning_subtrees) if(subtree.SelectBehaviors(src, delta_time) == SUBTREE_RETURN_FINISH_PLANNING) break + for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) + if(LAZYACCESS(planned_behaviors, current_behavior)) + continue + var/list/arguments = list(src, FALSE) + var/list/stored_arguments = behavior_args[type] + if(stored_arguments) + arguments += stored_arguments + current_behavior.finish_action(arglist(arguments)) + ///This proc handles changing ai status, and starts/stops processing if required. /datum/ai_controller/proc/set_ai_status(new_ai_status) if(ai_status == new_ai_status) @@ -231,6 +251,7 @@ multiple modular subtrees with behaviors if(!behavior.setup(arglist(arguments))) return LAZYADD(current_behaviors, behavior) + LAZYADDASSOC(planned_behaviors, behavior, TRUE) arguments.Cut(1, 2) if(length(arguments)) behavior_args[behavior_type] = arguments