#define MAX_RANGE_FIND 32 /mob/living/carbon/monkey var/aggressive=0 // set to 1 using VV for an angry monkey var/frustration=0 var/pickupTimer=0 var/list/enemies = list() var/mob/living/target var/obj/item/pickupTarget var/mode = MONKEY_IDLE var/list/myPath = list() var/list/blacklistItems = list() var/maxStepsTick = 6 var/best_force = 0 var/martial_art = new/datum/martial_art var/resisting = FALSE var/pickpocketing = FALSE var/disposing_body = FALSE var/obj/machinery/disposal/bodyDisposal = null var/next_battle_screech = 0 var/battle_screech_cooldown = 50 /mob/living/carbon/monkey/proc/IsStandingStill() return resisting || pickpocketing || disposing_body // blocks // taken from /mob/living/carbon/human/interactive/ /mob/living/carbon/monkey/proc/walk2derpless(target) if(!target || IsStandingStill()) return FALSE if(myPath.len <= 0) myPath = get_path_to(src, target, 250, 1) if(myPath) if(myPath.len > 0) for(var/i = 0; i < maxStepsTick; ++i) if(!IsDeadOrIncap()) if(myPath.len >= 1) walk_to(src,myPath[1],0,5) myPath -= myPath[1] return TRUE // failed to path correctly so just try to head straight for a bit walk_to(src,get_turf(target),0,5) sleep(1) walk_to(src,0) return FALSE // taken from /mob/living/carbon/human/interactive/ /mob/living/carbon/monkey/proc/IsDeadOrIncap(checkDead = TRUE) if(!CHECK_MOBILITY(src, MOBILITY_MOVE)) return TRUE if(health <= 0 && checkDead) return TRUE return FALSE /mob/living/carbon/monkey/proc/battle_screech() if(next_battle_screech < world.time) emote(pick("roar","screech")) for(var/mob/living/carbon/monkey/M in view(7,src)) M.next_battle_screech = world.time + battle_screech_cooldown /mob/living/carbon/monkey/proc/equip_item(var/obj/item/I) if(I.loc == src) return TRUE if(I.anchored || !put_in_hands(I)) blacklistItems[I] ++ return FALSE if(I.force >= best_force) best_force = I.force else addtimer(CALLBACK(src, PROC_REF(pickup_and_wear), I), 5) return TRUE /mob/living/carbon/monkey/proc/pickup_and_wear(obj/item/I) if(QDELETED(I) || I.loc != src) return equip_to_appropriate_slot(I, TRUE) /mob/living/carbon/monkey/resist_restraints() var/obj/item/I = null if(handcuffed) I = handcuffed else if(legcuffed) I = legcuffed if(I) MarkResistTime() cuff_resist(I) /mob/living/carbon/monkey/proc/should_target(var/mob/living/L) if(HAS_TRAIT(src, TRAIT_PACIFISM)) return FALSE if(enemies[L]) return TRUE // target non-monkey mobs when aggressive, with a small probability of monkey v monkey if(aggressive && (!istype(L, /mob/living/carbon/monkey/) || prob(MONKEY_AGGRESSIVE_MVM_PROB))) return TRUE return FALSE /mob/living/carbon/monkey/proc/handle_combat() if(pickupTarget) if(restrained() || blacklistItems[pickupTarget] || HAS_TRAIT(pickupTarget, TRAIT_NODROP)) pickupTarget = null else if(!isobj(loc) || istype(loc, /obj/item/clothing/head/mob_holder)) pickupTimer++ if(pickupTimer >= 4) blacklistItems[pickupTarget] ++ pickupTarget = null pickupTimer = 0 else INVOKE_ASYNC(src, PROC_REF(walk2derpless), pickupTarget.loc) if(Adjacent(pickupTarget) || Adjacent(pickupTarget.loc)) // next to target drop_all_held_items() // who cares about these items, i want that one! if(isturf(pickupTarget.loc)) // on floor equip_item(pickupTarget) pickupTarget = null pickupTimer = 0 else if(ismob(pickupTarget.loc)) // in someones hand if(istype(pickupTarget, /obj/item/clothing/head/mob_holder)) return//dont let them pickpocket themselves or hold other monkys. var/mob/M = pickupTarget.loc if(!pickpocketing) pickpocketing = TRUE M.visible_message("[src] starts trying to take [pickupTarget] from [M]", "[src] tries to take [pickupTarget]!") INVOKE_ASYNC(src, PROC_REF(pickpocket), M) return TRUE switch(mode) if(MONKEY_IDLE) // idle if(enemies.len) var/list/around = view(src, MONKEY_ENEMY_VISION) // scan for enemies for(var/mob/living/L in around) if( should_target(L) ) if(L.stat == CONSCIOUS) battle_screech() retaliate(L) return TRUE else bodyDisposal = locate(/obj/machinery/disposal/) in around if(bodyDisposal) target = L mode = MONKEY_DISPOSE return TRUE // pickup any nearby objects if(!pickupTarget) var/obj/item/I = locate(/obj/item/) in oview(2,src) if(I && !blacklistItems[I]) pickupTarget = I else var/mob/living/carbon/human/H = locate(/mob/living/carbon/human/) in oview(2,src) if(H) pickupTarget = pick(H.held_items) if(MONKEY_HUNT) // hunting for attacker if(health < MONKEY_FLEE_HEALTH) mode = MONKEY_FLEE return TRUE if(target != null) INVOKE_ASYNC(src, PROC_REF(walk2derpless), target) // pickup any nearby weapon if(!pickupTarget && prob(MONKEY_WEAPON_PROB)) var/obj/item/W = locate(/obj/item/) in oview(2,src) if(!locate(/obj/item) in held_items) best_force = 0 if(W && !blacklistItems[W] && W.force > best_force) pickupTarget = W // recruit other monkies var/list/around = view(src, MONKEY_ENEMY_VISION) for(var/mob/living/carbon/monkey/M in around) if(M.mode == MONKEY_IDLE && prob(MONKEY_RECRUIT_PROB)) M.battle_screech() M.target = target M.mode = MONKEY_HUNT // switch targets for(var/mob/living/L in around) if(L != target && should_target(L) && L.stat == CONSCIOUS && prob(MONKEY_SWITCH_TARGET_PROB)) target = L return TRUE // if can't reach target for long enough, go idle if(frustration >= MONKEY_HUNT_FRUSTRATION_LIMIT) back_to_idle() return TRUE if(target && target.stat == CONSCIOUS) // make sure target exists if(Adjacent(target) && isturf(target.loc) && !IsDeadOrIncap()) // if right next to perp // check if target has a weapon var/obj/item/W for(var/obj/item/I in target.held_items) if(!(I.item_flags & ABSTRACT)) W = I break // if the target has a weapon, chance to disarm them if(W && prob(MONKEY_ATTACK_DISARM_PROB)) pickupTarget = W a_intent = INTENT_DISARM monkey_attack(target) else a_intent = INTENT_HARM monkey_attack(target) return TRUE else // not next to perp var/turf/olddist = get_dist(src, target) if((get_dist(src, target)) >= (olddist)) frustration++ else frustration = 0 else back_to_idle() if(MONKEY_FLEE) var/list/around = view(src, MONKEY_FLEE_VISION) target = null // flee from anyone who attacked us and we didn't beat down for(var/mob/living/L in around) if( enemies[L] && L.stat == CONSCIOUS ) target = L if(target != null) walk_away(src, target, MONKEY_ENEMY_VISION, 5) else back_to_idle() return TRUE if(MONKEY_DISPOSE) // if can't dispose of body go back to idle if(!target || !bodyDisposal || frustration >= MONKEY_DISPOSE_FRUSTRATION_LIMIT) back_to_idle() return TRUE if(target.pulledby != src && !istype(target.pulledby, /mob/living/carbon/monkey/)) INVOKE_ASYNC(src, PROC_REF(walk2derpless), target.loc) if(Adjacent(target) && isturf(target.loc)) a_intent = INTENT_GRAB target.grabbedby(src) else var/turf/olddist = get_dist(src, target) if((get_dist(src, target)) >= (olddist)) frustration++ else frustration = 0 else if(!disposing_body) INVOKE_ASYNC(src, PROC_REF(walk2derpless), bodyDisposal.loc) if(Adjacent(bodyDisposal)) disposing_body = TRUE addtimer(CALLBACK(src, PROC_REF(stuff_mob_in)), 5) else var/turf/olddist = get_dist(src, bodyDisposal) if((get_dist(src, bodyDisposal)) >= (olddist)) frustration++ else frustration = 0 return TRUE return IsStandingStill() /mob/living/carbon/monkey/proc/pickpocket(var/mob/M) if(do_mob(src, M, MONKEY_ITEM_SNATCH_DELAY) && pickupTarget) for(var/obj/item/I in M.held_items) if(I == pickupTarget) M.visible_message("[src] snatches [pickupTarget] from [M].", "[src] snatched [pickupTarget]!") if(M.temporarilyRemoveItemFromInventory(pickupTarget) && !QDELETED(pickupTarget)) if(!equip_item(pickupTarget)) dropItemToGround(pickupTarget) else M.visible_message("[src] tried to snatch [pickupTarget] from [M], but failed!", "[src] tried to grab [pickupTarget]!") pickpocketing = FALSE pickupTarget = null pickupTimer = 0 /mob/living/carbon/monkey/proc/stuff_mob_in() if(bodyDisposal && target && Adjacent(bodyDisposal)) bodyDisposal.stuff_mob_in(target, src) disposing_body = FALSE back_to_idle() /mob/living/carbon/monkey/proc/back_to_idle() if(pulling) stop_pulling() mode = MONKEY_IDLE target = null a_intent = INTENT_HELP frustration = 0 walk_to(src,0) // attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little /mob/living/carbon/monkey/proc/monkey_attack(mob/living/L) var/obj/item/Weapon = locate(/obj/item) in held_items // attack with weapon if we have one if(Weapon) L.attackby(Weapon, src) else L.attack_paw(src) // no de-aggro if(aggressive) return // if we arn't enemies, we were likely recruited to attack this target, jobs done if we calm down so go back to idle if(!enemies[L]) if( target == L && prob(MONKEY_HATRED_REDUCTION_PROB) ) back_to_idle() return // already de-aggroed if(prob(MONKEY_HATRED_REDUCTION_PROB)) enemies[L] -- // if we are not angry at our target, go back to idle if(enemies[L] <= 0) enemies.Remove(L) if( target == L ) back_to_idle() // get angry are a mob /mob/living/carbon/monkey/proc/retaliate(mob/living/L) mode = MONKEY_HUNT target = L if(L != src) enemies[L] += MONKEY_HATRED_AMOUNT if(a_intent != INTENT_HARM) battle_screech() a_intent = INTENT_HARM /mob/living/carbon/monkey/on_attack_hand(mob/living/L) if(L.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) retaliate(L) else if(L.a_intent == INTENT_DISARM && prob(MONKEY_RETALIATE_DISARM_PROB)) retaliate(L) return ..() /mob/living/carbon/monkey/attack_alien(mob/living/carbon/alien/humanoid/M) if(M.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) retaliate(M) else if(M.a_intent == INTENT_DISARM && prob(MONKEY_RETALIATE_DISARM_PROB)) retaliate(M) return ..() /mob/living/carbon/monkey/attack_larva(mob/living/carbon/alien/larva/L) if(L.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) retaliate(L) return ..() /mob/living/carbon/monkey/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) retaliate(user) return ..() /mob/living/carbon/monkey/attack_paw(mob/living/L) if(L.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) retaliate(L) else if(L.a_intent == INTENT_DISARM && prob(MONKEY_RETALIATE_DISARM_PROB)) retaliate(L) return ..() /mob/living/carbon/monkey/attackby(obj/item/W, mob/user, params) ..() if((W.force) && (!target) && (W.damtype != STAMINA) ) retaliate(user) /mob/living/carbon/monkey/bullet_act(obj/item/projectile/Proj) if(istype(Proj , /obj/item/projectile/beam)||istype(Proj, /obj/item/projectile/bullet)) if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) if(!Proj.nodamage && Proj.damage < src.health && isliving(Proj.firer)) retaliate(Proj.firer) return ..() /mob/living/carbon/monkey/hitby(atom/movable/hitting_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) if(istype(hitting_atom, /obj/item)) var/obj/item/item_hitby = hitting_atom var/mob/thrown_by = item_hitby.thrownby?.resolve() if(item_hitby.throwforce < src.health && thrown_by && ishuman(thrown_by)) var/mob/living/carbon/human/human_throwee = thrown_by retaliate(human_throwee) ..() /mob/living/carbon/monkey/Crossed(atom/movable/AM) if(!IsDeadOrIncap() && ismob(AM) && target) var/mob/living/carbon/monkey/M = AM if(!istype(M) || !M) return knockOver(M) return ..() /mob/living/carbon/monkey/proc/monkeyDrop(var/obj/item/A) if(A) dropItemToGround(A, TRUE) update_icons() /mob/living/carbon/monkey/grabbedby(mob/living/carbon/user) . = ..() if(!IsDeadOrIncap() && pulledby && (mode != MONKEY_IDLE || prob(MONKEY_PULL_AGGRO_PROB))) // nuh uh you don't pull me! if(Adjacent(pulledby)) a_intent = INTENT_DISARM monkey_attack(pulledby) retaliate(pulledby) return TRUE #undef MAX_RANGE_FIND