diff --git a/code/game/objects/items/devices/scanners/autopsy_scanner.dm b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
index 46f32f3cf86..af0ff55abd1 100644
--- a/code/game/objects/items/devices/scanners/autopsy_scanner.dm
+++ b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
@@ -20,9 +20,9 @@
if(!user.can_read(src) || user.is_blind())
return ITEM_INTERACT_BLOCKING
- var/mob/living/M = interacting_with
+ var/mob/living/scanned = interacting_with
- if(M.stat != DEAD && !HAS_TRAIT(M, TRAIT_FAKEDEATH)) // good job, you found a loophole
+ if(scanned.stat != DEAD && !HAS_TRAIT(scanned, TRAIT_FAKEDEATH)) // good job, you found a loophole
to_chat(user, span_deadsay("[icon2html(src, user)] ERROR! CANNOT SCAN LIVE CADAVERS. PROCURE HEALTH ANALYZER OR TERMINATE PATIENT."))
return ITEM_INTERACT_BLOCKING
@@ -38,10 +38,10 @@
\n[span_info("Body temperature: ???")]")
return
- user.visible_message(span_notice("[user] scans [M]'s cadaver."))
+ user.visible_message(span_notice("[user] scans [scanned]'s cadaver."))
to_chat(user, span_deadsay("[icon2html(src, user)] ANALYZING CADAVER."))
- healthscan(user, M, advanced = TRUE)
+ healthscan(user, scanned, advanced = TRUE)
add_fingerprint(user)
@@ -49,40 +49,142 @@
if(scanned.stat != DEAD)
return
+ var/obj/item/paper/autopsy_report = new(get_turf(src))
+ autopsy_report.color = "#99ccff"
+ autopsy_report.name = "autopsy report of [scanned] - [station_time_timestamp()])"
+ var/final_report_text = "
Autopsy report. Time of Autopsy: [station_time_timestamp()]"
+
+ //A lot of this is extremely similar to /proc/healthscan() - but with different formatting, no color, and some added/removed info
+ //Does not list quirks/exhaustion/how to repair wounds
+ //DOES list wound sources/
var/list/autopsy_information = list()
- autopsy_information += "[scanned.name] - Species: [scanned.dna.species.name]"
- autopsy_information += "Time of Death - [scanned.station_timestamp_timeofdeath]"
- autopsy_information += "Time of Autopsy - [station_time_timestamp()]"
- autopsy_information += "Autopsy Coroner - [user.name]"
+ autopsy_information += "Autopsy Coroner - [user.name]
"
- autopsy_information += "Toxin damage: [CEILING(scanned.getToxLoss(), 1)]"
- autopsy_information += "Oxygen damage: [CEILING(scanned.getOxyLoss(), 1)]"
+ autopsy_information += "Analyzing results for [scanned.name]:"
+ autopsy_information += "Time of Death - [scanned.station_timestamp_timeofdeath]"
+ autopsy_information += "Subject has been dead for [DisplayTimeText(round(world.time - scanned.timeofdeath))].
"
- autopsy_information += "Bodypart Data
"
- for(var/obj/item/bodypart/bodyparts as anything in scanned.bodyparts)
- autopsy_information += "[bodyparts.name]
"
- autopsy_information += "BRUTE: [bodyparts.brute_dam] | BURN: [bodyparts.burn_dam]
"
- if(!bodyparts.wounds)
+ var/oxy_loss = scanned.getOxyLoss()
+ var/tox_loss = scanned.getToxLoss()
+ var/fire_loss = scanned.getFireLoss()
+ var/brute_loss = scanned.getBruteLoss()
+ /// "Body Data" portion of the autopsy - damage, wounds, and limbs
+ var/dmgreport = "Body Data:\
+ \
+ \
+ | Damage: | \
+ Brute | \
+ Burn | \
+ Toxin | \
+ Suffocation | \
+
"
+ for(var/zone in scanned.get_all_limbs()) //Same order every time for consistency across all humans
+ var/obj/item/bodypart/limb = scanned.get_bodypart(zone)
+ if(isnull(limb))
+ dmgreport += ""
+ dmgreport += "| [capitalize(parse_zone(zone))]: | "
+ dmgreport += "- | "
+ dmgreport += "- | "
+ dmgreport += "
"
+ dmgreport += "| ↳ Physical trauma: Dismembered |
"
continue
- autopsy_information += "Wounds found:
"
- for(var/datum/wound/wounds as anything in bodyparts.wounds)
- if(wounds.wound_source)
- autopsy_information += "[wounds.name] - Caused by [wounds.wound_source]
"
+ var/has_any_embeds = length(limb.embedded_objects) >= 1
+ var/has_any_wounds = length(limb.wounds) >= 1
+ dmgreport += ""
+ dmgreport += "| [capitalize(limb.name)]: | "
+ dmgreport += "[limb.brute_dam > 0 ? ceil(limb.brute_dam) : "0"] | "
+ dmgreport += "[limb.burn_dam > 0 ? ceil(limb.burn_dam) : "0"] | "
+ if(zone == BODY_ZONE_CHEST) // tox/oxy is stored in the chest
+ dmgreport += "[tox_loss > 0 ? ceil(tox_loss) : "0"] | "
+ dmgreport += "[oxy_loss > 0 ? ceil(oxy_loss) : "0"] | "
+ dmgreport += "
"
+ if(has_any_embeds)
+ var/list/embedded_names = list()
+ for(var/obj/item/embed as anything in limb.embedded_objects)
+ embedded_names[capitalize(embed.name)] += 1
+ for(var/embedded_name in embedded_names)
+ var/displayed = embedded_name
+ var/embedded_amt = embedded_names[embedded_name]
+ if(embedded_amt > 1)
+ displayed = "[embedded_amt]x [embedded_name]"
+ dmgreport += "| ↳ Foreign object(s): [displayed]. |
"
+ if(has_any_wounds)
+ for(var/datum/wound/wound as anything in limb.wounds)
+ dmgreport += "| ↳ Physical trauma: [wound.name] ([wound.severity_text()]) - Caused by [wound.wound_source]. |
"
- autopsy_information += "Organ Data"
- for(var/obj/item/organ/organs as anything in scanned.organs)
- autopsy_information += "[organs.name]: [CEILING(organs.damage, 1)] damage
"
+ dmgreport += "\
+ | Overall: | \
+ [ceil(brute_loss)] Brute | \
+ [ceil(fire_loss)] Burn | \
+ [ceil(tox_loss)] Toxin | \
+ [ceil(oxy_loss)] Suffocation | \
+
"
+ dmgreport += "
"
+ autopsy_information += dmgreport
- autopsy_information += "Chemical Data"
- for(var/datum/reagent/scanned_reagents as anything in scanned.reagents.reagent_list)
- if(scanned_reagents.chemical_flags & REAGENT_INVISIBLE)
- continue
- autopsy_information += "[round(scanned_reagents.volume, 0.1)] unit\s of [scanned_reagents.name]
"
- autopsy_information += "Chemical Information: [scanned_reagents.description]
"
+ // Only humanoids list organs, implants, gene stability, species and core-temp
+ if(ishuman(scanned))
+ var/mob/living/carbon/human/humantarget = scanned
+ /// "Organ Data" portion of the autopsy - damage, functional status (or if it's missing), and implants
+ var/organreport = "Organ Data:\
+ \
+ \
+ | Organ: | \
+ Dmg | \
+ Status | \
+
"
- autopsy_information += "Blood Data"
+ var/list/missing_organs = humantarget.get_missing_organs()
+ for(var/sorted_slot in GLOB.organ_process_order) //Same order every time for consistency across all humans
+ var/obj/item/organ/organ = humantarget.get_organ_slot(sorted_slot)
+ if(isnull(organ))
+ if(missing_organs[sorted_slot])
+ organreport += "| [missing_organs[sorted_slot]]: | \
+ - | \
+ Missing |
"
+ continue
+ var/status = organ.get_status_text(advanced = TRUE, add_tooltips = FALSE, colored = FALSE)
+ var/appendix = organ.get_status_appendix(advanced = TRUE, add_tooltips = FALSE)
+ if(!status)
+ status ||= "OK" // otherwise flawless organs have no status reported by default
+ organreport += "\
+ | [capitalize(organ.name)]: | \
+ [organ.damage > 0 ? ceil(organ.damage) : "0"] | \
+ [status] | \
+
"
+ if(appendix)
+ organreport += "| ↳ [appendix] |
"
+ organreport += "
" //
comes after Cybernetics below
+ autopsy_information += organreport
+
+ var/mutant = HAS_TRAIT(humantarget, TRAIT_HULK) //Also applied by genetics infusions
+ // Cybernetics
+ var/list/cyberimps
+ for(var/obj/item/organ/target_organ as anything in humantarget.organs)
+ if(IS_ROBOTIC_ORGAN(target_organ) && !(target_organ.organ_flags & ORGAN_HIDDEN))
+ LAZYADD(cyberimps, target_organ.examine_title(user))
+ if(target_organ.organ_flags & ORGAN_MUTANT)
+ mutant = TRUE
+ if(LAZYLEN(cyberimps))
+ autopsy_information += "Detected cybernetic modifications:"
+ autopsy_information += "[english_list(cyberimps, and_text = ", and ")]"
+
+ autopsy_information += "
"
+
+ // Genetic Stability, Species, and Body Temperature
+ if(humantarget.has_dna() && humantarget.dna.stability != initial(humantarget.dna.stability))
+ autopsy_information += "Genetic Stability: [humantarget.dna.stability]%."
+ var/datum/species/targetspecies = humantarget.dna.species
+ var/disguised = !ishumanbasic(humantarget) && istype(humantarget.head, /obj/item/clothing/head/hooded/human_head) && istype(humantarget.wear_suit, /obj/item/clothing/suit/hooded/bloated_human)
+ var/species_name = "[disguised ? "\"[/datum/species/human::name]\"" : targetspecies.name][mutant ? "-derived mutant" : ""]"
+ autopsy_information += "Species: [species_name]"
+ autopsy_information += "Core temperature: [round(humantarget.coretemperature-T0C, 0.1)] °C ([round(humantarget.coretemperature*1.8-459.67,0.1)] °F)"
+ // (End of humanoid-only information)
+ autopsy_information += "Body temperature: [round(scanned.bodytemperature-T0C, 0.1)] °C ([round(scanned.bodytemperature*1.8-459.67,0.1)] °F)"
+
+ // Blood Info
if(HAS_TRAIT(scanned, TRAIT_HUSK))
- autopsy_information += "Blood can't be found, victim is husked by: "
+ autopsy_information += "Blood can't be found, subject is husked by: "
if(HAS_TRAIT_FROM(scanned, TRAIT_HUSK, BURN))
autopsy_information += "Severe burns."
else if (HAS_TRAIT_FROM(scanned, TRAIT_HUSK, CHANGELING_DRAIN))
@@ -93,23 +195,46 @@
var/datum/blood_type/blood_type = scanned.get_bloodtype()
if(blood_type)
var/blood_percent = round((scanned.blood_volume / BLOOD_VOLUME_NORMAL) * 100)
- if (blood_type.get_type())
- autopsy_information += "[blood_type.get_blood_name()] Type: [blood_type.get_type()]
"
- autopsy_information += "[blood_type.get_blood_name()] Volume: [scanned.blood_volume] cl ([blood_percent]%)
"
+ var/blood_type_format
+ var/level_format
+ if(scanned.blood_volume <= BLOOD_VOLUME_SAFE && scanned.blood_volume > BLOOD_VOLUME_OKAY)
+ level_format = "LOW [blood_percent]%, [scanned.blood_volume] cl"
+ else if(scanned.blood_volume <= BLOOD_VOLUME_OKAY)
+ level_format = "CRITICAL [blood_percent]%, [scanned.blood_volume] cl"
+ else
+ level_format = "[blood_percent]%, [scanned.blood_volume] cl"
+ if(blood_type.get_type())
+ blood_type_format = "type: [blood_type.get_type()]"
+ autopsy_information += "[blood_type.get_blood_name()] level: [level_format], [blood_type_format]"
+ var/blood_alcohol_content = scanned.get_blood_alcohol_content()
+ if(blood_alcohol_content > 0)
+ autopsy_information += "↳ [blood_type?.get_blood_name() || "Blood"] alcohol content: [blood_alcohol_content]%"
+ autopsy_information += "
"
+ autopsy_information += "Chemical Data:"
+ for(var/datum/reagent/scanned_reagents as anything in scanned.reagents.reagent_list)
+ if(scanned_reagents.chemical_flags & REAGENT_INVISIBLE)
+ continue
+ autopsy_information += "[scanned_reagents.name]: [round(scanned_reagents.volume, 0.1)] unit\s in bloodstream."
+ autopsy_information += "↳ [scanned_reagents.description]"
+ autopsy_information += "
"
+
+ autopsy_information += "Disease Data:"
for(var/datum/disease/diseases as anything in scanned.diseases)
- autopsy_information += "Name: [diseases.name] | Type: [diseases.spread_text]
"
+ autopsy_information += "Disease Name: \"[diseases.name]\""
+ autopsy_information += "Transmission Type: [diseases.spread_text]"
if(!istype(diseases, /datum/disease/advance))
continue
- autopsy_information += "Symptoms:
"
+ autopsy_information += "Symptoms:"
var/datum/disease/advance/advanced_disease = diseases
for(var/datum/symptom/symptom as anything in advanced_disease.symptoms)
- autopsy_information += "[symptom.name] - [symptom.desc]
"
+ autopsy_information += "↳ [symptom.name] - [symptom.desc]"
+ autopsy_information += "
"
- var/obj/item/paper/autopsy_report = new(user.drop_location())
- autopsy_report.name = "autopsy report of [scanned] - [station_time_timestamp()])"
- autopsy_report.add_raw_text(autopsy_information.Join("\n"))
- autopsy_report.color = "#99ccff"
+ autopsy_information += "Coroner's Notes:" //Bottom of the page, anything past here is player-written
+
+ final_report_text += jointext(autopsy_information, "")
+ autopsy_report.add_raw_text(final_report_text)
autopsy_report.update_appearance()
user.put_in_hands(autopsy_report)
user.balloon_alert(user, "report printed")
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index d8033149045..b8e2918734b 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -283,24 +283,7 @@
Status | \
"
- var/list/missing_organs = list()
- if(!humantarget.get_organ_slot(ORGAN_SLOT_BRAIN))
- missing_organs[ORGAN_SLOT_BRAIN] = "Brain"
- if(humantarget.needs_heart() && !humantarget.get_organ_slot(ORGAN_SLOT_HEART))
- missing_organs[ORGAN_SLOT_HEART] = "Heart"
- if(!HAS_TRAIT_FROM(humantarget, TRAIT_NOBREATH, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantlungs) && !humantarget.get_organ_slot(ORGAN_SLOT_LUNGS))
- missing_organs[ORGAN_SLOT_LUNGS] = "Lungs"
- if(!HAS_TRAIT_FROM(humantarget, TRAIT_LIVERLESS_METABOLISM, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantliver) && !humantarget.get_organ_slot(ORGAN_SLOT_LIVER))
- missing_organs[ORGAN_SLOT_LIVER] = "Liver"
- if(!HAS_TRAIT_FROM(humantarget, TRAIT_NOHUNGER, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantstomach) && !humantarget.get_organ_slot(ORGAN_SLOT_STOMACH))
- missing_organs[ORGAN_SLOT_STOMACH] ="Stomach"
- if(!isnull(humantarget.dna.species.mutanttongue) && !humantarget.get_organ_slot(ORGAN_SLOT_TONGUE))
- missing_organs[ORGAN_SLOT_TONGUE] = "Tongue"
- if(!isnull(humantarget.dna.species.mutantears) && !humantarget.get_organ_slot(ORGAN_SLOT_EARS))
- missing_organs[ORGAN_SLOT_EARS] = "Ears"
- if(!isnull(humantarget.dna.species.mutantears) && !humantarget.get_organ_slot(ORGAN_SLOT_EYES))
- missing_organs[ORGAN_SLOT_EYES] = "Eyes"
-
+ var/list/missing_organs = humantarget.get_missing_organs()
// Follow same order as in the organ_process_order so it's consistent across all humans
for(var/sorted_slot in GLOB.organ_process_order)
var/obj/item/organ/organ = humantarget.get_organ_slot(sorted_slot)
diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm
index 54de85f5467..66164ada11e 100644
--- a/code/modules/surgery/organs/_organ.dm
+++ b/code/modules/surgery/organs/_organ.dm
@@ -395,28 +395,28 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
replacement.set_organ_damage(damage)
/// Called by medical scanners to get a simple summary of how healthy the organ is. Returns an empty string if things are fine.
-/obj/item/organ/proc/get_status_text(advanced, add_tooltips)
+/obj/item/organ/proc/get_status_text(advanced, add_tooltips, colored = TRUE)
if(advanced && (organ_flags & ORGAN_HAZARDOUS))
- return conditional_tooltip("Harmful Foreign Body", "Remove surgically.", add_tooltips)
+ return conditional_tooltip("[colored ? "" : ""]Harmful Foreign Body[colored ? "" : ""]", "Remove surgically.", add_tooltips)
if(organ_flags & ORGAN_EMP)
- return conditional_tooltip("EMP-Derived Failure", "Repair or replace surgically.", add_tooltips)
+ return conditional_tooltip("[colored ? "" : ""]EMP-Derived Failure[colored ? "" : ""]", "Repair or replace surgically.", add_tooltips)
var/tech_text = ""
if(owner.has_reagent(/datum/reagent/inverse/technetium))
tech_text = "[round((damage / maxHealth) * 100, 1)]% damaged"
if(organ_flags & ORGAN_FAILING)
- return conditional_tooltip("[tech_text || "Non-Functional"]", "Repair or replace surgically.", add_tooltips)
+ return conditional_tooltip("[colored ? "" : ""][tech_text || "Non-Functional"][colored ? "" : ""]", "Repair or replace surgically.", add_tooltips)
if(damage > high_threshold)
- return conditional_tooltip("[tech_text || "Severely Damaged"]", "[healing_factor ? "Treat with rest or use specialty medication." : "Repair surgically or use specialty medication."]", add_tooltips && owner.stat != DEAD)
+ return conditional_tooltip("[colored ? "" : ""][tech_text || "Severely Damaged"][colored ? "" : ""]", "[healing_factor ? "Treat with rest or use specialty medication." : "Repair surgically or use specialty medication."]", add_tooltips && owner.stat != DEAD)
if(damage > low_threshold)
- return conditional_tooltip("[tech_text || "Mildly Damaged"] ", "[healing_factor ? "Treat with rest." : "Use specialty medication."]", add_tooltips && owner.stat != DEAD)
+ return conditional_tooltip("[colored ? "" : ""][tech_text || "Mildly Damaged"][colored ? "" : ""]", "[healing_factor ? "Treat with rest." : "Use specialty medication."]", add_tooltips && owner.stat != DEAD)
if(tech_text)
- return "[tech_text]"
+ return "[colored ? "" : ""][tech_text][colored ? "" : ""]"
return ""
diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm
index bec23ed5689..5ae800d95ae 100644
--- a/code/modules/surgery/organs/helpers.dm
+++ b/code/modules/surgery/organs/helpers.dm
@@ -43,3 +43,30 @@
/mob/living/carbon/get_organ_slot(slot)
. = organs_slot[slot]
+/**
+ * Returns a list of all missing organs this species should have
+ *
+ * list [key] is the ORGAN_SLOT missing an organ, list value is the text name of the slot organ
+ */
+/mob/living/carbon/human/proc/get_missing_organs()
+ var/mob/living/carbon/human/humantarget = src
+ var/list/missing_organs = list()
+
+ if(!humantarget.get_organ_slot(ORGAN_SLOT_BRAIN))
+ missing_organs[ORGAN_SLOT_BRAIN] = "Brain"
+ if(humantarget.needs_heart() && !humantarget.get_organ_slot(ORGAN_SLOT_HEART))
+ missing_organs[ORGAN_SLOT_HEART] = "Heart"
+ if(!HAS_TRAIT_FROM(humantarget, TRAIT_NOBREATH, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantlungs) && !humantarget.get_organ_slot(ORGAN_SLOT_LUNGS))
+ missing_organs[ORGAN_SLOT_LUNGS] = "Lungs"
+ if(!HAS_TRAIT_FROM(humantarget, TRAIT_LIVERLESS_METABOLISM, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantliver) && !humantarget.get_organ_slot(ORGAN_SLOT_LIVER))
+ missing_organs[ORGAN_SLOT_LIVER] = "Liver"
+ if(!HAS_TRAIT_FROM(humantarget, TRAIT_NOHUNGER, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantstomach) && !humantarget.get_organ_slot(ORGAN_SLOT_STOMACH))
+ missing_organs[ORGAN_SLOT_STOMACH] ="Stomach"
+ if(!isnull(humantarget.dna.species.mutanttongue) && !humantarget.get_organ_slot(ORGAN_SLOT_TONGUE))
+ missing_organs[ORGAN_SLOT_TONGUE] = "Tongue"
+ if(!isnull(humantarget.dna.species.mutantears) && !humantarget.get_organ_slot(ORGAN_SLOT_EARS))
+ missing_organs[ORGAN_SLOT_EARS] = "Ears"
+ if(!isnull(humantarget.dna.species.mutantears) && !humantarget.get_organ_slot(ORGAN_SLOT_EYES))
+ missing_organs[ORGAN_SLOT_EYES] = "Eyes"
+
+ return missing_organs
diff --git a/code/modules/surgery/organs/internal/appendix/_appendix.dm b/code/modules/surgery/organs/internal/appendix/_appendix.dm
index e7594666c4f..29dbbe6a73d 100644
--- a/code/modules/surgery/organs/internal/appendix/_appendix.dm
+++ b/code/modules/surgery/organs/internal/appendix/_appendix.dm
@@ -99,7 +99,7 @@
ADD_TRAIT(organ_owner, TRAIT_DISEASELIKE_SEVERITY_MEDIUM, type)
organ_owner.med_hud_set_status()
-/obj/item/organ/appendix/get_status_text(advanced, add_tooltips)
+/obj/item/organ/appendix/get_status_text(advanced, add_tooltips, colored)
if(!(organ_flags & ORGAN_FAILING) && inflamation_stage)
return conditional_tooltip("Inflamed", "Remove surgically.", add_tooltips)
return ..()
diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm
index 0e00dce883a..ce04a2fb66d 100644
--- a/code/modules/surgery/organs/internal/heart/_heart.dm
+++ b/code/modules/surgery/organs/internal/heart/_heart.dm
@@ -97,7 +97,7 @@
/obj/item/organ/heart/proc/is_beating()
return beating
-/obj/item/organ/heart/get_status_text(advanced, add_tooltips)
+/obj/item/organ/heart/get_status_text(advanced, add_tooltips, colored)
if(owner.has_status_effect(/datum/status_effect/heart_attack))
return conditional_tooltip("Myocardial Infarction", "Apply defibrillation immediately. Similar electric shocks may work in emergencies.", add_tooltips)
if((!beating && !(organ_flags & ORGAN_FAILING) && owner.needs_heart() && owner.stat != DEAD))