Files
CHOMPStation2/code/game/machinery/bioprinter.dm
2025-09-26 15:21:19 +02:00

410 lines
14 KiB
Plaintext

// GENERIC PRINTER - DO NOT USE THIS OBJECT.
// Flesh and robot printers are defined below this object.
/obj/machinery/organ_printer
name = "organ printer"
desc = "It's a machine that prints organs."
icon = 'icons/obj/surgery.dmi'
icon_state = "bioprinter"
anchored = TRUE
density = TRUE
use_power = USE_POWER_IDLE
idle_power_usage = 40
active_power_usage = 300
var/obj/item/reagent_containers/container = null // This is the beaker that holds all of the biomass
var/print_delay = 100
var/base_print_delay = 100 // For Adminbus reasons
var/printing
var/loaded_dna //Blood sample for DNA hashing.
var/malfunctioning = FALSE // May cause rejection, or the printing of some alien limb instead!
var/complex_organs = FALSE // Can it print more 'complex' organs?
var/anomalous_organs = FALSE // Can it print anomalous organs?
var/engineered_organs = FALSE // CHOMPedit - Can it print advanced engineered organs that any species can use (Abductor gameplay)
// These should be subtypes of /obj/item/organ
// Costs roughly 20u Phoron (1 sheet) per internal organ, limbs are 60u for limb and extremity
var/list/products = list(
"Heart" = list(/obj/item/organ/internal/heart, 20),
"Lungs" = list(/obj/item/organ/internal/lungs, 20),
"Kidneys" = list(/obj/item/organ/internal/kidneys,20),
"Eyes" = list(/obj/item/organ/internal/eyes, 20),
"Liver" = list(/obj/item/organ/internal/liver, 20),
"Spleen" = list(/obj/item/organ/internal/spleen, 20),
"Stomach" = list(/obj/item/organ/internal/stomach, 20),
"Intestine" = list(/obj/item/organ/internal/intestine, 20), //ChompAdd: this was missing.
"Arm, Left" = list(/obj/item/organ/external/arm, 40),
"Arm, Right" = list(/obj/item/organ/external/arm/right, 40),
"Leg, Left" = list(/obj/item/organ/external/leg, 40),
"Leg, Right" = list(/obj/item/organ/external/leg/right, 40),
"Foot, Left" = list(/obj/item/organ/external/foot, 20),
"Foot, Right" = list(/obj/item/organ/external/foot/right, 20),
"Hand, Left" = list(/obj/item/organ/external/hand, 20),
"Hand, Right" = list(/obj/item/organ/external/hand/right, 20),
"Organ Lattice" = list(/obj/item/organ/internal/malignant/engineered/lattice, 30) // CHOMPedit - Bioprinting engineered lattice organs
)
var/list/complex_products = list(
"Brain" = list(/obj/item/organ/internal/brain, 60),
"Larynx" = list(/obj/item/organ/internal/voicebox, 20),
"Head" = list(/obj/item/organ/external/head, 40)
)
var/list/anomalous_products = list(
"Lymphatic Complex" = list(/obj/item/organ/internal/immunehub, 120),
"Respiration Nexus" = list(/obj/item/organ/internal/lungs/replicant/mending, 80),
"Adrenal Valve Cluster" = list(/obj/item/organ/internal/heart/replicant/rage, 80)
)
// CHOMPadd begin - engineered organs
var/list/engineered_products = list(
"Phoroketic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/phoron, 90),
"Trioketic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/tricord, 90),
"Tramoketic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/tramadol, 90),
"Dylovetic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/dylovene, 90),
"Citometic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/citalopram, 90),
"Bicordic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/bicaridine, 90),
"Dermalic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/dermaline, 90),
"Kelovetic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/kelotane, 90),
"Dexalic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/dexalin, 90),
"Hypalic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/hyperzine, 90),
"Spaceacilic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/spaceacillin, 90),
"Inaprovic Gland" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/inaprovaline, 90),
"Euphorian" = list(/obj/item/organ/internal/malignant/engineered/chemorgan/bliss, 90)
)
// CHOMPadd end
/obj/machinery/organ_printer/attackby(var/obj/item/O, var/mob/user)
if(default_deconstruction_screwdriver(user, O))
return
if(default_deconstruction_crowbar(user, O))
return
if(default_part_replacement(user, O))
return
if(default_unfasten_wrench(user, O, 20))
return
return ..()
/obj/machinery/organ_printer/update_icon()
//VOREStation Edit
cut_overlays()
if(panel_open)
add_overlay("bioprinter_panel_open")
if(printing)
add_overlay("bioprinter_working")
//VOREStation Edit End
/obj/machinery/organ_printer/Initialize(mapload)
. = ..()
default_apply_parts()
AddElement(/datum/element/climbable)
/obj/machinery/organ_printer/examine(var/mob/user)
. = ..()
var/biomass = get_biomass_volume()
if(biomass)
. += span_notice("It is loaded with [biomass] units of biomass.")
else
. += span_notice("It is not loaded with any biomass.")
/obj/machinery/organ_printer/RefreshParts()
// Print Delay updating
print_delay = base_print_delay
var/manip_rating = 0
for(var/obj/item/stock_parts/manipulator/manip in component_parts)
manip_rating += manip.rating
print_delay -= (manip.rating-1)*10
print_delay = max(0,print_delay)
manip_rating = round(manip_rating / 2)
if(manip_rating >= 5)
malfunctioning = TRUE
else
malfunctioning = initial(malfunctioning)
if(manip_rating >= 3)
complex_organs = TRUE
if(manip_rating >= 4)
anomalous_organs = TRUE
if(manip_rating >= 5)
malfunctioning = TRUE
else
complex_organs = initial(complex_organs)
anomalous_organs = initial(anomalous_organs)
malfunctioning = initial(malfunctioning)
. = ..()
/obj/machinery/organ_printer/attack_hand(mob/user)
if(stat & (BROKEN|NOPOWER))
return
if(panel_open)
to_chat(user, span_warning("Close the panel first!"))
return
if(printing)
to_chat(user, span_notice("\The [src] is busy!"))
return
if(container)
var/response = tgui_alert(user, "What do you want to do?", "Bioprinter Menu", list("Print Limbs", "Cancel"))
if(response == "Print Limbs")
printing_menu(user)
else
to_chat(user, span_warning("\The [src] can't operate without a reagent reservoir!"))
/obj/machinery/organ_printer/proc/printing_menu(mob/user)
var/list/possible_list = list()
possible_list |= products
if(complex_organs)
possible_list |= complex_products
if(anomalous_organs)
possible_list |= anomalous_products
// CHOMPedit begin - engineered organs
if(engineered_organs)
possible_list |= engineered_products
// CHOMPedit end
var/choice = tgui_input_list(user, "What would you like to print?", "Print Choice", possible_list)
if(!choice || printing || (stat & (BROKEN|NOPOWER)))
return
if(!can_print(choice, possible_list[choice][2]))
return
container.reagents.remove_reagent(REAGENT_ID_BIOMASS, possible_list[choice][2])
update_use_power(USE_POWER_ACTIVE)
printing = 1
update_icon()
visible_message(span_infoplain(span_bold("\The [src]") + " begins churning."))
sleep(print_delay)
update_use_power(USE_POWER_IDLE)
printing = 0
update_icon()
if(!choice || !src || (stat & (BROKEN|NOPOWER)))
return
print_organ(possible_list[choice][1])
return
/obj/machinery/organ_printer/verb/eject_beaker()
set name = "Eject Beaker"
set category = "Object"
set src in oview(1)
if(usr.stat != 0)
return
add_fingerprint(usr)
remove_beaker()
return
// Does exactly what it says it does
// Returns 1 if it succeeds, 0 if it fails. Added in case someone wants to add messages to the user.
/obj/machinery/organ_printer/proc/remove_beaker()
if(container)
container.forceMove(get_turf(src))
container = null
return 1
return 0
// Checks for reagents, then reports how much biomass it has in it
/obj/machinery/organ_printer/proc/get_biomass_volume()
var/biomass_count = 0
if(container && container.reagents)
for(var/datum/reagent/R in container.reagents.reagent_list)
if(R.id == REAGENT_ID_BIOMASS)
biomass_count += R.volume
return biomass_count
/obj/machinery/organ_printer/proc/can_print(var/choice, var/masscount = 0)
var/biomass = get_biomass_volume()
if(biomass < masscount)
visible_message(span_infoplain(span_bold("\The [src]") + " displays a warning: 'Not enough biomass. [biomass] stored and [masscount] needed.'"))
return 0
if(!loaded_dna || !loaded_dna["donor"])
visible_message(span_info("\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 = choice
var/obj/item/organ/O = new new_organ(get_turf(src))
O.status |= ORGAN_CUT_AWAY
var/mob/living/carbon/human/C = loaded_dna["donor"]
O.set_dna(C.dna)
O.data.setup_from_species(C.species)
var/malfunctioned = FALSE
if(malfunctioning && prob(30)) // Alien Tech is a hell of a drug.
malfunctioned = TRUE
var/possible_species = list(SPECIES_HUMAN, SPECIES_VOX, SPECIES_SKRELL, SPECIES_ZADDAT, SPECIES_UNATHI, SPECIES_GOLEM, SPECIES_SHADOW)
var/new_species = pick(possible_species)
if(!GLOB.all_species[new_species])
new_species = SPECIES_HUMAN
O.data.setup_from_species(GLOB.all_species[new_species])
if(istype(O, /obj/item/organ/external) && !malfunctioned)
var/obj/item/organ/external/E = O
E.sync_colour_to_human(C)
O.pixel_x = rand(-6.0, 6)
O.pixel_y = rand(-6.0, 6)
if(O.data)
// 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.data.get_species_mob_size(), MOB_MEDIUM), 1)
return O
// END GENERIC PRINTER
// CIRCUITS
/obj/item/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/stock_parts/matter_bin = 2,
/obj/item/stock_parts/manipulator = 2)
// 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/circuitboard/bioprinter
/obj/machinery/organ_printer/flesh/full/Initialize(mapload)
. = ..()
container = new /obj/item/reagent_containers/glass/bottle/biomass(src)
/obj/machinery/organ_printer/flesh/dismantle()
var/turf/T = get_turf(src)
if(T)
if(container)
container.forceMove(T)
container = null
return ..()
/obj/machinery/organ_printer/flesh/print_organ(var/choice)
var/obj/item/organ/O = ..()
playsound(src, 'sound/machines/ding.ogg', 50, 1)
visible_message(span_info("\The [src] dings, then spits out \a [O]."))
return O
/obj/machinery/organ_printer/flesh/attackby(obj/item/W, mob/user)
// DNA sample from syringe.
if(istype(W,/obj/item/reagent_containers/syringe)) //TODO: Make this actually empty the syringe
var/obj/item/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.Copy()
S.reagents.remove_reagent(REAGENT_ID_BLOOD, injected.volume)
to_chat(user, span_info("You scan the blood sample into the bioprinter."))
return
else if(istype(W,/obj/item/reagent_containers/glass))
var/obj/item/reagent_containers/glass/G = W
if(container)
to_chat(user, span_warning("\The [src] already has a container loaded!"))
return
else if(do_after(user, 1 SECOND, target = src))
user.visible_message("[user] has loaded \the [G] into \the [src].", "You load \the [G] into \the [src].")
container = G
user.drop_item()
G.forceMove(src)
return
return ..()
// END FLESH ORGAN PRINTER
/* Roboprinter is made obsolete by the system already in place and mapped into Robotics
/obj/item/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/stock_parts/matter_bin = 2,
/obj/item/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/circuitboard/roboprinter
var/matter_amount_per_sheet = 10
var/matter_type = MAT_STEEL
/obj/machinery/organ_printer/robot/full/Initialize(mapload)
. = ..()
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, 1))
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, 'sound/machines/ding.ogg', 50, 1)
audible_message(span_info("\The [src] dings, then spits out \a [O]."))
return O
/obj/machinery/organ_printer/robot/attackby(var/obj/item/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, span_warning("\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, 1))
if(sheets_to_take <= 0)
to_chat(user, span_warning("\The [src] is too full."))
return
stored_matter = min(max_stored_matter, stored_matter + (sheets_to_take*matter_amount_per_sheet))
to_chat(user, span_info("\The [src] processes \the [W]. Levels of stored matter now: [stored_matter]"))
S.use(sheets_to_take)
return
else if(istype(W,/obj/item/reagent_containers/syringe)) //TODO: Make this actuall empty the syringe
var/obj/item/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, span_info("You scan the blood sample into the bioprinter."))
return
return ..()
// END ROBOT ORGAN PRINTER
*/