diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm
index aab0560fe1..b9373e1818 100644
--- a/code/game/machinery/bioprinter.dm
+++ b/code/game/machinery/bioprinter.dm
@@ -1,84 +1,266 @@
-//These machines are mostly just here for debugging/spawning. Skeletons of the feature to come.
+// GENERIC PRINTER - DO NOT USE THIS OBJECT.
+// Flesh and robot printers are defined below this object.
-/obj/machinery/bioprinter
- name = "organ bioprinter"
- desc = "It's a machine that grows replacement organs."
+/obj/machinery/organ_printer
+ name = "organ printer"
+ desc = "It's a machine that prints organs."
icon = 'icons/obj/surgery.dmi'
+ icon_state = "bioprinter"
anchored = 1
density = 1
use_power = 1
idle_power_usage = 40
+ active_power_usage = 300
- icon_state = "bioprinter"
-
- var/prints_prosthetics
- var/stored_matter = 200
+ var/stored_matter = 0
+ var/max_stored_matter = 0
+ var/print_delay = 100
+ var/printing
var/loaded_dna //Blood sample for DNA hashing.
+
+ // These should be subtypes of /obj/item/organ
var/list/products = list(
- O_HEART = list(/obj/item/organ/internal/heart, 50),
- O_LUNGS = list(/obj/item/organ/internal/lungs, 40),
- O_KIDNEYS = list(/obj/item/organ/internal/kidneys,20),
- O_EYES = list(/obj/item/organ/internal/eyes, 30),
- O_LIVER = list(/obj/item/organ/internal/liver, 50)
+ "Heart" = list(/obj/item/organ/internal/heart, 25),
+ "Lungs" = list(/obj/item/organ/internal/lungs, 25),
+ "Kidneys" = list(/obj/item/organ/internal/kidneys,20),
+ "Eyes" = list(/obj/item/organ/internal/eyes, 20),
+ "Liver" = list(/obj/item/organ/internal/liver, 25),
+ "Arm, Left" = list(/obj/item/organ/external/arm, 65),
+ "Arm, Right" = list(/obj/item/organ/external/arm/right, 65),
+ "Leg, Left" = list(/obj/item/organ/external/leg, 65),
+ "Leg, Right" = list(/obj/item/organ/external/leg/right, 65),
+ "Foot, Left" = list(/obj/item/organ/external/foot, 40),
+ "Foot, Right" = list(/obj/item/organ/external/foot/right, 40),
+ "Hand, Left" = list(/obj/item/organ/external/hand, 40),
+ "Hand, Right" = list(/obj/item/organ/external/hand/right, 40)
)
-/obj/machinery/bioprinter/prosthetics
- name = "prosthetics fabricator"
- desc = "It's a machine that prints prosthetic organs."
- prints_prosthetics = 1
+/obj/machinery/organ_printer/attackby(var/obj/item/O, var/mob/user)
+ if(default_deconstruction_screwdriver(user, O))
+ updateUsrDialog()
+ return
+ if(default_deconstruction_crowbar(user, O))
+ return
+ if(default_part_replacement(user, O))
+ return
+ return ..()
-/obj/machinery/bioprinter/attack_hand(mob/user)
+/obj/machinery/organ_printer/update_icon()
+ overlays.Cut()
+ if(panel_open)
+ overlays += "bioprinter_panel_open"
+ if(printing)
+ overlays += "bioprinter_working"
- var/choice = input("What would you like to print?") as null|anything in products
- if(!choice)
+/obj/machinery/organ_printer/New()
+ ..()
+ component_parts = list()
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
+ RefreshParts()
+
+/obj/machinery/organ_printer/examine(var/mob/user)
+ . = ..()
+ to_chat(user, "It is loaded with [stored_matter]/[max_stored_matter] matter units.")
+
+/obj/machinery/organ_printer/RefreshParts()
+ print_delay = initial(print_delay)
+ max_stored_matter = 0
+ for(var/obj/item/weapon/stock_parts/matter_bin/bin in component_parts)
+ max_stored_matter += bin.rating * 100
+ for(var/obj/item/weapon/stock_parts/manipulator/manip in component_parts)
+ print_delay -= (manip.rating-1)*10
+ print_delay = max(0,print_delay)
+ . = ..()
+
+/obj/machinery/organ_printer/attack_hand(mob/user)
+
+ if(stat & (BROKEN|NOPOWER))
return
- if(stored_matter >= products[choice][2])
+ if(panel_open)
+ to_chat(user, "Close the panel first!")
+ return
- stored_matter -= products[choice][2]
- var/new_organ = products[choice][1]
- var/obj/item/organ/O = new new_organ(get_turf(src))
+ if(printing)
+ to_chat(user, "\The [src] is busy!")
+ return
- if(prints_prosthetics)
- O.robotize()
- else if(loaded_dna)
- visible_message("The printer injects the stored DNA into the biomass..")
- O.transplant_data = list()
- var/mob/living/carbon/C = loaded_dna["donor"]
- O.transplant_data["species"] = C.species.name
- O.transplant_data["blood_type"] = loaded_dna["blood_type"]
- O.transplant_data["blood_DNA"] = loaded_dna["blood_DNA"]
+ var/choice = input("What would you like to print?") as null|anything in products
- visible_message("The bioprinter spits out a new organ.")
+ if(!choice || printing || (stat & (BROKEN|NOPOWER)))
+ return
- else
- user << "There is not enough matter in the printer."
+ if(!can_print(choice))
+ return
-/obj/machinery/bioprinter/attackby(obj/item/weapon/W, mob/user)
+ stored_matter -= products[choice][2]
- // DNA sample from syringe.
- if(!prints_prosthetics && istype(W,/obj/item/weapon/reagent_containers/syringe))
+ use_power = 2
+ printing = 1
+ update_icon()
+
+ visible_message("\The [src] begins churning.")
+
+ sleep(print_delay)
+
+ use_power = 1
+ printing = 0
+ update_icon()
+
+ if(!choice || !src || (stat & (BROKEN|NOPOWER)))
+ return
+
+ print_organ(choice)
+
+/obj/machinery/organ_printer/proc/can_print(var/choice)
+ if(stored_matter < products[choice][2])
+ visible_message("\The [src] displays a warning: 'Not enough matter. [stored_matter] stored and [products[choice][2]] needed.'")
+ return 0
+
+ if(!loaded_dna || !loaded_dna["donor"])
+ visible_message("\The [src] displays a warning: 'No DNA saved. Insert a blood sample.'")
+ return 0
+
+ return 1
+
+/obj/machinery/organ_printer/proc/print_organ(var/choice)
+ var/new_organ = products[choice][1]
+ var/obj/item/organ/O = new new_organ(get_turf(src))
+ O.status |= ORGAN_CUT_AWAY
+ var/mob/living/carbon/C = loaded_dna["donor"]
+ O.set_dna(C.dna)
+
+ if(O.species)
+ // This is a very hacky way of doing of what organ/New() does if it has an owner
+ O.w_class = max(O.w_class + mob_size_difference(O.species.mob_size, MOB_MEDIUM), 1)
+
+ return O
+// END GENERIC PRINTER
+
+// CIRCUITS
+/obj/item/weapon/circuitboard/bioprinter
+ name = "bioprinter circuit"
+ build_path = /obj/machinery/organ_printer/flesh
+ board_type = new /datum/frame/frame_types/machine
+ origin_tech = list(TECH_DATA = 3, TECH_BIO = 3)
+ req_components = list(
+ /obj/item/stack/cable_coil = 2,
+ /obj/item/weapon/stock_parts/matter_bin = 2,
+ /obj/item/weapon/stock_parts/manipulator = 2)
+
+/obj/item/weapon/circuitboard/roboprinter
+ name = "roboprinter circuit"
+ build_path = /obj/machinery/organ_printer/robot
+ board_type = new /datum/frame/frame_types/machine
+ origin_tech = list(TECH_DATA = 3, TECH_BIO = 3)
+ req_components = list(
+ /obj/item/stack/cable_coil = 2,
+ /obj/item/weapon/stock_parts/matter_bin = 2,
+ /obj/item/weapon/stock_parts/manipulator = 2)
+
+// ROBOT ORGAN PRINTER
+// Still Requires DNA, /obj/machinery/pros_fab is better for limbs
+/obj/machinery/organ_printer/robot
+ name = "prosthetic organ fabricator"
+ desc = "It's a machine that prints prosthetic organs."
+ icon_state = "roboprinter"
+ circuit = /obj/item/weapon/circuitboard/roboprinter
+
+ var/matter_amount_per_sheet = 10
+ var/matter_type = DEFAULT_WALL_MATERIAL
+
+/obj/machinery/organ_printer/robot/full/New()
+ . = ..()
+ stored_matter = max_stored_matter
+
+/obj/machinery/organ_printer/robot/dismantle()
+ if(stored_matter >= matter_amount_per_sheet)
+ new /obj/item/stack/material/steel(get_turf(src), Floor(stored_matter/matter_amount_per_sheet))
+ return ..()
+
+/obj/machinery/organ_printer/robot/print_organ(var/choice)
+ var/obj/item/organ/O = ..()
+ O.robotize()
+ O.status |= ORGAN_CUT_AWAY // robotize() resets status to 0
+ playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
+ audible_message("\The [src] dings, then spits out \a [O].")
+ return O
+
+/obj/machinery/organ_printer/robot/attackby(var/obj/item/weapon/W, var/mob/user)
+ if(istype(W, /obj/item/stack/material) && W.get_material_name() == matter_type)
+ if((max_stored_matter-stored_matter) < matter_amount_per_sheet)
+ to_chat(user, "\The [src] is too full.")
+ return
+ var/obj/item/stack/S = W
+ var/space_left = max_stored_matter - stored_matter
+ var/sheets_to_take = min(S.amount, Floor(space_left/matter_amount_per_sheet))
+ if(sheets_to_take <= 0)
+ to_chat(user, "\The [src] is too full.")
+ return
+ stored_matter = min(max_stored_matter, stored_matter + (sheets_to_take*matter_amount_per_sheet))
+ to_chat(user, "\The [src] processes \the [W]. Levels of stored matter now: [stored_matter]")
+ S.use(sheets_to_take)
+ return
+ else if(istype(W,/obj/item/weapon/reagent_containers/syringe)) //TODO: Make this actuall empty the syringe
var/obj/item/weapon/reagent_containers/syringe/S = W
var/datum/reagent/blood/injected = locate() in S.reagents.reagent_list //Grab some blood
if(injected && injected.data)
loaded_dna = injected.data
- user << "You inject the blood sample into the bioprinter."
- return
- // Meat for biomass.
- if(!prints_prosthetics && istype(W, /obj/item/weapon/reagent_containers/food/snacks/meat))
- stored_matter += 50
- user.drop_item()
- user << "\The [src] processes \the [W]. Levels of stored biomass now: [stored_matter]"
- qdel(W)
- return
- // Steel for matter.
- if(prints_prosthetics && istype(W, /obj/item/stack/material) && W.get_material_name() == DEFAULT_WALL_MATERIAL)
- var/obj/item/stack/S = W
- stored_matter += S.amount * 10
- user.drop_item()
- user << "\The [src] processes \the [W]. Levels of stored matter now: [stored_matter]"
- qdel(W)
+ to_chat(user, "You scan the blood sample into the bioprinter.")
return
+ return ..()
+// END ROBOT ORGAN PRINTER
- return..()
\ No newline at end of file
+// FLESH ORGAN PRINTER
+/obj/machinery/organ_printer/flesh
+ name = "bioprinter"
+ desc = "It's a machine that prints replacement organs."
+ icon_state = "bioprinter"
+ circuit = /obj/item/weapon/circuitboard/bioprinter
+
+ var/amount_per_slab = 50
+
+/obj/machinery/organ_printer/flesh/full/New()
+ . = ..()
+ stored_matter = max_stored_matter
+
+/obj/machinery/organ_printer/flesh/dismantle()
+ var/turf/T = get_turf(src)
+ if(T)
+ while(stored_matter >= amount_per_slab)
+ stored_matter -= amount_per_slab
+ new /obj/item/weapon/reagent_containers/food/snacks/meat(T)
+ return ..()
+
+/obj/machinery/organ_printer/flesh/print_organ(var/choice)
+ var/obj/item/organ/O = ..()
+
+ playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
+ visible_message("\The [src] dings, then spits out \a [O].")
+ return O
+
+/obj/machinery/organ_printer/flesh/attackby(obj/item/weapon/W, mob/user)
+ // Load with matter for printing.
+ if(istype(W, /obj/item/weapon/reagent_containers/food/snacks/meat))
+ if((max_stored_matter - stored_matter) < amount_per_slab)
+ to_chat(user, "\The [src] is too full.")
+ return
+ stored_matter += amount_per_slab
+ user.drop_item()
+ to_chat(user, "\The [src] processes \the [W]. Levels of stored biomass now: [stored_matter]")
+ qdel(W)
+ return
+ // DNA sample from syringe.
+ else if(istype(W,/obj/item/weapon/reagent_containers/syringe)) //TODO: Make this actually empty the syringe
+ var/obj/item/weapon/reagent_containers/syringe/S = W
+ var/datum/reagent/blood/injected = locate() in S.reagents.reagent_list //Grab some blood
+ if(injected && injected.data)
+ loaded_dna = injected.data
+ to_chat(user, "You scan the blood sample into the bioprinter.")
+ return
+ return ..()
+// END FLESH ORGAN PRINTER
\ No newline at end of file
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index 02450c2c5c..cdf5893d3c 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -78,8 +78,9 @@ var/list/organ_cache = list()
/obj/item/organ/proc/set_dna(var/datum/dna/new_dna)
if(new_dna)
dna = new_dna.Clone()
- blood_DNA.Cut()
- blood_DNA[dna.unique_enzymes] = dna.b_type
+ if(blood_DNA)
+ blood_DNA.Cut()
+ blood_DNA[dna.unique_enzymes] = dna.b_type
/obj/item/organ/proc/die()
if(robotic >= ORGAN_ROBOT)
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index a933bc6a2f..15ef72276f 100644
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ