//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 (?)
//Find a dead mob with a brain and client.
/proc/find_dead_player(var/find_key)
if(isnull(find_key))
return
var/mob/selected = null
for(var/mob/living/M in player_list)
//Dead people only thanks!
if((M.stat != 2) || (!M.client))
continue
//They need a brain!
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(!H.has_brain())
continue
if(M.ckey == find_key)
selected = M
break
return selected
#define MINIMUM_HEAL_LEVEL 40
/obj/machinery/clonepod
name = "cloning pod"
desc = "An electronically-lockable pod for growing organic tissue."
density = TRUE
anchored = TRUE
circuit = /obj/item/circuitboard/clonepod
icon = 'icons/obj/cloning.dmi'
icon_state = "pod_0"
req_access = list(access_genetics) // For premature unlocking.
var/mob/living/occupant
var/heal_level = 20 // The clone is released once its health reaches this level.
var/heal_rate = 1
var/locked = 0
var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine.
var/mess = 0 // Need to clean out it if it's full of exploded clone.
var/attempting = 0 // One clone attempt at a time thanks
var/eject_wait = 0 // Don't eject them as soon as they are created fuckkk
var/list/containers = list() // Beakers for our liquid biomass
var/container_limit = 3 // How many beakers can the machine hold?
var/speed_coeff
var/efficiency
/obj/machinery/clonepod/Initialize(mapload)
. = ..()
default_apply_parts()
update_icon()
/obj/machinery/clonepod/attack_ai(mob/user as mob)
add_hiddenprint(user)
return attack_hand(user)
/obj/machinery/clonepod/attack_hand(mob/user as mob)
if((isnull(occupant)) || (stat & NOPOWER))
return
if((!isnull(occupant)) && (occupant.stat != 2))
var/completion = (100 * ((occupant.health + 50) / (heal_level + 100))) // Clones start at -150 health
to_chat(user, "Current clone cycle is [round(completion)]% complete.")
return
//Start growing a human clone in the pod!
/obj/machinery/clonepod/proc/growclone(var/datum/dna2/record/R)
if(mess || attempting)
return 0
var/datum/mind/clonemind = locate(R.mind)
if(!istype(clonemind, /datum/mind)) //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
else
for(var/mob/observer/dead/G in player_list)
if(G.ckey == R.ckey)
if(G.can_reenter_corpse)
break
else
return 0
for(var/modifier_type in R.genetic_modifiers) //Can't be cloned, even if they had a previous scan
if(istype(modifier_type, /datum/modifier/no_clone))
return 0
// Remove biomass when the cloning is started, rather than when the guy pops out
remove_biomass(CLONE_BIOMASS)
attempting = 1 //One at a time!!
locked = 1
eject_wait = 1
spawn(30)
eject_wait = 0
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species)
occupant = H
if(!R.dna.real_name) //to prevent null names
R.dna.real_name = "clone ([rand(0,999)])"
H.real_name = R.dna.real_name
H.gender = R.gender
//Get the clone body ready
H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite
H.Paralyse(4)
//Here let's calculate their health so the pod doesn't immediately eject them!!!
H.updatehealth()
clonemind.transfer_to(H)
H.ckey = R.ckey
to_chat(H, span_warning(span_bold("Consciousness slowly creeps over you as your body regenerates.") + "
" + span_bold(span_large("Your recent memories are fuzzy, and it's hard to remember anything from today...")) + \
"
" + span_notice(span_italics("So this is what cloning feels like?"))))
// -- Mode/mind specific stuff goes here
callHook("clone", list(H))
update_antag_icons(H.mind)
// -- End mode specific stuff
if(!R.dna)
H.dna = new /datum/dna()
qdel_swap(H.dna, new /datum/dna())
else
qdel_swap(H.dna, R.dna)
H.UpdateAppearance()
H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
H.sync_organ_dna()
H.initialize_vessel()
H.set_cloned_appearance()
update_icon()
// A modifier is added which makes the new clone be unrobust.
var/modifier_lower_bound = 25 MINUTES
var/modifier_upper_bound = 40 MINUTES
// Upgraded cloners can reduce the time of the modifier, up to 80%
var/clone_sickness_length = abs(((heal_level - 20) / 100 ) - 1)
clone_sickness_length = between(0.2, clone_sickness_length, 1.0) // Caps it off just incase.
modifier_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1)
modifier_upper_bound = round(modifier_upper_bound * clone_sickness_length, 1)
H.add_modifier(H.species.cloning_modifier, rand(modifier_lower_bound, modifier_upper_bound))
// Modifier that doesn't do anything.
H.add_modifier(/datum/modifier/cloned)
// This is really stupid.
for(var/modifier_type in R.genetic_modifiers)
H.add_modifier(modifier_type)
for(var/datum/language/L in R.languages)
H.add_language(L.name)
H.flavor_texts = R.flavor.Copy()
H.suiciding = 0
attempting = 0
return 1
//Grow clones to maturity then kick them out. FREELOADERS
/obj/machinery/clonepod/process()
if(stat & NOPOWER) //Autoeject if power is lost
if(occupant)
locked = 0
go_out()
return
if((occupant) && (occupant.loc == src))
if((occupant.stat == DEAD) || (occupant.suiciding) || !occupant.key) //Autoeject corpses and suiciding dudes.
locked = 0
go_out()
connected_message("Clone Rejected: Deceased.")
return
else if(occupant.health < heal_level && occupant.getCloneLoss() > 0)
occupant.Paralyse(4)
//Slowly get that clone healed and finished.
occupant.adjustCloneLoss(-2 * heal_rate)
//Premature clones may have brain damage.
occupant.adjustBrainLoss(-(CEILING(0.5*heal_rate, 1)))
//So clones don't die of oxyloss in a running pod.
if(occupant.reagents.get_reagent_amount(REAGENT_ID_INAPROVALINE) < 30)
occupant.reagents.add_reagent(REAGENT_ID_INAPROVALINE, 60)
occupant.Sleeping(30)
//Also heal some oxyloss ourselves because inaprovaline is so bad at preventing it!!
occupant.adjustOxyLoss(-4)
use_power(7500) //This might need tweaking.
return
else if((occupant.health >= heal_level || occupant.health == occupant.getMaxHealth()) && (!eject_wait))
playsound(src, 'sound/machines/medbayscanner1.ogg', 50, 1)
audible_message("\The [src] signals that the cloning process is complete.", runemessage = "ding")
connected_message("Cloning Process Complete.")
locked = 0
go_out()
return
else if((!occupant) || (occupant.loc != src))
occupant = null
if(locked)
locked = 0
return
return
//Let's unlock this early I guess. Might be too early, needs tweaking.
/obj/machinery/clonepod/attackby(obj/item/W as obj, mob/user as mob)
if(isnull(occupant))
if(default_deconstruction_screwdriver(user, W))
return
if(default_deconstruction_crowbar(user, W))
return
if(default_part_replacement(user, W))
return
if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda))
if(!check_access(W))
to_chat(user, span_warning("Access Denied."))
return
if((!locked) || (isnull(occupant)))
return
if((occupant.health < -20) && (occupant.stat != 2))
to_chat(user, span_warning("Access Refused."))
return
else
locked = 0
to_chat(user, "System unlocked.")
else if(istype(W,/obj/item/reagent_containers/glass))
if(LAZYLEN(containers) >= container_limit)
to_chat(user, span_warning("\The [src] has too many containers loaded!"))
else if(do_after(user, 1 SECOND))
user.visible_message("[user] has loaded \the [W] into \the [src].", "You load \the [W] into \the [src].")
containers += W
user.drop_item()
W.forceMove(src)
return
else if(W.has_tool_quality(TOOL_WRENCH))
if(locked && (anchored || occupant))
to_chat(user, span_warning("Can not do that while [src] is in use."))
else
if(anchored)
anchored = FALSE
connected.pods -= src
connected = null
else
anchored = TRUE
playsound(src, W.usesound, 100, 1)
if(anchored)
user.visible_message("[user] secures [src] to the floor.", "You secure [src] to the floor.")
else
user.visible_message("[user] unsecures [src] from the floor.", "You unsecure [src] from the floor.")
else if(istype(W, /obj/item/multitool))
var/obj/item/multitool/M = W
M.connecting = src
to_chat(user, span_notice("You load connection data from [src] to [M]."))
M.update_icon()
return
else
..()
/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user)
if(isnull(occupant))
return
to_chat(user, "You force an emergency ejection.")
locked = 0
go_out()
return 1
//Put messages in the connected computer's temp var for display.
/obj/machinery/clonepod/proc/connected_message(var/message)
if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning)))
return 0
if(!message)
return 0
connected.temp = "[name] : [message]"
connected.updateUsrDialog()
return 1
/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)
/obj/machinery/clonepod/proc/get_completion()
. = (100 * ((occupant.health + 100) / (heal_level + 100)))
/obj/machinery/clonepod/verb/eject()
set name = "Eject Cloner"
set category = "Object"
set src in oview(1)
if(usr.stat != 0)
return
go_out()
add_fingerprint(usr)
return
/obj/machinery/clonepod/proc/go_out()
if(locked)
return
if(mess) //Clean that mess and dump those gibs!
mess = 0
gibs(src.loc)
update_icon()
return
if(!(occupant))
return
if(occupant.client)
occupant.client.eye = occupant.client.mob
occupant.client.perspective = MOB_PERSPECTIVE
occupant.loc = src.loc
eject_wait = 0 //If it's still set somehow.
if(ishuman(occupant)) //Need to be safe.
var/mob/living/carbon/human/patient = occupant
if(!(patient.species.flags & NO_DNA)) //If, for some reason, someone makes a genetically-unalterable clone, let's not make them permanently disabled.
domutcheck(occupant) //Waiting until they're out before possible transforming.
occupant.UpdateAppearance()
occupant = null
update_icon()
return
// Returns the total amount of biomass reagent in all of the pod's stored containers
/obj/machinery/clonepod/proc/get_biomass()
var/biomass_count = 0
if(LAZYLEN(containers))
for(var/obj/item/reagent_containers/glass/G in containers)
for(var/datum/reagent/R in G.reagents.reagent_list)
if(R.id == REAGENT_ID_BIOMASS)
biomass_count += R.volume
return biomass_count
// Removes [amount] biomass, spread across all containers. Doesn't have any check that you actually HAVE enough biomass, though.
/obj/machinery/clonepod/proc/remove_biomass(var/amount = CLONE_BIOMASS) //Just in case it doesn't get passed a new amount, assume one clone
var/to_remove = 0 // Tracks how much biomass has been found so far
if(LAZYLEN(containers))
for(var/obj/item/reagent_containers/glass/G in containers)
if(to_remove < amount) //If we have what we need, we can stop. Checked every time we switch beakers
for(var/datum/reagent/R in G.reagents.reagent_list)
if(R.id == REAGENT_ID_BIOMASS) // Finds Biomass
var/need_remove = max(0, amount - to_remove) //Figures out how much biomass is in this container
if(R.volume >= need_remove) //If we have more than enough in this beaker, only take what we need
R.remove_self(need_remove)
to_remove = amount
else //Otherwise, take everything and move on
to_remove += R.volume
R.remove_self(R.volume)
else
continue
else
return 1
return 0
// Empties all of the beakers from the cloning pod, used to refill it
/obj/machinery/clonepod/verb/empty_beakers()
set name = "Eject Beakers"
set category = "Object"
set src in oview(1)
if(usr.stat != 0)
return
add_fingerprint(usr)
drop_beakers()
return
// Actually does all of the beaker dropping
// Returns 1 if it succeeds, 0 if it fails. Added in case someone wants to add messages to the user.
/obj/machinery/clonepod/proc/drop_beakers()
if(LAZYLEN(containers))
var/turf/T = get_turf(src)
if(T)
for(var/obj/item/reagent_containers/glass/G in containers)
G.forceMove(T)
containers -= G
return 1
return 0
/obj/machinery/clonepod/proc/malfunction()
if(occupant)
connected_message("Critical Error!")
mess = 1
update_icon()
occupant.ghostize()
QDEL_IN(occupant, 0.5 SECONDS)
/obj/machinery/clonepod/relaymove(mob/user)
if(user.stat)
return
go_out()
/obj/machinery/clonepod/emp_act(severity)
if(prob(100/severity))
malfunction()
..()
/obj/machinery/clonepod/ex_act(severity)
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
ex_act(severity)
qdel(src)
return
if(2.0)
if(prob(50))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
ex_act(severity)
qdel(src)
return
if(3.0)
if(prob(25))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
ex_act(severity)
qdel(src)
return
return
/obj/machinery/clonepod/update_icon()
..()
icon_state = "pod_0"
if(occupant && !(stat & NOPOWER))
icon_state = "pod_1"
else if(mess)
icon_state = "pod_g"
/obj/machinery/clonepod/full/Initialize(mapload)
. = ..()
for(var/i = 1 to container_limit)
containers += new /obj/item/reagent_containers/glass/bottle/biomass(src)
//Health Tracker Implant
/obj/item/implant/health
name = "health implant"
known_implant = TRUE
var/healthstring = ""
/obj/item/implant/health/proc/sensehealth()
if(!implanted)
return "ERROR"
else
if(isliving(implanted))
var/mob/living/L = implanted
healthstring = "[round(L.getOxyLoss())] - [round(L.getFireLoss())] - [round(L.getToxLoss())] - [round(L.getBruteLoss())]"
if(!healthstring)
healthstring = "ERROR"
return healthstring
#undef MINIMUM_HEAL_LEVEL
/*
* Diskette Box
*/
/obj/item/storage/box/disks
name = "Diskette Box"
icon_state = "disk_kit"
/obj/item/storage/box/disks/Initialize(mapload)
. = ..()
new /obj/item/disk/body_record(src)
new /obj/item/disk/body_record(src)
new /obj/item/disk/body_record(src)
new /obj/item/disk/body_record(src)
new /obj/item/disk/body_record(src)
new /obj/item/disk/body_record(src)
new /obj/item/disk/body_record(src)
/*
* Manual -- A big ol' manual.
*/
/obj/item/paper/Cloning
name = "H-87 Cloning Apparatus Manual"
info = {"
Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. Profile Deletion has been restricted to \[Station Head\] level access.
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.
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.