diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index 9b6455a55054..50ace9f0c6be 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -179,6 +179,7 @@ #define COMSIG_KB_XENO_PURCHASE_STRAIN "keybinding_purchase_strain" #define COMSIG_KB_XENO_RESET_STRAIN "keybinding_reset_strain" #define COMSIG_KB_XENO_BECOME_SEETHROUGH "keybinding_become_seethrough" +#define COMSIG_KB_XENO_RIP_LIMB "keybinding_rip_limb" // Yautja diff --git a/code/datums/keybinding/xenomorph.dm b/code/datums/keybinding/xenomorph.dm index b362e35f928a..2f8b433121cf 100644 --- a/code/datums/keybinding/xenomorph.dm +++ b/code/datums/keybinding/xenomorph.dm @@ -264,3 +264,17 @@ current_xeno.toggle_seethrough() return TRUE +/datum/keybinding/xenomorph/rip_limb + hotkey_keys = list("Unbound") + classic_keys = list("Unbound") + name = "rip_limb" + full_name = "Rip Limb" + keybind_signal = COMSIG_KB_XENO_RIP_LIMB + +/datum/keybinding/xenomorph/rip_limb/down(client/user) + . = ..() + if(.) + return + + var/mob/living/carbon/xenomorph/current_xeno = user?.mob + current_xeno.rip_limb() diff --git a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm index 7986d1d17b54..7896d44e0e7d 100644 --- a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm +++ b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm @@ -601,7 +601,41 @@ /// Called when pulling something and attacking yourself wth the pull (Z hotkey) override for caste specific behaviour /mob/living/carbon/xenomorph/proc/pull_power(mob/mob) - return + var/mob/living/carbon/pulled = src.pulling + if(!istype(pulled)) + return + if(isxeno(pulled) || issynth(pulled)) + to_chat(src, SPAN_WARNING("That wouldn't serve a purpose.")) + return + if(pulled.buckled) + to_chat(src, SPAN_WARNING("[pulled] is buckled to something.")) + return + if(pulled.stat == DEAD && !pulled.chestburst) + to_chat(src, SPAN_WARNING("Ew, [pulled] is already starting to rot.")) + return + if(src.hauled_mob?.resolve()) // We can't carry more than one mob + to_chat(src, SPAN_WARNING("You already are carrying something, there's no way that will work.")) + return + if(HAS_TRAIT(pulled, TRAIT_HAULED)) + to_chat(src, SPAN_WARNING("They are already being hauled by someone else.")) + return + if(src.action_busy) + to_chat(src, SPAN_WARNING("We are already busy with something.")) + return + SEND_SIGNAL(src, COMSIG_MOB_EFFECT_CLOAK_CANCEL) + src.visible_message(SPAN_DANGER("[src] starts to restrain [pulled]!"), + SPAN_DANGER("We start restraining [pulled]!"), null, 5) + if(HAS_TRAIT(src, TRAIT_CLOAKED)) //cloaked don't show the visible message, so we gotta work around + to_chat(pulled, FONT_SIZE_HUGE(SPAN_DANGER("[src] is trying to restrain you!"))) + if(do_after(src, 50, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE)) + if((isxeno(pulled.loc) && !src.hauled_mob) || HAS_TRAIT(pulled, TRAIT_HAULED)) + to_chat(src, SPAN_WARNING("Someone already took \the [pulled].")) + return + if(src.pulling == pulled && !pulled.buckled && (pulled.stat != DEAD || pulled.chestburst) && !src.hauled_mob?.resolve()) //make sure you've still got them in your claws, and alive + if(SEND_SIGNAL(pulled, COMSIG_MOB_HAULED, src) & COMPONENT_CANCEL_HAUL) + return FALSE + src.haul(pulled) + src.stop_pulling() // Vent Crawl /mob/living/carbon/xenomorph/proc/vent_crawl() diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 87b8d45b76b2..0d05d95c42a4 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -293,7 +293,6 @@ var/crest_defense = FALSE /// 0/FALSE - upright, 1/TRUE - all fours var/agility = FALSE - var/ripping_limb = FALSE /// For drones/hivelords. Extends the maximum build range they have var/extra_build_dist = 0 /// tiles from self you can plant eggs. diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm index 5f54c29ed14e..867ff4771697 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm @@ -181,90 +181,6 @@ bound_xeno.remove_filter("empower_rage") -/// Warrior specific behaviour for increasing pull power, limb rip. -/mob/living/carbon/xenomorph/warrior/pull_power(mob/mob) - if(!ripping_limb && mob.stat != DEAD) - if(mob.status_flags & XENO_HOST) - to_chat(src, SPAN_XENOWARNING("This would harm the embryo!")) - return - ripping_limb = TRUE - if(rip_limb(mob)) - stop_pulling() - ripping_limb = FALSE - - -/// Warrior Rip Limb - called by pull_power() -/mob/living/carbon/xenomorph/warrior/proc/rip_limb(mob/mob) - if(!istype(mob, /mob/living/carbon/human)) - return FALSE - - if(action_busy) //can't stack the attempts - return FALSE - - var/mob/living/carbon/human/human = mob - var/obj/limb/limb = human.get_limb(check_zone(zone_selected)) - - if(can_not_harm(human)) - to_chat(src, SPAN_XENOWARNING("We can't harm this host!")) - return - - if(!limb || limb.body_part == BODY_FLAG_CHEST || limb.body_part == BODY_FLAG_GROIN || (limb.status & LIMB_DESTROYED)) //Only limbs and head. - to_chat(src, SPAN_XENOWARNING("We can't rip off that limb.")) - return FALSE - - var/limb_time = rand(40,60) - if(limb.body_part == BODY_FLAG_HEAD) - limb_time = rand(90,110) - - visible_message(SPAN_XENOWARNING("[src] begins pulling on [mob]'s [limb.display_name] with incredible strength!"), - SPAN_XENOWARNING("We begin to pull on [mob]'s [limb.display_name] with incredible strength!")) - - if(!do_after(src, limb_time, INTERRUPT_ALL|INTERRUPT_DIFF_SELECT_ZONE, BUSY_ICON_HOSTILE) || mob.stat == DEAD || mob.status_flags & XENO_HOST) - to_chat(src, SPAN_NOTICE("We stop ripping off the limb.")) - return FALSE - - if(mob.status_flags & XENO_HOST) - to_chat(src, SPAN_NOTICE("We detect an embryo inside [mob] which overwhelms our instinct to rip.")) - return FALSE - - if(limb.status & LIMB_DESTROYED) - return FALSE - - if(limb.status & (LIMB_ROBOT|LIMB_SYNTHSKIN)) - limb.take_damage(rand(30,40), 0, 0) // just do more damage - visible_message(SPAN_XENOWARNING("You hear [mob]'s [limb.display_name] being pulled beyond its load limits!"), - SPAN_XENOWARNING("[mob]'s [limb.display_name] begins to tear apart!")) - else - visible_message(SPAN_XENOWARNING("We hear the bones in [mob]'s [limb.display_name] snap with a sickening crunch!"), - SPAN_XENOWARNING("[mob]'s [limb.display_name] bones snap with a satisfying crunch!")) - limb.take_damage(rand(15,25), 0, 0) - limb.fracture(100) - mob.last_damage_data = create_cause_data(initial(caste_type), src) - src.attack_log += text("\[[time_stamp()]\] ripped the [limb.display_name] off of [mob.name] ([mob.ckey]) 1/2 progress") - mob.attack_log += text("\[[time_stamp()]\] had their [limb.display_name] ripped off by [src.name] ([src.ckey]) 1/2 progress") - log_attack("[src.name] ([src.ckey]) ripped the [limb.display_name] off of [mob.name] ([mob.ckey]) 1/2 progress") - - if(!do_after(src, limb_time, INTERRUPT_ALL|INTERRUPT_DIFF_SELECT_ZONE, BUSY_ICON_HOSTILE) || mob.stat == DEAD || iszombie(mob)) - to_chat(src, SPAN_NOTICE("We stop ripping off the limb.")) - return FALSE - - if(mob.status_flags & XENO_HOST) - to_chat(src, SPAN_NOTICE("We detect an embryo inside [mob] which overwhelms our instinct to rip.")) - return FALSE - - if(limb.status & LIMB_DESTROYED) - return FALSE - - visible_message(SPAN_XENOWARNING("[src] rips [mob]'s [limb.display_name] away from their body!"), - SPAN_XENOWARNING("[mob]'s [limb.display_name] rips away from their body!")) - src.attack_log += text("\[[time_stamp()]\] ripped the [limb.display_name] off of [mob.name] ([mob.ckey]) 2/2 progress") - mob.attack_log += text("\[[time_stamp()]\] had their [limb.display_name] ripped off by [src.name] ([src.ckey]) 2/2 progress") - log_attack("[src.name] ([src.ckey]) ripped the [limb.display_name] off of [mob.name] ([mob.ckey]) 2/2 progress") - - limb.droplimb(0, 0, initial(name)) - - return TRUE - /datum/action/xeno_action/activable/lunge/use_ability(atom/affected_atom) var/mob/living/carbon/xenomorph/lunge_user = owner diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm index 7ff20c5807e3..34cdfa4be06f 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm @@ -173,3 +173,88 @@ // var/datum/techtree/T = GET_TREE(TREE_XENO) // T.enter_mob(src) + +/mob/living/carbon/xenomorph/verb/rip_limb() + set name = "Rip Limb" + set desc = "Rip off a limb from living pray." + set category = "Alien" + + if(caste_type != XENO_CASTE_WARRIOR) + to_chat(src, SPAN_WARNING("Only Warriors can do this!")) + return + + if(action_busy) //can't stack the attempts + return + + if(!pulling) + to_chat(src, SPAN_XENOWARNING("We need to grab a target first!")) + return + + if(!istype(pulling, /mob/living/carbon/human)) + to_chat(src, SPAN_XENOWARNING("We prefer to rip off limbs from humanoids!")) + return + + var/mob/living/carbon/human/target_human = pulling + var/obj/limb/limb = target_human.get_limb(check_zone(zone_selected)) + + if(!istype(target_human, /mob/living/carbon/human)) + return + + if(can_not_harm(target_human)) + to_chat(src, SPAN_XENOWARNING("We can't harm this host!")) + return + + if(!limb || limb.body_part == BODY_FLAG_CHEST || limb.body_part == BODY_FLAG_GROIN || (limb.status & LIMB_DESTROYED)) //Only limbs and head. + to_chat(src, SPAN_XENOWARNING("We can't rip off that limb.")) + return + + var/limb_time = rand(40,60) + if(limb.body_part == BODY_FLAG_HEAD) + limb_time = rand(90,110) + + visible_message(SPAN_XENOWARNING("[src] begins pulling on [target_human]'s [limb.display_name] with incredible strength!"), + SPAN_XENOWARNING("We begin to pull on [target_human]'s [limb.display_name] with incredible strength!")) + + if(!do_after(src, limb_time, INTERRUPT_ALL|INTERRUPT_DIFF_SELECT_ZONE, BUSY_ICON_HOSTILE) || target_human.stat == DEAD || target_human.status_flags & XENO_HOST) + to_chat(src, SPAN_NOTICE("We stop ripping off the limb.")) + return + + if(target_human.status_flags & XENO_HOST) + to_chat(src, SPAN_NOTICE("We detect an embryo inside [target_human] which overwhelms our instinct to rip.")) + return + + if(limb.status & LIMB_DESTROYED) + return + + if(limb.status & (LIMB_ROBOT|LIMB_SYNTHSKIN)) + limb.take_damage(rand(30,40), 0, 0) // just do more damage + visible_message(SPAN_XENOWARNING("You hear [target_human]'s [limb.display_name] being pulled beyond its load limits!"), + SPAN_XENOWARNING("[target_human]'s [limb.display_name] begins to tear apart!")) + else + visible_message(SPAN_XENOWARNING("We hear the bones in [target_human]'s [limb.display_name] snap with a sickening crunch!"), + SPAN_XENOWARNING("[target_human]'s [limb.display_name] bones snap with a satisfying crunch!")) + limb.take_damage(rand(15,25), 0, 0) + limb.fracture(100) + target_human.last_damage_data = create_cause_data(initial(caste_type), src) + src.attack_log += text("\[[time_stamp()]\] ripped the [limb.display_name] off of [target_human.name] ([target_human.ckey]) 1/2 progress") + target_human.attack_log += text("\[[time_stamp()]\] had their [limb.display_name] ripped off by [src.name] ([src.ckey]) 1/2 progress") + log_attack("[src.name] ([src.ckey]) ripped the [limb.display_name] off of [target_human.name] ([target_human.ckey]) 1/2 progress") + + if(!do_after(src, limb_time, INTERRUPT_ALL|INTERRUPT_DIFF_SELECT_ZONE, BUSY_ICON_HOSTILE) || target_human.stat == DEAD || iszombie(target_human)) + to_chat(src, SPAN_NOTICE("We stop ripping off the limb.")) + return + + if(target_human.status_flags & XENO_HOST) + to_chat(src, SPAN_NOTICE("We detect an embryo inside [target_human] which overwhelms our instinct to rip.")) + return + + if(limb.status & LIMB_DESTROYED) + return + + visible_message(SPAN_XENOWARNING("[src] rips [target_human]'s [limb.display_name] away from their body!"), + SPAN_XENOWARNING("[target_human]'s [limb.display_name] rips away from their body!")) + src.attack_log += text("\[[time_stamp()]\] ripped the [limb.display_name] off of [target_human.name] ([target_human.ckey]) 2/2 progress") + target_human.attack_log += text("\[[time_stamp()]\] had their [limb.display_name] ripped off by [src.name] ([src.ckey]) 2/2 progress") + log_attack("[src.name] ([src.ckey]) ripped the [limb.display_name] off of [target_human.name] ([target_human.ckey]) 2/2 progress") + + limb.droplimb(0, 0, initial(name)) diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 74a5ca5e1bbb..c35f1b3fe618 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -65,7 +65,7 @@ return if(!ishuman(user)) //only humans can reinforce a grab. - if (isxeno(user)) + if(isxeno(user)) var/mob/living/carbon/xenomorph/xeno = user xeno.pull_power(grabbed_thing) return