From 4105ca9ea6df5aef2b3dd7fb52988f93aae29428 Mon Sep 17 00:00:00 2001 From: Trugath Date: Fri, 6 Jan 2017 13:59:37 +0000 Subject: [PATCH] Tgmonkey (#22338) * Monkeys are a bit more of a handful They will aggro, steal, pick up weapons and stuff bodies in disposals * search using held_items instead of manually * Only natural monkies should reliably stun Natural monkies don't try to stun-lock * Pushes equip_in_one_of_slots to carbon from human * Removed unused helper. * Make the trip and stun only against monkeys The main reason this is here is for the swarm behaivour. * Just return true for now. It should flee on next life tick. * remove blocking call from Life * stuff_mob_in blocks so make it non-blocking * add check to stop add/remove in same function Only occours if the monkey attacks someone who isn't in the enemies list. This only happens during a monkey swarm. Best to check. * Fix for monkeys ripping stuff off walls. * Moved shared emote/weaken down into living Called the new function knockOVer. Monkey and Secbot both have different control flow before the shared code. * Stop monkeys fighting over body * Remove commented out code * Reduce flee range * replace spawn with addTimer by moving code into callbacks * Move magic numbers to defines Also removed 900 delay in resisting cuffs as its a useless argument * Refactor. Attacking has a function: monkey_attack Combined de-aggro into it * rework monkeyDrop to force drop Also check for deleted items when snatching items * Aggresive can be toggled by an admin Allows for a mass mob of angry monkies * Tweak aggressive monkeys to attack each other less Monkey vs Monkey probability is a define Warning: Once MVM combat breaks out in large mobs, mayhem insues * Set the monkey back to idle once it attemps to dispose the body * fix for aggressive monkeys self-harming * comment wording fix * changed walk2derpless to a callback and fixed deathgrip --- code/__DEFINES/monkeys.dm | 38 ++ code/game/objects/items/weapons/handcuffs.dm | 8 +- .../mob/living/carbon/human/human_defense.dm | 12 + .../mob/living/carbon/human/inventory.dm | 10 - code/modules/mob/living/carbon/inventory.dm | 7 + .../mob/living/carbon/monkey/combat.dm | 457 ++++++++++++++++++ code/modules/mob/living/carbon/monkey/life.dm | 16 +- code/modules/mob/living/living.dm | 12 + .../mob/living/simple_animal/bot/secbot.dm | 9 +- .../reagents/reagent_containers/syringes.dm | 6 + tgstation.dme | 2 + 11 files changed, 552 insertions(+), 25 deletions(-) create mode 100644 code/__DEFINES/monkeys.dm create mode 100644 code/modules/mob/living/carbon/monkey/combat.dm diff --git a/code/__DEFINES/monkeys.dm b/code/__DEFINES/monkeys.dm new file mode 100644 index 00000000000..76f7ea850c3 --- /dev/null +++ b/code/__DEFINES/monkeys.dm @@ -0,0 +1,38 @@ +//Monkey defines, placed here so they can be read by other things! + +//Mode defines +#define MONKEY_IDLE 0 // idle +#define MONKEY_HUNT 1 // found target, hunting +#define MONKEY_FLEE 2 // free from enemies +#define MONKEY_DISPOSE 3 // dump body in disposals + +#define MONKEY_FLEE_HEALTH 50 // below this health value the monkey starts to flee from enemies +#define MONKEY_ENEMY_VISION 9 // how close an enemy must be to trigger aggression +#define MONKEY_FLEE_VISION 4 // how close an enemy must be before it triggers flee +#define MONKEY_ITEM_SNATCH_DELAY 25 // How long does it take the item to be taken from a mobs hand +#define MONKEY_CUFF_RETALIATION_PROB 20 // Probability monkey will aggro when cuffed +#define MONKEY_SYRINGE_RETALIATION_PROB 20 // Probability monkey will aggro when syringed + +// Probability per Life tick that the monkey will: +#define MONKEY_RESIST_PROB 50 // resist out of restraints + // when the monkey is idle +#define MONKEY_PULL_AGGRO_PROB 5 // aggro against the mob pulling it +#define MONKEY_PICKUP_PROB 5 // if not currently getting an item, pickup an item around it +#define MONKEY_STEAL_PROB 5 // if not currently getting an item, steal an item from someone around it + // when the monkey is hunting +#define MONKEY_ATTACK_DISARM_PROB 50 // disarm an armed attacker +#define MONKEY_WEAPON_PROB 20 // if not currently getting an item, search for a weapon around it +#define MONKEY_RECRUIT_PROB 25 // recruit a monkey near it +#define MONKEY_SWITCH_TARGET_PROB 25 // switch targets if it sees another enemy + +#define MONKEY_RETALIATE_HARM_PROB 95 // probability for the monkey to aggro when attacked with harm intent +#define MONKEY_RETALIATE_DISARM_PROB 20 // probability for the monkey to aggro when attacked with disarm intent + +#define MONKEY_HATRED_AMOUNT 4 // amount of aggro to add to an enemy when they attack user +#define MONKEY_HATRED_REDUCTION_PROB 25 // probability of reducing aggro by one when the monkey attacks + +// how many Life ticks the monkey will fail to: +#define MONKEY_HUNT_FRUSTRATION_LIMIT 8 // Chase after an enemy before giving up +#define MONKEY_DISPOSE_FRUSTRATION_LIMIT 16 // Dispose of a body before giving up + +#define MONKEY_AGGRESSIVE_MVM_PROB 1 // If you mass edit monkies to be aggressive. there is a small chance of in-fighting \ No newline at end of file diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index ba86befd06e..f09b2847b9d 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -29,7 +29,13 @@ user << "Uh... how do those things work?!" apply_cuffs(user,user) return - + + // chance of monkey retaliation + if(istype(C, /mob/living/carbon/monkey) && prob(MONKEY_CUFF_RETALIATION_PROB)) + var/mob/living/carbon/monkey/M + M = C + M.retaliate(user) + if(!C.handcuffed) if(C.get_num_arms() >= 2 || C.get_arm_ignore()) C.visible_message("[user] is trying to put [src.name] on [C]!", \ diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 9cb27e4a1ac..bf1385acb8e 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -213,6 +213,18 @@ ..() //shaking return 0 + if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stunned instead. + if(get_active_held_item() && drop_item()) + playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) + visible_message("[M] disarmed [src]!", \ + "[M] disarmed [src]!") + else if(!M.client || prob(5)) // only natural monkeys get to stun reliably, (they only do it occasionaly) + playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) + Weaken(5) + add_logs(M, src, "tackled") + visible_message("[M] has tackled down [src]!", \ + "[M] has tackled down [src]!") + if(M.limb_destroyer) dismembering_strike(M, affecting.body_zone) diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 5e79e85c99c..75ef76eb08e 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -1,16 +1,6 @@ /mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = 0) return dna.species.can_equip(I, slot, disable_warning, src) - -/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/I, list/slots, qdel_on_fail = 1) - for(var/slot in slots) - if(equip_to_slot_if_possible(I, slots[slot], qdel_on_fail = 0, disable_warning = TRUE)) - return slot - if(qdel_on_fail) - qdel(I) - return null - - // Return the item currently in the slot ID /mob/living/carbon/human/get_item_by_slot(slot_id) switch(slot_id) diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index 891a62cab9e..bb7ff6f45b6 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -14,6 +14,13 @@ return legcuffed return null +/mob/living/carbon/proc/equip_in_one_of_slots(obj/item/I, list/slots, qdel_on_fail = 1) + for(var/slot in slots) + if(equip_to_slot_if_possible(I, slots[slot], qdel_on_fail = 0, disable_warning = TRUE)) + return slot + if(qdel_on_fail) + qdel(I) + return null //This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible() /mob/living/carbon/equip_to_slot(obj/item/I, slot) diff --git a/code/modules/mob/living/carbon/monkey/combat.dm b/code/modules/mob/living/carbon/monkey/combat.dm new file mode 100644 index 00000000000..8ebd2216186 --- /dev/null +++ b/code/modules/mob/living/carbon/monkey/combat.dm @@ -0,0 +1,457 @@ + +/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 + +/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 0 + + if(myPath.len <= 0) + myPath = get_path_to(src, get_turf(target), /turf/proc/Distance, MAX_RANGE_FIND + 1, 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 1 + + // 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 0 + +// taken from /mob/living/carbon/human/interactive/ +/mob/living/carbon/monkey/proc/IsDeadOrIncap(checkDead = TRUE) + if(!canmove) + return 1 + if(health <= 0 && checkDead) + return 1 + if(paralysis) + return 1 + if(stunned) + return 1 + if(stat) + return 1 + return 0 + +/mob/living/carbon/monkey/proc/equip_item(var/obj/item/I) + + if(I.loc == src) + return TRUE + + if(I.anchored) + blacklistItems[I] ++ + return FALSE + + // WEAPONS + if(istype(I, /obj/item/weapon)) + var/obj/item/weapon/W = I + if(W.force >= best_force) + put_in_hands(W) + best_force = W.force + return TRUE + + // CLOTHING + else if(istype(I,/obj/item/clothing)) + var/obj/item/clothing/C = I + monkeyDrop(C) + addtimer(CALLBACK(src, .proc/pickup_and_wear, C), 5) + return TRUE + + // EVERYTHING ELSE + else + if(!get_item_for_held_index(1) || !get_item_for_held_index(2)) + put_in_hands(I) + return TRUE + + blacklistItems[I] ++ + return FALSE + +/mob/living/carbon/monkey/proc/pickup_and_wear(var/obj/item/clothing/C) + if(!equip_to_appropriate_slot(C)) + monkeyDrop(get_item_by_slot(C)) // remove the existing item if worn + sleep(5) + equip_to_appropriate_slot(C) + +/mob/living/carbon/monkey/resist_restraints() + var/obj/item/I = null + if(handcuffed) + I = handcuffed + else if(legcuffed) + I = legcuffed + if(I) + changeNext_move(CLICK_CD_BREAKOUT) + last_special = world.time + CLICK_CD_BREAKOUT + cuff_resist(I) + +/mob/living/carbon/monkey/proc/should_target(var/mob/living/L) + + if(L == src) + return 0 + + if(enemies[L]) + return 1 + + // 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 1 + + return 0 + +/mob/living/carbon/monkey/proc/handle_combat() + + if(on_fire || buckled || restrained()) + if(!resisting && prob(MONKEY_RESIST_PROB)) + resisting = TRUE + walk_to(src,0) + resist() + else + resisting = FALSE + + + if(IsDeadOrIncap()) + return TRUE + + // have we been disarmed + if(!locate(/obj/item/weapon) in held_items) + best_force = 0 + + if(restrained() || blacklistItems[pickupTarget]) + pickupTarget = null + + if(!resisting && pickupTarget) + pickupTimer++ + + // next to target + if(Adjacent(pickupTarget) || Adjacent(pickupTarget.loc)) + addtimer(CALLBACK(src, .proc/walk2derpless, pickupTarget.loc), 0) + + // who cares about these items, i want that one! + drop_all_held_items() + + // on floor + if(isturf(pickupTarget.loc)) + equip_item(pickupTarget) + pickupTarget = null + pickupTimer = 0 + + // in someones hand + else if(ismob(pickupTarget.loc)) + 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]!") + addtimer(CALLBACK(src, .proc/pickpocket, M), 0) + + else + if(pickupTimer >= 8) + blacklistItems[pickupTarget] ++ + pickupTarget = null + pickupTimer = 0 + else + addtimer(CALLBACK(src, .proc/walk2derpless, pickupTarget.loc), 0) + + return TRUE + + // nuh uh you don't pull me! + if(pulledby && (mode != MONKEY_IDLE || prob(MONKEY_PULL_AGGRO_PROB))) + if(Adjacent(pulledby)) + a_intent = INTENT_DISARM + monkey_attack(pulledby) + retaliate(pulledby) + return TRUE + + switch(mode) + + if(MONKEY_IDLE) // idle + + var/list/around = view(src, MONKEY_ENEMY_VISION) + bodyDisposal = locate(/obj/machinery/disposal/) in around + + // scan for enemies + for(var/mob/living/L in around) + if( should_target(L) ) + if(L.stat == CONSCIOUS) + emote(pick("roar","screech")) + retaliate(L) + return TRUE + else if(bodyDisposal) + target = L + mode = MONKEY_DISPOSE + return TRUE + + // pickup any nearby objects + if(!pickupTarget && prob(MONKEY_PICKUP_PROB)) + var/obj/item/I = locate(/obj/item/) in oview(5,src) + if(I && !blacklistItems[I]) + pickupTarget = I + + // I WANNA STEAL + if(!pickupTarget && prob(MONKEY_STEAL_PROB)) + var/mob/living/carbon/human/H = locate(/mob/living/carbon/human/) in oview(5,src) + if(H) + pickupTarget = pick(H.held_items) + + // clear any combat walking + if(!resisting) + walk_to(src,0) + + return IsStandingStill() + + if(MONKEY_HUNT) // hunting for attacker + if(health < MONKEY_FLEE_HEALTH) + mode = MONKEY_FLEE + return TRUE + + if(target != null) + addtimer(CALLBACK(src, .proc/walk2derpless, target), 0) + + // pickup any nearby weapon + if(!pickupTarget && prob(MONKEY_WEAPON_PROB)) + var/obj/item/weapon/W = locate(/obj/item/weapon/) in oview(2,src) + 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.emote(pick("roar","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)) // if right next to perp + + // check if target has a weapon + var/obj/item/weapon/W = locate(/obj/item/weapon) in target.held_items + + // 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/)) + + addtimer(CALLBACK(src, .proc/walk2derpless, target.loc), 0) + + 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) + addtimer(CALLBACK(src, .proc/walk2derpless, bodyDisposal.loc), 0) + + if(Adjacent(bodyDisposal)) + disposing_body = TRUE + addtimer(CALLBACK(src, .proc/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]!") + M.unEquip(pickupTarget) + if(!qdeleted(pickupTarget)) + equip_item(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/Weapon = locate(/obj/item/weapon) 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 + enemies[L] += MONKEY_HATRED_AMOUNT + + if(a_intent != INTENT_HARM) + emote(pick("roar","screech")) + a_intent = INTENT_HARM + +/mob/living/carbon/monkey/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_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/weapon/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) + retaliate(Proj.firer) + ..() + +/mob/living/carbon/monkey/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0) + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.throwforce < src.health && I.thrownby && ishuman(I.thrownby)) + var/mob/living/carbon/human/H = I.thrownby + retaliate(H) + ..() + +/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) + unEquip(A, 1) + update_icons() diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm index a55cf64dde7..5b686c0a175 100644 --- a/code/modules/mob/living/carbon/monkey/life.dm +++ b/code/modules/mob/living/carbon/monkey/life.dm @@ -10,13 +10,17 @@ if (notransform) return - ..() + if(..()) - if(!client && stat == CONSCIOUS) - if(prob(33) && canmove && isturf(loc) && !pulledby) - step(src, pick(cardinal)) - if(prob(1)) - emote(pick("scratch","jump","roll","tail")) + if(!client) + if(stat == CONSCIOUS) + if(!handle_combat()) + if(prob(33) && canmove && isturf(loc) && !pulledby) + step(src, pick(cardinal)) + if(prob(1)) + emote(pick("scratch","jump","roll","tail")) + else + walk_to(src,0) /mob/living/carbon/monkey/handle_mutations_and_radiation() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index af6bed51839..809303da446 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -898,3 +898,15 @@ IgniteMob() //Mobs on Fire end + +// used by secbot and monkeys Crossed +/mob/living/proc/knockOver(var/mob/living/carbon/C) + C.visible_message("[pick( \ + "[C] dives out of [src]'s way!", \ + "[C] stumbles over [src]!", \ + "[C] jumps out of [src]'s path!", \ + "[C] trips over [src] and falls!", \ + "[C] topples over [src]!", \ + "[C] leaps out of [src]'s way!")]") + C.Weaken(2) + \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index 8b441d3ed4a..8cf7faa3fab 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -403,14 +403,7 @@ Auto Patrol: []"}, var/mob/living/carbon/C = AM if(!istype(C) || !C || in_range(src, target)) return - C.visible_message("[pick( \ - "[C] dives out of [src]'s way!", \ - "[C] stumbles over [src]!", \ - "[C] jumps out of [src]'s path!", \ - "[C] trips over [src] and falls!", \ - "[C] topples over [src]!", \ - "[C] leaps out of [src]'s way!")]") - C.Weaken(2) + knockOver(C) return ..() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 51632b18a66..e63ea7aa406 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -61,6 +61,12 @@ if(!L.can_inject(user, 1)) return + // chance of monkey retaliation + if(istype(target, /mob/living/carbon/monkey) && prob(MONKEY_SYRINGE_RETALIATION_PROB)) + var/mob/living/carbon/monkey/M + M = target + M.retaliate(user) + switch(mode) if(SYRINGE_DRAW) diff --git a/tgstation.dme b/tgstation.dme index 5feb54186c1..223c360e542 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -42,6 +42,7 @@ #include "code\__DEFINES\MC.dm" #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\mobs.dm" +#include "code\__DEFINES\monkeys.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\preferences.dm" @@ -1482,6 +1483,7 @@ #include "code\modules\mob\living\carbon\human\species_types\skeletons.dm" #include "code\modules\mob\living\carbon\human\species_types\synths.dm" #include "code\modules\mob\living\carbon\human\species_types\zombies.dm" +#include "code\modules\mob\living\carbon\monkey\combat.dm" #include "code\modules\mob\living\carbon\monkey\death.dm" #include "code\modules\mob\living\carbon\monkey\inventory.dm" #include "code\modules\mob\living\carbon\monkey\life.dm"