/* AI controllers are a datumized form of AI that simulates the input a player would otherwise give to a mob. What this means is that these datums have ways of interacting with a specific mob and control it. */ ///OOK OOK OOK /datum/ai_controller/monkey movement_delay = 0.4 SECONDS planning_subtrees = list( /datum/ai_planning_subtree/generic_resist, /datum/ai_planning_subtree/monkey_combat, /datum/ai_planning_subtree/generic_hunger, /datum/ai_planning_subtree/generic_play_instrument, /datum/ai_planning_subtree/monkey_shenanigans, ) blackboard = list( BB_MONKEY_AGGRESSIVE = FALSE, BB_MONKEY_BEST_FORCE_FOUND = 0, BB_MONKEY_ENEMIES = list(), BB_MONKEY_BLACKLISTITEMS = list(), BB_MONKEY_PICKUPTARGET = null, BB_MONKEY_PICKPOCKETING = FALSE, BB_MONKEY_DISPOSING = FALSE, BB_MONKEY_TARGET_DISPOSAL = null, BB_MONKEY_CURRENT_ATTACK_TARGET = null, BB_MONKEY_GUN_NEURONS_ACTIVATED = FALSE, BB_MONKEY_GUN_WORKED = TRUE, BB_SONG_LINES = MONKEY_SONG, ) idle_behavior = /datum/idle_behavior/idle_monkey /datum/ai_controller/monkey/pun_pun movement_delay = 0.7 SECONDS //pun pun moves slower so the bartender can keep track of them planning_subtrees = list( /datum/ai_planning_subtree/generic_resist, /datum/ai_planning_subtree/monkey_combat, /datum/ai_planning_subtree/generic_hunger, /datum/ai_planning_subtree/generic_play_instrument, /datum/ai_planning_subtree/punpun_shenanigans, ) idle_behavior = /datum/idle_behavior/idle_monkey/pun_pun /datum/ai_controller/monkey/angry /datum/ai_controller/monkey/angry/TryPossessPawn(atom/new_pawn) . = ..() if(. & AI_CONTROLLER_INCOMPATIBLE) return blackboard[BB_MONKEY_AGGRESSIVE] = TRUE //Angry cunt /datum/ai_controller/monkey/TryPossessPawn(atom/new_pawn) if(!isliving(new_pawn)) return AI_CONTROLLER_INCOMPATIBLE var/mob/living/living_pawn = new_pawn RegisterSignal(new_pawn, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby)) RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand)) RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_PAW, PROC_REF(on_attack_paw)) RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_ANIMAL, PROC_REF(on_attack_animal)) RegisterSignal(new_pawn, COMSIG_MOB_ATTACK_ALIEN, PROC_REF(on_attack_alien)) RegisterSignal(new_pawn, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) RegisterSignal(new_pawn, COMSIG_ATOM_HITBY, PROC_REF(on_hitby)) RegisterSignal(new_pawn, COMSIG_LIVING_START_PULL, PROC_REF(on_startpulling)) RegisterSignal(new_pawn, COMSIG_LIVING_TRY_SYRINGE, PROC_REF(on_try_syringe)) RegisterSignal(new_pawn, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk)) RegisterSignal(new_pawn, COMSIG_CARBON_CUFF_ATTEMPTED, PROC_REF(on_attempt_cuff)) RegisterSignal(new_pawn, COMSIG_MOB_MOVESPEED_UPDATED, PROC_REF(update_movespeed)) movement_delay = living_pawn.cached_multiplicative_slowdown return ..() //Run parent at end /datum/ai_controller/monkey/UnpossessPawn(destroy) UnregisterSignal(pawn, list( COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_LIVING_START_PULL, COMSIG_LIVING_TRY_SYRINGE, COMSIG_ATOM_HULK_ATTACK, COMSIG_CARBON_CUFF_ATTEMPTED, COMSIG_MOB_MOVESPEED_UPDATED, COMSIG_ATOM_ATTACK_ANIMAL, COMSIG_MOB_ATTACK_ALIEN, )) return ..() //Run parent at end /datum/ai_controller/monkey/on_sentience_lost() . = ..() set_trip_mode(mode = TRUE) /datum/ai_controller/monkey/able_to_run() var/mob/living/living_pawn = pawn if(IS_DEAD_OR_INCAP(living_pawn)) return FALSE return ..() /datum/ai_controller/monkey/get_human_examine_text() var/text = "[span_monkey("[pawn.p_they(TRUE)] have a primal look in [pawn.p_their()] eyes.")]" return text /datum/ai_controller/monkey/proc/set_trip_mode(mode = TRUE) var/mob/living/carbon/regressed_monkey = pawn var/brain = regressed_monkey.getorganslot(ORGAN_SLOT_BRAIN) if(istype(brain, /obj/item/organ/internal/brain/primate)) // In case we are a monkey AI in a human brain by who was previously controlled by a client but it now not by some marvel var/obj/item/organ/internal/brain/primate/monkeybrain = brain monkeybrain.tripping = mode ///re-used behavior pattern by monkeys for finding a weapon /datum/ai_controller/monkey/proc/TryFindWeapon() var/mob/living/living_pawn = pawn if(!locate(/obj/item) in living_pawn.held_items) blackboard[BB_MONKEY_BEST_FORCE_FOUND] = 0 if(blackboard[BB_MONKEY_GUN_NEURONS_ACTIVATED] && (locate(/obj/item/gun) in living_pawn.held_items)) // We have a gun, what could we possibly want? return FALSE var/obj/item/weapon var/list/nearby_items = list() for(var/obj/item/item in oview(2, living_pawn)) nearby_items += item for(var/obj/item/item in living_pawn.held_items) // If we've got some garbage in out hands thats going to stop us from effectivly attacking, we should get rid of it. if(item.force < 2) living_pawn.dropItemToGround(item) weapon = GetBestWeapon(src, nearby_items, living_pawn.held_items) var/pickpocket = FALSE for(var/mob/living/carbon/human/human in oview(5, living_pawn)) var/obj/item/held_weapon = GetBestWeapon(src, human.held_items + weapon, living_pawn.held_items) if(held_weapon == weapon) // It's just the same one, not a held one continue pickpocket = TRUE weapon = held_weapon if(!weapon || (weapon in living_pawn.held_items)) return FALSE if(weapon.force < 2) // our bite does 2 damage on avarage, no point in settling for anything less return FALSE blackboard[BB_MONKEY_PICKUPTARGET] = weapon current_movement_target = weapon if(pickpocket) queue_behavior(/datum/ai_behavior/monkey_equip/pickpocket) else queue_behavior(/datum/ai_behavior/monkey_equip/ground) return TRUE ///Reactive events to being hit /datum/ai_controller/monkey/proc/retaliate(mob/living/L) var/list/enemies = blackboard[BB_MONKEY_ENEMIES] enemies[WEAKREF(L)] += MONKEY_HATRED_AMOUNT /datum/ai_controller/monkey/proc/on_attackby(datum/source, obj/item/I, mob/user) SIGNAL_HANDLER if(I.force && I.damtype != STAMINA) retaliate(user) /datum/ai_controller/monkey/proc/on_attack_hand(datum/source, mob/living/user, list/modifiers) SIGNAL_HANDLER if((user.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) && prob(MONKEY_RETALIATE_PROB)) retaliate(user) /datum/ai_controller/monkey/proc/on_attack_paw(datum/source, mob/living/user, list/modifiers) SIGNAL_HANDLER if((user.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) && prob(MONKEY_RETALIATE_PROB)) retaliate(user) /datum/ai_controller/monkey/proc/on_attack_animal(datum/source, mob/living/user) SIGNAL_HANDLER if(user.melee_damage_upper > 0 && prob(MONKEY_RETALIATE_PROB)) retaliate(user) /datum/ai_controller/monkey/proc/on_attack_alien(datum/source, mob/living/user, list/modifiers) SIGNAL_HANDLER if((user.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) && prob(MONKEY_RETALIATE_PROB)) retaliate(user) /datum/ai_controller/monkey/proc/on_bullet_act(datum/source, obj/projectile/Proj) SIGNAL_HANDLER var/mob/living/living_pawn = pawn if(istype(Proj, /obj/projectile/beam) || istype(Proj, /obj/projectile/bullet)) if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) if(!Proj.nodamage && Proj.damage < living_pawn.health && isliving(Proj.firer)) retaliate(Proj.firer) /datum/ai_controller/monkey/proc/on_hitby(datum/source, atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) SIGNAL_HANDLER if(isitem(AM)) var/mob/living/living_pawn = pawn var/obj/item/I = AM var/mob/thrown_by = I.thrownby?.resolve() if(I.throwforce && I.throwforce < living_pawn.health && ishuman(thrown_by)) var/mob/living/carbon/human/H = thrown_by retaliate(H) /datum/ai_controller/monkey/proc/on_startpulling(datum/source, atom/movable/puller, state, force) SIGNAL_HANDLER var/mob/living/living_pawn = pawn if(!IS_DEAD_OR_INCAP(living_pawn) && prob(MONKEY_PULL_AGGRO_PROB)) // nuh uh you don't pull me! retaliate(living_pawn.pulledby) return TRUE /datum/ai_controller/monkey/proc/on_try_syringe(datum/source, mob/user) SIGNAL_HANDLER // chance of monkey retaliation if(prob(MONKEY_SYRINGE_RETALIATION_PROB)) retaliate(user) /datum/ai_controller/monkey/proc/on_attack_hulk(datum/source, mob/user) SIGNAL_HANDLER retaliate(user) /datum/ai_controller/monkey/proc/on_attempt_cuff(datum/source, mob/user) SIGNAL_HANDLER // chance of monkey retaliation if(prob(MONKEY_CUFF_RETALIATION_PROB)) retaliate(user) /datum/ai_controller/monkey/proc/update_movespeed(mob/living/pawn) SIGNAL_HANDLER movement_delay = pawn.cached_multiplicative_slowdown /datum/ai_controller/monkey/proc/target_del(target) SIGNAL_HANDLER blackboard[BB_MONKEY_BLACKLISTITEMS] -= target