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