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/cm_aliens/structures/fruit.dm b/code/modules/cm_aliens/structures/fruit.dm
index 94ae4827d769..8ea425e4d3c7 100644
--- a/code/modules/cm_aliens/structures/fruit.dm
+++ b/code/modules/cm_aliens/structures/fruit.dm
@@ -62,6 +62,20 @@
// Need to do it here because baseline initialize override the icon through config.
icon = 'icons/mob/xenos/fruits.dmi'
+/obj/effect/alien/resin/fruit/clicked(mob/user, list/mods)
+ var/mob/living/carbon/xenomorph/xeno = user
+ if(mods[ALT_CLICK])
+ if(!istype(xeno) || !Adjacent(xeno) || xeno != usr || xeno.is_mob_incapacitated() || xeno.body_position == LYING_DOWN)
+ return
+ xeno.pickup_fruit(src)
+ return TRUE
+ ..()
+
+/obj/effect/alien/resin/fruit/get_examine_text(mob/user)
+ . = ..()
+ if(isxeno(user) || isobserver(user))
+ . += "[SPAN_HELPFUL("Press Alt + Click to pick up fruit.")]"
+
/obj/effect/alien/resin/fruit/proc/on_weed_expire()
SIGNAL_HANDLER
qdel(src)
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..a34051702bcc 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.
@@ -403,6 +402,8 @@
wound_icon_holder = new(null, src)
vis_contents += wound_icon_holder
+ AddComponent(/datum/component/seethrough_mob)
+
///Handle transferring things from the old Xeno if we have one in the case of evolve, devolve etc.
AddComponent(/datum/component/deevolve_cooldown, old_xeno)
if(old_xeno)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
index f616d51916ea..22537e74a943 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
@@ -65,6 +65,7 @@
var/datum/effect_system/smoke_spread/xeno_acid/smoke
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm b/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
index 6f2b4820387c..f3837947db9f 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
@@ -54,6 +54,7 @@
organ_value = 1500
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
index b17bf5c17337..6cb50f052047 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
@@ -61,6 +61,7 @@
organ_value = 1000
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
index e4b135b5773f..9187e8efe02f 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
@@ -53,6 +53,7 @@
rebounds = FALSE // no more fucking pinball crooshers
organ_value = 3000
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
index 17d51bd063bf..947a6c93569a 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
@@ -42,6 +42,7 @@
organ_value = 1000
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Despoiler.dm b/code/modules/mob/living/carbon/xenomorph/castes/Despoiler.dm
index 08462667b1b6..196f28b45a97 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Despoiler.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Despoiler.dm
@@ -45,6 +45,7 @@
organ_value = 3000
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
index 8857404790c0..71ba37b28bf9 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
@@ -59,6 +59,7 @@
old_x = -12
xenonid_pixel_x = -8
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index fe6ec44f52ea..47a6bdadc24e 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -50,6 +50,7 @@
can_hivemind_speak = FALSE
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/onclick/xenohide,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm b/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
index 24300fc63e47..91bd485d7944 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
@@ -62,6 +62,7 @@
organ_value = 1500
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/King.dm b/code/modules/mob/living/carbon/xenomorph/castes/King.dm
index 325a4abec6da..641b1ccb88d1 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/King.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/King.dm
@@ -47,6 +47,7 @@
fire_immunity = FIRE_IMMUNITY_NO_DAMAGE
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
index d092d8e12e39..8e66372be65b 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
@@ -41,6 +41,7 @@
tier = 2
organ_value = 2000
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
index 621903578c1c..adc0a2b38921 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
@@ -54,6 +54,7 @@
organ_value = 3000
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
index 7a7d20ced49c..64b757c4a04b 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
@@ -55,6 +55,7 @@
small_explosives_stun = FALSE
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
index bc87c384ce4c..14c5986e1845 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
@@ -459,7 +459,6 @@
make_combat_effective()
AddComponent(/datum/component/footstep, 2 , 35, 11, 4, "alien_footstep_large")
- AddComponent(/datum/component/seethrough_mob)
if(hive.hivenumber == XENO_HIVE_NORMAL)
AddComponent(/datum/component/tacmap, has_drawing_tools=TRUE, minimap_flag=get_minimap_flag_for_faction(hive.hivenumber), has_update=TRUE, drawing=TRUE)
RegisterSignal(src, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(check_block))
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
index 3490dece6813..85472a71a63a 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
@@ -53,6 +53,7 @@
fire_immunity = FIRE_IMMUNITY_NO_DAMAGE|FIRE_IMMUNITY_XENO_FRENZY
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
index 94e5f3bc24fe..6d1f77feb688 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
@@ -53,6 +53,7 @@
mob_size = MOB_SIZE_XENO_SMALL
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm b/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
index 3d87e6d6932e..64b7e3247394 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
@@ -42,6 +42,7 @@
tier = 1
organ_value = 800
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
index 7ef2595cbc3a..4fe0425c410f 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
@@ -42,6 +42,7 @@
organ_value = 2000
tier = 2
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
index 5f54c29ed14e..50a2a8e1d05e 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
@@ -45,6 +45,7 @@
pull_speed = 2 // about what it was before, slightly faster
organ_value = 2000
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
@@ -181,90 +182,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/castes/lesser_drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
index 30230e9b6386..b3723f1825d8 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
@@ -59,6 +59,7 @@
gib_chance = 100
acid_blood_damage = 15
base_actions = list(
+ /datum/action/xeno_action/onclick/toggle_seethrough,
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
diff --git a/code/modules/mob/living/carbon/xenomorph/seethrough.dm b/code/modules/mob/living/carbon/xenomorph/seethrough.dm
index 290d5bbbec24..25ba2c17c8b3 100644
--- a/code/modules/mob/living/carbon/xenomorph/seethrough.dm
+++ b/code/modules/mob/living/carbon/xenomorph/seethrough.dm
@@ -72,6 +72,7 @@
animate(trickery_image, alpha = target_alpha, time = animation_time)
RegisterSignal(fool, COMSIG_MOB_LOGOUT, PROC_REF(on_client_disconnect))
+ RegisterSignal(fool, COMSIG_MOB_GHOSTIZE, PROC_REF(on_client_ghost))
///Remove the screen object and make us appear solid to ourselves again
/datum/component/seethrough_mob/proc/untrick_mob()
@@ -94,9 +95,17 @@
SIGNAL_HANDLER
var/mob/fool = parent
+ UnregisterSignal(fool, COMSIG_MOB_GHOSTIZE)
UnregisterSignal(fool, COMSIG_MOB_LOGOUT)
clear_image(trickery_image, fool.client)
+/datum/component/seethrough_mob/proc/on_client_ghost()
+ SIGNAL_HANDLER
+
+ var/mob/fool = parent
+ UnregisterSignal(fool, COMSIG_MOB_GHOSTIZE)
+ clear_image(trickery_image, fool.client)
+
/datum/component/seethrough_mob/proc/toggle_active()
is_active = !is_active
if(is_active)
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
index 7ff20c5807e3..df0a3d1a6950 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
@@ -173,3 +173,91 @@
// 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(!isxeno(src))
+ return
+
+ 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..097fba18a8a7 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
@@ -92,6 +92,16 @@
if(user.grab_level >= GRAB_AGGRESSIVE)
ADD_TRAIT(victim, TRAIT_FLOORED, CHOKEHOLD_TRAIT)
+/obj/item/grab/use_unique_action()
+ ..()
+ if(isxeno(usr))
+ unique_action(usr)
+
+/obj/item/grab/unique_action(mob/user)
+ if(isxeno(user))
+ var/mob/living/carbon/xenomorph/current_xeno = user
+ current_xeno.rip_limb()
+
/obj/item/grab/proc/progress_passive(mob/living/carbon/human/user, mob/living/victim)
if(SEND_SIGNAL(victim, COMSIG_MOB_AGGRESSIVELY_GRABBED, user) & COMSIG_MOB_AGGRESSIVE_GRAB_CANCEL)
to_chat(user, SPAN_WARNING("You can't grab [victim] aggressively!"))