//Cloning revival method. //The pod handles the actual cloning while the computer manages the clone profiles //Potential replacement for genetics revives or something I dunno (?) #define CLONE_BIOMASS 150 #define BIOMASS_BASE_AMOUNT 50 // How much biomass a BIOMASSABLE item gives the cloning pod // Not a comprehensive list: Further PRs should add appropriate items here. // Meat as usual, monstermeat covers goliath, xeno, spider, bear meat GLOBAL_LIST_INIT(cloner_biomass_items, list(\ /obj/item/reagent_containers/food/snacks/meat,\ /obj/item/reagent_containers/food/snacks/monstermeat, /obj/item/reagent_containers/food/snacks/carpmeat, /obj/item/reagent_containers/food/snacks/salmonmeat, /obj/item/reagent_containers/food/snacks/catfishmeat, /obj/item/reagent_containers/food/snacks/tofurkey)) #define MINIMUM_HEAL_LEVEL 40 #define CLONE_INITIAL_DAMAGE 190 #define BRAIN_INITIAL_DAMAGE 90 // our minds are too feeble for 190 /obj/machinery/clonepod anchored = TRUE name = "cloning pod" desc = "An electronically-lockable pod for growing organic tissue." density = TRUE icon = 'icons/obj/cloning.dmi' icon_state = "pod_idle" req_access = list(ACCESS_MEDICAL) //For premature unlocking. var/mob/living/carbon/human/occupant var/heal_level //The clone is released once its health reaches this level. var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. var/mess = FALSE //Need to clean out it if it's full of exploded clone. var/attempting = FALSE //One clone attempt at a time thanks var/biomass = 0 var/speed_coeff var/efficiency var/datum/mind/clonemind var/grab_ghost_when = CLONER_MATURE_CLONE var/obj/item/radio/Radio var/radio_announce = TRUE var/obj/effect/countdown/clonepod/countdown var/list/brine_types = list("corazone", "perfluorodecalin", "epinephrine", "salglu_solution") //stops heart attacks, heart failure, shock, and keeps their O2 levels normal var/list/missing_organs = list() var/organs_number = 0 light_color = LIGHT_COLOR_PURE_GREEN /obj/machinery/clonepod/power_change() ..() //we don't check return here because we also care about the BROKEN flag if(!(stat & (BROKEN|NOPOWER))) set_light(2) else set_light(0) /obj/machinery/clonepod/biomass biomass = CLONE_BIOMASS /obj/machinery/clonepod/Initialize(mapload) . = ..() countdown = new(src) Radio = new /obj/item/radio(src) Radio.listening = FALSE Radio.config(list("Medical" = 0)) Radio.follow_target = src component_parts = list() component_parts += new /obj/item/circuitboard/clonepod(null) component_parts += new /obj/item/stock_parts/scanning_module(null) component_parts += new /obj/item/stock_parts/scanning_module(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stack/sheet/glass(null) component_parts += new /obj/item/stack/cable_coil(null, 1) component_parts += new /obj/item/stack/cable_coil(null, 1) RefreshParts() update_icon() /obj/machinery/clonepod/upgraded/Initialize(mapload) . = ..() component_parts = list() component_parts += new /obj/item/circuitboard/clonepod(null) component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) component_parts += new /obj/item/stock_parts/manipulator/pico(null) component_parts += new /obj/item/stock_parts/manipulator/pico(null) component_parts += new /obj/item/stack/sheet/glass(null) component_parts += new /obj/item/stack/cable_coil(null, 1) component_parts += new /obj/item/stack/cable_coil(null, 1) biomass = CLONE_BIOMASS RefreshParts() /obj/machinery/clonepod/Destroy() if(connected) connected.pods -= src connected = null if(clonemind) UnregisterSignal(clonemind.current, COMSIG_LIVING_REVIVE) UnregisterSignal(clonemind, COMSIG_MIND_TRANSER_TO) QDEL_NULL(Radio) QDEL_NULL(countdown) QDEL_LIST_CONTENTS(missing_organs) return ..() /obj/machinery/clonepod/RefreshParts() speed_coeff = 0 efficiency = 0 for(var/obj/item/stock_parts/scanning_module/S in component_parts) efficiency += S.rating for(var/obj/item/stock_parts/manipulator/P in component_parts) speed_coeff += P.rating heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) //The return of data disks?? Just for transferring between genetics machine/cloning machine. //TO-DO: Make the genetics machine accept them. /obj/item/disk/data name = "Genetics Data Disk" icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. var/datum/dna2/record/buf = null var/read_only = FALSE //Well,it's still a floppy disk /obj/item/disk/data/proc/initialize() buf = new buf.dna=new /obj/item/disk/data/Destroy() QDEL_NULL(buf) return ..() /obj/item/disk/data/demo name = "data disk - 'God Emperor of Mankind'" read_only = TRUE /obj/item/disk/data/demo/New() ..() initialize() buf.types=DNA2_BUF_UE|DNA2_BUF_UI //data = "066000033000000000AF00330660FF4DB002690" //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff buf.dna.real_name="God Emperor of Mankind" buf.dna.unique_enzymes = md5(buf.dna.real_name) buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0xAF0,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x033,0x066,0x0FF,0x4DB,0x002,0x690,0x000,0x000,0x000,0x328,0x045,0x5FC,0x053,0x035,0x035,0x035) //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff if(buf.dna.UI.len != DNA_UI_LENGTH) //If there's a disparity b/w the dna UI string lengths, 0-fill the extra blocks in this UI. for(var/i in buf.dna.UI.len to DNA_UI_LENGTH) buf.dna.UI += 0x000 buf.dna.ResetSE() buf.dna.UpdateUI() /obj/item/disk/data/monkey name = "data disk - 'Mr. Muggles'" read_only = 1 /obj/item/disk/data/monkey/New() ..() initialize() buf.types=DNA2_BUF_SE var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) new_SE += rand(1,1024) buf.dna.SE=new_SE buf.dna.SetSEValueRange(GLOB.monkeyblock,0xDAC, 0xFFF) //Disk stuff. /obj/item/disk/data/New() ..() var/diskcolor = pick(0,1,2) icon_state = "datadisk[diskcolor]" /obj/item/disk/data/attack_self(mob/user as mob) read_only = !read_only to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") /obj/item/disk/data/examine(mob/user) . = ..() . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." //Clonepod /obj/machinery/clonepod/examine(mob/user) . = ..() if(mess) . += "It's filled with blood and viscera. You swear you can see it moving..." if(HAS_TRAIT(src, TRAIT_CMAGGED)) . += "Yellow ooze is dripping out of the synthmeat storage chamber..." if(!occupant || stat & (NOPOWER|BROKEN)) return if(occupant && occupant.stat != DEAD) . += "Current clone cycle is [round(get_completion())]% complete." /obj/machinery/clonepod/return_air() //non-reactive air RETURN_TYPE(/datum/gas_mixture) var/datum/gas_mixture/GM = new GM.nitrogen = MOLES_O2STANDARD + MOLES_N2STANDARD GM.temperature = T20C return GM /obj/machinery/clonepod/proc/get_completion() . = (100 * ((occupant.health + 100) / (heal_level + 100))) /obj/machinery/clonepod/attack_ai(mob/user) return examine(user) //Radio Announcement /obj/machinery/clonepod/proc/announce_radio_message(message) if(radio_announce) Radio.autosay(message, name, "Medical", list(z)) //Start growing a human clone in the pod! /obj/machinery/clonepod/proc/growclone(datum/dna2/record/R) if(mess || attempting || panel_open || stat & (NOPOWER|BROKEN)) return 0 clonemind = locate(R.mind) if(!istype(clonemind)) //not a mind return 0 if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body return 0 if(clonemind.active) //somebody is using that mind if(ckey(clonemind.key) != R.ckey ) return 0 if(clonemind.suicided) // and stay out! malfunction(go_easy = 0) return -1 // Flush the record else // get_ghost() will fail if they're unable to reenter their body var/mob/dead/observer/G = clonemind.get_ghost() if(!G) return 0 if(biomass >= CLONE_BIOMASS) biomass -= CLONE_BIOMASS else return 0 attempting = TRUE //One at a time!! countdown.start() if(!R.dna) R.dna = new /datum/dna() var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) H.set_species(R.dna.species.type) occupant = H if(!R.dna.real_name) //to prevent null names R.dna.real_name = H.real_name else H.real_name = R.dna.real_name H.dna = R.dna.Clone() for(var/datum/language/L in R.languages) H.add_language(L.name) domutcheck(H, MUTCHK_FORCED) //Ensures species that get powers by the species proc handle_dna keep them if(efficiency > 2 && efficiency < 5 && prob(25)) randmutb(H) if(efficiency > 5 && prob(20)) randmutg(H) if(efficiency < 3 && prob(50)) randmutb(H) H.dna.UpdateSE() H.dna.UpdateUI() H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it H.UpdateAppearance() check_brine() //Get the clone body ready maim_clone(H) H.Paralyse(8 SECONDS) if(grab_ghost_when == CLONER_FRESH_CLONE) clonemind.transfer_to(H) H.ckey = R.ckey update_clone_antag(H) //Since the body's got the mind, update their antag stuff right now. Otherwise, wait until they get kicked out (as per the CLONER_MATURE_CLONE business) to do it. var/message message += "Consciousness slowly creeps over you as your body regenerates.
" message += "So this is what cloning feels like?" to_chat(H, "[message]") else if(grab_ghost_when == CLONER_MATURE_CLONE) to_chat(clonemind.current, "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") // Set up a soul link with the dead body to catch a revival RegisterSignal(clonemind.current, COMSIG_LIVING_REVIVE, PROC_REF(occupant_got_revived)) RegisterSignal(clonemind, COMSIG_MIND_TRANSER_TO, PROC_REF(occupant_got_revived)) SSblackbox.record_feedback("tally", "players_revived", 1, "cloned") update_icon() H.suiciding = FALSE attempting = FALSE return 1 //Grow clones to maturity then kick them out. FREELOADERS /obj/machinery/clonepod/process() var/show_message = FALSE for(var/obj/item/item in range(1, src)) if(is_type_in_list(item, GLOB.cloner_biomass_items)) qdel(item) biomass += BIOMASS_BASE_AMOUNT show_message = TRUE if(show_message) visible_message("[src] sucks in and processes the nearby biomass.") if(stat & NOPOWER) //Autoeject if power is lost if(occupant) go_out() connected_message("Clone Ejected: Loss of power.") else if((occupant) && (occupant.loc == src)) if((occupant.stat == DEAD) || (occupant.suiciding)) //Autoeject corpses and suiciding dudes. announce_radio_message("The cloning of [occupant] has been aborted due to unrecoverable tissue failure.") go_out() connected_message("Clone Rejected: Deceased.") else if(occupant.cloneloss > (100 - heal_level)) occupant.Paralyse(8 SECONDS) //Slowly get that clone healed and finished. occupant.adjustCloneLoss(-((speed_coeff/2))) // For human species that lack non-vital parts for some weird reason if(organs_number) var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss() progress += (100 - MINIMUM_HEAL_LEVEL) var/milestone = CLONE_INITIAL_DAMAGE / organs_number // Doing this as a #define so that the value can change when evaluated multiple times #define INSTALLED (organs_number - length(missing_organs)) while((progress / milestone) > INSTALLED && length(missing_organs)) var/obj/item/organ/I = pick_n_take(missing_organs) I.safe_replace(occupant) #undef INSTALLED //Premature clones may have brain damage. occupant.adjustBrainLoss(-((speed_coeff/20)*efficiency)) check_brine() //Also heal some oxyloss ourselves just in case!! occupant.adjustOxyLoss(-10) use_power(7500) //This might need tweaking. else if((occupant.cloneloss <= (100 - heal_level))) connected_message("Cloning Process Complete.") announce_radio_message("The cloning cycle of [occupant] is complete.") go_out() else if((!occupant) || (occupant.loc != src)) occupant = null update_icon() use_power(200) //Let's unlock this early I guess. Might be too early, needs tweaking. /obj/machinery/clonepod/attackby(obj/item/I, mob/user, params) if(exchange_parts(user, I)) return if(I.GetID()) if(!check_access(I)) to_chat(user, "Access Denied.") return if(!(occupant || mess)) to_chat(user, "Error: Pod has no occupant.") return else connected_message("Authorized Ejection") announce_radio_message("An authorized ejection of [(occupant) ? occupant.real_name : "the malfunctioning pod"] has occurred") to_chat(user, "You force an emergency ejection.") go_out() // A user can feed in biomass sources manually. else if(is_type_in_list(I, GLOB.cloner_biomass_items)) if(user.drop_item()) to_chat(user, "[src] processes [I].") biomass += BIOMASS_BASE_AMOUNT qdel(I) else return ..() /obj/machinery/clonepod/crowbar_act(mob/user, obj/item/I) . = TRUE default_deconstruction_crowbar(user, I) /obj/machinery/clonepod/multitool_act(mob/user, obj/item/I) . = TRUE if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return if(!I.multitool_check_buffer(user)) return var/obj/item/multitool/M = I M.set_multitool_buffer(user, src) /obj/machinery/clonepod/screwdriver_act(mob/user, obj/item/I) . = TRUE // These icon states don't really matter since we need to call update_icon() to handle panel open/closed overlays anyway. default_deconstruction_screwdriver(user, null, null, I) update_icon() /obj/machinery/clonepod/wrench_act(mob/user, obj/item/I) . = TRUE if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return if(occupant) to_chat(user, "Can not do that while [src] is in use.") return if(anchored) WRENCH_UNANCHOR_MESSAGE anchored = FALSE connected.pods -= src connected = null else WRENCH_ANCHOR_MESSAGE anchored = TRUE /obj/machinery/clonepod/emag_act(user) malfunction() /obj/machinery/clonepod/cmag_act(mob/user) if(HAS_TRAIT(src, TRAIT_CMAGGED)) return playsound(src, "sparks", 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) to_chat(user, "A droplet of bananium ooze seeps into the synthmeat storage chamber...") ADD_TRAIT(src, TRAIT_CMAGGED, CLOWN_EMAG) /obj/machinery/clonepod/proc/update_clone_antag(mob/living/carbon/human/H) // Check to see if the clone's mind is an antagonist of any kind and handle them accordingly to make sure they get their spells, HUD/whatever else back. if(H.mind in SSticker.mode.syndicates) SSticker.mode.update_synd_icons_added() if(H.mind in SSticker.mode.cult) SSticker.mode.update_cult_icons_added(H.mind) // Adds the cult antag hud SSticker.mode.add_cult_actions(H.mind) // And all the actions if(SSticker.mode.cult_risen) SSticker.mode.rise(H) if(SSticker.mode.cult_ascendant) SSticker.mode.ascend(H) //Put messages in the connected computer's temp var for display. /obj/machinery/clonepod/proc/connected_message(message) if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) return FALSE if(!message) return FALSE connected.temp = "[name] : [message]" connected.updateUsrDialog() return TRUE /obj/machinery/clonepod/proc/go_out() countdown.stop() var/turf/T = get_turf(src) if(mess) //Clean that mess and dump those gibs! for(var/i in missing_organs) var/obj/I = i I.forceMove(T) missing_organs.Cut() mess = FALSE new /obj/effect/gibspawner/generic(get_turf(src), occupant) playsound(loc, 'sound/effects/splat.ogg', 50, 1) update_icon() return if(!occupant) return if(grab_ghost_when == CLONER_MATURE_CLONE) UnregisterSignal(clonemind.current, COMSIG_LIVING_REVIVE) UnregisterSignal(clonemind, COMSIG_MIND_TRANSER_TO) clonemind.transfer_to(occupant) occupant.grab_ghost() update_clone_antag(occupant) to_chat(occupant, "You remember nothing from the time that you were dead!") to_chat(occupant, "There is a bright flash!
\ You feel like a new being.
") if(HAS_TRAIT(src, TRAIT_CMAGGED)) playsound(loc, 'sound/items/bikehorn.ogg', 50, 1) occupant.dna.SetSEState(GLOB.clumsyblock, TRUE, FALSE) occupant.dna.SetSEState(GLOB.comicblock, TRUE, FALSE) singlemutcheck(occupant, GLOB.clumsyblock, MUTCHK_FORCED) singlemutcheck(occupant, GLOB.comicblock, MUTCHK_FORCED) occupant.dna.default_blocks.Add(GLOB.clumsyblock) //Until Genetics fixes you, this is your life now occupant.dna.default_blocks.Add(GLOB.comicblock) occupant.flash_eyes(visual = 1) clonemind = null QDEL_LIST_CONTENTS(missing_organs) occupant.SetLoseBreath(0) // Stop friggin' dying, gosh damn occupant.setOxyLoss(0) for(var/datum/disease/critical/crit in occupant.viruses) crit.cure() occupant.forceMove(T) occupant.update_body() domutcheck(occupant) //Waiting until they're out before possible notransform. occupant.special_post_clone_handling() occupant = null update_icon() /obj/machinery/clonepod/proc/malfunction(go_easy = FALSE) if(occupant) connected_message("Critical Error!") announce_radio_message("Critical error! Please contact a Thinktronic Systems technician, as your warranty may be affected.") UnregisterSignal(clonemind.current, COMSIG_LIVING_REVIVE) UnregisterSignal(clonemind, COMSIG_MIND_TRANSER_TO) if(!go_easy) if(occupant.mind != clonemind) clonemind.transfer_to(occupant) occupant.grab_ghost() // We really just want to make you suffer. var/message message += "Agony blazes across your consciousness as your body is torn apart.
" message += "Is this what dying is like? Yes it is." to_chat(occupant, "[message]") SEND_SOUND(occupant, sound('sound/hallucinations/veryfar_noise.ogg', 0, 1, 50)) QDEL_LIST_CONTENTS(missing_organs) clonemind = null spawn(40) qdel(occupant) playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, 0) mess = TRUE update_icon() /obj/machinery/clonepod/update_icon_state() if(occupant && !(stat & NOPOWER)) icon_state = "pod_cloning" else if(mess) icon_state = "pod_mess" else icon_state = "pod_idle" /obj/machinery/clonepod/update_overlays() . = ..() if(panel_open) . += "panel_open" /obj/machinery/clonepod/relaymove(mob/user) if(user.stat == CONSCIOUS) go_out() /obj/machinery/clonepod/emp_act(severity) if(prob(100/(severity*efficiency))) malfunction() ..() /obj/machinery/clonepod/ex_act(severity) ..() if(!QDELETED(src) && occupant) go_out() /obj/machinery/clonepod/handle_atom_del(atom/A) if(A == occupant) occupant = null countdown.stop() /obj/machinery/clonepod/deconstruct(disassembled = TRUE) if(occupant) go_out() ..() /obj/machinery/clonepod/proc/occupant_got_revived() // The old body's back in shape, time to ditch the cloning one malfunction(go_easy = TRUE) /obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) QDEL_LIST_CONTENTS(missing_organs) H.setCloneLoss(CLONE_INITIAL_DAMAGE, FALSE) H.setBrainLoss(BRAIN_INITIAL_DAMAGE) for(var/o in H.internal_organs) var/obj/item/organ/O = o if(!istype(O) || O.vital) continue // Let's non-specially remove all non-vital organs // What could possibly go wrong var/obj/item/I = O.remove(H) // Make this support stuff that turns into items when removed I.forceMove(src) missing_organs += I var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") for(var/zone in zones) var/obj/item/organ/external/E = H.get_organ(zone) var/obj/item/I = E.remove(H) I.forceMove(src) missing_organs += I organs_number = length(missing_organs) H.updatehealth() /obj/machinery/clonepod/proc/check_brine() // Clones are in a pickled bath of mild chemicals, keeping // them alive, despite their lack of internal organs for(var/bt in brine_types) if(occupant.reagents.get_reagent_amount(bt) < 1) occupant.reagents.add_reagent(bt, 1) /* * Genetics Diskette Box */ /obj/item/storage/box/disks name = "Genetics Diskette Box" icon_state = "disk_kit" /obj/item/storage/box/disks/populate_contents() for(var/i in 1 to 7) new /obj/item/disk/data(src) /* * Manual -- A big ol' manual. */ /obj/item/paper/Cloning name = "paper - 'H-87 Cloning Apparatus Manual" info = {"

Getting Started

Congratulations, your station has purchased the H-87 industrial cloning device!
Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
That's all there is to it!
Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.

Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR BIO-CHIP into the subject, which may be viewed from each profile. Profile Deletion has been restricted to \[Station Head\] level access.

Cloning from a profile

Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.

The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.

Profile Management

The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. These diskettes are used to transfer genetic information between machines and profiles. A load/save dialog will become available in each profile if a disk is inserted.


A good diskette is a great way to counter aforementioned genetic drift!

This technology produced under license from Thinktronic Systems, LTD."} //SOME SCRAPS I GUESS /* EMP grenade/spell effect if(istype(A, /obj/machinery/clonepod)) A:malfunction() */ #undef MINIMUM_HEAL_LEVEL