diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index e647c5a5e5a..b0c1e4e60c6 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -241,6 +241,11 @@ its easier to just keep the beam vertical.
/atom/proc/relaymove()
return
+//called to set the atom's dir and used to add behaviour to dir-changes - Not fully used (yet)
+/atom/proc/set_dir(new_dir)
+ . = new_dir != dir
+ dir = new_dir
+
/atom/proc/ex_act()
return
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index b2be2f8e29d..e5a5276e10b 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -226,4 +226,32 @@
return
/mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params)
- return
\ No newline at end of file
+ return
+
+/mob/living/carbon/human/proc/grab_joint(var/mob/living/user, var/def_zone)
+ var/has_grab = 0
+ for(var/obj/item/weapon/grab/G in list(user.l_hand, user.r_hand))
+ if(G.affecting == src && G.state == GRAB_NECK)
+ has_grab = 1
+ break
+
+ if(!has_grab)
+ return 0
+
+ if(!def_zone) def_zone = user.zone_sel.selecting
+ var/target_zone = check_zone(def_zone)
+ if(!target_zone)
+ return 0
+ var/obj/item/organ/external/organ = get_organ(check_zone(target_zone))
+ if(!organ || organ.is_broken())
+ return 0
+
+ if(organ.limb_name == "chest" || organ.limb_name == "groin")
+ return 0 //what how do you dislocate a chest with your hands or a groin really
+
+ user.visible_message("[user] begins to dislocate [src]'s [organ.name]!")
+ if(do_after(user, 100))
+ organ.fracture()
+ src.visible_message("[src]'s [organ.name] [pick("cracks","gives way","snaps","collapses")]!")
+ return 1
+ return 0
\ No newline at end of file
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index deb84cdf5e4..a6b4fdc207c 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -264,6 +264,11 @@
if(!(status_flags & CANPUSH))
return 0
+ for(var/obj/item/weapon/grab/G in src.grabbed_by)
+ if(G.assailant == user)
+ user << "You already grabbed [src]."
+ return
+
add_logs(user, src, "grabbed", addition="passively")
var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(user, src)
@@ -276,5 +281,11 @@
LAssailant = user
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- if(!supress_message)
- visible_message("[user] has grabbed [src] passively!")
+ if(user.dir == src.dir)
+ G.state = GRAB_AGGRESSIVE
+ G.adjust_position()
+ if(!supress_message)
+ visible_message("[user] has grabbed [src] from behind!")
+ else
+ if(!supress_message)
+ visible_message("[user] has grabbed [src] passively!")
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index b04c0832350..0e202d8df9e 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -5,11 +5,13 @@
name = "grab"
flags = NOBLUDGEON | ABSTRACT
var/obj/screen/grab/hud = null
- var/mob/affecting = null
+ var/mob/living/affecting = null
var/mob/assailant = null
var/state = GRAB_PASSIVE
+
var/allow_upgrade = 1
var/last_upgrade = 0
+ var/last_hit_zone = 0
layer = 21
item_state = "nothing"
@@ -28,8 +30,12 @@
hud = new /obj/screen/grab(src)
hud.icon_state = "reinforce"
+ icon_state = "grabbed"
hud.name = "reinforce grab"
hud.master = src
+
+ last_upgrade = world.time //makes it so you can't insta-agressive-grab.
+
//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code.
/obj/item/weapon/grab/proc/throw()
if(affecting)
@@ -64,10 +70,12 @@
var/obj/item/weapon/grab/G = assailant.l_hand
if(G.affecting != affecting)
allow_upgrade = 0
+
if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/weapon/grab)))
var/obj/item/weapon/grab/G = assailant.r_hand
if(G.affecting != affecting)
allow_upgrade = 0
+
if(state == GRAB_AGGRESSIVE)
var/h = affecting.hand
affecting.hand = 0
@@ -75,17 +83,38 @@
affecting.hand = 1
affecting.drop_item()
affecting.hand = h
+
for(var/obj/item/weapon/grab/G in affecting.grabbed_by)
if(G == src) continue
- if(G.state == GRAB_AGGRESSIVE)
+ if(G.state >= GRAB_AGGRESSIVE)
allow_upgrade = 0
+
if(allow_upgrade)
- hud.icon_state = "reinforce"
+ if(state < GRAB_AGGRESSIVE)
+ hud.icon_state = "reinforce"
+ else
+ hud.icon_state = "reinforce1"
else
hud.icon_state = "!reinforce"
- else
- if(!affecting.buckled)
- affecting.loc = assailant.loc
+
+ if(state >= GRAB_AGGRESSIVE)
+ var/hit_zone = assailant.zone_sel.selecting
+ var/announce = 0
+ if(hit_zone != last_hit_zone)
+ announce = 1
+ last_hit_zone = hit_zone
+ 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(state >= GRAB_NECK)
affecting.Stun(5) //It will hamper your voice, being choked and all.
if(isliving(affecting))
@@ -95,10 +124,46 @@
if(state >= GRAB_KILL)
affecting.Weaken(5) //Should keep you down unless you get help.
affecting.losebreath = min(affecting.losebreath + 2, 3)
-
+
/obj/item/weapon/grab/attack_self(mob/user)
s_click(hud)
+//Updating pixelshift, position and direction
+//Gets called on process, when the grab gets upgraded or the assailant moves
+/obj/item/weapon/grab/proc/adjust_position()
+ if(affecting.buckled)
+ return
+ var/shift = 0
+ var/adir = get_dir(assailant, affecting)
+ affecting.layer = 4
+ switch(state)
+ if(GRAB_PASSIVE)
+ shift = 6
+ if(GRAB_AGGRESSIVE)
+ shift = 12
+ if(GRAB_NECK, GRAB_UPGRADING)
+ shift = -10
+ adir = assailant.dir
+ affecting.set_dir(assailant.dir)
+ affecting.loc = assailant.loc
+ if(GRAB_KILL)
+ shift = 0
+ adir = 1
+ affecting.set_dir(reverse_dir[assailant.dir])
+ affecting.loc = assailant.loc
+ affecting.lying = 1
+
+ switch(adir)
+ if(NORTH)
+ animate(affecting, pixel_x = 0, pixel_y =-shift, 5, 1, LINEAR_EASING)
+ affecting.layer = 3.9
+ if(SOUTH)
+ animate(affecting, pixel_x = 0, pixel_y = shift, 5, 1, LINEAR_EASING)
+ if(WEST)
+ animate(affecting, pixel_x = shift, pixel_y = 0, 5, 1, LINEAR_EASING)
+ 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
@@ -111,6 +176,7 @@
if(!assailant.canmove || assailant.lying)
del(src)
return
+
last_upgrade = world.time
if(state < GRAB_AGGRESSIVE)
@@ -119,56 +185,38 @@
assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!")
state = GRAB_AGGRESSIVE
icon_state = "grabbed1"
- else
- if(state < GRAB_NECK)
- if(isslime(affecting))
- assailant << "You squeeze [affecting], but nothing interesting happens."
- return
+ hud.icon_state = "reinforce1"
+ else if(state < GRAB_NECK)
+ if(isslime(affecting))
+ assailant << "You squeeze [affecting], but nothing interesting happens."
+ return
- assailant.visible_message("[assailant] has reinforced \his grip on [affecting] (now neck)!")
- state = GRAB_NECK
- icon_state = "grabbed+1"
- if(!affecting.buckled)
- affecting.loc = assailant.loc
- affecting.attack_log += "\[[time_stamp()]\] Has had their neck grabbed by [assailant.name] ([assailant.ckey])"
- assailant.attack_log += "\[[time_stamp()]\] Grabbed the neck of [affecting.name] ([affecting.ckey])"
- log_attack("[assailant.name] ([assailant.ckey]) grabbed the neck of [affecting.name] ([affecting.ckey])")
- if(!iscarbon(assailant))
- affecting.LAssailant = null
- else
- affecting.LAssailant = assailant
- hud.icon_state = "disarm/kill"
- hud.name = "disarm/kill"
+ assailant.visible_message("[assailant] has reinforced \his grip on [affecting] (now neck)!")
+ state = GRAB_NECK
+ icon_state = "grabbed+1"
+ assailant.set_dir(get_dir(assailant, affecting))
+ affecting.attack_log += "\[[time_stamp()]\] Has had their neck grabbed by [assailant.name] ([assailant.ckey])"
+ assailant.attack_log += "\[[time_stamp()]\] Grabbed the neck of [affecting.name] ([affecting.ckey])"
+ log_attack("[assailant.name] ([assailant.ckey]) grabbed the neck of [affecting.name] ([affecting.ckey])")
+ if(!iscarbon(assailant))
+ affecting.LAssailant = null
else
- if(state < GRAB_UPGRADING)
- assailant.visible_message("[assailant] starts to tighten \his grip on [affecting]'s neck!")
- hud.icon_state = "disarm/kill1"
- state = GRAB_UPGRADING
- if(do_after(assailant, UPGRADE_KILL_TIMER))
- if(state == GRAB_KILL)
- return
- if(!affecting)
- del(src)
- return
- if(!assailant.canmove || assailant.lying)
- del(src)
- return
- state = GRAB_KILL
- assailant.visible_message("[assailant] has tightened \his grip on [affecting]'s neck!")
- affecting.attack_log += "\[[time_stamp()]\] Has been strangled (kill intent) by [assailant.name] ([assailant.ckey])"
- assailant.attack_log += "\[[time_stamp()]\] Strangled (kill intent) [affecting.name] ([affecting.ckey])"
- log_attack("[assailant.name] ([assailant.ckey]) Strangled (kill intent) [affecting.name] ([affecting.ckey])")
- if(!iscarbon(assailant))
- affecting.LAssailant = null
- else
- affecting.LAssailant = assailant
- assailant.changeNext_move(CLICK_CD_TKSTRANGLE)
- affecting.losebreath += 1
- else
- assailant.visible_message("[assailant] was unable to tighten \his grip on [affecting]'s neck!")
- hud.icon_state = "disarm/kill"
- state = GRAB_NECK
+ affecting.LAssailant = assailant
+ hud.icon_state = "kill"
+ hud.name = "kill"
+ else if(state < GRAB_UPGRADING)
+ assailant.visible_message("[assailant] starts to tighten \his grip on [affecting]'s neck!")
+ hud.icon_state = "kill1"
+ state = GRAB_KILL
+ assailant.visible_message("[assailant] has tightened \his grip on [affecting]'s neck!")
+ affecting.attack_log += "\[[time_stamp()]\] Has been strangled (kill intent) by [assailant.name] ([assailant.ckey])"
+ assailant.attack_log += "\[[time_stamp()]\] Strangled (kill intent) [affecting.name] ([affecting.ckey])"
+ msg_admin_attack("[key_name(assailant)] strangled (kill intent) [key_name(affecting)]")
+
+ assailant.next_move = world.time + 10
+ affecting.losebreath += 1
+ 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()
@@ -187,9 +235,20 @@
if(!affecting)
return
+ if(ishuman(M) && assailant.a_intent == "harm" && M == affecting)
+ var/mob/living/carbon/human/victim = M
+ if(state < GRAB_NECK)
+ assailant << "You require a better grab to do this."
+ return
+
+ if(victim.grab_joint(assailant))
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+ return
+
if(M == affecting)
s_click(hud)
return
+
if(M == assailant && state >= GRAB_AGGRESSIVE)
if( (ishuman(user) && (FAT in user.mutations) && iscarbon(affecting) ) || ( isalien(user) && iscarbon(affecting) ) || ( istype(user,/mob/living/carbon/human/kidan) && istype(affecting,/mob/living/carbon/monkey/diona) ) || ( ishuman(user) && user.get_species() == "Tajaran" && istype(affecting,/mob/living/simple_animal/mouse) ) )
var/mob/living/carbon/attacker = user
@@ -207,7 +266,15 @@
/obj/item/weapon/grab/dropped()
del(src)
+/obj/item/weapon/grab/Del()
+ //make sure the grabbed_by list doesn't fill up with nulls
+ if(affecting) affecting.grabbed_by -= src
+ ..()
/obj/item/weapon/grab/Destroy()
+ affecting.pixel_x = 0
+ affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing
+ affecting.layer = initial(affecting.layer)
+ if(affecting) affecting.grabbed_by -= src
del(hud)
- ..()
+ ..()
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 784ae01d668..61026e69e74 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -322,6 +322,11 @@
. = ..()
mob.last_movement=world.time
+ for (var/obj/item/weapon/grab/G in mob)
+ if (G.state == GRAB_NECK)
+ mob.set_dir(reverse_dir[direct])
+ G.adjust_position()
+
moving = 0
return .
diff --git a/icons/mob/screen1.dmi b/icons/mob/screen1.dmi
index 391366d3200..365ee17d386 100644
Binary files a/icons/mob/screen1.dmi and b/icons/mob/screen1.dmi differ