From cd412b06d2e76cae0fe460af35d667f1a17a5e84 Mon Sep 17 00:00:00 2001 From: Oxotnak <107066254+Oxotnak@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:25:26 +0300 Subject: [PATCH] Health analyzer can print results (#86464) ## About The Pull Request Adds det's scanner feature to health analyzer so it's able to print analyzed patient results. Show to your colleague how horribly was destroyed clown's body! Prove to sec that John Syndicate hurt you really badly! ![Screenshot_21](https://github.com/user-attachments/assets/4528e938-edc3-4f70-b707-0463b04f2481) ## Why It's Good For The Game It's good for when you want to roleplay in somethin' like a court and you able to prove that someone beat you up 'cause we can't always count on medic's words, people can lie you know ## Changelog :cl: add: health analyzer now able to print scanned results via Ctrl-shift-click! qol: text from healthscan proc now use < br > /:cl: --- .../items/devices/scanners/health_analyzer.dm | 158 +++++++++++------- 1 file changed, 96 insertions(+), 62 deletions(-) diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index 40ca839cb9a..73742548852 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -30,6 +30,8 @@ custom_price = PAYCHECK_COMMAND /// If this analyzer will give a bonus to wound treatments apon woundscan. var/give_wound_treatment_bonus = FALSE + var/last_scan_text + var/scanner_busy = FALSE /obj/item/healthanalyzer/Initialize(mapload) . = ..() @@ -38,7 +40,7 @@ /obj/item/healthanalyzer/examine(mob/user) . = ..() if(src.mode != SCANNER_NO_MODE) - . += span_notice("Alt-click [src] to toggle the limb damage readout.") + . += span_notice("Alt-click [src] to toggle the limb damage readout. Ctrl-shift-click to print readout report.") /obj/item/healthanalyzer/suicide_act(mob/living/carbon/user) user.visible_message(span_suicide("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!")) @@ -71,10 +73,10 @@ if ((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50)) user.visible_message(span_warning("[user] analyzes the floor's vitals!"), \ span_notice("You stupidly try to analyze the floor's vitals!")) - to_chat(user, "[span_info("Analyzing results for The floor:\n\tOverall status: Healthy")]\ - \n[span_info("Key: Suffocation/Toxin/Burn/Brute")]\ - \n[span_info("\tDamage specifics: 0-0-0-0")]\ - \n[span_info("Body temperature: ???")]") + to_chat(user, "[span_info("Analyzing results for The floor:
\tOverall status: Healthy")]\ +
[span_info("Key: Suffocation/Toxin/Burn/Brute")]\ +
[span_info("\tDamage specifics: 0-0-0-0")]\ +
[span_info("Body temperature: ???")]") return if(ispodperson(M) && !advanced) @@ -88,6 +90,7 @@ switch (scanmode) if (SCANMODE_HEALTH) healthscan(user, M, mode, advanced) + last_scan_text = healthscan(user, M, mode, advanced, tochat = FALSE) if (SCANMODE_WOUND) woundscan(user, M, src) @@ -149,12 +152,12 @@ mob_status = span_alert("Deceased") oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss - render_list += "[span_info("Analyzing results for [target]:")]\nOverall status: [mob_status]\n" + render_list += "[span_info("Analyzing results for [target]:")]
Overall status: [mob_status]
" if(ishuman(target)) var/mob/living/carbon/human/humantarget = target if(humantarget.undergoing_cardiac_arrest() && humantarget.stat != DEAD) - render_list += "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!\n" + render_list += "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!
" if(humantarget.has_reagent(/datum/reagent/inverse/technetium)) advanced = TRUE @@ -164,22 +167,22 @@ if(HAS_TRAIT(target, TRAIT_HUSK)) if(advanced) if(HAS_TRAIT_FROM(target, TRAIT_HUSK, BURN)) - render_list += "Subject has been husked by severe burns.\n" + render_list += "Subject has been husked by severe burns.
" else if (HAS_TRAIT_FROM(target, TRAIT_HUSK, CHANGELING_DRAIN)) - render_list += "Subject has been husked by dessication.\n" + render_list += "Subject has been husked by dessication.
" else - render_list += "Subject has been husked by mysterious causes.\n" + render_list += "Subject has been husked by mysterious causes.
" else - render_list += "Subject has been husked.\n" + render_list += "Subject has been husked.
" if(target.getStaminaLoss()) if(advanced) - render_list += "Fatigue level: [target.getStaminaLoss()]%.\n" + render_list += "Fatigue level: [target.getStaminaLoss()]%.
" else - render_list += "Subject appears to be suffering from fatigue.\n" + render_list += "Subject appears to be suffering from fatigue.
" if (!target.get_organ_slot(ORGAN_SLOT_BRAIN)) // kept exclusively for soul purposes - render_list += "Subject lacks a brain.\n" + render_list += "Subject lacks a brain.
" if(iscarbon(target)) var/mob/living/carbon/carbontarget = target @@ -198,14 +201,14 @@ trauma_desc += "permanent " trauma_desc += trauma.scan_desc trauma_text += trauma_desc - render_list += "Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n" + render_list += "Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].
" if(carbontarget.quirks.len) - render_list += "Subject Major Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY, from_scan = TRUE)].\n" + render_list += "Subject Major Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY, from_scan = TRUE)].
" if(advanced) - render_list += "Subject Minor Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MINOR_DISABILITY, TRUE)].\n" + render_list += "Subject Minor Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MINOR_DISABILITY, TRUE)].
" if (HAS_TRAIT(target, TRAIT_IRRADIATED)) - render_list += "Subject is irradiated. Supply toxin healing.\n" + render_list += "Subject is irradiated. Supply toxin healing.
" //Eyes and ears if(advanced && iscarbon(target)) @@ -215,24 +218,24 @@ var/obj/item/organ/internal/ears/ears = carbontarget.get_organ_slot(ORGAN_SLOT_EARS) if(istype(ears)) if(HAS_TRAIT_FROM(carbontarget, TRAIT_DEAF, GENETIC_MUTATION)) - render_list += "Subject is genetically deaf.\n" + render_list += "Subject is genetically deaf.
" else if(HAS_TRAIT_FROM(carbontarget, TRAIT_DEAF, EAR_DAMAGE)) - render_list += "Subject is deaf from ear damage.\n" + render_list += "Subject is deaf from ear damage.
" else if(HAS_TRAIT(carbontarget, TRAIT_DEAF)) - render_list += "Subject is deaf.\n" + render_list += "Subject is deaf.
" else if(ears.damage) - render_list += "Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.\n" + render_list += "Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.
" if(ears.deaf) - render_list += "Subject is [ears.damage > ears.maxHealth ? "permanently": "temporarily"] deaf.\n" + render_list += "Subject is [ears.damage > ears.maxHealth ? "permanently": "temporarily"] deaf.
" // Eye status var/obj/item/organ/internal/eyes/eyes = carbontarget.get_organ_slot(ORGAN_SLOT_EYES) if(istype(eyes)) if(carbontarget.is_blind()) - render_list += "Subject is blind.\n" + render_list += "Subject is blind.
" else if(carbontarget.is_nearsighted()) - render_list += "Subject is nearsighted.\n" + render_list += "Subject is nearsighted.
" // Body part damage report if(iscarbon(target)) @@ -264,7 +267,7 @@ render_list += dmgreport // tables do not need extra linebreak for(var/obj/item/bodypart/limb as anything in carbontarget.bodyparts) for(var/obj/item/embed as anything in limb.embedded_objects) - render_list += "Embedded object: [embed] located in \the [limb.plaintext_zone]\n" + render_list += "Embedded object: [embed] located in \the [limb.plaintext_zone]
" if(ishuman(target)) var/mob/living/carbon/human/humantarget = target @@ -316,34 +319,34 @@ //Genetic stability if(advanced && humantarget.has_dna()) - render_list += "Genetic Stability: [humantarget.dna.stability]%.\n" + render_list += "Genetic Stability: [humantarget.dna.stability]%.
" // Hulk and body temperature var/datum/species/targetspecies = humantarget.dna.species var/mutant = HAS_TRAIT(humantarget, TRAIT_HULK) - render_list += "Species: [targetspecies.name][mutant ? "-derived mutant" : ""]\n" + render_list += "Species: [targetspecies.name][mutant ? "-derived mutant" : ""]
" var/core_temperature_message = "Core temperature: [round(humantarget.coretemperature-T0C, 0.1)] °C ([round(humantarget.coretemperature*1.8-459.67,0.1)] °F)" if(humantarget.coretemperature >= humantarget.get_body_temp_heat_damage_limit()) - render_list += "☼ [core_temperature_message] ☼\n" + render_list += "☼ [core_temperature_message] ☼
" else if(humantarget.coretemperature <= humantarget.get_body_temp_cold_damage_limit()) - render_list += "❄ [core_temperature_message] ❄\n" + render_list += "❄ [core_temperature_message] ❄
" else - render_list += "[core_temperature_message]\n" + render_list += "[core_temperature_message]
" var/body_temperature_message = "Body temperature: [round(target.bodytemperature-T0C, 0.1)] °C ([round(target.bodytemperature*1.8-459.67,0.1)] °F)" if(target.bodytemperature >= target.get_body_temp_heat_damage_limit()) - render_list += "☼ [body_temperature_message] ☼\n" + render_list += "☼ [body_temperature_message] ☼
" else if(target.bodytemperature <= target.get_body_temp_cold_damage_limit()) - render_list += "❄ [body_temperature_message] ❄\n" + render_list += "❄ [body_temperature_message] ❄
" else - render_list += "[body_temperature_message]\n" + render_list += "[body_temperature_message]
" // Time of death if(target.station_timestamp_timeofdeath && (target.stat == DEAD || ((HAS_TRAIT(target, TRAIT_FAKEDEATH)) && !advanced))) - render_list += "Time of Death: [target.station_timestamp_timeofdeath]\n" + render_list += "Time of Death: [target.station_timestamp_timeofdeath]
" var/tdelta = round(world.time - target.timeofdeath) - render_list += "Subject died [DisplayTimeText(tdelta)] ago.\n" + render_list += "Subject died [DisplayTimeText(tdelta)] ago.
" // Wounds if(iscarbon(target)) @@ -354,14 +357,14 @@ render_list += "Physical trauma[LAZYLEN(wounded_part.wounds) > 1 ? "s" : ""] detected in [wounded_part.name]" for(var/k in wounded_part.wounds) var/datum/wound/W = k - render_list += "
[W.name] ([W.severity_text()])\nRecommended treatment: [W.treat_text]
" // less lines than in woundscan() so we don't overload people trying to get basic med info + render_list += "
[W.name] ([W.severity_text()])
Recommended treatment: [W.treat_text]
" // less lines than in woundscan() so we don't overload people trying to get basic med info render_list += "
" //Diseases for(var/datum/disease/disease as anything in target.diseases) if(!(disease.visibility_flags & HIDDEN_SCANNER)) - render_list += "Warning: [disease.form] detected\n\ -
Name: [disease.name].\nType: [disease.spread_text].\nStage: [disease.stage]/[disease.max_stages].\nPossible Cure: [disease.cure_text]
\ + render_list += "Warning: [disease.form] detected
\ +
Name: [disease.name].
Type: [disease.spread_text].
Stage: [disease.stage]/[disease.max_stages].
Possible Cure: [disease.cure_text]
\
" // divs do not need extra linebreak // Blood Level @@ -370,26 +373,26 @@ var/blood_id = carbontarget.get_blood_id() if(blood_id) if(carbontarget.is_bleeding()) - render_list += "Subject is bleeding!\n" + render_list += "Subject is bleeding!
" var/blood_percent = round((carbontarget.blood_volume / BLOOD_VOLUME_NORMAL) * 100) var/blood_type = carbontarget.dna.blood_type if(blood_id != /datum/reagent/blood) // special blood substance var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id] blood_type = R ? R.name : blood_id if(carbontarget.blood_volume <= BLOOD_VOLUME_SAFE && carbontarget.blood_volume > BLOOD_VOLUME_OKAY) - render_list += "Blood level: LOW [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]\n" + render_list += "Blood level: LOW [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]
" else if(carbontarget.blood_volume <= BLOOD_VOLUME_OKAY) - render_list += "Blood level: CRITICAL [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]\n" + render_list += "Blood level: CRITICAL [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]
" else - render_list += "Blood level: [blood_percent]%, [carbontarget.blood_volume] cl, type: [blood_type]\n" + render_list += "Blood level: [blood_percent]%, [carbontarget.blood_volume] cl, type: [blood_type]
" // Blood Alcohol Content var/blood_alcohol_content = target.get_blood_alcohol_content() if(blood_alcohol_content > 0) if(blood_alcohol_content >= 0.24) - render_list += "Blood alcohol content: CRITICAL [blood_alcohol_content]%\n" + render_list += "Blood alcohol content: CRITICAL [blood_alcohol_content]%
" else - render_list += "Blood alcohol content: [blood_alcohol_content]%\n" + render_list += "Blood alcohol content: [blood_alcohol_content]%
" // Cybernetics if(iscarbon(target)) @@ -399,8 +402,8 @@ if(IS_ROBOTIC_ORGAN(cyberimp) && !(cyberimp.organ_flags & ORGAN_HIDDEN)) cyberimp_detect += "[!cyberimp_detect ? "[cyberimp.examine_title(user)]" : ", [cyberimp.examine_title(user)]"]" if(cyberimp_detect) - render_list += "Detected cybernetic modifications:\n" - render_list += "[cyberimp_detect]\n" + render_list += "Detected cybernetic modifications:
" + render_list += "[cyberimp_detect]
" // we handled the last
so we don't need handholding if(tochat) @@ -408,6 +411,37 @@ else return(jointext(render_list, "")) +/obj/item/healthanalyzer/click_ctrl_shift(mob/user) + . = ..() + if(!LAZYLEN(last_scan_text)) + balloon_alert(user, "no scans!") + return + if(scanner_busy) + balloon_alert(user, "analyzer busy!") + return + scanner_busy = TRUE + balloon_alert(user, "printing report...") + addtimer(CALLBACK(src, PROC_REF(print_report)), 2 SECONDS) + +/obj/item/healthanalyzer/proc/print_report(mob/user) + var/obj/item/paper/report_paper = new(get_turf(src)) + + report_paper.color = COLOR_STARLIGHT + report_paper.name = "Health scan report" + var/report_text = "
Health scan report. Time of scan: [station_time_timestamp()]


" + report_text += last_scan_text + + report_paper.add_raw_text(report_text) + report_paper.update_appearance() + + if(ismob(loc)) + var/mob/printer = loc + printer.put_in_hands(report_paper) + balloon_alert(printer, "logs cleared") + + report_text = list() + scanner_busy = FALSE + /proc/chemscan(mob/living/user, mob/living/target) if(user.incapacitated) return @@ -422,12 +456,12 @@ var/datum/reagent/reagent = r if(reagent.chemical_flags & REAGENT_INVISIBLE) //Don't show hidden chems on scanners continue - render_block += "[round(reagent.volume, 0.001)] units of [reagent.name][reagent.overdosed ? " - [span_boldannounce("OVERDOSING")]" : ".
"]\n" + render_block += "[round(reagent.volume, 0.001)] units of [reagent.name][reagent.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]
" if(!length(render_block)) //If no VISIBLY DISPLAYED reagents are present, we report as if there is nothing. - render_list += "Subject contains no reagents in their blood.\n" + render_list += "Subject contains no reagents in their blood.
" else - render_list += "Subject contains the following reagents in their blood:\n" + render_list += "Subject contains the following reagents in their blood:
" render_list += render_block //Otherwise, we add the header, reagent readouts, and clear the readout block for use on the stomach. render_block.Cut() @@ -440,35 +474,35 @@ if(bit.chemical_flags & REAGENT_INVISIBLE) continue if(!belly.food_reagents[bit.type]) - render_block += "[round(bit.volume, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]\n" + render_block += "[round(bit.volume, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]
" else var/bit_vol = bit.volume - belly.food_reagents[bit.type] if(bit_vol > 0) - render_block += "[round(bit_vol, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]\n" + render_block += "[round(bit_vol, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]
" if(!length(render_block)) - render_list += "Subject contains no reagents in their stomach.\n" + render_list += "Subject contains no reagents in their stomach.
" else - render_list += "Subject contains the following reagents in their stomach:\n" + render_list += "Subject contains the following reagents in their stomach:
" render_list += render_block // Addictions if(LAZYLEN(target.mind?.active_addictions)) - render_list += "Subject is addicted to the following types of drug:\n" + render_list += "Subject is addicted to the following types of drug:
" for(var/datum/addiction/addiction_type as anything in target.mind.active_addictions) - render_list += "[initial(addiction_type.name)]\n" + render_list += "[initial(addiction_type.name)]
" // Special eigenstasium addiction if(target.has_status_effect(/datum/status_effect/eigenstasium)) - render_list += "Subject is temporally unstable. Stabilising agent is recommended to reduce disturbances.\n" + render_list += "Subject is temporally unstable. Stabilising agent is recommended to reduce disturbances.
" // Allergies for(var/datum/quirk/quirky as anything in target.quirks) if(istype(quirky, /datum/quirk/item_quirk/allergic)) var/datum/quirk/item_quirk/allergic/allergies_quirk = quirky var/allergies = allergies_quirk.allergy_string - render_list += "Subject is extremely allergic to the following chemicals:\n" - render_list += "[allergies]\n" + render_list += "Subject is extremely allergic to the following chemicals:
" + render_list += "[allergies]
" // we handled the last
so we don't need handholding to_chat(user, examine_block(jointext(render_list, "")), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO) @@ -505,7 +539,7 @@ render_list += "Warning: Physical trauma[LAZYLEN(wounded_part.wounds) > 1? "s" : ""] detected in [wounded_part.name]" for(var/limb_wound in wounded_part.wounds) var/datum/wound/current_wound = limb_wound - render_list += "
[simple_scan ? current_wound.get_simple_scanner_description() : current_wound.get_scanner_description()]
\n" + render_list += "
[simple_scan ? current_wound.get_simple_scanner_description() : current_wound.get_scanner_description()]

" if (scanner.give_wound_treatment_bonus) ADD_TRAIT(current_wound, TRAIT_WOUND_SCANNED, ANALYZER_TRAIT) if(!advised) @@ -670,8 +704,8 @@ var/list/render = list() for(var/datum/disease/disease as anything in patient.diseases) if(!(disease.visibility_flags & HIDDEN_SCANNER)) - render += "Warning: [disease.form] detected\n\ -
Name: [disease.name].\nType: [disease.spread_text].\nStage: [disease.stage]/[disease.max_stages].\nPossible Cure: [disease.cure_text]
\ + render += "Warning: [disease.form] detected
\ +
Name: [disease.name].
Type: [disease.spread_text].
Stage: [disease.stage]/[disease.max_stages].
Possible Cure: [disease.cure_text]
\
" if(!length(render))