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:\ + \ + \ + \ + \ + \ + \ + \ + " + 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 += "" + dmgreport += "" + dmgreport += "" + dmgreport += "" + dmgreport += "" 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 += "" + dmgreport += "" + dmgreport += "" + if(zone == BODY_ZONE_CHEST) // tox/oxy is stored in the chest + dmgreport += "" + dmgreport += "" + 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 += "" + if(has_any_wounds) + for(var/datum/wound/wound as anything in limb.wounds) + dmgreport += "" - 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 += "\ + \ + \ + \ + \ + \ + " + dmgreport += "
Damage:BruteBurnToxinSuffocation
[capitalize(parse_zone(zone))]:--
↳ Physical trauma: Dismembered
[capitalize(limb.name)]:[limb.brute_dam > 0 ? ceil(limb.brute_dam) : "0"][limb.burn_dam > 0 ? ceil(limb.burn_dam) : "0"][tox_loss > 0 ? ceil(tox_loss) : "0"][oxy_loss > 0 ? ceil(oxy_loss) : "0"]
↳ Foreign object(s): [displayed].
↳ Physical trauma: [wound.name] ([wound.severity_text()]) - Caused by [wound.wound_source].
Overall:[ceil(brute_loss)] Brute[ceil(fire_loss)] Burn[ceil(tox_loss)] Toxin[ceil(oxy_loss)] Suffocation

" + 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:\ + \ + \ + \ + \ + \ + " - 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 += "\ + \ + " + 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 += "\ + \ + \ + \ + " + if(appendix) + organreport += "" + organreport += "
Organ:DmgStatus
[missing_organs[sorted_slot]]:-Missing
[capitalize(organ.name)]:[organ.damage > 0 ? ceil(organ.damage) : "0"][status]
↳ [appendix]
" //
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))