mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
Freezer crates, freezer boxes, cryobags, and medical grippers now properly preserve organs, as well as organs attached to organs. Cleans up storage.dm slightly by removing loc setting with forceMove(), for the above to work.
360 lines
12 KiB
Plaintext
360 lines
12 KiB
Plaintext
var/list/organ_cache = list()
|
|
|
|
/obj/item/organ
|
|
name = "organ"
|
|
icon = 'icons/obj/surgery.dmi'
|
|
germ_level = 0
|
|
|
|
// Strings.
|
|
var/organ_tag = "organ" // Unique identifier.
|
|
var/parent_organ = BP_TORSO // Organ holding this object.
|
|
|
|
// Status tracking.
|
|
var/status = 0 // Various status flags
|
|
var/vital // Lose a vital limb, die immediately.
|
|
var/damage = 0 // Current damage to the organ
|
|
var/robotic = 0
|
|
|
|
// Reference data.
|
|
var/mob/living/carbon/human/owner // Current mob owning the organ.
|
|
var/list/transplant_data // Transplant match data.
|
|
var/list/autopsy_data = list() // Trauma data for forensics.
|
|
var/list/trace_chemicals = list() // Traces of chemicals in the organ.
|
|
var/datum/dna/dna // Original DNA.
|
|
var/datum/species/species // Original species.
|
|
|
|
// Damage vars.
|
|
var/min_bruised_damage = 10 // Damage before considered bruised
|
|
var/min_broken_damage = 30 // Damage before becoming broken
|
|
var/max_damage // Damage cap
|
|
var/rejecting // Is this organ already being rejected?
|
|
var/preserved = 0 // If this is 1, prevents organ decay.
|
|
|
|
/obj/item/organ/Destroy()
|
|
|
|
if(owner) owner = null
|
|
if(transplant_data) transplant_data.Cut()
|
|
if(autopsy_data) autopsy_data.Cut()
|
|
if(trace_chemicals) trace_chemicals.Cut()
|
|
dna = null
|
|
species = null
|
|
|
|
return ..()
|
|
|
|
/obj/item/organ/proc/update_health()
|
|
return
|
|
|
|
/obj/item/organ/New(var/mob/living/carbon/holder, var/internal)
|
|
..(holder)
|
|
create_reagents(5)
|
|
if(!max_damage)
|
|
max_damage = min_broken_damage * 2
|
|
if(istype(holder))
|
|
src.owner = holder
|
|
src.w_class = max(src.w_class + mob_size_difference(holder.mob_size, MOB_MEDIUM), 1) //smaller mobs have smaller organs.
|
|
species = all_species["Human"]
|
|
if(holder.dna)
|
|
dna = holder.dna.Clone()
|
|
species = all_species[dna.species]
|
|
else
|
|
log_debug("[src] at [loc] spawned without a proper DNA.")
|
|
var/mob/living/carbon/human/H = holder
|
|
if(istype(H))
|
|
if(internal)
|
|
var/obj/item/organ/external/E = H.get_organ(parent_organ)
|
|
if(E)
|
|
if(E.internal_organs == null)
|
|
E.internal_organs = list()
|
|
E.internal_organs |= src
|
|
if(dna)
|
|
if(!blood_DNA)
|
|
blood_DNA = list()
|
|
blood_DNA[dna.unique_enzymes] = dna.b_type
|
|
if(internal)
|
|
holder.internal_organs |= src
|
|
else
|
|
species = all_species["Human"]
|
|
|
|
/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
|
|
|
|
/obj/item/organ/proc/die()
|
|
if(robotic >= ORGAN_ROBOT)
|
|
return
|
|
damage = max_damage
|
|
status |= ORGAN_DEAD
|
|
processing_objects -= src
|
|
if(owner && vital)
|
|
owner.death()
|
|
|
|
/obj/item/organ/process()
|
|
|
|
if(loc != owner)
|
|
owner = null
|
|
|
|
//dead already, no need for more processing
|
|
if(status & ORGAN_DEAD)
|
|
return
|
|
// Don't process if we're in a freezer, an MMI or a stasis bag.or a freezer or something I dunno
|
|
if(istype(loc,/obj/item/device/mmi))
|
|
return
|
|
if(preserved)
|
|
return
|
|
//Process infections
|
|
if ((robotic >= ORGAN_ROBOT) || (owner && owner.species && (owner.species.flags & IS_PLANT)))
|
|
germ_level = 0
|
|
return
|
|
|
|
if(!owner && reagents)
|
|
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in reagents.reagent_list
|
|
if(B && prob(40))
|
|
reagents.remove_reagent("blood",0.1)
|
|
blood_splatter(src,B,1)
|
|
if(config.organs_decay) damage += rand(1,3)
|
|
if(damage >= max_damage)
|
|
damage = max_damage
|
|
germ_level += rand(2,6)
|
|
if(germ_level >= INFECTION_LEVEL_TWO)
|
|
germ_level += rand(2,6)
|
|
if(germ_level >= INFECTION_LEVEL_THREE)
|
|
die()
|
|
|
|
else if(owner && owner.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs
|
|
//** Handle antibiotics and curing infections
|
|
handle_antibiotics()
|
|
handle_rejection()
|
|
handle_germ_effects()
|
|
|
|
//check if we've hit max_damage
|
|
if(damage >= max_damage)
|
|
die()
|
|
|
|
/obj/item/organ/examine(mob/user)
|
|
..(user)
|
|
if(status & ORGAN_DEAD)
|
|
user << "<span class='notice'>The decay has set in.</span>"
|
|
|
|
/obj/item/organ/proc/handle_germ_effects()
|
|
//** Handle the effects of infections
|
|
var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin")
|
|
|
|
if (germ_level > 0 && germ_level < INFECTION_LEVEL_ONE/2 && prob(30))
|
|
germ_level--
|
|
|
|
if (germ_level >= INFECTION_LEVEL_ONE/2)
|
|
//aiming for germ level to go from ambient to INFECTION_LEVEL_TWO in an average of 15 minutes
|
|
if(antibiotics < 5 && prob(round(germ_level/6)))
|
|
germ_level++
|
|
|
|
if(germ_level >= INFECTION_LEVEL_ONE)
|
|
var/fever_temperature = (owner.species.heat_level_1 - owner.species.body_temperature - 5)* min(germ_level/INFECTION_LEVEL_TWO, 1) + owner.species.body_temperature
|
|
owner.bodytemperature += between(0, (fever_temperature - T20C)/BODYTEMP_COLD_DIVISOR + 1, fever_temperature - owner.bodytemperature)
|
|
|
|
if (germ_level >= INFECTION_LEVEL_TWO)
|
|
var/obj/item/organ/external/parent = owner.get_organ(parent_organ)
|
|
//spread germs
|
|
if (antibiotics < 5 && parent.germ_level < germ_level && ( parent.germ_level < INFECTION_LEVEL_ONE*2 || prob(30) ))
|
|
parent.germ_level++
|
|
|
|
if (prob(3)) //about once every 30 seconds
|
|
take_damage(1,silent=prob(30))
|
|
|
|
/obj/item/organ/proc/handle_rejection()
|
|
// Process unsuitable transplants. TODO: consider some kind of
|
|
// immunosuppressant that changes transplant data to make it match.
|
|
if(dna)
|
|
if(!rejecting)
|
|
if(blood_incompatible(dna.b_type, owner.dna.b_type, species, owner.species))
|
|
rejecting = 1
|
|
else
|
|
rejecting++ //Rejection severity increases over time.
|
|
if(rejecting % 10 == 0) //Only fire every ten rejection ticks.
|
|
switch(rejecting)
|
|
if(1 to 50)
|
|
germ_level++
|
|
if(51 to 200)
|
|
germ_level += rand(1,2)
|
|
if(201 to 500)
|
|
germ_level += rand(2,3)
|
|
if(501 to INFINITY)
|
|
germ_level += rand(3,5)
|
|
owner.reagents.add_reagent("toxin", rand(1,2))
|
|
|
|
/obj/item/organ/proc/receive_chem(chemical as obj)
|
|
return 0
|
|
|
|
/obj/item/organ/proc/remove_rejuv()
|
|
qdel(src)
|
|
|
|
/obj/item/organ/proc/rejuvenate(var/ignore_prosthetic_prefs)
|
|
damage = 0
|
|
status = 0
|
|
if(!ignore_prosthetic_prefs && owner && owner.client && owner.client.prefs && owner.client.prefs.real_name == owner.real_name)
|
|
var/status = owner.client.prefs.organ_data[organ_tag]
|
|
if(status == "assisted")
|
|
mechassist()
|
|
else if(status == "mechanical")
|
|
robotize()
|
|
|
|
/obj/item/organ/proc/is_damaged()
|
|
return damage > 0
|
|
|
|
/obj/item/organ/proc/is_bruised()
|
|
return damage >= min_bruised_damage
|
|
|
|
/obj/item/organ/proc/is_broken()
|
|
return (damage >= min_broken_damage || (status & ORGAN_CUT_AWAY) || (status & ORGAN_BROKEN))
|
|
|
|
//Germs
|
|
/obj/item/organ/proc/handle_antibiotics()
|
|
var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin")
|
|
|
|
if (!germ_level || antibiotics < 5)
|
|
return
|
|
|
|
if (germ_level < INFECTION_LEVEL_ONE)
|
|
germ_level = 0 //cure instantly
|
|
else if (germ_level < INFECTION_LEVEL_TWO)
|
|
germ_level -= 6 //at germ_level == 500, this should cure the infection in a minute
|
|
else
|
|
germ_level -= 2 //at germ_level == 1000, this will cure the infection in 5 minutes
|
|
|
|
//Adds autopsy data for used_weapon.
|
|
/obj/item/organ/proc/add_autopsy_data(var/used_weapon, var/damage)
|
|
var/datum/autopsy_data/W = autopsy_data[used_weapon]
|
|
if(!W)
|
|
W = new()
|
|
W.weapon = used_weapon
|
|
autopsy_data[used_weapon] = W
|
|
|
|
W.hits += 1
|
|
W.damage += damage
|
|
W.time_inflicted = world.time
|
|
|
|
//Note: external organs have their own version of this proc
|
|
/obj/item/organ/proc/take_damage(amount, var/silent=0)
|
|
if(src.robotic >= ORGAN_ROBOT)
|
|
src.damage = between(0, src.damage + (amount * 0.8), max_damage)
|
|
else
|
|
src.damage = between(0, src.damage + amount, max_damage)
|
|
|
|
//only show this if the organ is not robotic
|
|
if(owner && parent_organ && amount > 0)
|
|
var/obj/item/organ/external/parent = owner.get_organ(parent_organ)
|
|
if(parent && !silent)
|
|
owner.custom_pain("Something inside your [parent.name] hurts a lot.", 1)
|
|
|
|
/obj/item/organ/proc/robotize() //Being used to make robutt hearts, etc
|
|
robotic = ORGAN_ROBOT
|
|
src.status &= ~ORGAN_BROKEN
|
|
src.status &= ~ORGAN_BLEEDING
|
|
src.status &= ~ORGAN_SPLINTED
|
|
src.status &= ~ORGAN_CUT_AWAY
|
|
|
|
/obj/item/organ/proc/mechassist() //Used to add things like pacemakers, etc
|
|
robotize()
|
|
robotic = ORGAN_ASSISTED
|
|
min_bruised_damage = 15
|
|
min_broken_damage = 35
|
|
|
|
/obj/item/organ/proc/digitize() //Used to make the circuit-brain. On this level in the event more circuit-organs are added/tweaks are wanted.
|
|
robotize()
|
|
|
|
/obj/item/organ/emp_act(severity)
|
|
if(!(robotic >= ORGAN_ROBOT))
|
|
return
|
|
switch (severity)
|
|
if (1)
|
|
take_damage(5)
|
|
if (2)
|
|
take_damage(2)
|
|
|
|
/obj/item/organ/proc/removed(var/mob/living/user)
|
|
|
|
if(!istype(owner))
|
|
return
|
|
|
|
owner.internal_organs_by_name[organ_tag] = null
|
|
owner.internal_organs_by_name -= organ_tag
|
|
owner.internal_organs_by_name -= null
|
|
owner.internal_organs -= src
|
|
|
|
var/obj/item/organ/external/affected = owner.get_organ(parent_organ)
|
|
if(affected) affected.internal_organs -= src
|
|
|
|
loc = get_turf(owner)
|
|
processing_objects |= src
|
|
rejecting = null
|
|
var/datum/reagent/blood/organ_blood = locate(/datum/reagent/blood) in reagents.reagent_list
|
|
if(!organ_blood || !organ_blood.data["blood_DNA"])
|
|
owner.vessel.trans_to(src, 5, 1, 1)
|
|
|
|
if(owner && vital)
|
|
if(user)
|
|
user.attack_log += "\[[time_stamp()]\]<font color='red'> removed a vital organ ([src]) from [owner.name] ([owner.ckey]) (INTENT: [uppertext(user.a_intent)])</font>"
|
|
owner.attack_log += "\[[time_stamp()]\]<font color='orange'> had a vital organ ([src]) removed by [user.name] ([user.ckey]) (INTENT: [uppertext(user.a_intent)])</font>"
|
|
msg_admin_attack("[user.name] ([user.ckey]) removed a vital organ ([src]) from [owner.name] ([owner.ckey]) (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
|
owner.death()
|
|
|
|
owner = null
|
|
|
|
/obj/item/organ/proc/replaced(var/mob/living/carbon/human/target,var/obj/item/organ/external/affected)
|
|
|
|
if(!istype(target)) return
|
|
|
|
var/datum/reagent/blood/transplant_blood = locate(/datum/reagent/blood) in reagents.reagent_list
|
|
transplant_data = list()
|
|
if(!transplant_blood)
|
|
transplant_data["species"] = target.species.name
|
|
transplant_data["blood_type"] = target.dna.b_type
|
|
transplant_data["blood_DNA"] = target.dna.unique_enzymes
|
|
else
|
|
transplant_data["species"] = transplant_blood.data["species"]
|
|
transplant_data["blood_type"] = transplant_blood.data["blood_type"]
|
|
transplant_data["blood_DNA"] = transplant_blood.data["blood_DNA"]
|
|
|
|
owner = target
|
|
loc = owner
|
|
processing_objects -= src
|
|
target.internal_organs |= src
|
|
affected.internal_organs |= src
|
|
target.internal_organs_by_name[organ_tag] = src
|
|
|
|
/obj/item/organ/proc/bitten(mob/user)
|
|
|
|
if(robotic >= ORGAN_ROBOT)
|
|
return
|
|
|
|
user << "<span class='notice'>You take an experimental bite out of \the [src].</span>"
|
|
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in reagents.reagent_list
|
|
blood_splatter(src,B,1)
|
|
|
|
user.drop_from_inventory(src)
|
|
var/obj/item/weapon/reagent_containers/food/snacks/organ/O = new(get_turf(src))
|
|
O.name = name
|
|
O.icon = icon
|
|
O.icon_state = icon_state
|
|
|
|
// Pass over the blood.
|
|
reagents.trans_to(O, reagents.total_volume)
|
|
|
|
if(fingerprints) O.fingerprints = fingerprints.Copy()
|
|
if(fingerprintshidden) O.fingerprintshidden = fingerprintshidden.Copy()
|
|
if(fingerprintslast) O.fingerprintslast = fingerprintslast
|
|
|
|
user.put_in_active_hand(O)
|
|
qdel(src)
|
|
|
|
/obj/item/organ/attack_self(mob/user as mob)
|
|
|
|
// Convert it to an edible form, yum yum.
|
|
if(!(robotic >= ORGAN_ROBOT) && user.a_intent == I_HELP && user.zone_sel.selecting == O_MOUTH)
|
|
bitten(user)
|
|
return
|
|
|
|
/obj/item/organ/proc/can_feel_pain()
|
|
return !(robotic >= (ORGAN_ROBOT|ORGAN_DESTROYED)) && !(species.flags & NO_PAIN)
|