diff --git a/code/__DEFINES/chat.dm b/code/__DEFINES/chat.dm
index 6a70a3c0f25..8533a537c5d 100644
--- a/code/__DEFINES/chat.dm
+++ b/code/__DEFINES/chat.dm
@@ -38,3 +38,5 @@
#define debug_world_log(msg) if (GLOB.Debug2) log_world("DEBUG: [msg]")
/// Adds a generic box around whatever message you're sending in chat. Really makes things stand out.
#define examine_block(str) ("
" + str + "
")
+/// Helper which creates a chat message which may have a tooltip in some contexts, but not others.
+#define conditional_tooltip(normal_text, tooltip_text, condition) ((condition) ? (span_tooltip(tooltip_text, normal_text)) : (normal_text))
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 120d84ffb79..5b5ec34873f 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -262,6 +262,8 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define BODY_ZONE_PRECISE_L_FOOT "l_foot"
#define BODY_ZONE_PRECISE_R_FOOT "r_foot"
+GLOBAL_LIST_INIT(all_body_zones, list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+
//We will round to this value in damage calculations.
#define DAMAGE_PRECISION 0.1
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index 77728567b50..95248abe508 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -13,6 +13,10 @@
#define ORGAN_SYNTHETIC_EMP (1<<5)
///Can't be removed using surgery
#define ORGAN_UNREMOVABLE (1<<6)
+/// An organ that is ostensibly dangerous when inside a body
+#define ORGAN_HAZARDOUS (1<<7)
+/// Can't be seen by scanners
+#define ORGAN_HIDDEN (1<<8)
// Flags for the bodypart_flags var on /obj/item/bodypart
/// Bodypart cannot be dismembered or amputated
diff --git a/code/datums/components/irradiated.dm b/code/datums/components/irradiated.dm
index 44cfa091be2..3c427ca0542 100644
--- a/code/datums/components/irradiated.dm
+++ b/code/datums/components/irradiated.dm
@@ -51,11 +51,13 @@
/datum/component/irradiated/RegisterWithParent()
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean))
RegisterSignal(parent, COMSIG_GEIGER_COUNTER_SCAN, PROC_REF(on_geiger_counter_scan))
+ RegisterSignal(parent, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_healthscan))
/datum/component/irradiated/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_GEIGER_COUNTER_SCAN,
+ COMSIG_LIVING_HEALTHSCAN,
))
/datum/component/irradiated/Destroy(force, silent)
@@ -186,6 +188,12 @@
return COMSIG_GEIGER_COUNTER_SCAN_SUCCESSFUL
+/datum/component/irradiated/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode, tochat)
+ SIGNAL_HANDLER
+
+ render_list += conditional_tooltip("Subject is irradiated.", "Supply antiradiation or antitoxin, such as [/datum/reagent/medicine/potass_iodide::name] or [/datum/reagent/medicine/pen_acid::name].", tochat)
+ render_list += "
"
+
/atom/movable/screen/alert/irradiated
name = "Irradiated"
desc = "You're irradiated! Heal your toxins quick, and stand under a shower to halt the incoming damage."
diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm
index 267750f5761..a9541c17e4c 100644
--- a/code/datums/quirks/_quirk.dm
+++ b/code/datums/quirks/_quirk.dm
@@ -199,8 +199,9 @@
* Arguments:
* * Medical- If we want the long, fancy descriptions that show up in medical records, or if not, just the name
* * Category- Which types of quirks we want to print out. Defaults to everything
+ * * from_scan- If this call originates from a medical scanner. Currently unused.
*/
-/mob/living/proc/get_quirk_string(medical, category = CAT_QUIRK_ALL) //helper string. gets a string of all the quirks the mob has
+/mob/living/proc/get_quirk_string(medical, category = CAT_QUIRK_ALL, from_scan = FALSE) //helper string. gets a string of all the quirks the mob has
var/list/dat = list()
switch(category)
if(CAT_QUIRK_ALL)
diff --git a/code/datums/status_effects/debuffs/hallucination.dm b/code/datums/status_effects/debuffs/hallucination.dm
index cebb96c35a3..92f23cb9850 100644
--- a/code/datums/status_effects/debuffs/hallucination.dm
+++ b/code/datums/status_effects/debuffs/hallucination.dm
@@ -43,13 +43,13 @@
))
/// Signal proc for [COMSIG_LIVING_HEALTHSCAN]. Show we're hallucinating to (advanced) scanners.
-/datum/status_effect/hallucination/proc/on_health_scan(datum/source, list/render_list, advanced, mob/user, mode)
+/datum/status_effect/hallucination/proc/on_health_scan(datum/source, list/render_list, advanced, mob/user, mode, tochat)
SIGNAL_HANDLER
if(!advanced)
return
-
- render_list += "Subject is hallucinating.\n"
+ render_list += conditional_tooltip("Subject is hallucinating.", "Supply antipsychotic medication.", tochat)
+ render_list += "
"
/// Signal proc for [COMSIG_CARBON_CHECKING_BODYPART],
/// checking bodyparts while hallucinating can cause them to appear more damaged than they are
diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm
index da8812868dc..f6c16a415da 100644
--- a/code/datums/wounds/_wounds.dm
+++ b/code/datums/wounds/_wounds.dm
@@ -28,6 +28,8 @@
var/desc = ""
/// The basic treatment suggested by health analyzers
var/treat_text = ""
+ /// Even more basic treatment
+ var/treat_text_short = ""
/// What the limb looks like on a cursory examine
var/examine_desc = "is badly hurt"
@@ -648,9 +650,18 @@
return span_bold("[desc]!")
return "[desc]."
+/**
+ * Prints the details about the wound for the wound scanner on simple mode
+ */
/datum/wound/proc/get_scanner_description(mob/user)
- return "Type: [name]\nSeverity: [severity_text()]\nDescription: [desc]\nRecommended Treatment: [treat_text]"
+ return "Type: [name]
\
+ Severity: [severity_text()]
\
+ Description: [desc]
\
+ Recommended Treatment: [treat_text]"
+/**
+ * Returns what text describes this wound
+ */
/datum/wound/proc/severity_text()
switch(severity)
if(WOUND_SEVERITY_TRIVIAL)
@@ -658,9 +669,9 @@
if(WOUND_SEVERITY_MODERATE)
return "Moderate"
if(WOUND_SEVERITY_SEVERE)
- return "Severe"
+ return "Severe"
if(WOUND_SEVERITY_CRITICAL)
- return "Critical"
+ return "Critical"
/// Returns TRUE if our limb is the head or chest, FALSE otherwise.
/// Essential in the sense of "we cannot live without it".
diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm
index 61e63fb8122..ed612cfe0ec 100644
--- a/code/datums/wounds/bones.dm
+++ b/code/datums/wounds/bones.dm
@@ -217,7 +217,9 @@
/datum/wound/blunt/bone/moderate
name = "Joint Dislocation"
desc = "Patient's limb has been unset from socket, causing pain and reduced motor function."
- treat_text = "Recommended application of bonesetter to affected limb, though manual relocation by applying an aggressive grab to the patient and helpfully interacting with afflicted limb may suffice."
+ treat_text = "Apply Bonesetter to the affected limb. \
+ Manual relocation by via an aggressive grab and a tight hug to the affected limb may also suffice."
+ treat_text_short = "Apply Bonesetter, or manually relocate the limb."
examine_desc = "is awkwardly janked out of place"
occur_text = "janks violently and becomes unseated"
severity = WOUND_SEVERITY_MODERATE
@@ -341,7 +343,9 @@
/datum/wound/blunt/bone/severe
name = "Hairline Fracture"
desc = "Patient's bone has suffered a crack in the foundation, causing serious pain and reduced limb functionality."
- treat_text = "Recommended light surgical application of bone gel, though a sling of medical gauze will prevent worsening situation."
+ treat_text = "Repair surgically. In the event of an emergency, an application of bone gel over the affected area will fix over time. \
+ A splint or sling of medical gauze can also be used to prevent the fracture from worsening."
+ treat_text_short = "Repair surgically, or apply bone gel. A splint or gauze sling can also be used."
examine_desc = "appears grotesquely swollen, jagged bumps hinting at chips in the bone"
occur_text = "sprays chips of bone and develops a nasty looking bruise"
@@ -369,8 +373,11 @@
/// Compound Fracture (Critical Blunt)
/datum/wound/blunt/bone/critical
name = "Compound Fracture"
- desc = "Patient's bones have suffered multiple gruesome fractures, causing significant pain and near uselessness of limb."
- treat_text = "Immediate binding of affected limb, followed by surgical intervention ASAP."
+ desc = "Patient's bones have suffered multiple fractures, \
+ couped with a break in the skin, causing significant pain and near uselessness of limb."
+ treat_text = "Immediately bind the affected limb with gauze or a splint. Repair surgically. \
+ In the event of an emergency, bone gel and surgical tape can be applied to the affected area to fix over a long period of time."
+ treat_text_short = "Repair surgically, or apply bone gel and surgical tape. A splint or gauze sling should also be used."
examine_desc = "is thoroughly pulped and cracked, exposing shards of bone to open air"
occur_text = "cracks apart, exposing broken bones to open air"
diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm
index 741885c2911..733dc32fa4e 100644
--- a/code/datums/wounds/burns.dm
+++ b/code/datums/wounds/burns.dm
@@ -137,6 +137,13 @@
victim.gain_trauma(sepsis)
strikes_to_lose_limb--
+/datum/wound/burn/flesh/set_disabling(new_value)
+ . = ..()
+ if(new_value && strikes_to_lose_limb <= 0)
+ treat_text_short = "Amputate or augment limb immediately, or place the patient into cryogenics."
+ else
+ treat_text_short = initial(treat_text_short)
+
/datum/wound/burn/flesh/get_wound_description(mob/user)
if(strikes_to_lose_limb <= 0)
return span_deadsay("[victim.p_their(TRUE)] [limb.plaintext_zone] has locked up completely and is non-functional.")
@@ -170,10 +177,26 @@
return "[condition.Join()]"
+/datum/wound/burn/flesh/severity_text(simple = FALSE)
+ . = ..()
+ . += " Burn / "
+ switch(infestation)
+ if(-INFINITY to WOUND_INFECTION_MODERATE)
+ . += "No"
+ if(WOUND_INFECTION_MODERATE to WOUND_INFECTION_SEVERE)
+ . += "Moderate"
+ if(WOUND_INFECTION_SEVERE to WOUND_INFECTION_CRITICAL)
+ . += "Severe"
+ if(WOUND_INFECTION_CRITICAL to WOUND_INFECTION_SEPTIC)
+ . += "Critical"
+ if(WOUND_INFECTION_SEPTIC to INFINITY)
+ . += "Total"
+ . += " Infection"
+
/datum/wound/burn/flesh/get_scanner_description(mob/user)
if(strikes_to_lose_limb <= 0) // Unclear if it can go below 0, best to not take the chance
- var/oopsie = "Type: [name]\nSeverity: [severity_text()]"
- oopsie += "Infection Level: [span_deadsay("The body part has suffered complete sepsis and must be removed. Amputate or augment limb immediately.")]
"
+ var/oopsie = "Type: [name]
Severity: [severity_text()]"
+ oopsie += "Infection Level: [span_deadsay("The body part has suffered complete sepsis and must be removed. Amputate or augment limb immediately, or place the patient in a cryotube.")]
"
return oopsie
. = ..()
@@ -251,6 +274,10 @@
// people complained about burns not healing on stasis beds, so in addition to checking if it's cured, they also get the special ability to very slowly heal on stasis beds if they have the healing effects stored
/datum/wound/burn/flesh/on_stasis(delta_time, times_fired)
. = ..()
+ if(strikes_to_lose_limb <= 0) // we've already hit sepsis, nothing more to do
+ if(DT_PROB(0.5, delta_time))
+ victim.visible_message(span_danger("The infection on the remnants of [victim]'s [limb.plaintext_zone] shift and bubble nauseatingly!"), span_warning("You can feel the infection on the remnants of your [limb.plaintext_zone] coursing through your veins!"), vision_distance = COMBAT_MESSAGE_RANGE)
+ return
if(flesh_healing > 0)
flesh_damage = max(flesh_damage - (0.1 * delta_time), 0)
if((flesh_damage <= 0) && (infestation <= 1))
@@ -278,7 +305,8 @@
/datum/wound/burn/flesh/moderate
name = "Second Degree Burns"
desc = "Patient is suffering considerable burns with mild skin penetration, weakening limb integrity and increased burning sensations."
- treat_text = "Recommended application of topical ointment or regenerative mesh to affected region."
+ treat_text = "Apply topical ointment or regenerative mesh to the wound."
+ treat_text_short = "Apply healing aid such as regenerative mesh."
examine_desc = "is badly burned and breaking out in blisters"
occur_text = "breaks out with violent red burns"
severity = WOUND_SEVERITY_MODERATE
@@ -298,7 +326,11 @@
/datum/wound/burn/flesh/severe
name = "Third Degree Burns"
desc = "Patient is suffering extreme burns with full skin penetration, creating serious risk of infection and greatly reduced limb integrity."
- treat_text = "Recommended immediate disinfection and excision of any infected skin, followed by bandaging and ointment."
+ treat_text = "Swiftly apply healing aids such as Synthflesh or regenerative mesh to the wound. \
+ Disinfect the wound and surgically debride any infected skin, and wrap in clean gauze / use ointment to prevent further infection. \
+ If the limb has locked up, it must be amputated, augmented or treated with cryogenics."
+ treat_text_short = "Apply healing aid such as regenerative mesh, Synthflesh, or cryogenics and disinfect / debride. \
+ Clean gauze or ointment will slow infection rate."
examine_desc = "appears seriously charred, with aggressive red splotches"
occur_text = "chars rapidly, exposing ruined tissue and spreading angry red burns"
severity = WOUND_SEVERITY_SEVERE
@@ -320,7 +352,11 @@
/datum/wound/burn/flesh/critical
name = "Catastrophic Burns"
desc = "Patient is suffering near complete loss of tissue and significantly charred muscle and bone, creating life-threatening risk of infection and negligible limb integrity."
- treat_text = "Immediate surgical debriding of any infected skin, followed by potent tissue regeneration formula and bandaging."
+ treat_text = "Immediately apply healing aids such as Synthflesh or regenerative mesh to the wound. \
+ Disinfect the wound and surgically debride any infected skin, and wrap in clean gauze / use ointment to prevent further infection. \
+ If the limb has locked up, it must be amputated, augmented or treated with cryogenics."
+ treat_text_short = "Apply healing aid such as regenerative mesh, Synthflesh, or cryogenics and disinfect / debride. \
+ Clean gauze or ointment will slow infection rate."
examine_desc = "is a ruined mess of blanched bone, melted fat, and charred tissue"
occur_text = "vaporizes as flesh, bone, and fat melt together in a horrifying mess"
severity = WOUND_SEVERITY_CRITICAL
diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm
index a1daf2296c7..3425c8747b4 100644
--- a/code/datums/wounds/pierce.dm
+++ b/code/datums/wounds/pierce.dm
@@ -165,7 +165,10 @@
/datum/wound/pierce/bleed/moderate
name = "Minor Skin Breakage"
desc = "Patient's skin has been broken open, causing severe bruising and minor internal bleeding in affected area."
- treat_text = "Treat affected site with bandaging or exposure to extreme cold. In dire cases, brief exposure to vacuum may suffice." // space is cold in ss13, so it's like an ice pack!
+ treat_text = "Apply bandaging or suturing to the wound, make use of blood clotting agents, \
+ cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
+ Follow with food and a rest period."
+ treat_text_short = "Apply bandaging or suturing."
examine_desc = "has a small, circular hole, gently bleeding"
occur_text = "spurts out a thin stream of blood"
sound_effect = 'sound/effects/wounds/pierce1.ogg'
@@ -193,7 +196,10 @@
/datum/wound/pierce/bleed/severe
name = "Open Puncture"
desc = "Patient's internal tissue is penetrated, causing sizeable internal bleeding and reduced limb stability."
- treat_text = "Repair punctures in skin by suture or cautery, extreme cold may also work."
+ treat_text = "Swiftly apply bandaging or suturing to the wound, make use of blood clotting agents or saline-glucose, \
+ cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
+ Follow with iron supplements and a rest period."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "is pierced clear through, with bits of tissue obscuring the open hole"
occur_text = "looses a violent spray of blood, revealing a pierced wound"
sound_effect = 'sound/effects/wounds/pierce2.ogg'
@@ -220,7 +226,10 @@
/datum/wound/pierce/bleed/critical
name = "Ruptured Cavity"
desc = "Patient's internal tissue and circulatory system is shredded, causing significant internal bleeding and damage to internal organs."
- treat_text = "Surgical repair of puncture wound, followed by supervised resanguination."
+ treat_text = "Immediately apply bandaging or suturing to the wound, make use of blood clotting agents or saline-glucose, \
+ cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
+ Follow with supervised resanguination."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "is ripped clear through, barely held together by exposed bone"
occur_text = "blasts apart, sending chunks of viscera flying in all directions"
sound_effect = 'sound/effects/wounds/pierce3.ogg'
diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm
index e31b8d21e5c..c30f67d03b7 100644
--- a/code/datums/wounds/slash.dm
+++ b/code/datums/wounds/slash.dm
@@ -303,7 +303,9 @@
/datum/wound/slash/flesh/moderate
name = "Rough Abrasion"
desc = "Patient's skin has been badly scraped, generating moderate blood loss."
- treat_text = "Application of clean bandages or first-aid grade sutures, followed by food and rest."
+ treat_text = "Apply bandaging or suturing to the wound. \
+ Follow up with food and a rest period."
+ treat_text_short = "Apply bandaging or suturing."
examine_desc = "has an open cut"
occur_text = "is cut open, slowly leaking blood"
sound_effect = 'sound/effects/wounds/blood1.ogg'
@@ -329,7 +331,10 @@
/datum/wound/slash/flesh/severe
name = "Open Laceration"
desc = "Patient's skin is ripped clean open, allowing significant blood loss."
- treat_text = "Speedy application of first-aid grade sutures and clean bandages, followed by vitals monitoring to ensure recovery."
+ treat_text = "Swiftly apply bandaging or suturing to the wound, \
+ or make use of blood clotting agents or cauterization. \
+ Follow up with iron supplements or saline-glucose and a rest period."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "has a severe cut"
occur_text = "is ripped open, veins spurting blood"
sound_effect = 'sound/effects/wounds/blood2.ogg'
@@ -356,7 +361,10 @@
/datum/wound/slash/flesh/critical
name = "Weeping Avulsion"
desc = "Patient's skin is completely torn open, along with significant loss of tissue. Extreme blood loss will lead to quick death without intervention."
- treat_text = "Immediate bandaging and either suturing or cauterization, followed by supervised resanguination."
+ treat_text = "Immediately apply bandaging or suturing to the wound, \
+ or make use of blood clotting agents or cauterization. \
+ Follow up supervised resanguination."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "is carved down to the bone, spraying blood wildly"
examine_desc = "is carved down to the bone"
occur_text = "is torn open, spraying blood wildly"
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index 24413fd7966..850ac3c1691 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -28,6 +28,7 @@
var/scanmode = SCANMODE_HEALTH
var/advanced = FALSE
custom_price = PAYCHECK_COMMAND
+ var/last_scan_text
/obj/item/healthanalyzer/Initialize(mapload)
. = ..()
@@ -55,12 +56,20 @@
// Clumsiness/brain damage check
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: ???")]")
+ var/turf/scan_turf = get_turf(user)
+ user.visible_message(
+ span_warning("[user] analyzes [scan_turf]'s vitals!"),
+ span_notice("You stupidly try to analyze [scan_turf]'s vitals!"),
+ )
+
+ var/floor_text = "Analyzing results for [scan_turf] ([station_time_timestamp()]):
"
+ floor_text += "Overall status: Unknown
"
+ floor_text += "Subject lacks a brain.
"
+ floor_text += "Body temperature: [scan_turf?.return_temperature() || "???"]
"
+
+ if(user.can_read(src) && !user.is_blind())
+ to_chat(user, examine_block(floor_text))
+ last_scan_text = floor_text
return
if(ispodperson(M)&& !advanced)
@@ -70,11 +79,13 @@
user.visible_message(span_notice("[user] analyzes [M]'s vitals."))
balloon_alert(user, "analyzing vitals")
+ var/readability_check = user.can_read(src) && !user.is_blind()
switch (scanmode)
if (SCANMODE_HEALTH)
- healthscan(user, M, mode, advanced)
+ last_scan_text = healthscan(user, M, mode, advanced, tochat = readability_check)
if (SCANMODE_WOUND)
- woundscan(user, M, src)
+ if(readability_check)
+ woundscan(user, M, src)
add_fingerprint(user)
@@ -110,37 +121,33 @@
return
// the final list of strings to render
- var/render_list = list()
+ var/list/render_list = list()
// Damage specifics
var/oxy_loss = target.getOxyLoss()
var/tox_loss = target.getToxLoss()
var/fire_loss = target.getFireLoss()
var/brute_loss = target.getBruteLoss()
- var/mob_status = (target.stat == DEAD ? span_alert("Deceased") : "[round(target.health/target.maxHealth,0.01)*100]% healthy")
+ var/mob_status = (target.stat == DEAD ? span_alert("Deceased") : "[round(target.health / target.maxHealth, 0.01) * 100]% healthy")
if(HAS_TRAIT(target, TRAIT_FAKEDEATH) && !advanced)
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] ([station_time_timestamp()]):")]
Overall status: [mob_status]
"
- SEND_SIGNAL(target, COMSIG_LIVING_HEALTHSCAN, render_list, advanced, user, mode)
+ if(!advanced && target.has_reagent(/datum/reagent/inverse/technetium))
+ advanced = TRUE
- 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"
- if(humantarget.has_reagent(/datum/reagent/inverse/technetium))
- advanced = TRUE
+ SEND_SIGNAL(target, COMSIG_LIVING_HEALTHSCAN, render_list, advanced, user, mode, tochat)
// Husk detection
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 [conditional_tooltip("severe burns", "Tend burns and apply a de-husking agent, such as [/datum/reagent/medicine/c2/synthflesh::name].", tochat)].
"
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 [conditional_tooltip("desiccation", "Irreparable. Under normal circumstances, revival can only proceed via brain transplant.", tochat)].
"
else
render_list += "Subject has been husked by mysterious causes.\n"
@@ -162,138 +169,157 @@
if(iscarbon(target))
var/mob/living/carbon/carbontarget = target
- if(LAZYLEN(carbontarget.get_traumas()))
- var/list/trauma_text = list()
- for(var/datum/brain_trauma/trauma in carbontarget.get_traumas())
- var/trauma_desc = ""
- switch(trauma.resilience)
- if(TRAUMA_RESILIENCE_SURGERY)
- trauma_desc += "severe "
- if(TRAUMA_RESILIENCE_LOBOTOMY)
- trauma_desc += "deep-rooted "
- if(TRAUMA_RESILIENCE_WOUND)
- trauma_desc += "fracture-derived "
- if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
- 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"
- if(carbontarget.quirks.len)
- render_list += "Subject Major Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY)].\n"
+ if(LAZYLEN(carbontarget.quirks))
+ 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)].\n"
- if (HAS_TRAIT(target, TRAIT_IRRADIATED))
- render_list += "Subject is irradiated. Supply toxin healing.\n"
-
- //Eyes and ears
- if(advanced && iscarbon(target))
- var/mob/living/carbon/carbontarget = target
-
- // Ear status
- var/obj/item/organ/internal/ears/ears = carbontarget.getorganslot(ORGAN_SLOT_EARS)
- if(istype(ears))
- if(HAS_TRAIT_FROM(carbontarget, TRAIT_DEAF, GENETIC_MUTATION))
- render_list = "Subject is genetically deaf.\n"
- else if(HAS_TRAIT_FROM(carbontarget, TRAIT_DEAF, EAR_DAMAGE))
- render_list = "Subject is deaf from ear damage.\n"
- else if(HAS_TRAIT(carbontarget, TRAIT_DEAF))
- render_list = "Subject is deaf.\n"
- else
- if(ears.damage)
- render_list += "Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.\n"
- if(ears.deaf)
- render_list += "Subject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.\n"
-
- // Eye status
- var/obj/item/organ/internal/eyes/eyes = carbontarget.getorganslot(ORGAN_SLOT_EYES)
- if(istype(eyes))
- if(carbontarget.is_blind())
- render_list += "Subject is blind.\n"
- else if(HAS_TRAIT(carbontarget, TRAIT_NEARSIGHT))
- render_list += "Subject is nearsighted.\n"
-
// Body part damage report
if(iscarbon(target))
var/mob/living/carbon/carbontarget = target
- var/list/damaged = carbontarget.get_damaged_bodyparts(1,1)
- if(length(damaged)>0 || oxy_loss>0 || tox_loss>0 || fire_loss>0)
- var/dmgreport = "General status:\
- \
+ var/any_damage = brute_loss > 0 || fire_loss > 0 || oxy_loss > 0 || tox_loss > 0 || fire_loss > 0
+ var/any_missing = length(carbontarget.bodyparts) < (carbontarget.dna?.species?.max_bodypart_count || 6)
+ var/any_wounded = length(carbontarget.all_wounds)
+ var/any_embeds = carbontarget.has_embedded_objects()
+ if(any_damage || (mode == SCANNER_VERBOSE && (any_missing || any_wounded || any_embeds)))
+ render_list += "
"
+ var/dmgreport = "Body status:\
+ \
+ \
+ \
Damage: | \
Brute | \
Burn | \
Toxin | \
- Suffocation |
\
- Overall: | \
- [CEILING(brute_loss,1)] | \
- [CEILING(fire_loss,1)] | \
- [CEILING(tox_loss,1)] | \
- [CEILING(oxy_loss,1)] |
"
+ Suffocation | \
+ \
+ \
+ Overall: | \
+ [ceil(brute_loss)] | \
+ [ceil(fire_loss)] | \
+ [ceil(tox_loss)] | \
+ [ceil(oxy_loss)] | \
+
"
if(mode == SCANNER_VERBOSE)
- for(var/obj/item/bodypart/limb as anything in damaged)
- if(limb.bodytype & BODYTYPE_ROBOTIC)
- dmgreport += "[capitalize(limb.name)]: | "
- else
- dmgreport += "
[capitalize(limb.plaintext_zone)]: | "
- dmgreport += "[(limb.brute_dam > 0) ? "[CEILING(limb.brute_dam,1)]" : "0"] | "
- dmgreport += "[(limb.burn_dam > 0) ? "[CEILING(limb.burn_dam,1)]" : "0"] |
"
- dmgreport += "
"
+ // Follow same body zone list every time so it's consistent across all humans
+ for(var/zone in GLOB.all_body_zones)
+ var/obj/item/bodypart/limb = carbontarget.get_bodypart(zone)
+ if(isnull(limb))
+ dmgreport += ""
+ dmgreport += "[capitalize(parse_zone(zone))]: | "
+ dmgreport += "- | "
+ dmgreport += "- | "
+ dmgreport += "
"
+ dmgreport += "↳ Physical trauma: [conditional_tooltip("Dismembered", "Reattach or replace surgically.", tochat)] |
"
+ continue
+ var/has_any_embeds = length(limb.embedded_objects) >= 1
+ var/has_any_wounds = length(limb.wounds) >= 1
+ var/is_damaged = limb.burn_dam > 0 || limb.brute_dam > 0
+ if(!is_damaged && (zone != BODY_ZONE_CHEST || (tox_loss <= 0 && oxy_loss <= 0)) && !has_any_embeds && !has_any_wounds)
+ continue
+ dmgreport += ""
+ dmgreport += "[capitalize((limb.bodytype & BODYTYPE_ROBOTIC) ? limb.name : limb.plaintext_zone)]: | "
+ 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): [conditional_tooltip(displayed, "Use a hemostat to remove.", tochat)] |
"
+ if(has_any_wounds)
+ for(var/datum/wound/wound as anything in limb.wounds)
+ dmgreport += "↳ Physical trauma: [conditional_tooltip("[wound.name] ([wound.severity_text()])", wound.treat_text_short, tochat)] |
"
+
+ dmgreport += "
"
render_list += dmgreport // tables do not need extra linebreak
if(ishuman(target))
var/mob/living/carbon/human/humantarget = target
// Organ damage, missing organs
- if(humantarget.internal_organs && humantarget.internal_organs.len)
- var/render = FALSE
- var/toReport = "Organs:\
- \
- Organ: | \
- [advanced ? "Dmg | " : ""]\
- Status | "
-
- for(var/obj/item/organ/organ as anything in humantarget.internal_organs)
- var/status = organ.get_status_text()
- if (status != "")
+ var/render = FALSE
+ var/toReport = "Organ status:\
+ \
+ \
+ \
+ Organ: | \
+ [advanced ? "Dmg | " : ""]\
+ Status | \
+
"
+
+ var/list/missing_organs = list()
+ if(!humantarget.internal_organs_slot[ORGAN_SLOT_BRAIN])
+ missing_organs[ORGAN_SLOT_BRAIN] = "Brain"
+ if(!humantarget.needs_heart() && !humantarget.internal_organs_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.internal_organs_slot[ORGAN_SLOT_LUNGS])
+ missing_organs[ORGAN_SLOT_LUNGS] = "Lungs"
+ if(!isnull(humantarget.dna.species.mutantliver) && !humantarget.internal_organs_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.internal_organs_slot[ORGAN_SLOT_STOMACH])
+ missing_organs[ORGAN_SLOT_STOMACH] ="Stomach"
+ if(!isnull(humantarget.dna.species.mutanttongue) && !humantarget.internal_organs_slot[ORGAN_SLOT_TONGUE])
+ missing_organs[ORGAN_SLOT_TONGUE] = "Tongue"
+ if(!isnull(humantarget.dna.species.mutantears) && !humantarget.internal_organs_slot[ORGAN_SLOT_EARS])
+ missing_organs[ORGAN_SLOT_EARS] = "Ears"
+ if(!isnull(humantarget.dna.species.mutantears) && !humantarget.internal_organs_slot[ORGAN_SLOT_EYES])
+ missing_organs[ORGAN_SLOT_EYES] = "Eyes"
+
+ // 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.internal_organs_slot[sorted_slot]
+ if(isnull(organ))
+ if(missing_organs[sorted_slot])
render = TRUE
- toReport += "[organ.name]: | \
- [advanced ? "[CEILING(organ.damage,1)] | " : ""]\
- [status] |
"
-
- var/datum/species/the_dudes_species = humantarget.dna.species
- var/missing_organs = list()
- if(!humantarget.getorganslot(ORGAN_SLOT_BRAIN))
- missing_organs += "brain"
- if(!HAS_TRAIT(humantarget, TRAIT_NOBLOOD) && !humantarget.getorganslot(ORGAN_SLOT_HEART))
- missing_organs += "heart"
- if(!(TRAIT_NOBREATH in the_dudes_species.species_traits) && !humantarget.getorganslot(ORGAN_SLOT_LUNGS))
- missing_organs += "lungs"
- if(!(TRAIT_NOMETABOLISM in the_dudes_species.species_traits) && !humantarget.getorganslot(ORGAN_SLOT_LIVER))
- missing_organs += "liver"
- if(!the_dudes_species.mutantstomach && !humantarget.getorganslot(ORGAN_SLOT_STOMACH))
- missing_organs += "stomach"
- if(!the_dudes_species.mutanttongue && !humantarget.getorganslot(ORGAN_SLOT_TONGUE))
- missing_organs += "tongue"
- if(!humantarget.getorganslot(ORGAN_SLOT_EARS))
- missing_organs += "ears"
- if(!humantarget.getorganslot(ORGAN_SLOT_EYES))
- missing_organs += "eyes"
-
- if(length(missing_organs))
+ toReport += "[missing_organs[sorted_slot]]: | \
+ [advanced ? "- | " : ""]\
+ Missing |
"
+ continue
+ if(mode != SCANNER_VERBOSE && !organ.show_on_condensed_scans())
+ continue
+ var/status = organ.get_status_text(advanced, tochat)
+ var/appendix = organ.get_status_appendix(advanced, tochat)
+ if(status || appendix)
+ status ||= "OK" // otherwise flawless organs have no status reported by default
render = TRUE
- for(var/organ in missing_organs)
- toReport += "[organ]: | \
- [advanced ? "["-"] | " : ""]\
- ["Missing"] |
"
-
- if(render)
- render_list += toReport + "
" // tables do not need extra linebreak
+ toReport += "\
+ [capitalize(organ.name)]: | \
+ [advanced ? "[organ.damage > 0 ? ceil(organ.damage) : "0"] | " : ""]\
+ [status] | \
+
"
+ if(appendix)
+ toReport += "↳ [appendix] |
"
+
+ if(render)
+ render_list += "
"
+ render_list += toReport + "
" // tables do not need extra linebreak
+
+ // Cybernetics
+ var/list/cyberimps
+ for(var/obj/item/organ/internal/cyberimp/cyberimp in humantarget.internal_organs)
+ if((cyberimp.status & ORGAN_ROBOTIC) && !(cyberimp.organ_flags & ORGAN_HIDDEN))
+ LAZYADD(cyberimps, "\a [cyberimp]")
+ if(LAZYLEN(cyberimps))
+ if(!render)
+ render_list += "
"
+ render_list += "Detected cybernetic modifications:
"
+ render_list += "[english_list(cyberimps, and_text = ", and ")]
"
+
+ render_list += "
"
//Genetic stability
- if(advanced && humantarget.has_dna())
- render_list += "Genetic Stability: [humantarget.dna.stability]%.\n"
+ if(advanced && humantarget.has_dna() && humantarget.dna.stability != initial(humantarget.dna.stability))
+ render_list += "Genetic Stability: [humantarget.dna.stability]%.
"
// Species and body temperature
var/datum/species/targetspecies = humantarget.dna.species
@@ -313,69 +339,87 @@
render_list += "Core temperature: [round(humantarget.coretemperature-T0C,0.1)] °C ([round(humantarget.coretemperature*1.8-459.67,0.1)] °F)\n"
render_list += "Body temperature: [round(target.bodytemperature-T0C,0.1)] °C ([round(target.bodytemperature*1.8-459.67,0.1)] °F)\n"
- // Time of death
- if(target.tod && (target.stat == DEAD || ((HAS_TRAIT(target, TRAIT_FAKEDEATH)) && !advanced)))
- render_list += "Time of Death: [target.tod]\n"
- var/tdelta = round(world.time - target.timeofdeath)
- render_list += "Subject died [DisplayTimeText(tdelta)] ago.\n"
+ // Blood Level
+ var/mob/living/carbon/carbontarget = target
+ var/blood_id = carbontarget.get_blood_id()
+ if(blood_id)
+ 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/real_reagent = GLOB.chemical_reagents_list[blood_id]
+ blood_type = real_reagent?.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]")]
"
+ else if(carbontarget.blood_volume <= BLOOD_VOLUME_OKAY)
+ 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]
"
- // Wounds
- if(iscarbon(target))
- var/mob/living/carbon/carbontarget = target
- var/list/wounded_parts = carbontarget.get_wounded_bodyparts()
- for(var/i in wounded_parts)
- var/obj/item/bodypart/wounded_part = i
- 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 += ""
+ 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]%
"
+ else
+ render_list += "Blood alcohol content: [blood_alcohol_content]%
"
//Diseases
- for(var/thing in target.diseases)
- var/datum/disease/D = thing
- if(!(D.visibility_flags & HIDDEN_SCANNER))
- render_list += "Warning: [D.form] detected\n\
- Name: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]
\
- " // divs do not need extra linebreak
+ var/disease_hr = FALSE
+ for(var/datum/disease/disease as anything in target.diseases)
+ if(disease.visibility_flags & HIDDEN_SCANNER)
+ continue
+ if(!disease_hr)
+ render_list += "
"
+ disease_hr = TRUE
+ render_list += "\
+ Warning: [disease.form] detected
\
+ \
+ Name: [disease.name].
\
+ Type: [disease.spread_text].
\
+ Stage: [disease.stage]/[disease.max_stages].
\
+ Possible Cure: [disease.cure_text]
\
+ "
- // Blood Level
- if(target.has_dna())
- var/mob/living/carbon/carbontarget = target
- var/blood_id = carbontarget.get_blood_id()
- if(blood_id)
- if(ishuman(carbontarget))
- var/mob/living/carbon/human/humantarget = carbontarget
- if(humantarget.is_bleeding())
- render_list += "Subject is bleeding!\n"
- 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"
- 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"
- else
- render_list += "Blood level: [blood_percent] %, [carbontarget.blood_volume] cl, type: [blood_type]\n"
-
- // Cybernetics
- if(iscarbon(target))
- var/mob/living/carbon/carbontarget = target
- var/cyberimp_detect
- for(var/obj/item/organ/internal/cyberimp/CI in carbontarget.internal_organs)
- if(CI.status == ORGAN_ROBOTIC && !CI.syndicate_implant)
- cyberimp_detect += "[!cyberimp_detect ? "[CI.get_examine_string(user)]" : ", [CI.get_examine_string(user)]"]"
- if(cyberimp_detect)
- render_list += "Detected cybernetic modifications:\n"
- render_list += "[cyberimp_detect]\n"
- // we handled the last
so we don't need handholding
+ // Time of death
+ if(target.tod && (target.stat == DEAD || (HAS_TRAIT(target, TRAIT_FAKEDEATH) && !advanced)))
+ render_list += "
"
+ render_list += "Time of Death: [target.tod]
"
+ render_list += "Subject died [DisplayTimeText(round(world.time - target.timeofdeath))] ago.
"
+ . = jointext(render_list, "")
if(tochat)
- to_chat(user, examine_block(jointext(render_list, "")), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO)
- else
- return(jointext(render_list, ""))
+ to_chat(user, examine_block(.), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO)
+ return .
+
+// /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 = "#99ccff"
+// report_paper.name = "health scan report - [station_time_timestamp()]"
+// var/report_text = "Health scan report. Time of retrieval: [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())
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index d6cc800ec53..9448282891c 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -360,3 +360,16 @@
var/obj/effect/decal/cleanable/oil/B = locate() in T.contents
if(!B)
B = new(T)
+
+// Conversion between internal drunk power and common blood alcohol content
+#define DRUNK_POWER_TO_BLOOD_ALCOHOL 0.003
+
+/mob/living/proc/get_blood_alcohol_content()
+ var/blood_alcohol_content = 0
+ var/datum/status_effect/inebriated/inebriation = has_status_effect(/datum/status_effect/inebriated)
+ if(!isnull(inebriation))
+ blood_alcohol_content = round(inebriation.drunk_value * DRUNK_POWER_TO_BLOOD_ALCOHOL, 0.01)
+
+ return blood_alcohol_content
+
+#undef DRUNK_POWER_TO_BLOOD_ALCOHOL
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 39ef87c050e..62e3f9be240 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -8,7 +8,7 @@
layer = ABOVE_MOB_LAYER
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_BRAIN
- organ_flags = ORGAN_VITAL
+ organ_flags = ORGAN_ORGANIC | ORGAN_VITAL
attack_verb_continuous = list("attacks", "slaps", "whacks")
attack_verb_simple = list("attack", "slap", "whack")
@@ -203,6 +203,26 @@
else
. += span_info("This one is completely devoid of life.")
+/obj/item/organ/internal/brain/get_status_appendix(advanced, add_tooltips)
+ var/list/trauma_text
+ for(var/datum/brain_trauma/trauma as anything in traumas)
+ var/trauma_desc = ""
+ switch(trauma.resilience)
+ if(TRAUMA_RESILIENCE_BASIC)
+ trauma_desc = conditional_tooltip("Mild ", "Repair via brain surgery or medication such as [/datum/reagent/medicine/neurine::name].", add_tooltips)
+ if(TRAUMA_RESILIENCE_SURGERY)
+ trauma_desc = conditional_tooltip("Severe ", "Repair via brain surgery.", add_tooltips)
+ if(TRAUMA_RESILIENCE_LOBOTOMY)
+ trauma_desc = conditional_tooltip("Deep-rooted ", "Repair via Lobotomy.", add_tooltips)
+ if(TRAUMA_RESILIENCE_WOUND)
+ trauma_desc = conditional_tooltip("Fracture-derived ", "Repair via treatment of wounds afflicting the head.", add_tooltips)
+ if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
+ trauma_desc = conditional_tooltip("Permanent ", "Irreparable under normal circumstances.", add_tooltips)
+ trauma_desc += capitalize(trauma.scan_desc)
+ LAZYADD(trauma_text, trauma_desc)
+ if(LAZYLEN(trauma_text))
+ return "Mental trauma: [english_list(trauma_text, and_text = ", and ")]."
+
/obj/item/organ/internal/brain/attack(mob/living/carbon/C, mob/user)
if(!istype(C))
return ..()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 4319f8d4222..e16afec719f 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1133,6 +1133,9 @@
/mob/proc/is_nearsighted()
return HAS_TRAIT(src, TRAIT_NEARSIGHT)
+/mob/proc/is_nearsighted_from(source)
+ return HAS_TRAIT_FROM(src, TRAIT_NEARSIGHT, source)
+
/**
* Proc that returns TRUE if the mob can write using the writing_instrument, FALSE otherwise.
*
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index fabe50c20ab..f9a68c01b18 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -211,6 +211,9 @@
SHOULD_BE_PURE(TRUE)
return eye_blind ? TRUE : HAS_TRAIT(src, TRAIT_BLIND)
+/mob/proc/is_blind_from(source)
+ return eye_blind ? HAS_TRAIT_FROM(src, TRAIT_BLIND, source) : FALSE
+
// moved out of admins.dm because things other than admin procs were calling this.
/// Returns TRUE if the game has started and we're either an AI with a 0th law, or we're someone with a special role/antag datum
/proc/is_special_character(mob/M)
diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm
index 9331da80a67..cccf6f197b1 100644
--- a/code/modules/surgery/organs/_organ.dm
+++ b/code/modules/surgery/organs/_organ.dm
@@ -326,15 +326,53 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
replacement.setOrganDamage(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()
- var/status = ""
+/obj/item/organ/proc/get_status_text(advanced, add_tooltips)
+ if(advanced && (organ_flags & ORGAN_HAZARDOUS))
+ return conditional_tooltip("Harmful Foreign Body", "Remove surgically.", add_tooltips)
+
+ if(organ_flags & ORGAN_SYNTHETIC_EMP)
+ return conditional_tooltip("EMP-Derived Failure", "Repair or replace surgically.", add_tooltips)
+
+ var/tech_text = ""
if(owner.has_reagent(/datum/reagent/inverse/technetium))
- status = " organ is [round((damage/maxHealth)*100, 1)]% damaged."
- else if(organ_flags & ORGAN_FAILING)
- status = "Non-Functional"
- else if(damage > high_threshold)
- status = "Severely Damaged"
- else if (damage > low_threshold)
- status = "Mildly Damaged"
-
- return status
+ 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)
+
+ 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)
+
+ if(damage > low_threshold)
+ return conditional_tooltip("[tech_text || "Mildly Damaged"] ", "[healing_factor ? "Treat with rest." : "Use specialty medication."]", add_tooltips && owner.stat != DEAD)
+
+ if(tech_text)
+ return "[tech_text]"
+
+ return ""
+
+/// Determines if this organ is shown when a user has condensed scans enabled
+/obj/item/organ/proc/show_on_condensed_scans()
+ // We don't need to show *most* damaged organs as they have no effects associated
+ return (organ_flags & (ORGAN_HAZARDOUS|ORGAN_FAILING|ORGAN_VITAL))
+
+/// Similar to get_status_text, but appends the text after the damage report, for additional status info
+/obj/item/organ/proc/get_status_appendix(advanced, add_tooltips)
+ return
+
+/// Tries to replace the existing organ on the passed mob with this one, with special handling for replacing a brain without ghosting target
+/obj/item/organ/proc/replace_into(mob/living/carbon/new_owner)
+ return Insert(new_owner, special = TRUE, drop_if_replaced = FALSE)
+
+
+/// Get all possible organ slots by checking every organ, and then store it and give it whenever needed
+/proc/get_all_slots()
+ var/static/list/all_organ_slots = list()
+
+ if(!all_organ_slots.len)
+ for(var/obj/item/organ/an_organ as anything in subtypesof(/obj/item/organ))
+ if(!initial(an_organ.slot))
+ continue
+ all_organ_slots |= initial(an_organ.slot)
+
+ return all_organ_slots
diff --git a/code/modules/surgery/organs/appendix.dm b/code/modules/surgery/organs/appendix.dm
index 0ea204f834e..ee54db515ff 100644
--- a/code/modules/surgery/organs/appendix.dm
+++ b/code/modules/surgery/organs/appendix.dm
@@ -84,11 +84,10 @@
ADD_TRAIT(organ_owner, TRAIT_DISEASELIKE_SEVERITY_MEDIUM, type)
organ_owner.med_hud_set_status()
-/obj/item/organ/internal/appendix/get_status_text()
- if((!(organ_flags & ORGAN_FAILING)) && inflamation_stage)
- return "Inflamed"
- else
- return ..()
+/obj/item/organ/internal/appendix/get_status_text(advanced, add_tooltips)
+ if(!(organ_flags & ORGAN_FAILING) && inflamation_stage)
+ return conditional_tooltip("Inflamed", "Remove surgically.", add_tooltips)
+ return ..()
#undef APPENDICITIS_PROB
#undef INFLAMATION_ADVANCEMENT_PROB
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 64952b9568b..53b281ea957 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -160,6 +160,36 @@
eye_owner.clear_fullscreen("eye_damage")
eye_owner.cure_blind(EYE_DAMAGE)
+/// Similar to get_status_text, but appends the text after the damage report, for additional status info
+/obj/item/organ/internal/eyes/get_status_appendix(advanced, add_tooltips)
+ if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_KNOCKEDOUT))
+ return
+ if(owner.is_blind())
+ if(advanced)
+ if(owner.is_blind_from(QUIRK_TRAIT))
+ return conditional_tooltip("Subject is permanently blind.", "Irreparable under normal circumstances.", add_tooltips)
+ if(owner.is_blind_from(TRAUMA_TRAIT))
+ return conditional_tooltip("Subject is blind from mental trauma.", "Repair via treatment of associated trauma.", add_tooltips)
+ if(owner.is_blind_from(GENETIC_MUTATION))
+ return conditional_tooltip("Subject is genetically blind.", "Use medication such as [/datum/reagent/medicine/mutadone::name].", add_tooltips)
+ if(owner.is_blind_from(EYE_DAMAGE))
+ return conditional_tooltip("Subject is blind from eye damage.", "Repair surgically, use medication such as [/datum/reagent/medicine/oculine::name], or protect eyes with a blindfold.", add_tooltips)
+ return "Subject is blind."
+ if(owner.is_nearsighted())
+ if(advanced)
+ if(owner.is_nearsighted_from(QUIRK_TRAIT))
+ return conditional_tooltip("Subject is permanently nearsighted.", "Irreparable under normal circumstances. Prescription glasses will assuage the effects.", add_tooltips)
+ if(owner.is_nearsighted_from(GENETIC_MUTATION))
+ return conditional_tooltip("Subject is genetically nearsighted.", "Use medication such as [/datum/reagent/medicine/mutadone::name]. Prescription glasses will assuage the effects.", add_tooltips)
+ if(owner.is_nearsighted_from(EYE_DAMAGE))
+ return conditional_tooltip("Subject is nearsighted from eye damage.", "Repair surgically or use medication such as [/datum/reagent/medicine/oculine::name]. Prescription glasses will assuage the effects.", add_tooltips)
+ return "Subject is nearsighted."
+ return ""
+
+/obj/item/organ/internal/eyes/show_on_condensed_scans()
+ // Always show if we have an appendix
+ return ..() || (owner.stat != DEAD && !HAS_TRAIT(owner, TRAIT_KNOCKEDOUT) && (owner.is_blind() || owner.is_nearsighted()))
+
/obj/item/organ/internal/eyes/night_vision
see_in_dark = NIGHTVISION_FOV_RANGE
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE