From aba162f350ac526454855826f1eddcbc355d8f2c Mon Sep 17 00:00:00 2001 From: Time-Green Date: Fri, 20 Jul 2018 01:30:55 +0200 Subject: [PATCH] Medical Machinery: Organ Harvester (#39064) Adds an organ harvester. It's a machine. A human goes in, and the machine starts removing organs and limbs and ejects them right next to the machine. The process can be stopped at any time by either disabling power or prying it open. The machine does not work if the subject has clothes or appears alive. The health scan can be disabled by emagging, but they'll still need to be naked. It consists of 4 micro-manipulators, each tier making it faster, but it shouldn't ever really go below 2.4 seconds for every iteration, barring magic fuckery It's basically a slower acting gibber, but it preserves all limbs and organs. Useful in conjunction with either a limb grower, bounties, extra food, organ replacement or for something yet to come --- code/__HELPERS/_lists.dm | 1 + code/game/machinery/harvester.dm | 171 ++++++++++++++++++ .../circuitboards/machine_circuitboards.dm | 7 +- .../research/designs/machine_designs.dm | 8 + code/modules/research/techweb/all_nodes.dm | 2 +- .../surgery/bodyparts/dismemberment.dm | 5 +- icons/obj/machines/harvester.dmi | Bin 0 -> 2738 bytes tgstation.dme | 1 + 8 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 code/game/machinery/harvester.dm create mode 100644 icons/obj/machines/harvester.dmi 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 0000000000000000000000000000000000000000..d6d9b01fc6244362a745a4048dc79ccbd40015e8 GIT binary patch literal 2738 zcmV;j3QhHiP)G#gu=9kUpjlkQ$;O~XA%y+BCWun8&(Ai{}wz0_7Z*zoVWp`ha zuWxdM*4W?X=~eVNkCt8JQ6Xl@11x4F-w~28Rg*ehdb23HrlEp(V!zUTPA``A55~dRgpAQFl4+%3)U37Pe(v^$A zRY9>eB+7GSxjH7F6$)So1ax?cVWhKdh?curM}#30q!|ydAQPD#5sncEdkF-E4F-x1 z26G4lR|f;Gvch9jRihmdX9fad2?Q21K5A@! ztU4;kVoJ^+5S0rBi3bCJ2m^Bl0y+Tzo1(J}tFi!)mjRxoszoo?oQvbOtJ#-`r63Zl z9ucS&38N7Qkq85u2m_Q42VD^h*5~U+*WXv(<}K0K0>H=&b$XQ}7L6Pc;G~(&M=G;V zGq^1pnjaEn2m@LM0?n_x(`0UJYGOe>Lj+CP+9Mz0|z)>xM5eRJw1VC|! zYHfl3001{SOHH%6YHfdc00642yvuM_)su964hD$~1*MawfavhPL@SgZ4}Ap!czchR zArzw=4T2E}z`(%OV1UI}c)U<@wMlHRMQieMj-N18q%u{oQF66bc&I^Ur8ZfqI$oqb zW2HD<<7tMvJ6*OzW1cKdtv+JbS9_W!N6k=jT-=U%mB;)GssD6 z+gp5~H(j+gS;vfjN&o-=0d!JMQvg8b*k%9#0E&84Sad{Xb7OL8aCB*JZU6vyoKseC za&`CgQ*iP1Y8m>vb5z@G~ch(v-w_LWE&L=0h+0Eg?(7Y2)j z!oXQ9R>&A07e9_CDH1@1UVg)m%klB?^A8IL0UGS;kIey%z=%(Ze7?VcF9=MIN{LEMo0}}21p-9+ zFn`s&`Bz_aE#SKA0Sj&z6Hie$#%r`1T?T?4hCw2cL?)5+=o$J61Oid;TtRY5YEW>A*ky$fJ*~T~t)Gu6X^14I7LlB_+nk-SJ33ml$7KSO^=V%g`HEBLPFC zq%5+WM8}MvNf#CxoMNpXk2eBE~!BRPr2hk zAWoMU8&jBJ(4)y4Rs%Agu2n@Ol@DzJN-_$3;l_oTnPD*X%3*$URrNFTpWU)`aqc$2 z_U+qijL*5_T>^EHWK3*AAs|tcq1WrR20{XpriT&`_4N%6jWC8F5DL>WD@ixMx$5~B zUVLfI%SA;+ue|!&mK{4@f8$MeJP0Hdz7cQIX4ZIxVU%g7#B*wo=m4I;B$C@>vVx5@^~D8V3(H9y``>`0$=R){P3di{x_g z@e{y*{zXpJmyjMZmy~Sys<@=tLc8Np4O)?aPVd?sgC2G$h%8mA7)E`v!#fYjx`JmgpgvBnPE&au?+ZczO4d*s^XGuTTh+Zy0~WhUV?VVqZ-VC4bnh$4SLNS z1Jprdk*Y0BSs9})tz*WXB7`=Dxs4Hv8M8~^y9Ka8N7lg+EnfG1Gt}V+i^Uxe0q?xl*0(J%mzYPS7%{rYZ<~U zm$!}Zl`ulutdLkPlNB&lD}#$?F{;*}R%X=a2)#rQj0z@#o=lrH#OlFUx_C1z#KmLc zSwqU6pFCz9{yRU>Cv^rhn8E)9Y=4j8vH5ZRJ%-2T$Mp9&9)}6~`+vnx0PqLxr)_|a z&Q6=HbDTfG`-dq((exQRf&+jaWBh?v{$Wxee{lAUy{qHgIcIlIPj_!mkHga+Ozj`A z`K5IG>GSrku5;&Xj-DYnM*9Pdf4~dS?LVIF>gww2>$W?4dwU)I{rwIPe=xOw@J7Hn zVEd`B&t`Kt9rpeUNWjA%Ozj`M5IF4|Xg}ZQ00GF|e*xg|@CQ@-2d@M!{`|}N_71cN zTerh*v-NuTgGv0u;H68K2E7vK===5bMO(*!6CG058a(~MB>rLWw@bedLjR8!u$}*- zZ(yJs3SfuyXn6XAN&JIr1^c`aX#dmc>_zoO==}?xp8jAm|1dZ>i28poYVbD_aN1qF z({G0&d-{W^{ew3(=!6aGhU)g)yF2@#4&9#qU~2#1g#gYUVETWo0M;L1`hTnd&L80V sf2;t`AK?0b9I*ZX*Z*S$rn&$B2am`V@?&ymUH||907*qoM6N<$g1wRf<^TWy literal 0 HcmV?d00001 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"