Files
2020-02-19 19:48:29 -08:00

962 lines
33 KiB
Plaintext

#define MEAT_NEEDED_TO_CLONE 16
#define MAXIMUM_MEAT_LEVEL 100
#define DEFAULT_MEAT_USED_PER_TICK 0.6
#define DEFAULT_SPEED_BONUS 1
#define MEAT_LOW_LEVEL MAXIMUM_MEAT_LEVEL * 0.15
#define MAX_FAILED_CLONE_TICKS 200 // vOv
/obj/machinery/clonepod
anchored = 1
name = "cloning pod"
desc = "An electronically-lockable pod for growing organic tissue."
density = 1
icon = 'icons/obj/cloning.dmi'
icon_state = "pod_0_lowmeat"
req_access = list(access_medlab) //For premature unlocking.
object_flags = CAN_REPROGRAM_ACCESS
mats = 15
var/meat_used_per_tick = DEFAULT_MEAT_USED_PER_TICK
var/mob/living/occupant
var/heal_level = 90 //The clone is released once its health reaches this level.
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/previous_heal = 0
var/portable = 0 //Are we part of a port-a-clone?
var/operating = 0 //Are we currently cloning some duder?
var/id = null
var/cloneslave = 0 //Is a traitor enslaving the clones?
var/mob/implant_master = null // Who controls the clones?
var/datum/bioEffect/BE = null // Any bioeffects to add upon cloning (used with the geneclone module)
var/mindwipe = 0 // Are we wiping people's minds?
var/is_speedy = 0 // Speed module installed?
var/is_efficient = 0 // Efficiency module installed?
var/gen_analysis = 1 //Are we analysing the genes while reassembling the duder? (read: Do we work faster or do we give a material bonus?)
var/gen_bonus = 1 //Normal generation speed
var/speed_bonus = DEFAULT_SPEED_BONUS // Multiplier that can be modified by modules
power_usage = 200
var/failed_tick_counter = 0 // goes up while someone is stuck in there and there's not enough meat to clone them, after so many ticks they'll get dumped out
var/message = null
var/mailgroup = "medbay"
var/mailgroup2 = "medresearch"
var/net_id = null
var/pdafrequency = 1149
var/datum/radio_frequency/pda_connection
var/datum/light/light
var/meat_level = MAXIMUM_MEAT_LEVEL / 4
New()
..()
var/datum/reagents/R = new/datum/reagents(100)
reagents = R
R.my_atom = src
src.update_icon()
genResearch.clonepods.Add(src) //This will be used for genetics bonuses when cloning
light = new /datum/light/point
light.set_brightness(1)
light.set_color(1.0,0.1,0.1)
light.set_height(0.75)
light.attach(src)
SPAWN_DBG(10 SECONDS)
if (radio_controller)
pda_connection = radio_controller.add_object(src, "[pdafrequency]")
if (!src.net_id)
src.net_id = generate_net_id(src)
disposing()
radio_controller.remove_object(src, "[pdafrequency]")
genResearch.clonepods.Remove(src) //Bye bye
..()
proc/send_pda_message(var/msg)
if (!msg && src.message)
msg = src.message
else if (!msg)
return
if (msg && mailgroup && pda_connection)
var/datum/signal/newsignal = get_free_signal()
newsignal.source = src
newsignal.transmission_method = TRANSMISSION_RADIO
newsignal.data["command"] = "text_message"
newsignal.data["sender_name"] = "CLONEPOD-MAILBOT"
newsignal.data["message"] = "[msg]"
newsignal.data["address_1"] = "00000000"
newsignal.data["group"] = mailgroup
newsignal.data["sender"] = src.net_id
pda_connection.post_signal(src, newsignal)
if (msg && mailgroup2 && pda_connection)
var/datum/signal/newsignal = get_free_signal()
newsignal.source = src
newsignal.transmission_method = TRANSMISSION_RADIO
newsignal.data["command"] = "text_message"
newsignal.data["sender_name"] = "CLONEPOD-MAILBOT"
newsignal.data["message"] = "[msg]"
newsignal.data["address_1"] = "00000000"
newsignal.data["group"] = mailgroup2
newsignal.data["sender"] = src.net_id
pda_connection.post_signal(src, newsignal)
/obj/machinery/clonepod/attack_ai(mob/user as mob)
return attack_hand(user)
/obj/machinery/clonepod/attack_hand(mob/user as mob)
interact_particle(user,src)
if (status & NOPOWER)
return
if ((!isnull(src.occupant)) && (!isdead(src.occupant)))
var/completion = (100 * ((src.occupant.health + 100) / (src.heal_level + 100)))
boutput(user, "Current clone cycle is [round(completion)]% complete.")
boutput(user, "Biomatter reserves are [round( 100 * (src.meat_level / MAXIMUM_MEAT_LEVEL) )]% full.")
if (src.meat_level <= 0)
boutput(user, "<span style='color:red'>Alert: Biomatter reserves depleted.</span>")
else if (src.meat_level <= MEAT_LOW_LEVEL)
boutput(user, "<span style='color:red'>Alert: Biomatter reserves low.</span>")
return
/obj/machinery/clonepod/is_open_container()
return 2
/obj/machinery/clonepod/proc/update_icon()
if (src.portable) // no need here
return
if (src.mess)
src.icon_state = "pod_g"
else
src.icon_state = "pod_[src.occupant ? "1" : "0"][src.meat_level ? "" : "_lowmeat"][src.cloneslave ? "_mindslave" : "" ][src.mindwipe ? "_mindwipe" : ""]"
//Start growing a human clone in the pod!
/obj/machinery/clonepod/proc/growclone(mob/ghost as mob, var/clonename, var/datum/mind/mindref, var/datum/bioHolder/oldholder, var/datum/abilityHolder/oldabilities, var/list/traits)
if (((!ghost) || (!ghost.client)) || src.mess || src.attempting)
return 0
if (src.meat_level < MEAT_NEEDED_TO_CLONE)
src.connected_message("Insufficient biomatter to begin.")
return 0
if (ghost.mind.dnr)
src.connected_message("Ephemereal conscience detected, seance protocols reveal this corpse cannot be cloned.")
return 0
src.attempting = 1 //One at a time!!
src.locked = 1
src.failed_tick_counter = 0 // make sure we start here
src.eject_wait = 1
SPAWN_DBG(3 SECONDS)
src.eject_wait = 0
src.occupant = new /mob/living/carbon/human(src)
if (istype(oldholder))
oldholder.clone_generation++
src.occupant.bioHolder.CopyOther( oldholder )
else
logTheThing("debug", null, null, "<b>Cloning:</b> growclone([english_list(args)]) with invalid holder.")
if (istype(oldabilities))
src.occupant.abilityHolder = oldabilities // This should already be a copy.
src.occupant.abilityHolder.transferOwnership(src.occupant) //mbc : fixed clone removing abilities bug!
src.occupant.abilityHolder.remove_unlocks()
if (traits && traits.len && src.occupant.traitHolder)
src.occupant.traitHolder.traits = traits
if (src.occupant.traitHolder.hasTrait("puritan"))
src.mess = 1
ghost.client.mob = src.occupant
src.update_icon()
//Get the clone body ready
SPAWN_DBG(5 DECI SECONDS) //Organs may not exist yet if we call this right away.
random_brute_damage(src.occupant, 90, 1)
src.occupant.take_toxin_damage(50)
src.occupant.take_oxygen_deprivation(40)
src.occupant.take_brain_damage(90)
src.occupant.changeStatus("paralysis", 60)
src.occupant.bioHolder.AddEffect("premature_clone")
if(src.occupant.bioHolder.clone_generation > 1)
src.occupant.setStatus("maxhealth-", null, -((src.occupant.bioHolder.clone_generation - 1) * 15))
//src.occupant.max_health -= (src.occupant.bioHolder.clone_generation - 1) * 15 //Genetic degradation! Oh no!!
//Here let's calculate their health so the pod doesn't immediately eject them!!!
src.occupant.health = (src.occupant.get_brute_damage() + src.occupant.get_toxin_damage() + src.occupant.get_oxygen_deprivation())
boutput(src.occupant, "<span style='color:blue'><b>Clone generation process initiated.</b></span>")
boutput(src.occupant, "<span style='color:blue'>This will take a moment, please hold.</span>")
if (clonename)
if (prob(10))
src.occupant.real_name = "[pick("Almost", "Sorta", "Mostly", "Kinda", "Nearly", "Pretty Much", "Roughly", "Not Quite", "Just About", "Something Resembling", "Somewhat")] [clonename]"
else
src.occupant.real_name = clonename
else
src.occupant.real_name = "clone" //No null names!!
if ((mindref) && (istype(mindref))) //Move that mind over!!
mindref.transfer_to(src.occupant)
else //welp
logTheThing("debug", null, null, "<b>Mind</b> Clonepod forced to create new mind for key \[[src.occupant.key ? src.occupant.key : "INVALID KEY"]]")
src.occupant.mind = new /datum/mind( )
src.occupant.mind.key = src.occupant.key
src.occupant.mind.transfer_to(src.occupant)
ticker.minds += src.occupant.mind
// -- Mode/mind specific stuff goes here
if ((ticker && ticker.mode && istype(ticker.mode, /datum/game_mode/revolution)) && ((src.occupant.mind in ticker.mode:revolutionaries) || (src.occupant.mind in ticker.mode:head_revolutionaries)))
ticker.mode:update_all_rev_icons() //So the icon actually appears
// -- End mode specific stuff
logTheThing("combat", usr, src.occupant, "starts cloning %target% at [log_loc(src)].")
if (isobserver(ghost))
qdel(ghost) //Don't leave ghosts everywhere!!
if (src.reagents && src.reagents.total_volume)
src.reagents.reaction(src.occupant, 2, 1000)
src.reagents.trans_to(src.occupant, 1000)
// Oh boy someone is cloning themselves up an army!
if(cloneslave && implant_master != null)
// No need to check near as much with a standard implant, as the cloned person is dead and is therefore enslavable upon cloning.
// How did this happen. Why is someone cloning you as a slave to yourself. WHO KNOWS?!
if(implant_master == src.occupant)
boutput(src.occupant, "<span style=\"color:red\">You feel utterly strengthened in your resolve! You are the most important person in the universe!</span>")
else
if (src.occupant.mind && ticker.mode)
if (!src.occupant.mind.special_role)
src.occupant.mind.special_role = "mindslave"
if (!(src.occupant.mind in ticker.mode.Agimmicks))
ticker.mode.Agimmicks += src.occupant.mind
src.occupant.mind.master = implant_master.ckey
boutput(src.occupant, "<h2><span style=\"color:red\">You feel an unwavering loyalty to [implant_master.real_name]! You feel you must obey \his every order! Do not tell anyone about this unless your master tells you to!</span></h2>")
SHOW_MINDSLAVE_TIPS(src.occupant)
// Someone is having their brain zapped. 75% chance of them being de-antagged if they were one
//MBC todo : logging. This shouldn't be an issue thoug because the mindwipe doesn't even appear ingame (yet?)
if(mindwipe)
if(prob(75))
SHOW_MINDWIPE_TIPS(src.occupant)
boutput(src.occupant, "<h2><span style=\"color:red\">You have awakened with a new outlook on life!</span></h2>")
src.occupant.mind.memory = "You cannot seem to remember much from before you were cloned. Weird!<BR>"
else
boutput(src.occupant, "<span style=\"color:red\">You feel your memories fading away, but you manage to hang on to them!</span>")
// Lucky person - they get a power on cloning!
if (src.BE)
src.occupant.bioHolder.AddEffectInstance(BE,1)
previous_heal = src.occupant.health
src.attempting = 0
src.operating = 1
src.gen_bonus = src.healing_multiplier()
return 1
//Grow clones to maturity then kick them out. FREELOADERS
/obj/machinery/clonepod/process()
if (src.occupant && src.meat_level)
power_usage = 7500
else
power_usage = 200
..()
if (status & NOPOWER) //Autoeject if power is lost
if (src.occupant)
src.locked = 0
src.go_out()
return
var/abort = 0
if ((src.occupant) && (src.occupant.loc == src) && src.occupant.traitHolder && src.occupant.traitHolder.hasTrait("puritan"))
abort = 1
src.occupant.take_toxin_damage(300)
src.occupant.death()
if(src.occupant && src.cloneslave == 1 && prob(10))
playsound(src.loc, pick("sound/machines/glitch1.ogg","sound/machines/glitch2.ogg",
"sound/machines/genetics.ogg","sound/machines/shieldoverload.ogg"), 50, 1)
if ((src.occupant) && (src.occupant.loc == src))
if ((isdead(src.occupant)) || (src.occupant.suiciding) || abort) //Autoeject corpses and suiciding dudes.
src.locked = 0
src.go_out()
src.connected_message("Clone Rejected: Deceased.")
src.send_pda_message("Clone Rejected: Deceased")
return
else if (src.failed_tick_counter >= MAX_FAILED_CLONE_TICKS) // you been in there too long, get out
src.locked = 0
src.go_out()
src.connected_message("Clone Ejected: Low Biomatter.")
src.send_pda_message("Clone Ejected: Low Biomatter")
return
else if (!src.meat_level)
src.failed_tick_counter ++
if (src.failed_tick_counter == (MAX_FAILED_CLONE_TICKS / 2)) // halfway to ejection
src.send_pda_message("Low Biomatter: Preparing to Eject Clone")
src.update_icon()
use_power(200)
return
else if (src.occupant.health < src.heal_level)
src.failed_tick_counter = 0
src.occupant.changeStatus("paralysis", 60)
//Slowly get that clone healed and finished.
src.occupant.HealDamage("All", 1 * gen_bonus, 1 * gen_bonus)
//At this rate one clone takes about 95 seconds to produce.(with heal_level 90)
src.occupant.take_toxin_damage(-0.5 * gen_bonus)
//Premature clones may have brain damage.
src.occupant.take_brain_damage(-2 * gen_bonus)
//So clones don't die of oxy damage in a running pod.
if (src.occupant.reagents.get_reagent_amount("perfluorodecalin") < 6) // cogwerks: changed from epinephrine
src.occupant.reagents.add_reagent("perfluorodecalin", 2)
if (src.occupant.reagents.get_reagent_amount("epinephrine") < 8) // lowering this but keeping it in a fully value range
src.occupant.reagents.add_reagent("epinephrine", 4)
if (src.occupant.reagents.get_reagent_amount("saline") < 10) // cogwerks: adding this because it'll be funny when someone gets scanned
src.occupant.reagents.add_reagent("saline", 4)
if (src.occupant.reagents.get_reagent_amount("synthflesh") < 50) // cogwerks: adding this because it'll be funny when someone gets scanned
src.occupant.reagents.add_reagent("synthflesh", 10)
if (src.occupant.reagents.get_reagent_amount("mannitol") < 6) // after new internal organs, some clones seem to be dying in-vat from brain damage. stopgap
src.occupant.reagents.add_reagent("mannitol", 2)
//Also heal some oxy ourselves because epinephrine is so bad at preventing it!!
src.occupant.take_oxygen_deprivation(-10) // cogwerks: speeding this up too
src.meat_level = max( 0, src.meat_level - meat_used_per_tick)
if (!src.meat_level)
src.connected_message("Additional biomatter required to continue.")
src.send_pda_message("Low Biomatter")
src.visible_message("<span style='color:red'>[src] emits an urgent boop!</span>")
playsound(src.loc, "sound/machines/buzz-two.ogg", 50, 0)
src.failed_tick_counter ++
use_power(7500) //This might need tweaking.
var/heal_delta = (src.occupant.health - previous_heal)
previous_heal = src.occupant.health
if (heal_delta <= 0 && src.occupant.health > 50 && !eject_wait)
src.connected_message("Cloning Process Complete.")
src.send_pda_message("Cloning Process Complete")
src.locked = 0
src.go_out()
else // go_out() updates icon too, so vOv
src.update_icon()
return
else if ((src.occupant.health >= src.heal_level) && (!src.eject_wait))
src.connected_message("Cloning Process Complete.")
src.send_pda_message("Cloning Process Complete")
src.locked = 0
src.go_out()
return
else
src.occupant = null
src.operating = 0 //Welp. Where did you go? Jerk.
src.failed_tick_counter = 0
if (src.locked)
src.locked = 0
if (!src.mess)
src.update_icon()
use_power(200)
return
return
/obj/machinery/clonepod/emag_act(var/mob/user, var/obj/item/card/emag/E)
if (isnull(src.occupant))
return 0
if(user)
boutput(user, "You force an emergency ejection.")
src.locked = 0
src.go_out()
return 1
//Let's unlock this early I guess.
/obj/machinery/clonepod/attackby(obj/item/W as obj, mob/user as mob)
if (istype(W, /obj/item/device/pda2) && W:ID_card)
W = W:ID_card
if (istype(W, /obj/item/card/id))
if (!src.check_access(W))
boutput(user, "<span style='color:red'>Access Denied.</span>")
return
if ((!src.locked) || (isnull(src.occupant)))
return
if ((src.occupant.health < -20) && (!isdead(src.occupant)))
boutput(user, "<span style='color:red'>Access Refused.</span>")
return
else
src.locked = 0
boutput(user, "System unlocked.")
else if (istype(W, /obj/item/card/emag)) //This is needed to suppress the SYNDI CAT HITS CLONING POD message *cry
return
else if (istype(W, /obj/item/reagent_containers/glass))
return
else if (istype(W, /obj/item/cloneModule/speedyclone)) // speed module
if(is_speedy)
boutput(user,"<span style=\"color:red\">There's already a speed booster in the slot!</span>")
return
if(operating)
boutput(user,"<span style=\"color:red\">The cloning pod emits an angry boop!</span>")
return
user.visible_message("[user] installs [W] into [src].", "You install [W] into [src].")
logTheThing("combat", src, user, "[user] installed ([W]) to ([src]) at [log_loc(user)].")
speed_bonus += 1
meat_used_per_tick *= 3
is_speedy = 1
user.drop_item()
qdel(W)
return
else if (istype(W, /obj/item/cloneModule/efficientclone)) // efficiency module
if(is_efficient)
boutput(user,"<span style=\"color:red\">There's already an efficiency booster in the slot!</span>")
return
if(operating)
boutput(user,"<span style=\"color:red\">The cloning pod emits a[pick("n angry", " grumpy", "n annoyed", " cheeky")] [pick("boop","bop", "beep", "blorp", "burp")]!</span>")
return
user.visible_message("[user] installs [W] into [src].", "You install [W] into [src].")
logTheThing("combat", src, user, "[user] installed ([W]) to ([src]) at [log_loc(user)].")
meat_used_per_tick *= 0.5
is_efficient = 1
user.drop_item()
qdel(W)
return
else if (istype(W, /obj/item/cloneModule/mindslave_module)) // Time to re enact the clone wars
if(operating)
boutput(user,"<span style=\"color:red\">The cloning pod emits a[pick("n angry", " grumpy", "n annoyed", " cheeky")] [pick("boop","bop", "beep", "blorp", "burp")]!</span>")
return
logTheThing("combat", src, user, "[user] installed ([W]) to ([src]) at [log_loc(user)].")
cloneslave = 1
implant_master = user
// Clone armies are not allowed to use speed or efficiency modules under article 7.2 p5 of the space geneva convention
is_speedy = 1
is_efficient = 1
speed_bonus = DEFAULT_SPEED_BONUS
meat_used_per_tick = DEFAULT_MEAT_USED_PER_TICK
light.enable()
user.drop_item()
qdel(W)
return
else if(istype(W, /obj/item/screwdriver) && cloneslave == 1) // Wait nevermind the clone wars were a terrible idea
if(src.occupant)
boutput(user, "<space style=\"color:red\">You must wait for the current cloning cycle to finish before you can remove the mindslave module.</span>")
return
boutput(user, "<span style=\"color:blue\">You begin detatching the mindslave cloning module...</span>")
playsound(src.loc, "sound/items/Screwdriver.ogg", 50, 1)
if(do_after(user,50))
new /obj/item/cloneModule/mindslave_module( src.loc )
cloneslave = 0
implant_master = null
boutput(user,"<span style=\"color:red\">The mindslave cloning module falls to the floor with a dull thunk </span>")
playsound(src.loc, "sound/effects/thunk.ogg", 50, 0)
light.disable()
else
boutput(user,"<span style=\"color:red\">You were interrupted!</span>")
return
else
..()
var/list/clonepod_accepted_reagents = list("blood"=0.5,"synthflesh"=1,"beff"=0.75,"pepperoni"=0.5,"meat_slurry"=1,"bloodc"=0.5)
/obj/machinery/clonepod/on_reagent_change()
for(var/reagent_id in src.reagents.reagent_list)
if (reagent_id in clonepod_accepted_reagents)
var/datum/reagent/theReagent = src.reagents.reagent_list[reagent_id]
if (theReagent)
src.meat_level = min(src.meat_level + (theReagent.volume * clonepod_accepted_reagents[reagent_id]), MAXIMUM_MEAT_LEVEL)
src.reagents.del_reagent(reagent_id)
if (src.occupant)
src.reagents.reaction(src.occupant, 1000) // why was there a 2 here? it was injecting ice cold reagents that burn people
src.reagents.trans_to(src.occupant, 1000)
//Put messages in the connected computer's temp var for display.
/obj/machinery/clonepod/proc/connected_message(var/msg)
if ((isnull(src.connected)) || (!istype(src.connected, /obj/machinery/computer/cloning)))
return 0
if (!msg)
return 0
src.connected.temp = msg
src.connected.updateUsrDialog()
return 1
/obj/machinery/clonepod/verb/eject()
set src in oview(1)
set category = "Local"
if (!isalive(usr))
return
src.go_out()
add_fingerprint(usr)
return
/obj/machinery/clonepod/proc/go_out()
if (src.locked)
return
src.operating = 0 //Welp
src.failed_tick_counter = 0
if (src.mess) //Clean that mess and dump those gibs!
src.mess = 0
gibs(get_turf(src)) // we don't need to do if/else things just to say "put gibs on this thing's turf"
src.update_icon()
for (var/obj/O in src)
O.set_loc(get_turf(src))
if (prob(33))
step_rand(O) // cogwerks - let's spread that mess instead of having a pile! bahaha
return
if (!(src.occupant))
return
for (var/obj/O in src)
O.set_loc(get_turf(src))
if ((src.occupant.health >= heal_level - 50) && src.occupant.bioHolder) // this seems to often not work right, changing 20 to 50
src.occupant.bioHolder.RemoveEffect("premature_clone")
src.occupant.update_face()
src.occupant.update_body()
src.occupant.update_clothing()
if (src.occupant.get_oxygen_deprivation())
src.occupant.take_oxygen_deprivation(-INFINITY)
src.occupant.updatehealth()
if (src.occupant.losebreath) // STOP FUCKING SUFFOCATING GOD DAMN
src.occupant.losebreath = 0
if(iscarbon(src.occupant))
var/mob/living/carbon/C = src.occupant
C.remove_ailments() // no more cloning with heart failure
src.occupant.set_loc(get_turf(src))
src.update_icon()
src.eject_wait = 0 //If it's still set somehow.
src.occupant = null
return
/obj/machinery/clonepod/proc/malfunction()
if (src.occupant)
src.connected_message("Critical Error!")
src.send_pda_message("Critical Error")
src.mess = 1
src.failed_tick_counter = 0
src.update_icon()
src.occupant.ghostize()
SPAWN_DBG(5 DECI SECONDS)
qdel(src.occupant)
return
/obj/machinery/clonepod/proc/operating_nominally()
return operating && src.meat_level && gen_analysis //Only operate nominally for non-shit cloners
/obj/machinery/clonepod/proc/healing_multiplier()
if (wagesystem.clones_for_cash)
return (2 + (!gen_analysis * 0.15)) * speed_bonus
else
return (1 + (!gen_analysis * 0.15)) * speed_bonus //If the analysis feature is disabled, then generate the clone slightly faster
/obj/machinery/clonepod/relaymove(mob/user as mob)
if (user.stat)
return
src.go_out()
return
/obj/machinery/clonepod/ex_act(severity)
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
A.set_loc(src.loc)
A.ex_act(severity)
qdel(src)
return
if(2.0)
if (prob(50))
for(var/atom/movable/A as mob|obj in src)
A.set_loc(src.loc)
A.ex_act(severity)
qdel(src)
return
if(3.0)
if (prob(25))
for(var/atom/movable/A as mob|obj in src)
A.set_loc(src.loc)
A.ex_act(severity)
qdel(src)
return
else
return
//SOME SCRAPS I GUESS
/* EMP grenade/spell effect
if(istype(A, /obj/machinery/clonepod))
A:malfunction()
*/
//WHAT DO YOU WANT FROM ME(AT)
/obj/machinery/clonegrinder
name = "enzymatic reclaimer"
desc = "A tank resembling a rather large blender, designed to recover biomatter for use in cloning."
icon = 'icons/obj/cloning.dmi'
icon_state = "grinder0"
anchored = 1
density = 1
mats = 10
var/list/pods = null // cloning pods we're tied to
var/id = null // if this isn't null, we'll only look for pods with this ID
var/pod_range = 4 // if we don't have an ID, we look for pods in orange(this value)
var/process_timer = 0
var/mob/living/occupant = null
var/list/meats = list() //Meat that we want to reclaim.
var/max_meat = 7 //To be honest, I added the meat reclamation thing in part because I wanted a "max_meat" var.
var/emagged = 0
var/auto_strip = 1 // disabled when emagged (people were babies about this when it being turned off was the default) :V
var/upgraded = 0 // upgrade card makes the reclaimer more efficient
New()
..()
UnsubscribeProcess()
var/datum/reagents/R = new/datum/reagents(100)
reagents = R
R.my_atom = src
src.update_icon(1)
SPAWN_DBG(0)
src.find_pods()
proc/find_pods()
if (!islist(src.pods))
src.pods = list()
if (!isnull(src.id) && genResearch && islist(genResearch.clonepods) && genResearch.clonepods.len)
for (var/obj/machinery/clonepod/pod in genResearch.clonepods)
if (pod.id == src.id && !src.pods.Find(pod))
src.pods += pod
DEBUG_MESSAGE("[src] adds pod [log_loc(pod)] (ID [src.id]) in genResearch.clonepods")
else
for (var/obj/machinery/clonepod/pod in orange(src.pod_range))
if (!src.pods.Find(pod))
src.pods += pod
DEBUG_MESSAGE("[src] adds pod [log_loc(pod)] in orange([src.pod_range])")
verb/eject()
set src in oview(1)
set category = "Local"
if (!isalive(usr)) return
if (src.process_timer > 0) return
src.go_out()
add_fingerprint(usr)
return
relaymove(mob/user as mob)
src.go_out()
return
proc/go_out()
if (!src.occupant)
return
for(var/obj/O in src)
O.set_loc(src.loc)
src.occupant.set_loc(src.loc)
src.occupant = null
return
process()
if (process_timer-- < 1)
UnsubscribeProcess()
update_icon(1)
DEBUG_MESSAGE("[src].reagents.total_volume on completion of cycle: [src.reagents.total_volume]")
if (islist(src.pods) && pods.len && src.reagents.total_volume)
for (var/obj/machinery/clonepod/pod in src.pods)
src.reagents.trans_to(pod, (src.reagents.total_volume / max(pods.len, 1))) // give an equal amount of reagents to each pod that happens to be around
DEBUG_MESSAGE("[src].reagents.trans_to([pod] [log_loc(pod)], [src.reagents.total_volume]/[max(pods.len, 1)])")
return
var/mult = src.upgraded ? 2 : 1
src.reagents.add_reagent("blood", 2 * mult)
src.reagents.add_reagent("meat_slurry", 2 * mult)
if (prob(2))
src.reagents.add_reagent("beff", 1 * mult)
return
on_reagent_change()
src.update_icon(0)
emag_act(var/mob/user, var/obj/item/card/emag/E)
if (!src.emagged)
if (user)
boutput(user, "<span style='color:blue'>You override the reclaimer's safety mechanism.</span>")
logTheThing("combat", user, "emagged [src] at [log_loc(src)].")
emagged = 1
return 1
else
if (user)
boutput(user, "The safety mechanism's already burnt out!")
return 0
demag(var/mob/user)
if (!src.emagged)
return 0
emagged = 0
if (user)
boutput(user, "<span style ='color:blue'>You repair the reclaimer's safety mechanism.</span>")
return 1
attack_hand(mob/user as mob)
interact_particle(user,src)
if (src.process_timer > 0)
boutput(user, "<span style='color:red'>The [src.name] is already running!</span>")
return
if (!src.meats.len && !src.occupant)
boutput(user, "<span style='color:red'>There is nothing loaded to reclaim!</span>")
return
if (src.occupant && src.occupant.loc != src)
src.occupant = null
boutput(user, "<span style='color:red'>There is nothing loaded to reclaim!</span>")
return
user.visible_message("<b>[user]</b> activates [src]!", "You activate [src].")
if (istype(src.occupant))
logTheThing("combat", user, src.occupant, "activated [src.name] with %target% ([isdead(src.occupant) ? "dead" : "alive"]) inside at [log_loc(src)].")
if (!isdead(src.occupant))
message_admins("[key_name(user)] activated [src.name] with [key_name(src.occupant, 1)] ([isdead(src.occupant) ? "dead" : "alive"]) inside at [log_loc(src)].")
src.start_cycle()
return
proc/start_cycle()
src.find_pods()
if (istype(src.occupant))
src.occupant.death(1)
var/humanOccupant = (ishuman(src.occupant) && !ismonkey(src.occupant))
var/decomp = ishuman(src.occupant) ? src.occupant:decomp_stage : 0 // changed from only checking humanOccupant to running ishuman again so monkeys' decomp will be considered
if (src.occupant.mind)
src.occupant.ghostize()
qdel(src.occupant)
else
qdel(src.occupant)
var/mult = src.upgraded ? rand(2,4) : rand(4,8)
src.process_timer = (humanOccupant ? 2 : 1)// * (rand(4,8) - (2 * decomp))
src.process_timer *= (mult - (2 * decomp))
DEBUG_MESSAGE("[src] process_timer calced as [src.process_timer] (upgraded [src.upgraded], mult [mult], humanOccupant [humanOccupant])")
DEBUG_MESSAGE("[src] rough end result of cycle: [(src.process_timer * (src.upgraded ? 8 : 4))]u + up to [(src.process_timer * (src.upgraded ? 2 : 1))]u")
if (src.meats.len)
for (var/obj/item/theMeat in src.meats)
src.meats -= theMeat
if (theMeat.reagents)
theMeat.reagents.trans_to(src, src.upgraded ? 10 : 5)
qdel(theMeat)
src.process_timer += (src.upgraded ? 1 : 2)
src.meats.len = 0
src.update_icon(1)
SubscribeToProcess()
attackby(obj/item/grab/G as obj, mob/user as mob)
if (istype(G, /obj/item/grinder_upgrade))
if (src.upgraded)
boutput(user, "<span style='color:red'>There is already an upgrade card installed.</span>")
return
user.visible_message("[user] installs [G] into [src].", "You install [G] into [src].")
src.upgraded = 1
user.drop_item()
qdel(G)
return
if (src.process_timer > 0)
boutput(user, "<span style='color:red'>The [src.name] is still running, hold your horses!</span>")
return
if (istype(G, /obj/item/reagent_containers/food/snacks/ingredient/meat) || (istype(G, /obj/item/reagent_containers/food) && (findtext(G.name, "meat")||findtext(G.name,"bacon"))) || (istype(G, /obj/item/parts/human_parts)) || istype(G, /obj/item/clothing/head/butt) || istype(G, /obj/item/organ) || istype(G,/obj/item/raw_material/martian))
if (src.meats.len >= src.max_meat)
boutput(user, "<span style='color:red'>There is already enough meat in there! You should not exceed the maximum safe meat level!</span>")
return
if (G.contents && G.contents.len > 0)
for (var/obj/item/W in G.contents)
if (istype(W, /obj/item/skull) || istype(W, /obj/item/organ/brain) || istype(W, /obj/item/organ/eye))
continue
if (W)
W.set_loc(user.loc)
W.dropped(user)
W.layer = initial(W.layer)
src.meats += G
user.u_equip(G)
G.set_loc(src)
user.visible_message("<b>[user]</b> loads [G] into [src].","You load [G] into [src]")
return
else if (istype(G, /obj/item/reagent_containers/glass))
return
else if (!istype(G) || !iscarbon(G.affecting))
boutput(user, "<span style='color:red'>This item is not suitable for [src].</span>")
return
if (src.occupant)
boutput(user, "<span style='color:red'>There is already somebody in there.</span>")
return
else if (G && G.affecting && !src.emagged && !isdead(G.affecting) && !ismonkey(G.affecting))
user.visible_message("<span style='color:red'>[user] tries to stuff [G.affecting] into [src], but it beeps angrily as the safety overrides engage!</span>")
return
src.add_fingerprint(user)
actions.start(new /datum/action/bar/icon/put_in_reclaimer(G.affecting, src, G, 50), user)
return
proc/update_icon(var/update_grindpaddle=0)
var/fluid_level = ((src.reagents.total_volume >= (src.reagents.maximum_volume * 0.6)) ? 2 : (src.reagents.total_volume >= (src.reagents.maximum_volume * 0.2) ? 1 : 0))
src.icon_state = "grinder[fluid_level]"
if (update_grindpaddle)
src.overlays = null
src.overlays += "grindpaddle[src.process_timer > 0 ? 1 : 0]"
src.overlays += "grindglass[fluid_level]"
return
ex_act(severity)
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
A.set_loc(src.loc)
A.ex_act(severity)
qdel(src)
return
if(2.0)
if (prob(50))
for(var/atom/movable/A as mob|obj in src)
A.set_loc(src.loc)
A.ex_act(severity)
qdel(src)
return
if(3.0)
if (prob(25))
src.status |= BROKEN
src.icon_state = "grinderb"
else
return
is_open_container()
return -1
custom_suicide = 1
suicide(var/mob/user as mob)
if (!src.user_can_suicide(user))
return 0
if (src.process_timer > 0)
return 0
src.visible_message("<span style='color:red'><b>[user] climbs into [src] and turns it on!</b></span>")
user.unequip_all()
user.set_loc(src)
src.occupant = user
src.start_cycle()
SPAWN_DBG(50 SECONDS)
if (user && !isdead(user)) // how????????? ?
user.suiciding = 0 // just in case I guess
return 1
/datum/action/bar/icon/put_in_reclaimer
id = "put_in_reclaimer"
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
duration = 50
icon = 'icons/mob/screen1.dmi'
icon_state = "grabbed"
var/mob/living/carbon/human/target
var/obj/machinery/clonegrinder/grinder
var/obj/item/grab/grab
New(var/mob/living/carbon/human/ntarg, var/obj/machinery/clonegrinder/ngrind, var/obj/item/grab/ngrab, var/duration_i)
..()
if (ntarg)
target = ntarg
if (ngrind)
grinder = ngrind
if (ngrab)
grab = ngrab
if (duration_i)
duration = duration_i
onUpdate()
..()
if (grab == null || target == null || grinder == null || owner == null || get_dist(owner, grinder) > 1 || get_dist(owner, target) > 1 || get_dist(target, grinder) > 1)
interrupt(INTERRUPT_ALWAYS)
return
var/mob/source = owner
if (!istype(source) || !source.find_in_hand(grab) || grab.affecting != target)
interrupt(INTERRUPT_ALWAYS)
onStart()
..()
owner.visible_message("<span style='color:red'><b>[owner] starts to put [target] into [grinder]!</b></span>")
onEnd()
..()
owner.visible_message("<span style='color:red'><b>[owner] stuffs [target] into [grinder]!</b></span>")
logTheThing("combat", owner, target, "forced %target% ([isdead(target) ? "dead" : "alive"]) into \an [grinder] at [log_loc(grinder)].")
if (!isdead(target))
message_admins("[key_name(owner)] forced [key_name(target, 1)] ([target == 2 ? "dead" : "alive"]) into \an [grinder] at [log_loc(grinder)].")
if (grinder.auto_strip && !grinder.emagged)
target.unequip_all()
target.set_loc(grinder)
grinder.occupant = target
qdel(grab)
#undef MEAT_NEEDED_TO_CLONE
#undef MAXIMUM_MEAT_LEVEL
#undef DEFAULT_MEAT_USED_PER_TICK
#undef DEFAULT_SPEED_BONUS
#undef MEAT_LOW_LEVEL