diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm
index 3c97894faa3..3455f302935 100644
--- a/code/__HELPERS/trait_helpers.dm
+++ b/code/__HELPERS/trait_helpers.dm
@@ -265,6 +265,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
///An item that can be pointed at mobs, while on non-help intent.
#define TRAIT_CAN_POINT_WITH "can_point_with"
+///An organ that was inserted into a dead mob, that has not been revived yet
+#define TRAIT_ORGAN_INSERTED_WHILE_DEAD "organ_inserted_while_dead"
+
//
// common trait sources
#define TRAIT_GENERIC "generic"
diff --git a/code/datums/uplink_items/uplink_general.dm b/code/datums/uplink_items/uplink_general.dm
index ee1a2b44af6..162c2e18527 100644
--- a/code/datums/uplink_items/uplink_general.dm
+++ b/code/datums/uplink_items/uplink_general.dm
@@ -619,7 +619,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/storage/box/syndie_kit/teleporter
cost = 40
-
+/datum/uplink_item/device_tools/organ_extractor
+ name = "Organ Extractor"
+ desc = "A device that can remove organs or cybernetic implants from a target, and stores them inside. \
+ Stored organs can be implanted into the user, or into other targets. Synthesizes chemicals to keep the organs fresh."
+ reference = "OREX"
+ item = /obj/item/organ_extractor
+ cost = 20
//Space Suits and Hardsuits
/datum/uplink_item/suits
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index 6a612dba6c5..20be9c3c465 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -194,7 +194,8 @@
/obj/item/wrench/medical,
/obj/item/handheld_defibrillator,
/obj/item/reagent_containers/applicator,
- /obj/item/geiger_counter
+ /obj/item/geiger_counter,
+ /obj/item/organ_extractor
)
/obj/item/storage/belt/medical/surgery
diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm
index 80cc843d18c..5760a51ebaf 100644
--- a/code/modules/surgery/organs/organ.dm
+++ b/code/modules/surgery/organs/organ.dm
@@ -139,6 +139,8 @@
return TRUE
if(is_found_within(/obj/machinery/clonepod))
return TRUE
+ if(is_found_within(/obj/item/organ_extractor))
+ return TRUE
if(isturf(loc))
if(world.time - last_freezer_update_time > freezer_update_period)
// I don't want to loop through everything in the tile constantly, especially since it'll be a pile of organs
diff --git a/code/modules/surgery/organs/organ_extractor.dm b/code/modules/surgery/organs/organ_extractor.dm
new file mode 100644
index 00000000000..b02ff3633bb
--- /dev/null
+++ b/code/modules/surgery/organs/organ_extractor.dm
@@ -0,0 +1,156 @@
+/obj/item/organ_extractor
+ name = "organ extractor"
+ desc = "A device that can remove organs from a target, and store them inside. Stored organs can be implanted into the user. Synthesizes chemicals to keep the organ fresh."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "organ_extractor"
+ item_state = ""
+ w_class = WEIGHT_CLASS_SMALL
+ origin_tech = "biotech=6;materials=5;syndicate=2"
+ var/obj/item/organ/internal/storedorgan
+
+/obj/item/organ_extractor/examine(mob/user)
+ . = ..()
+ if(storedorgan)
+ . += "It has [storedorgan] floating around inside the jar!"
+ . += "You can Screwdriver [src] to try to adjust [storedorgan]'s configuration."
+ . += "You can Wrench [src] to eject [storedorgan]!"
+
+/obj/item/organ_extractor/screwdriver_act(mob/living/user, obj/item/I)
+ if(storedorgan)
+ return storedorgan.screwdriver_act(user, I)
+
+/obj/item/organ_extractor/wrench_act(mob/living/user, obj/item/I)
+ if(storedorgan)
+ to_chat(user, "You unwrench the jar, and [storedorgan] falls onto the floor!")
+ storedorgan.forceMove(get_turf(user))
+ storedorgan = null
+ playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
+ overlays.Cut()
+ return TRUE
+
+/obj/item/organ_extractor/emag_act(mob/user)
+ if(storedorgan)
+ storedorgan.emag_act(user)
+
+/obj/item/organ_extractor/emp_act(severity)
+ if(storedorgan)
+ storedorgan.emp_act(severity)
+
+/obj/item/organ_extractor/attack_self(mob/user)
+ insert_organ(user, user)
+
+/obj/item/organ_extractor/attack(mob/living/M, mob/living/user, def_zone)
+ if(in_use)
+ to_chat(user, "[src] is already busy!")
+ return
+ if(!iscarbon(M))
+ to_chat(user, "ERROR: [M] has no organs to harvest!")
+ return
+ var/mob/living/carbon/C = M
+ if(!length(C.client_mobs_in_contents)) //Basically, we don't want someone putting organs in monkeys then extracting from it. Has to be someone who had a client in the past
+ to_chat(user, "ERROR: [C] has no soul trace to assist in targeting the drill bit!")
+ return
+ if(!IS_HORIZONTAL(C) && C.stat == CONSCIOUS)
+ to_chat(user, "ERROR: [C] is not restrained, and may move during the operation! Correction required.")
+ return
+ if(storedorgan)
+ to_chat(user, "NOTICE: Internal organ deteced. Beginning insertion procedure!")
+ insert_organ(user, C)
+ return
+ in_use = TRUE
+ var/obj/item/chosen_organ = tgui_input_list(user, "Please select an organ for removal", "Organ Selection", C.internal_organs)
+ if(!chosen_organ || !user.Adjacent(C))
+ in_use = FALSE
+ return
+ if(!istype(chosen_organ, /obj/item/organ/internal)) //Saftey first
+ to_chat(user, "ERROR: [chosen_organ] is not valid for removal for unknown reasons!")
+ in_use = FALSE
+ return
+ if(istype(chosen_organ, /obj/item/organ/internal/brain/mmi_holder)) //This breaks shit
+ to_chat(user, "ERROR: [chosen_organ] is too big for the holding tank and would damage [src] too much!")
+ in_use = FALSE
+ return
+ if(HAS_TRAIT(chosen_organ, TRAIT_ORGAN_INSERTED_WHILE_DEAD))
+ to_chat(user, "ERROR: [chosen_organ] was inserted when [C] was dead, and has no soul trace to lock onto!")
+ in_use = FALSE
+ return
+ var/obj/item/organ/internal/internal_organ = chosen_organ
+ var/drilled_organ = internal_organ.parent_organ
+ user.visible_message("[user] activates [src] and begins to drill into [C]!", "You level the extractor at [M] and hold down the trigger.")
+ to_chat(C, "You feel a lot of pain as [user] drills into your [drilled_organ]!")
+ playsound(get_turf(user), 'sound/weapons/circsawhit.ogg', 75, TRUE)
+ C.apply_damage(15, BRUTE, drilled_organ)
+ if(!do_after_once(user, 12 SECONDS, target = C))// Slightly longer than stamina crit, at least cuff and buckle them to a pipe or something
+ to_chat(user, "ERROR: Process interrupted!")
+ in_use = FALSE
+ return
+ if(!internal_organ || !istype(internal_organ) || !(internal_organ.owner == C)) //Organ got deleted / moved somewhere else?
+ to_chat(user, "ERROR: unable to find the desired organ!")
+ in_use = FALSE
+ return
+ user.visible_message("[user] removes [internal_organ] from [C]!", "You remove [internal_organ] from [C] as it gets sucked into [src]'s internal container!")
+ playsound(get_turf(user), 'sound/weapons/circsawhit.ogg', 75, TRUE)
+ C.apply_damage(10, BRUTE, drilled_organ)
+ internal_organ.remove(C)
+ internal_organ.forceMove(src)
+ storedorgan = internal_organ
+ storedorgan.rejuvenate() //Organ gets dumped into the internal mito tank, heals it up. And nanites for robotic organs, I guess.
+ in_use = FALSE
+ var/organ_x = internal_organ.pixel_x
+ var/organ_y = internal_organ.pixel_y
+ internal_organ.pixel_x = 2
+ internal_organ.pixel_y = -2
+ var/image/img = image("icon" = internal_organ, "layer" = FLOAT_LAYER)
+ var/matrix/MA = matrix(transform)
+ MA.Scale(0.66, 0.66)
+ img.transform = MA
+ img.plane = FLOAT_PLANE
+ overlays += img
+ internal_organ.pixel_x = organ_x
+ internal_organ.pixel_y = organ_y
+ overlays += "organ_extractor_2" //should look nicer for transparent stuff.
+
+/obj/item/organ_extractor/proc/insert_organ(mob/user, mob/our_target)
+ if(!storedorgan)
+ to_chat(user, "[src] currently has no organ stored.")
+ return
+ if(in_use)
+ to_chat(user, "[src] is already busy!")
+ return
+ var/user_is_target = FALSE
+ if(user == our_target)
+ user_is_target = TRUE
+ if(istype(storedorgan, /obj/item/organ/internal/brain) && user_is_target)
+ to_chat(user, "It would be really stupid to replace your brain with another one.")
+ return
+ if(!iscarbon(our_target))
+ return
+ var/mob/living/carbon/C = our_target
+ in_use = TRUE
+ user.visible_message("[user] activates [src] and begins to drill into [C]!", "You level the extractor at [user_is_target ? "yourself" : C] and hold down the trigger.")
+ var/drilled_organ = storedorgan.parent_organ
+ C.apply_damage(5, BRUTE, drilled_organ)
+ playsound(get_turf(C), 'sound/weapons/circsawhit.ogg', 50, TRUE)
+ if(user_is_target)
+ if(!do_after_once(C, 7 SECONDS, target = C))
+ to_chat(user, "ERROR: Process interrupted!")
+ in_use = FALSE
+ return
+ else
+ if(!do_after_once(C, 12 SECONDS, target = C))
+ to_chat(user, "ERROR: Process interrupted!")
+ in_use = FALSE
+ return
+ C.apply_damage(10, BRUTE, drilled_organ)
+ var/obj/item/organ/internal/replaced = C.get_organ_slot(storedorgan.slot)
+ if(replaced) //Lets not destroy someones brain fully by putting someone elses brain in that slot.
+ replaced.remove(C)
+ replaced.forceMove(get_turf(src))
+ storedorgan.insert(C)
+ playsound(get_turf(C), 'sound/weapons/circsawhit.ogg', 50, TRUE)
+ storedorgan = null
+ in_use = FALSE
+ overlays.Cut()
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ H.set_heartattack(FALSE) //Otherwise you die if you try to do an organic heart, very funny, very bad
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index 9f624bc55c5..988fb825dfa 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -71,6 +71,10 @@
if(vital)
M.update_stat("Vital organ inserted")
STOP_PROCESSING(SSobj, src)
+ if(owner.stat == DEAD)
+ ADD_TRAIT(src, TRAIT_ORGAN_INSERTED_WHILE_DEAD, "[UID()]")
+ RegisterSignal(owner, COMSIG_LIVING_DEFIBBED, PROC_REF(on_revival))
+
// Removes the given organ from its owner.
// Returns the removed object, which is usually just itself
@@ -79,6 +83,9 @@
if(!owner)
stack_trace("\'remove\' called on [src] without an owner! Mob: [M], [atom_loc_line(M)]")
SEND_SIGNAL(owner, COMSIG_CARBON_LOSE_ORGAN)
+ REMOVE_TRAIT(src, TRAIT_ORGAN_INSERTED_WHILE_DEAD, "[UID()]")
+ UnregisterSignal(owner, COMSIG_LIVING_DEFIBBED)
+
owner = null
if(M)
M.internal_organs -= src
@@ -395,3 +402,8 @@
if(germ_level >= INFECTION_LEVEL_TWO)
if(prob(3)) //about once every 30 seconds
receive_damage(1, silent = prob(30))
+
+/obj/item/organ/internal/proc/on_revival() //The goal of this proc / trait is to prevent one implanting organs in a corpse, in order to remove them with the organ extractor. Has to be legitimently implanted, or on just a living person, which has risk
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(src, TRAIT_ORGAN_INSERTED_WHILE_DEAD, "[UID()]")
+ UnregisterSignal(owner, COMSIG_LIVING_DEFIBBED)
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index 492a2dbc999..0aec30495a1 100644
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 768f714573a..3fbe62c257b 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -2757,6 +2757,7 @@
#include "code\modules\surgery\organs\mmi_holder.dm"
#include "code\modules\surgery\organs\organ.dm"
#include "code\modules\surgery\organs\organ_external.dm"
+#include "code\modules\surgery\organs\organ_extractor.dm"
#include "code\modules\surgery\organs\organ_helpers.dm"
#include "code\modules\surgery\organs\organ_icon.dm"
#include "code\modules\surgery\organs\organ_internal.dm"