diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 35e19f568a1..afb2ca35a73 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -33,6 +33,7 @@ if (can_operate(M)) //Checks if mob is lying down on table for surgery if (do_surgery(M,user,src)) return 0 + if (istype(M,/mob/living/carbon/brain)) messagesource = M:container if (hitsound && force > 0) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 92ccf048809..656a38928b3 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -244,7 +244,8 @@ 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 diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 86eae591aff..f0de8268303 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -572,21 +572,21 @@ for(var/obj/item/weapon/grab/G in usr.grabbed_by) resisting++ if (G.state == 1) - del(G) + qdel(G) else if(G.state == 2) - if(prob(25)) + if(prob(60)) for(var/mob/O in viewers(L, null)) O.show_message(text("\red [] has broken free of []'s grip!", L, G.assailant), 1) - del(G) + qdel(G) else if(G.state == 3) if(prob(5)) for(var/mob/O in viewers(usr, null)) O.show_message(text("\red [] has broken free of []'s headlock!", L, G.assailant), 1) - del(G) + qdel(G) if(resisting) for(var/mob/O in viewers(usr, null)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 1af04f7de9a..0e4a5d42d3c 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -268,6 +268,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) @@ -280,5 +285,11 @@ LAssailant = user playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + /*if(user.dir == src.dir) + G.state = GRAB_AGGRESSIVE + G.last_upgrade = world.time + if(!supress_message) + visible_message("[user] has grabbed [src] from behind!") + else*///This is an example of how you can make special types of grabs simply based on direction. 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 1a7360185ef..20090a0e9cc 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -12,11 +12,15 @@ name = "grab" flags = NOBLUDGEON | ABSTRACT var/obj/screen/grab/hud = null - var/mob/affecting = null - var/mob/assailant = null + var/mob/living/affecting = null + var/mob/living/assailant = null var/state = GRAB_PASSIVE + var/allow_upgrade = 1 var/last_upgrade = 0 + var/last_hit_zone = 0 +// var/force_down //determines if the affecting mob will be pinned to the ground //disabled due to balance, kept for an example for any new things. + var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position layer = 21 item_state = "nothing" @@ -30,13 +34,26 @@ affecting = victim if(affecting.anchored) - del(src) + qdel(src) return + affecting.grabbed_by += src + 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/weapon/grab/G in assailant.grabbed_by) + if(G.assailant == affecting && G.affecting == assailant) + G.dancing = 1 + G.adjust_position() + dancing = 1 + 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/weapon/grab/proc/throw() if(affecting) @@ -59,6 +76,7 @@ confirm() if(assailant.client) + if(!hud) return //this somehow can runtime under the right circumstances assailant.client.screen -= hud assailant.client.screen += hud @@ -67,6 +85,7 @@ 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/weapon/grab))) var/obj/item/weapon/grab/G = assailant.l_hand if(G.affecting != affecting) @@ -75,24 +94,55 @@ 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 - affecting.drop_item() - 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) - allow_upgrade = 0 + + //disallow upgrading past aggressive if we're being grabbed aggressively + for(var/obj/item/weapon/grab/G in affecting.grabbed_by) + if(G == src) continue + 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/h = affecting.hand + affecting.hand = 0 + affecting.drop_item() + affecting.hand = 1 + affecting.drop_item() + affecting.hand = h + + var/hit_zone = assailant.zone_sel.selecting + //var/announce = 0 + //(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*///These are being left in the code as an example for adding new hit-zone based things. + + if(force_down) + if(affecting.loc != assailant.loc) + force_down = 0 + else + affecting.Weaken(3) //This is being left in the code as an example of adding a new variable to do something in grab code. + +*/ + if(state >= GRAB_NECK) affecting.Stun(5) //It will hamper your voice, being choked and all. if(isliving(affecting)) @@ -100,12 +150,61 @@ L.adjustOxyLoss(1) if(state >= GRAB_KILL) - affecting.Weaken(5) //Should keep you down unless you get help. - affecting.losebreath = min(affecting.losebreath + 2, 3) + //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/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 + if(affecting.lying && state != GRAB_KILL) + animate(affecting, pixel_x = 0, pixel_y = 0, 5, 1, LINEAR_EASING) + return //KJK + /* if(force_down) //THIS GOES ABOVE THE RETURN LABELED KJK + affecting.set_dir(SOUTH)*///This shows how you can apply special directions based on a variable. //face up + + var/shift = 0 + var/adir = get_dir(assailant, affecting) + affecting.layer = 4 + 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 = 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 @@ -116,87 +215,152 @@ if(world.time < (last_upgrade + UPGRADE_COOLDOWN)) return if(!assailant.canmove || assailant.lying) - del(src) + qdel(src) return + last_upgrade = world.time if(state < GRAB_AGGRESSIVE) if(!allow_upgrade) return + //if(!affecting.lying) 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 //This is an example of a new feature based on the context of the location of the victim. + */ //It means that upgrading while someone is lying on the ground would cause you to go into pin mode. 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" + affecting.Stun(10) //10 ticks of ensured grab + 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 + 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) - del(src) + qdel(src) return 0 if(affecting) if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) - del(src) + qdel(src) return 0 return 1 -/obj/item/weapon/grab/attack(mob/M, mob/user) +/obj/item/weapon/grab/attack(mob/living/M, mob/living/carbon/user) if(!affecting) return - if(M == affecting) //what the actual fuck is this - s_click(hud) - return + if(M == affecting) + if(ishuman(M) && ishuman(assailant)) + var/mob/living/carbon/human/affected = affecting + var/mob/living/carbon/human/attacker = assailant + switch(assailant.a_intent) + if("help") + /*if(force_down) + assailant << "You no longer pin [affecting] to the ground." + force_down = 0 + return*///This is a very basic demonstration of a new feature based on attacking someone with the grab, based on intent. + //This specific example would allow you to stop pinning people to the floor without moving away from them. + return + + if("grab") + return + + if("harm") //This checks that the user is on harm intent. + if(last_hit_zone == "head") //This checks the hitzone the user has selected. In this specific case, they have the head selected. + if(affecting.lying) + return + assailant.visible_message("[assailant] thrusts \his head into [affecting]'s skull!") //A visible message for what is going on. + var/damage = 5 + var/obj/item/clothing/hat = attacker.head + if(istype(hat)) + damage += hat.force * 3 + affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee")) + 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)]") + return + + /*if(last_hit_zone == "eyes") + if(state < GRAB_NECK) + assailant << "You require a better grab to do this." + return + if((affected.head && affected.head.flags & HEADCOVERSEYES) || \ + (affected.wear_mask && affected.wear_mask.flags & MASKCOVERSEYES) || \ + (affected.glasses && affected.glasses.flags & GLASSESCOVERSEYES)) + assailant << "You're going to need to remove the eye covering first." + return + if(!affected.internal_organs_by_name["eyes"]) + assailant << "You cannot locate any eyes on [affecting]!" + return + assailant.visible_message("[assailant] presses \his fingers into [affecting]'s eyes!") + affecting << "You feel immense pain as digits are being pressed into your eyes!" + assailant.attack_log += text("\[[time_stamp()]\] Pressed fingers into the eyes of [affecting.name] ([affecting.ckey])") + affecting.attack_log += text("\[[time_stamp()]\] Had fingers pressed into their eyes by [assailant.name] ([assailant.ckey])") + msg_admin_attack("[key_name(assailant)] has pressed his fingers into [key_name(affecting)]'s eyes.") + var/obj/item/organ/eyes/eyes = affected.internal_organs_by_name["eyes"] + eyes.damage += rand(3,4) + if (eyes.damage >= eyes.min_broken_damage) + if(M.stat != 2) + M << "\red You go blind!"*///This is a demonstration of adding a new damaging type based on intent as well as hitzone. + //This specific example would allow you to squish people's eyes with a GRAB_NECK. + + if("disarm") //This checks that the user is on disarm intent. + /* if(state < GRAB_AGGRESSIVE) + assailant << "You require a better grab to do this." + return + if(!force_down) + assailant.visible_message("[user] 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*///This is an example of something being done with an agressive grab + disarm intent. + return + if(M == assailant && state >= GRAB_AGGRESSIVE) //no eatin unless you have an agressive grab if(checkvalid(user, affecting)) //wut @@ -207,10 +371,9 @@ user.visible_message("[user] devours \the [affecting]!") - affecting.loc = user //add the mob to the user - attacker.stomach_contents.Add(affecting) //list keeping - - del(src) + affecting.loc = user + attacker.stomach_contents.Add(affecting) + qdel(src) /obj/item/weapon/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab if(ishuman(attacker) && (FAT in attacker.mutations) && iscarbon(prey) && !isalien(prey)) //Fat people eating carbon mobs but not xenos @@ -237,14 +400,22 @@ return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it /obj/item/weapon/grab/dropped() - del(src) + qdel(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) ..() #undef EAT_TIME_XENO #undef EAT_TIME_FAT -#undef EAT_TIME_MOUSE \ No newline at end of file +#undef EAT_TIME_MOUSE diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index cc40497b37c..5afda13a9be 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -336,6 +336,13 @@ . = ..() 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() + for (var/obj/item/weapon/grab/G in mob.grabbed_by) + 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