#define UPGRADE_KILL_TIMER 100 ///Process_Grab() ///Called by client/Move() ///Checks to see if you are grabbing or being grabbed by anything and if moving will affect your grab. /client/proc/Process_Grab() //if we are being grabbed if(isliving(mob)) var/mob/living/L = mob if(!L.canmove && L.grabbed_by.len) L.resist() //shortcut for resisting grabs //if we are grabbing someone for(var/obj/item/grab/G in list(L.l_hand, L.r_hand)) G.reset_kill_state() //no wandering across the station/asteroid while choking someone /obj/item/grab name = "grab" icon = 'icons/mob/screen1.dmi' icon_state = "reinforce" flags = 0 var/obj/screen/grab/hud = null var/mob/living/affecting = null var/mob/living/carbon/human/assailant = null var/state = GRAB_PASSIVE var/allow_upgrade = 1 var/last_action = 0 var/last_hit_zone = 0 var/force_down //determines if the affecting mob will be pinned to the ground var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position abstract = 1 item_state = "nothing" w_class = ITEMSIZE_HUGE destroy_on_drop = TRUE //VOREStation Edit /obj/item/grab/New(mob/user, mob/victim) ..() loc = user assailant = user affecting = victim if(affecting.anchored || !assailant.Adjacent(victim)) qdel(src) return affecting.grabbed_by += src affecting.reveal(span_warning("You are revealed as [assailant] grabs you.")) assailant.reveal(span_warning("You reveal yourself as you grab [affecting].")) hud = new /obj/screen/grab(src) hud.icon_state = "reinforce" icon_state = "grabbed" hud.name = "reinforce grab" hud.master = src //check if assailant is grabbed by victim as well if(assailant.grabbed_by) for (var/obj/item/grab/G in assailant.grabbed_by) if(G.assailant == affecting && G.affecting == assailant) G.dancing = 1 G.adjust_position() dancing = 1 //stop pulling the affected if(assailant.pulling == affecting) assailant.stop_pulling() adjust_position() //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/grab/proc/throw_held() if(affecting) if(affecting.buckled) return null if(state >= GRAB_AGGRESSIVE) animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1) return affecting return null //This makes sure that the grab screen object is displayed in the correct hand. /obj/item/grab/proc/synch() //why is this needed? if(QDELETED(src)) return if(affecting) if(assailant.r_hand == src) hud.screen_loc = ui_rhand else hud.screen_loc = ui_lhand /obj/item/grab/process() if(QDELETED(src)) // GC is trying to delete us, we'll kill our processing so we can cleanly GC return PROCESS_KILL confirm() if(!assailant) qdel(src) // Same here, except we're trying to delete ourselves. return PROCESS_KILL if(assailant.client) assailant.client.screen -= hud assailant.client.screen += hud if(state <= GRAB_AGGRESSIVE) allow_upgrade = 1 //disallow upgrading if we're grabbing more than one person if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/grab))) var/obj/item/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/grab))) var/obj/item/grab/G = assailant.r_hand if(G.affecting != affecting) allow_upgrade = 0 //disallow upgrading past aggressive if we're being grabbed aggressively for(var/obj/item/grab/G in affecting.grabbed_by) if(G == src) continue if(G.state >= GRAB_AGGRESSIVE) allow_upgrade = 0 if(allow_upgrade) if(state < GRAB_AGGRESSIVE) hud.icon_state = "reinforce" else hud.icon_state = "reinforce1" else hud.icon_state = "!reinforce" if(state >= GRAB_AGGRESSIVE) affecting.drop_l_hand() affecting.drop_r_hand() if(iscarbon(affecting)) handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting) if(force_down) if(affecting.loc != assailant.loc || size_difference(affecting, assailant) > 0) force_down = 0 else affecting.Weaken(2) if(state >= GRAB_NECK) affecting.Stun(3) if(isliving(affecting)) var/mob/living/L = affecting L.adjustOxyLoss(1) if(state >= GRAB_KILL) //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. affecting.stuttering = max(affecting.stuttering, 5) //It will hamper your voice, being choked and all. affecting.Weaken(5) //Should keep you down unless you get help. affecting.losebreath = max(affecting.losebreath + 2, 3) adjust_position() /obj/item/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(O_MOUTH) if(announce) user.visible_message(span_warning("\The [user] covers [target]'s mouth!")) if(target.silent < 3) target.silent = 3 if(O_EYES) if(announce) assailant.visible_message(span_warning("[assailant] covers [affecting]'s eyes!")) if(affecting.eye_blind < 3) affecting.Blind(3) if(BP_HEAD) if(force_down) if(user.a_intent == I_HELP) if(announce) assailant.visible_message(span_warning("[assailant] sits on [target]'s face!")) //VOREStation Edit End /obj/item/grab/attack_self() return s_click(hud) //Updating pixelshift, position and direction //Gets called on process, when the grab gets upgraded or the assailant moves /obj/item/grab/proc/adjust_position() if(!affecting) qdel(src) return if(affecting.buckled) animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) return if(affecting.lying && state != GRAB_KILL) animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) if(force_down) affecting.set_dir(SOUTH) //face up return var/shift = 0 var/adir = get_dir(assailant, affecting) affecting.layer = MOB_LAYER switch(state) if(GRAB_PASSIVE) shift = 8 if(dancing) //look at partner shift = 10 assailant.set_dir(get_dir(assailant, affecting)) 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(SOUTH) //face up affecting.loc = assailant.loc switch(adir) if(NORTH) animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y =-shift, 5, 1, LINEAR_EASING) affecting.layer = BELOW_MOB_LAYER if(SOUTH) animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = shift, 5, 1, LINEAR_EASING) if(WEST) animate(affecting, pixel_x = shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) if(EAST) animate(affecting, pixel_x =-shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) /obj/item/grab/proc/s_click(obj/screen/S) if(QDELETED(src)) return if(!affecting) return if(state == GRAB_UPGRADING) return if(world.time < (last_action + UPGRADE_COOLDOWN)) return if(!assailant.canmove || assailant.lying) qdel(src) return var/datum/gender/TU = gender_datums[assailant.get_visible_gender()] last_action = world.time if(state < GRAB_AGGRESSIVE) if(!allow_upgrade) return if(!affecting.lying || size_difference(affecting, assailant) > 0) assailant.visible_message(span_warning("[assailant] has grabbed [affecting] aggressively (now hands)!")) else assailant.visible_message(span_warning("[assailant] pins [affecting] down to the ground (now hands)!")) apply_pinning(affecting, assailant) state = GRAB_AGGRESSIVE icon_state = "grabbed1" hud.icon_state = "reinforce1" add_attack_logs(assailant, affecting, "Aggressively grabbed", FALSE) // Not important enough to notify admins, but still helpful. else if(state < GRAB_NECK) if(isslime(affecting)) to_chat(assailant, span_notice("You squeeze [affecting], but nothing interesting happens.")) return assailant.visible_message(span_warning("[assailant] has reinforced [TU.his] grip on [affecting] (now neck)!")) state = GRAB_NECK icon_state = "grabbed+1" assailant.set_dir(get_dir(assailant, affecting)) add_attack_logs(assailant,affecting,"Neck grabbed") hud.icon_state = "kill" hud.name = "kill" affecting.Stun(10) //10 ticks of ensured grab else if(state < GRAB_UPGRADING) assailant.visible_message(span_danger("[assailant] starts to tighten [TU.his] grip on [affecting]'s neck!")) hud.icon_state = "kill1" state = GRAB_KILL assailant.visible_message(span_danger("[assailant] has tightened [TU.his] grip on [affecting]'s neck!")) add_attack_logs(assailant,affecting,"Strangled") affecting.setClickCooldown(10) affecting.AdjustLosebreath(1) 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/grab/proc/confirm() if(!assailant || !affecting) qdel(src) return 0 if(affecting) if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) qdel(src) return 0 return 1 /obj/item/grab/attack(mob/M, mob/living/user) if(QDELETED(src)) return 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(affecting)) var/mob/living/carbon/human/H = affecting var/hit_zone = assailant.zone_sel.selecting flick(hud.icon_state, hud) switch(assailant.a_intent) if(I_HELP) if(force_down) to_chat(assailant, span_warning("You are no longer pinning [affecting] to the ground.")) force_down = 0 return if(state >= GRAB_AGGRESSIVE) H.apply_pressure(assailant, hit_zone) else inspect_organ(affecting, assailant, hit_zone) if(I_GRAB) jointlock(affecting, assailant, hit_zone) if(I_HURT) if(hit_zone == O_EYES) attack_eye(affecting, assailant) else if(hit_zone == BP_HEAD) headbutt(affecting, assailant) else dislocate(affecting, assailant, hit_zone) if(I_DISARM) pin_down(affecting, assailant) //clicking on yourself while grabbing them if(M == assailant && state >= GRAB_AGGRESSIVE) devour(affecting, assailant) /obj/item/grab/dropped() loc = null if(!QDELETED(src)) qdel(src) /obj/item/grab/proc/reset_kill_state() if(state == GRAB_KILL) var/datum/gender/T = gender_datums[assailant.get_visible_gender()] assailant.visible_message(span_warning("[assailant] lost [T.his] tight grip on [affecting]'s neck!")) hud.icon_state = "kill" state = GRAB_NECK /obj/item/grab/proc/handle_resist() var/grab_name var/break_strength = 1 var/list/break_chance_table = list(100) switch(state) //if(GRAB_PASSIVE) if(GRAB_AGGRESSIVE) grab_name = "grip" //Being knocked down makes it harder to break a grab, so it is easier to cuff someone who is down without forcing them into unconsciousness. if(!affecting.incapacitated(INCAPACITATION_KNOCKDOWN)) break_strength++ break_chance_table = list(15, 60, 100) if(GRAB_NECK) grab_name = "headlock" //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun. if(world.time - assailant.l_move_time < 30 || !affecting.stunned) break_strength++ break_chance_table = list(3, 18, 45, 100) if(GRAB_KILL) grab_name = "stranglehold" break_chance_table = list(5, 20, 40, 80, 100) //It's easier to break out of a grab by a smaller mob break_strength += max(size_difference(affecting, assailant), 0) //CHOMPEdit Begin var/prob_mult = 1 var/mob/living/carbon/human/grabbee = affecting var/mob/living/carbon/human/grabber = assailant if(istype(grabbee)) prob_mult /= grabbee.species.grab_resist_divisor_self break_strength += grabbee.species.grab_power_self if(istype(grabber)) prob_mult /= grabber.species.grab_resist_divisor_victims break_strength += grabber.species.grab_power_victims var/break_chance = CLAMP(prob_mult*break_chance_table[CLAMP(break_strength, 1, break_chance_table.len)],0,100) //CHOMPEdit End if(prob(break_chance)) if(state == GRAB_KILL) reset_kill_state() return else if(grab_name) affecting.visible_message(span_warning("[affecting] has broken free of [assailant]'s [grab_name]!")) qdel(src) //returns the number of size categories between affecting and assailant, rounded. Positive means A is larger than B /obj/item/grab/proc/size_difference(mob/A, mob/B) return mob_size_difference(A.mob_size, B.mob_size) /obj/item/grab/Destroy() animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) affecting.reset_plane_and_layer() if(affecting) affecting.grabbed_by -= src affecting = null if(assailant) if(assailant.client) assailant.client.screen -= hud assailant = null qdel(hud) hud = null return ..() #undef UPGRADE_KILL_TIMER