diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index b25d1e58d5f..8ada0c20b83 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -20,6 +20,7 @@ #define LAZYLEN(L) length(L) #define LAZYCLEARLIST(L) if(L) L.Cut() #define SANITIZE_LIST(L) ( islist(L) ? L : list() ) +#define reverseList(L) reverseRange(L.Copy()) // binary search sorted insert // IN: Object to be inserted diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm new file mode 100644 index 00000000000..1531fc0c791 --- /dev/null +++ b/code/game/machinery/harvester.dm @@ -0,0 +1,171 @@ +/obj/machinery/harvester + name = "organ harvester" + desc = "An advanced machine used for harvesting organs and limbs from the deceased." + density = TRUE + icon = 'icons/obj/machines/harvester.dmi' + icon_state = "harvester" + verb_say = "states" + state_open = FALSE + idle_power_usage = 50 + circuit = /obj/item/circuitboard/machine/harvester + light_color = LIGHT_COLOR_BLUE + var/interval = 20 + var/harvesting = FALSE + var/list/operation_order = list() //Order of wich we harvest limbs. + var/allow_clothing = FALSE + var/allow_living = FALSE + +/obj/machinery/harvester/Initialize() + . = ..() + if(prob(1)) + name = "auto-autopsy" + +/obj/machinery/harvester/RefreshParts() + interval = 0 + var/max_time = 40 + for(var/obj/item/stock_parts/micro_laser/L in component_parts) + max_time -= L.rating + interval = max(max_time,1) + +/obj/machinery/harvester/update_icon(warming_up) + if(warming_up) + icon_state = initial(icon_state)+"-charging" + return + if(state_open) + icon_state = initial(icon_state)+"-open" + else if(harvesting) + icon_state = initial(icon_state)+"-active" + else + icon_state = initial(icon_state) + +/obj/machinery/harvester/open_machine(drop = TRUE) + if(panel_open) + return + . = ..() + harvesting = FALSE + +/obj/machinery/harvester/attack_hand(mob/user) + if(state_open) + close_machine() + else if(!harvesting) + open_machine() + +/obj/machinery/harvester/AltClick(mob/user) + if(harvesting || !user || !isliving(user) || state_open) + return + if(can_harvest()) + start_harvest() + +/obj/machinery/harvester/proc/can_harvest() + if(!powered(EQUIP) || state_open || !occupant || !iscarbon(occupant)) + return + var/mob/living/carbon/C = occupant + if(!allow_clothing) + for(var/A in C.held_items + C.get_equipped_items()) + if(!isitem(A)) + continue + var/obj/item/I = A + if(!(I.item_flags & NODROP)) + say("Subject may not have abiotic items on.") + playsound(src, 'sound/machines/buzz-sigh.ogg', 30, 1) + return + if(!(MOB_ORGANIC in C.mob_biotypes)) + say("Subject is not organic.") + playsound(src, 'sound/machines/buzz-sigh.ogg', 30, 1) + return + if(!allow_living && !(C.stat == DEAD || C.has_trait(TRAIT_FAKEDEATH))) //I mean, the machines scanners arent advanced enough to tell you're alive + say("Subject is still alive.") + playsound(src, 'sound/machines/buzz-sigh.ogg', 30, 1) + return + return TRUE + +/obj/machinery/harvester/proc/start_harvest() + if(!occupant || !iscarbon(occupant)) + return + var/mob/living/carbon/C = occupant + operation_order = reverseList(C.bodyparts) //Chest and head are first in bodyparts, so we invert it to make them suffer more + harvesting = TRUE + visible_message("The [name] begins warming up!") + update_icon(TRUE) + addtimer(CALLBACK(src, .proc/harvest), interval) + +/obj/machinery/harvester/proc/harvest() + update_icon() + if(!harvesting || state_open || !powered(EQUIP) || !occupant || !iscarbon(occupant)) + return + playsound(src, 'sound/machines/juicer.ogg', 20, 1) + var/mob/living/carbon/C = occupant + if(!LAZYLEN(operation_order)) //The list is empty, so we're done here + end_harvesting() + return + var/turf/target + for(var/adir in list(EAST,NORTH,SOUTH,WEST)) + var/turf/T = get_step(src,adir) + if(!T) + continue + if(istype(T, /turf/closed)) + continue + target = T + break + if(!target) + target = get_turf(src) + for(var/obj/item/bodypart/BP in operation_order) //first we do non-essential limbs + BP.drop_limb() + C.emote("scream") + if(BP.body_zone != "chest") + BP.forceMove(target) //Move the limbs right next to it, except chest, that's a weird one + BP.drop_organs() + else + for(var/obj/item/organ/O in BP.dismember()) + O.forceMove(target) //Some organs, like chest ones, are different so we need to manually move them + operation_order.Remove(BP) + break + use_power(5000) + addtimer(CALLBACK(src, .proc/harvest), interval) + +/obj/machinery/harvester/proc/end_harvesting() + harvesting = FALSE + open_machine() + say("Subject has been succesfuly harvested.") + playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, 0) + +/obj/machinery/harvester/screwdriver_act(mob/living/user, obj/item/I) + if(!state_open && !occupant) + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) + return + +/obj/machinery/harvester/crowbar_act(mob/living/user, obj/item/I) + if(default_pry_open(I)) + return + if(default_deconstruction_crowbar(I)) + return + +/obj/machinery/harvester/default_pry_open(obj/item/I) //wew + . = !(state_open || panel_open || (flags_1 & NODECONSTRUCT_1)) && I.tool_behaviour == TOOL_CROWBAR //We removed is_operational() here + if(.) + I.play_tool_sound(src, 50) + visible_message("[usr] pries open \the [src].", "You pry open [src].") + open_machine() + +/obj/machinery/harvester/emag_act(mob/user) + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED + allow_living = TRUE + to_chat(user, "You overload [src]'s lifesign scanners.") + +/obj/machinery/harvester/container_resist(mob/living/user) + if(!harvesting) + visible_message("[occupant] emerges from [src]!", + "You climb out of [src]!") + open_machine() + else + to_chat(user,"[src] is active and can't be opened!") //rip + +/obj/machinery/harvester/Exited(atom/movable/user) + if (!state_open && user == occupant) + container_resist(user) + +/obj/machinery/harvester/relaymove(mob/user) + if (!state_open) + container_resist(user) diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 8d91b3855af..26c46c7843e 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -921,4 +921,9 @@ /obj/item/circuitboard/machine/circulator name = "Circulator/Heat Exchanger (Machine Board)" build_path = /obj/machinery/atmospherics/components/binary/circulator - req_components = list() \ No newline at end of file + req_components = list() + +/obj/item/circuitboard/machine/harvester + name = "Harvester (Machine Board)" + build_path = /obj/machinery/harvester + req_components = list(/obj/item/stock_parts/micro_laser = 4) \ No newline at end of file diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index 89f1eb85b99..9bcc38e559b 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -475,6 +475,14 @@ category = list("Medical Machinery") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/board/harvester + name = "Machine Design (Organ Harvester Board)" + desc = "The circuit board for an organ harvester." + id = "harvester" + build_path = /obj/item/circuitboard/machine/harvester + category = list("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + /datum/design/board/deepfryer name = "Machine Design (Deep Fryer)" desc = "The circuit board for a Deep Fryer." diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index d4cc38e050b..3a9c6d70212 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -27,7 +27,7 @@ display_name = "Advanced Biotechnology" description = "Advanced Biotechnology" prereq_ids = list("biotech") - design_ids = list("piercesyringe", "crewpinpointer", "smoke_machine", "plasmarefiller", "limbgrower", "defibrillator", "meta_beaker", "healthanalyzer_advanced") + design_ids = list("piercesyringe", "crewpinpointer", "smoke_machine", "plasmarefiller", "limbgrower", "defibrillator", "meta_beaker", "healthanalyzer_advanced","harvester") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 7af309f0d93..cf315f9f7a3 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -51,7 +51,7 @@ return FALSE if(C.has_trait(TRAIT_NODISMEMBER)) return FALSE - + . = list() var/organ_spilled = 0 var/turf/T = get_turf(C) C.add_splatter_floor(T) @@ -64,14 +64,15 @@ O.Remove(C) O.forceMove(T) organ_spilled = 1 + . += X if(cavity_item) cavity_item.forceMove(T) + . += cavity_item cavity_item = null organ_spilled = 1 if(organ_spilled) C.visible_message("[C]'s internal organs spill out onto the floor!") - return 1 diff --git a/icons/obj/machines/harvester.dmi b/icons/obj/machines/harvester.dmi new file mode 100644 index 00000000000..d6d9b01fc62 Binary files /dev/null and b/icons/obj/machines/harvester.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 19a805fe38e..790a65bc949 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -547,6 +547,7 @@ #include "code\game\machinery\flasher.dm" #include "code\game\machinery\gulag_item_reclaimer.dm" #include "code\game\machinery\gulag_teleporter.dm" +#include "code\game\machinery\harvester.dm" #include "code\game\machinery\hologram.dm" #include "code\game\machinery\igniter.dm" #include "code\game\machinery\iv_drip.dm"