diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm index 55c75c99c02..06b98dc6a5c 100644 --- a/code/__DEFINES/ai.dm +++ b/code/__DEFINES/ai.dm @@ -60,8 +60,6 @@ #define HAUNTED_ITEM_ATTACK_HAUNT_CHANCE 10 ///Chance for haunted item to try to get itself let go. #define HAUNTED_ITEM_ESCAPE_GRASP_CHANCE 20 -///Chance for haunted item to warp somewhere new -#define HAUNTED_ITEM_TELEPORT_CHANCE 4 ///Amount of aggro you get when picking up a haunted item #define HAUNTED_ITEM_AGGRO_ADDITION 2 @@ -77,7 +75,6 @@ //defines ///how far a cursed item will still try to chase a target #define CURSED_VIEW_RANGE 7 -#define CURSED_ITEM_TELEPORT_CHANCE 4 //blackboards ///Actual mob the item is haunting at the moment diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index a53e0828cfe..b3da847b65a 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -32,6 +32,9 @@ multiple modular subtrees with behaviors ///All subtrees this AI has available, will run them in order, so make sure they're in the order you want them to run. On initialization of this type, it will start as a typepath(s) and get converted to references of ai_subtrees found in SSai_controllers when init_subtrees() is called var/list/planning_subtrees + ///The idle behavior this AI performs when it has no actions. + var/datum/idle_behavior/idle_behavior = null + // Movement related things here ///Reference to the movement datum we use. Is a type on initialize but becomes a ref afterwards. var/datum/ai_movement/ai_movement = /datum/ai_movement/dumb @@ -52,6 +55,9 @@ multiple modular subtrees with behaviors change_ai_movement_type(ai_movement) init_subtrees() + if(idle_behavior) + idle_behavior = new idle_behavior() + PossessPawn(new_pawn) /datum/ai_controller/Destroy(force, ...) @@ -130,8 +136,8 @@ multiple modular subtrees with behaviors walk(pawn, 0) //stop moving return //this should remove them from processing in the future through event-based stuff. - if(!LAZYLEN(current_behaviors)) - PerformIdleBehavior(delta_time) //Do some stupid shit while we have nothing to do + if(!LAZYLEN(current_behaviors) && idle_behavior) + idle_behavior.perform_idle_behavior(delta_time, src) //Do some stupid shit while we have nothing to do return if(current_movement_target && get_dist(pawn, current_movement_target) > max_target_distance) //The distance is out of range @@ -174,10 +180,6 @@ multiple modular subtrees with behaviors ProcessBehavior(action_delta_time, current_behavior) return -///Perform some dumb idle behavior. -/datum/ai_controller/proc/PerformIdleBehavior(delta_time) - return - ///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. diff --git a/code/datums/ai/cursed/cursed_controller.dm b/code/datums/ai/cursed/cursed_controller.dm index bc8ee10d625..11501a3f9fb 100644 --- a/code/datums/ai/cursed/cursed_controller.dm +++ b/code/datums/ai/cursed/cursed_controller.dm @@ -13,6 +13,7 @@ BB_CURSED_THROW_ATTEMPT_COUNT ) planning_subtrees = list(/datum/ai_planning_subtree/cursed) + idle_behavior = /datum/idle_behavior/idle_ghost_item /datum/ai_controller/cursed/TryPossessPawn(atom/new_pawn) if(!isitem(new_pawn)) @@ -25,14 +26,6 @@ UnregisterSignal(pawn, list(COMSIG_MOVABLE_IMPACT, COMSIG_ITEM_EQUIPPED)) return ..() //Run parent at end -/datum/ai_controller/cursed/PerformIdleBehavior(delta_time) - var/obj/item/item_pawn = pawn - if(ismob(item_pawn.loc)) //Being held. dont teleport - return - if(DT_PROB(CURSED_ITEM_TELEPORT_CHANCE, delta_time)) - playsound(item_pawn.loc, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE) - do_teleport(pawn, get_turf(pawn), 4, channel = TELEPORT_CHANNEL_MAGIC) - ///signal called by the pawn hitting something after a throw /datum/ai_controller/cursed/proc/on_throw_hit(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum) SIGNAL_HANDLER diff --git a/code/datums/ai/dog/dog_controller.dm b/code/datums/ai/dog/dog_controller.dm index 60dd207a646..45557ac6b24 100644 --- a/code/datums/ai/dog/dog_controller.dm +++ b/code/datums/ai/dog/dog_controller.dm @@ -55,27 +55,6 @@ return simple_pawn.access_card -/datum/ai_controller/dog/PerformIdleBehavior(delta_time) - var/mob/living/living_pawn = pawn - if(!isturf(living_pawn.loc) || living_pawn.pulledby) - return - - // if we were just ordered to heel, chill out for a bit - if(!COOLDOWN_FINISHED(src, heel_cooldown)) - return - - // if we're just ditzing around carrying something, occasionally print a message so people know we have something - if(blackboard[BB_SIMPLE_CARRY_ITEM] && DT_PROB(5, delta_time)) - var/obj/item/carry_item = blackboard[BB_SIMPLE_CARRY_ITEM] - living_pawn.visible_message(span_notice("[living_pawn] gently teethes on \the [carry_item] in [living_pawn.p_their()] mouth."), vision_distance=COMBAT_MESSAGE_RANGE) - - if(DT_PROB(5, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE)) - var/move_dir = pick(GLOB.alldirs) - living_pawn.Move(get_step(living_pawn, move_dir), move_dir) - else if(DT_PROB(10, delta_time)) - living_pawn.manual_emote(pick("dances around.","chases [living_pawn.p_their()] tail!")) - living_pawn.AddComponent(/datum/component/spinny) - /// Someone has thrown something, see if it's someone we care about and start listening to the thrown item so we can see if we want to fetch it when it lands /datum/ai_controller/dog/proc/listened_throw(datum/source, mob/living/carbon/carbon_thrower) SIGNAL_HANDLER diff --git a/code/datums/ai/hauntium/haunted_controller.dm b/code/datums/ai/hauntium/haunted_controller.dm index 6e9670a85da..0228b7dbcea 100644 --- a/code/datums/ai/hauntium/haunted_controller.dm +++ b/code/datums/ai/hauntium/haunted_controller.dm @@ -5,6 +5,7 @@ BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT) planning_subtrees = list(/datum/ai_planning_subtree/haunted) + idle_behavior = /datum/idle_behavior/idle_ghost_item /datum/ai_controller/haunted/TryPossessPawn(atom/new_pawn) if(!isitem(new_pawn)) @@ -16,14 +17,6 @@ UnregisterSignal(pawn, COMSIG_ITEM_EQUIPPED) return ..() //Run parent at end -/datum/ai_controller/haunted/PerformIdleBehavior(delta_time) - var/obj/item/item_pawn = pawn - if(ismob(item_pawn.loc)) //Being held. dont teleport - return - if(DT_PROB(HAUNTED_ITEM_TELEPORT_CHANCE, delta_time)) - playsound(item_pawn.loc, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE) - do_teleport(pawn, get_turf(pawn), 4, channel = TELEPORT_CHANNEL_MAGIC) - ///Signal response for when the item is picked up; stops listening for follow up equips, just waits for a drop. /datum/ai_controller/haunted/proc/on_equip(datum/source, mob/equipper, slot) SIGNAL_HANDLER diff --git a/code/datums/ai/idle_behaviors/_idle_behavior.dm b/code/datums/ai/idle_behaviors/_idle_behavior.dm new file mode 100644 index 00000000000..a5ab827636a --- /dev/null +++ b/code/datums/ai/idle_behaviors/_idle_behavior.dm @@ -0,0 +1,4 @@ +/datum/idle_behavior + +/datum/idle_behavior/proc/perform_idle_behavior(delta_time, datum/ai_controller/controller) + return diff --git a/code/datums/ai/idle_behaviors/idle_dog.dm b/code/datums/ai/idle_behaviors/idle_dog.dm new file mode 100644 index 00000000000..fd27bc6968e --- /dev/null +++ b/code/datums/ai/idle_behaviors/idle_dog.dm @@ -0,0 +1,21 @@ +///Dog specific idle behavior. +/datum/idle_behavior/idle_dog/perform_idle_behavior(delta_time, datum/ai_controller/dog/controller) + var/mob/living/living_pawn = controller.pawn + if(!isturf(living_pawn.loc) || living_pawn.pulledby) + return + + // if we were just ordered to heel, chill out for a bit + if(!COOLDOWN_FINISHED(controller, heel_cooldown)) + return + + // if we're just ditzing around carrying something, occasionally print a message so people know we have something + if(controller.blackboard[BB_SIMPLE_CARRY_ITEM] && DT_PROB(5, delta_time)) + var/obj/item/carry_item = controller.blackboard[BB_SIMPLE_CARRY_ITEM] + living_pawn.visible_message(span_notice("[living_pawn] gently teethes on \the [carry_item] in [living_pawn.p_their()] mouth."), vision_distance=COMBAT_MESSAGE_RANGE) + + if(DT_PROB(5, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE)) + var/move_dir = pick(GLOB.alldirs) + living_pawn.Move(get_step(living_pawn, move_dir), move_dir) + else if(DT_PROB(10, delta_time)) + living_pawn.manual_emote(pick("dances around.","chases [living_pawn.p_their()] tail!")) + living_pawn.AddComponent(/datum/component/spinny) diff --git a/code/datums/ai/idle_behaviors/idle_haunted.dm b/code/datums/ai/idle_behaviors/idle_haunted.dm new file mode 100644 index 00000000000..bca1fca98b3 --- /dev/null +++ b/code/datums/ai/idle_behaviors/idle_haunted.dm @@ -0,0 +1,12 @@ +///If not held, teleport somewhere else +/datum/idle_behavior/idle_ghost_item + ///Chance for item to teleport somewhere else + var/teleport_chance = 4 + +/datum/idle_behavior/idle_ghost_item/perform_idle_behavior(delta_time, datum/ai_controller/controller) + var/obj/item/item_pawn = controller.pawn + if(ismob(item_pawn.loc)) //Being held. dont teleport + return + if(DT_PROB(teleport_chance, delta_time)) + playsound(item_pawn.loc, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE) + do_teleport(item_pawn, get_turf(item_pawn), 4, channel = TELEPORT_CHANNEL_MAGIC) diff --git a/code/datums/ai/idle_behaviors/idle_monkey.dm b/code/datums/ai/idle_behaviors/idle_monkey.dm new file mode 100644 index 00000000000..3b12e1f275e --- /dev/null +++ b/code/datums/ai/idle_behaviors/idle_monkey.dm @@ -0,0 +1,10 @@ +/datum/idle_behavior/idle_monkey/perform_idle_behavior(delta_time, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + + if(DT_PROB(25, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) + var/move_dir = pick(GLOB.alldirs) + living_pawn.Move(get_step(living_pawn, move_dir), move_dir) + else if(DT_PROB(5, delta_time)) + INVOKE_ASYNC(living_pawn, /mob.proc/emote, pick("screech")) + else if(DT_PROB(1, delta_time)) + INVOKE_ASYNC(living_pawn, /mob.proc/emote, pick("scratch","jump","roll","tail")) diff --git a/code/datums/ai/idle_behaviors/idle_random_walk.dm b/code/datums/ai/idle_behaviors/idle_random_walk.dm new file mode 100644 index 00000000000..1187d8786c1 --- /dev/null +++ b/code/datums/ai/idle_behaviors/idle_random_walk.dm @@ -0,0 +1,12 @@ +/datum/idle_behavior/idle_random_walk + ///Chance that the mob random walks per second + var/walk_chance = 25 + +/datum/idle_behavior/idle_random_walk/perform_idle_behavior(delta_time, datum/ai_controller/controller) + . = ..() + var/mob/living/living_pawn = controller.pawn + + if(DT_PROB(walk_chance, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) + var/move_dir = pick(GLOB.alldirs) + living_pawn.Move(get_step(living_pawn, move_dir), move_dir) + diff --git a/code/datums/ai/monkey/monkey_controller.dm b/code/datums/ai/monkey/monkey_controller.dm index be5b2f923bc..154b652c46c 100644 --- a/code/datums/ai/monkey/monkey_controller.dm +++ b/code/datums/ai/monkey/monkey_controller.dm @@ -24,6 +24,7 @@ have ways of interacting with a specific mob and control it. var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = .proc/on_entered, ) + idle_behavior = /datum/idle_behavior/idle_monkey /datum/ai_controller/monkey/angry @@ -159,18 +160,6 @@ have ways of interacting with a specific mob and control it. return TRUE return FALSE -//When idle just kinda fuck around. -/datum/ai_controller/monkey/PerformIdleBehavior(delta_time) - var/mob/living/living_pawn = pawn - - if(DT_PROB(25, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) - var/move_dir = pick(GLOB.alldirs) - living_pawn.Move(get_step(living_pawn, move_dir), move_dir) - else if(DT_PROB(5, delta_time)) - INVOKE_ASYNC(living_pawn, /mob.proc/emote, pick("screech")) - else if(DT_PROB(1, delta_time)) - INVOKE_ASYNC(living_pawn, /mob.proc/emote, pick("scratch","jump","roll","tail")) - ///Reactive events to being hit /datum/ai_controller/monkey/proc/retaliate(mob/living/L) var/list/enemies = blackboard[BB_MONKEY_ENEMIES] diff --git a/code/datums/ai/oldhostile/hostile_tameable.dm b/code/datums/ai/oldhostile/hostile_tameable.dm index 4ab634f6f5d..04ee10fd908 100644 --- a/code/datums/ai/oldhostile/hostile_tameable.dm +++ b/code/datums/ai/oldhostile/hostile_tameable.dm @@ -10,6 +10,7 @@ BB_HOSTILE_ATTACK_WORD = "growls", ) ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk/hostile_tameable var/ride_penalty_movement = 1 SECONDS @@ -62,15 +63,6 @@ return simple_pawn.access_card -/datum/ai_controller/hostile_friend/PerformIdleBehavior(delta_time) - var/mob/living/living_pawn = pawn - if(!isturf(living_pawn.loc) || living_pawn.pulledby || length(living_pawn.buckled_mobs)) - return - - if(DT_PROB(5, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE)) - var/move_dir = pick(GLOB.alldirs) - living_pawn.Move(get_step(living_pawn, move_dir), move_dir) - /datum/ai_controller/hostile_friend/proc/on_ridden_driver_move(atom/movable/movable_parent, mob/living/user, direction) SIGNAL_HANDLER PauseAi(ride_penalty_movement) @@ -223,3 +215,7 @@ if(living_pawn.buckled) queue_behavior(/datum/ai_behavior/resist)//in case they are in bed or something queue_behavior(/datum/ai_behavior/attack) + + +/datum/idle_behavior/idle_random_walk/hostile_tameable + walk_chance = 5 diff --git a/code/modules/mob/living/basic/vermin/cockroach.dm b/code/modules/mob/living/basic/vermin/cockroach.dm index ca04c8d5847..72b7fc5bb51 100644 --- a/code/modules/mob/living/basic/vermin/cockroach.dm +++ b/code/modules/mob/living/basic/vermin/cockroach.dm @@ -51,21 +51,12 @@ ai_traits = STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( /datum/ai_planning_subtree/random_speech/cockroach, /datum/ai_planning_subtree/find_and_hunt_target ) - -/datum/ai_controller/basic_controller/cockroach/PerformIdleBehavior(delta_time) - . = ..() - var/mob/living/living_pawn = pawn - - if(DT_PROB(25, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) - var/move_dir = pick(GLOB.alldirs) - living_pawn.Move(get_step(living_pawn, move_dir), move_dir) - - /obj/projectile/glockroachbullet damage = 10 //same damage as a hivebot damage_type = BRUTE diff --git a/tgstation.dme b/tgstation.dme index 583b5140f76..37330511dc0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -494,6 +494,11 @@ #include "code\datums\ai\hauntium\haunted_controller.dm" #include "code\datums\ai\hauntium\hauntium_subtrees.dm" #include "code\datums\ai\hunting_behavior\hunting_behaviors.dm" +#include "code\datums\ai\idle_behaviors\_idle_behavior.dm" +#include "code\datums\ai\idle_behaviors\idle_dog.dm" +#include "code\datums\ai\idle_behaviors\idle_haunted.dm" +#include "code\datums\ai\idle_behaviors\idle_monkey.dm" +#include "code\datums\ai\idle_behaviors\idle_random_walk.dm" #include "code\datums\ai\monkey\monkey_behaviors.dm" #include "code\datums\ai\monkey\monkey_controller.dm" #include "code\datums\ai\monkey\monkey_subtrees.dm"