diff --git a/baystation12.dme b/baystation12.dme
index d151796b68..2041937f4b 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1095,6 +1095,7 @@
#include "code\modules\mob\mob_cleanup.dm"
#include "code\modules\mob\mob_defines.dm"
#include "code\modules\mob\mob_grab.dm"
+#include "code\modules\mob\mob_grab_specials.dm"
#include "code\modules\mob\mob_helpers.dm"
#include "code\modules\mob\mob_movement.dm"
#include "code\modules\mob\mob_transformation_simple.dm"
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index fc9f6f6519..e5b5f858d8 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -576,12 +576,14 @@ default behaviour is:
if(can_resist())
next_move = world.time + 20
- process_resist()
+ resist_grab()
+ if(!weakened && !restrained())
+ process_resist()
/mob/living/proc/can_resist()
//need to allow !canmove, or otherwise neck grabs can't be resisted
- //so just check weakened instead.
- if(stat || weakened)
+ //similar thing with weakened and pinning
+ if(stat)
return 0
if(next_move > world.time)
return 0
@@ -593,10 +595,6 @@ default behaviour is:
escape_inventory(src.loc)
return
- //resisting grabs (as if it helps anyone...)
- if (!restrained())
- resist_grab()
-
//unbuckling yourself
if(buckled)
spawn() escape_buckle()
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index acc95fbe40..af17182d7f 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -1,6 +1,13 @@
#define UPGRADE_COOLDOWN 40
#define UPGRADE_KILL_TIMER 100
+///Process_Grab()
+///Called by client/Move()
+///Checks to see if you are grabbing anything and if moving will affect your grab.
+/client/proc/Process_Grab()
+ for(var/obj/item/weapon/grab/G in list(mob.l_hand, mob.r_hand))
+ G.reset_kill_state() //no wandering across the station/asteroid while choking someone
+
/obj/item/weapon/grab
name = "grab"
icon = 'icons/mob/screen1.dmi'
@@ -69,7 +76,6 @@
else
hud.screen_loc = ui_lhand
-
/obj/item/weapon/grab/process()
if(gcDestroyed) // GC is trying to delete us, we'll kill our processing so we can cleanly GC
return PROCESS_KILL
@@ -116,23 +122,9 @@
affecting.drop_l_hand()
affecting.drop_r_hand()
- var/hit_zone = assailant.zone_sel.selecting
- var/announce = 0
- if(hit_zone != last_hit_zone)
- announce = 1
- last_hit_zone = hit_zone
- if(ishuman(affecting))
- switch(hit_zone)
- if("mouth")
- if(announce)
- assailant.visible_message("[assailant] covers [affecting]'s mouth!")
- if(affecting:silent < 3)
- affecting:silent = 3
- if("eyes")
- if(announce)
- assailant.visible_message("[assailant] covers [affecting]'s eyes!")
- if(affecting:eye_blind < 3)
- affecting:eye_blind = 3
+ if(iscarbon(affecting))
+ handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting)
+
if(force_down)
if(affecting.loc != assailant.loc)
force_down = 0
@@ -153,6 +145,21 @@
adjust_position()
+/obj/item/weapon/grab/proc/handle_eye_mouth_covering(mob/living/carbon/target, mob/user, var/target_zone)
+ var/announce = (target_zone != last_hit_zone) //only display messages when switching between different target zones
+ last_hit_zone = target_zone
+
+ switch(target_zone)
+ if("mouth")
+ if(announce)
+ user.visible_message("\The [user] covers [target]'s mouth!")
+ if(target.silent < 3)
+ target.silent = 3
+ if("eyes")
+ if(announce)
+ assailant.visible_message("[assailant] covers [affecting]'s eyes!")
+ if(affecting.eye_blind < 3)
+ affecting.eye_blind = 3
/obj/item/weapon/grab/attack_self()
return s_click(hud)
@@ -202,8 +209,6 @@
if(EAST)
animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING)
-
-
/obj/item/weapon/grab/proc/s_click(obj/screen/S)
if(!affecting)
return
@@ -226,11 +231,8 @@
assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!")
else
assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!")
- force_down = 1
- affecting.Weaken(3)
- step_to(assailant, affecting)
- assailant.set_dir(EAST) //face the victim
- affecting.set_dir(SOUTH) //face up
+ apply_pinning(affecting, assailant)
+
state = GRAB_AGGRESSIVE
icon_state = "grabbed1"
hud.icon_state = "reinforce1"
@@ -264,7 +266,6 @@
affecting.set_dir(WEST)
adjust_position()
-
//This is used to make sure the victim hasn't managed to yackety sax away before using the grab.
/obj/item/weapon/grab/proc/confirm()
if(!assailant || !affecting)
@@ -278,17 +279,18 @@
return 1
-
/obj/item/weapon/grab/attack(mob/M, mob/living/user)
if(!affecting)
return
-
if(world.time < (last_action + 20))
return
-
+
+ last_action = world.time
+ reset_kill_state() //using special grab moves will interrupt choking them
+
+ //clicking on the victim while grabbing them
if(M == affecting)
- if(ishuman(M))
- last_action = world.time
+ if(ishuman(affecting))
var/hit_zone = assailant.zone_sel.selecting
flick(hud.icon_state, hud)
switch(assailant.a_intent)
@@ -297,160 +299,37 @@
assailant << "You are no longer pinning [affecting] to the ground."
force_down = 0
return
- var/mob/living/carbon/human/H = M
- var/obj/item/organ/external/E = H.get_organ(hit_zone)
- if(E && !(E.status & ORGAN_DESTROYED))
- assailant.visible_message("[assailant] starts inspecting [affecting]'s [E.name] carefully.")
- if(do_mob(assailant,H, 10))
- if(E.wounds.len)
- assailant << "You find [E.get_wounds_desc()]"
- else
- assailant << "You find no visible wounds."
- else
- assailant << "You must stand still to inspect [E] for wounds."
- assailant << "Checking bones now..."
- if(do_mob(assailant, H, 20))
- if(E.status & ORGAN_BROKEN)
- assailant << "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!"
- H.custom_pain("Your [E.name] hurts where it's poked.")
- else
- assailant << "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine."
- else
- assailant << "You must stand still to feel [E] for fractures."
- assailant << "Checking skin now..."
- if(do_mob(assailant, H, 10))
- var/bad = 0
- if(H.getToxLoss() >= 40)
- assailant << "[H] has an unhealthy skin discoloration."
- bad = 1
- if(H.getOxyLoss() >= 20)
- assailant << "[H]'s skin is unusaly pale."
- bad = 1
- if(E.status & ORGAN_DEAD)
- assailant << "[E] is decaying!"
- bad = 1
- if(!bad)
- assailant << "[H]'s skin is normal."
- else
- assailant << "You must stand still to check [H]'s skin for abnormalities."
- else
- assailant << "[H] is missing that bodypart."
+ inspect_organ(affecting, assailant, hit_zone)
+
if(I_GRAB)
- if(state < GRAB_AGGRESSIVE)
- assailant << "You require a better grab to do this."
- return
- var/obj/item/organ/external/organ = affecting:get_organ(check_zone(hit_zone))
- if(!organ || organ.dislocated == -1)
- return
- assailant.visible_message("[assailant] [pick("bent", "twisted")] [affecting]'s [organ.name] into a jointlock!")
- var/armor = affecting:run_armor_check(affecting, "melee")
- if(armor < 2)
- affecting << "You feel extreme pain!"
- affecting.adjustHalLoss(Clamp(0, 40-affecting.halloss, 40)) //up to 40 halloss
- return
+ jointlock(affecting, assailant, hit_zone)
+
if(I_HURT)
-
if(hit_zone == "eyes")
- var/mob/living/carbon/human/H = affecting
- var/datum/unarmed_attack/attack = H.get_unarmed_attack(src, hit_zone)
- if(!attack)
- return
-
- if(state < GRAB_NECK)
- assailant << "You require a better grab to do this."
- return
- if((affecting:head && affecting:head.flags & HEADCOVERSEYES) || \
- (affecting:wear_mask && affecting:wear_mask.flags & MASKCOVERSEYES) || \
- (affecting:glasses && affecting:glasses.flags & GLASSESCOVERSEYES))
- assailant << "You're going to need to remove the eye covering first."
- return
- if(!affecting.has_eyes())
- assailant << "You cannot locate any eyes on [affecting]!"
- return
- assailant.attack_log += text("\[[time_stamp()]\] Attacked [affecting.name]'s eyes using grab ([affecting.ckey])")
- affecting.attack_log += text("\[[time_stamp()]\] Had eyes attacked by [assailant.name]'s grab ([assailant.ckey])")
- msg_admin_attack("[key_name(assailant)] attacked [key_name(affecting)]'s eyes using a grab action.")
-
- attack.handle_eye_attack(assailant, affecting)
- else if(hit_zone != "head")
- if(state < GRAB_NECK)
- assailant << "You require a better grab to do this."
- return
- if(affecting:grab_joint(assailant))
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- return
+ attack_eye(affecting, assailant)
+ else if(hit_zone == "head")
+ headbut(affecting, assailant)
else
- if(affecting.lying)
- return
- assailant.visible_message("[assailant] thrusts \his head into [affecting]'s skull!")
- var/damage = 20
- var/obj/item/clothing/hat = assailant.head
- if(istype(hat))
- damage += hat.force * 10
- var/armor = affecting:run_armor_check(affecting, "melee")
- affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", armor)
- assailant.apply_damage(10*rand(90, 110)/100, BRUTE, "head", assailant:run_armor_check("head", "melee"))
- if(!armor && prob(damage))
- affecting.apply_effect(20, PARALYZE)
- affecting.visible_message("[affecting] has been knocked unconscious!")
- playsound(assailant.loc, "swing_hit", 25, 1, -1)
- assailant.attack_log += text("\[[time_stamp()]\] Headbutted [affecting.name] ([affecting.ckey])")
- affecting.attack_log += text("\[[time_stamp()]\] Headbutted by [assailant.name] ([assailant.ckey])")
- msg_admin_attack("[key_name(assailant)] has headbutted [key_name(affecting)]")
- assailant.drop_from_inventory(src)
- src.loc = null
- qdel(src)
- return
+ dislocate(affecting, assailant, hit_zone)
+
if(I_DISARM)
- if(state < GRAB_AGGRESSIVE)
- assailant << "You require a better grab to do this."
- return
- assailant << "You start forcing [affecting] to the ground."
- if(!force_down)
- if(do_after(assailant, 20) && affecting)
- assailant.visible_message("[assailant] is forcing [affecting] to the ground!")
- force_down = 1
- affecting.Weaken(3)
- affecting.lying = 1
- step_to(assailant, affecting)
- assailant.set_dir(EAST) //face the victim
- affecting.set_dir(SOUTH) //face up
- return
- else
- assailant << "You are already pinning [affecting] to the ground."
- return
-
+ pin_down(affecting, assailant)
+
+ //clicking on yourself while grabbing them
if(M == assailant && state >= GRAB_AGGRESSIVE)
-
- var/can_eat
- if((FAT in user.mutations) && issmall(affecting))
- can_eat = 1
- else
- var/mob/living/carbon/human/H = user
- if(istype(H) && H.species.gluttonous)
- if(H.species.gluttonous == 2)
- can_eat = 2
- else if(!ishuman(affecting) && !issmall(affecting) && (affecting.small || iscarbon(affecting)))
- can_eat = 1
-
- if(can_eat)
- var/mob/living/carbon/attacker = user
- user.visible_message("[user] is attempting to devour [affecting]!")
- if(can_eat == 2)
- if(!do_mob(user, affecting)||!do_after(user, 30)) return
- else
- if(!do_mob(user, affecting)||!do_after(user, 100)) return
- user.visible_message("[user] devours [affecting]!")
- affecting.loc = user
- attacker.stomach_contents.Add(affecting)
- qdel(src)
-
+ devour(affecting, assailant)
/obj/item/weapon/grab/dropped()
loc = null
if(!destroying)
qdel(src)
+/obj/item/weapon/grab/proc/reset_kill_state()
+ if(state == GRAB_KILL)
+ assailant.visible_message("[assailant] lost \his tight grip on [affecting]'s neck!")
+ hud.icon_state = "kill"
+ state = GRAB_NECK
+
/obj/item/weapon/grab
var/destroying = 0
diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm
new file mode 100644
index 0000000000..f4364dc684
--- /dev/null
+++ b/code/modules/mob/mob_grab_specials.dm
@@ -0,0 +1,165 @@
+
+/obj/item/weapon/grab/proc/inspect_organ(mob/living/carbon/human/H, mob/user, var/target_zone)
+
+ var/obj/item/organ/external/E = H.get_organ(target_zone)
+
+ if(!E || (E.status & ORGAN_DESTROYED))
+ user << "[H] is missing that bodypart."
+ return
+
+ user.visible_message("[user] starts inspecting [affecting]'s [E.name] carefully.")
+ if(!do_mob(user,H, 10))
+ user << "You must stand still to inspect [E] for wounds."
+ else if(E.wounds.len)
+ user << "You find [E.get_wounds_desc()]"
+ else
+ user << "You find no visible wounds."
+
+ user << "Checking bones now..."
+ if(!do_mob(user, H, 20))
+ user << "You must stand still to feel [E] for fractures."
+ else if(E.status & ORGAN_BROKEN)
+ user << "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!"
+ H.custom_pain("Your [E.name] hurts where it's poked.")
+ else
+ user << "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine."
+
+ user << "Checking skin now..."
+ if(!do_mob(user, H, 10))
+ user << "You must stand still to check [H]'s skin for abnormalities."
+ else
+ var/bad = 0
+ if(H.getToxLoss() >= 40)
+ user << "[H] has an unhealthy skin discoloration."
+ bad = 1
+ if(H.getOxyLoss() >= 20)
+ user << "[H]'s skin is unusaly pale."
+ bad = 1
+ if(E.status & ORGAN_DEAD)
+ user << "[E] is decaying!"
+ bad = 1
+ if(!bad)
+ user << "[H]'s skin is normal."
+
+/obj/item/weapon/grab/proc/jointlock(mob/living/carbon/human/target, mob/attacker, var/target_zone)
+ if(state < GRAB_AGGRESSIVE)
+ attacker << "You require a better grab to do this."
+ return
+
+ var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone))
+ if(!organ || organ.dislocated == -1)
+ return
+
+ attacker.visible_message("[attacker] [pick("bent", "twisted")] [target]'s [organ.name] into a jointlock!")
+ var/armor = target.run_armor_check(target, "melee")
+ if(armor < 2)
+ target << "You feel extreme pain!"
+ affecting.adjustHalLoss(Clamp(0, 60-affecting.halloss, 30)) //up to 60 halloss
+
+/obj/item/weapon/grab/proc/attack_eye(mob/living/carbon/human/target, mob/living/carbon/human/attacker)
+ if(!istype(attacker))
+ return
+
+ var/datum/unarmed_attack/attack = attacker.get_unarmed_attack(target, "eyes")
+
+ if(!attack)
+ return
+ if(state < GRAB_NECK)
+ attacker << "You require a better grab to do this."
+ return
+ for(var/obj/item/protection in list(target.head, target.wear_mask, target.glasses))
+ if(protection && (protection.flags & HEADCOVERSEYES))
+ attacker << "You're going to need to remove the eye covering first."
+ return
+ if(!target.has_eyes())
+ attacker << "You cannot locate any eyes on [target]!"
+ return
+
+ attacker.attack_log += text("\[[time_stamp()]\] Attacked [target.name]'s eyes using grab ([target.ckey])")
+ target.attack_log += text("\[[time_stamp()]\] Had eyes attacked by [attacker.name]'s grab ([attacker.ckey])")
+ msg_admin_attack("[key_name(attacker)] attacked [key_name(target)]'s eyes using a grab action.")
+
+ attack.handle_eye_attack(attacker, target)
+
+/obj/item/weapon/grab/proc/headbut(mob/living/carbon/human/target, mob/living/carbon/human/attacker)
+ if(!istype(attacker))
+ return
+ if(target.lying)
+ return
+ attacker.visible_message("[attacker] thrusts \his head into [target]'s skull!")
+
+ var/damage = 20
+ var/obj/item/clothing/hat = attacker.head
+ if(istype(hat))
+ damage += hat.force * 10
+
+ var/armor = target.run_armor_check("head", "melee")
+ target.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", armor)
+ attacker.apply_damage(10*rand(90, 110)/100, BRUTE, "head", attacker.run_armor_check("head", "melee"))
+
+ if(!armor && prob(damage))
+ target.apply_effect(20, PARALYZE)
+ target.visible_message("[target] has been knocked unconscious!")
+
+ playsound(attacker.loc, "swing_hit", 25, 1, -1)
+ attacker.attack_log += text("\[[time_stamp()]\] Headbutted [target.name] ([target.ckey])")
+ target.attack_log += text("\[[time_stamp()]\] Headbutted by [attacker.name] ([attacker.ckey])")
+ msg_admin_attack("[key_name(attacker)] has headbutted [key_name(target)]")
+
+ attacker.drop_from_inventory(src)
+ src.loc = null
+ qdel(src)
+ return
+
+/obj/item/weapon/grab/proc/dislocate(mob/living/carbon/human/target, mob/living/attacker, var/target_zone)
+ if(state < GRAB_NECK)
+ attacker << "You require a better grab to do this."
+ return
+ if(target.grab_joint(attacker, target_zone))
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+ return
+
+/obj/item/weapon/grab/proc/pin_down(mob/target, mob/attacker)
+ if(state < GRAB_AGGRESSIVE)
+ attacker << "You require a better grab to do this."
+ return
+ if(force_down)
+ attacker << "You are already pinning [target] to the ground."
+
+ attacker.visible_message("[attacker] starts forcing [target] to the ground!")
+ if(do_after(attacker, 20) && target)
+ last_action = world.time
+ attacker.visible_message("[attacker] forces [target] to the ground!")
+ apply_pinning(target, attacker)
+
+/obj/item/weapon/grab/proc/apply_pinning(mob/target, mob/attacker)
+ force_down = 1
+ target.Weaken(3)
+ target.lying = 1
+ step_to(attacker, target)
+ attacker.set_dir(EAST) //face the victim
+ target.set_dir(SOUTH) //face up
+
+/obj/item/weapon/grab/proc/devour(mob/target, mob/user)
+ var/can_eat
+ if((FAT in user.mutations) && issmall(target))
+ can_eat = 1
+ else
+ var/mob/living/carbon/human/H = user
+ if(istype(H) && H.species.gluttonous)
+ if(H.species.gluttonous == 2)
+ can_eat = 2
+ else if(!ishuman(target) && !issmall(target) && (target.small || iscarbon(target)))
+ can_eat = 1
+
+ if(can_eat)
+ var/mob/living/carbon/attacker = user
+ user.visible_message("[user] is attempting to devour [target]!")
+ if(can_eat == 2)
+ if(!do_mob(user, target)||!do_after(user, 30)) return
+ else
+ if(!do_mob(user, target)||!do_after(user, 100)) return
+ user.visible_message("[user] devours [target]!")
+ target.loc = user
+ attacker.stomach_contents.Add(target)
+ qdel(src)
\ No newline at end of file
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 0404131642..7ed4f4d280 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -360,16 +360,6 @@
return Move(n, direct)
-///Process_Grab()
-///Called by client/Move()
-///Checks to see if you are grabbing anything and if moving will affect your grab.
-/client/proc/Process_Grab()
- for(var/obj/item/weapon/grab/G in list(mob.l_hand, mob.r_hand))
- if(G.state == GRAB_KILL) //no wandering across the station/asteroid while choking someone
- mob.visible_message("[mob] lost \his tight grip on [G.affecting]'s neck!")
- G.hud.icon_state = "kill"
- G.state = GRAB_NECK
-
///Process_Incorpmove
///Called by client/Move()
///Allows mobs to run though walls