[MIRROR] Useful Stethoscopes and Penlights [MDB IGNORE] (#22183)

* Useful Stethoscopes and Penlights (#75800)

## About The Pull Request
This PR overhauls and adds functionality to Stethoscopes and Flashlight
examinations (i.e. shining a penlight into someone's eyes/mouth).

Stethoscope Changes
- Dynamic results based on targeted body part.
- Chest: Assess whether the heart is beating, whether it is damaged
(above 10 dmg), and whether or not the player has suffered severe blood
loss. Assess whether the player is breathing, has lung damage (above
10dmg), and whether or not they have suffocation damage (above 10dmg).
- Head/Extremities: Assess heart function as above, and blood level
(nominal/low/critical).
- Groin: Assess whether or not the liver or the appendix have suffered
damage (above 10dmg). (The latter is only detectable if the patient is
conscious)

Flashlight Changes
- Eyes: Assess whether the patient is dead, has sustained brain damage
(above 20dmg), or has an X-Ray mutation.
- Mouth: Assess mouth organs, dental implants, suffocation damage (above
20dmg), and blood volume (nominal/low/critical)
## Why It's Good For The Game
Adds some proper functionality to two very rarely used tools.

Loosens up medical players' dependence on health analyzers to assess
their patients Provides an alternative, if manual and less accurate,
means of assessment.

For players interested in a more hands-on, RP focused approach to medbay
(or those in areas without power to recharge analyzers); I think this
will be a welcome addition.
## Proof of Testing

<details>
<summary>Screenshots/Videos</summary>

https://github.com/tgstation/tgstation/assets/107971606/43e87774-6db5-4db1-b7fa-92c3565c1009

https://github.com/tgstation/tgstation/assets/107971606/9ec42cc4-a996-4d60-a6f1-1aecf24b9bc8

</details>

## Changelog
🆑
add: Stethoscopes may be used on the chest, groin, or extremities to
assess organ damage, blood level, and/or suffocation damage depending on
the targeted area.
add: Shining flashlights into the mouth or eyes of other players will
additionally assess brain health, suffocation damage, and/or blood level
depending on the targeted area.
balance: Halves the duration of the flash effect from shining lights
into players' eyes (2s -> 1s). Use combat mode to get the full duration.
/🆑

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@ users.noreply.github.com>
Co-authored-by: Jacquerel <hnevard@ gmail.com>

* Useful Stethoscopes and Penlights

---------

Co-authored-by: LovliestPlant <107971606+LovliestPlant@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@ users.noreply.github.com>
Co-authored-by: Jacquerel <hnevard@ gmail.com>
This commit is contained in:
SkyratBot
2023-07-01 22:02:30 +02:00
committed by GitHub
parent 1ebf18fcd5
commit e0043fc2ea
2 changed files with 173 additions and 37 deletions
+69 -25
View File
@@ -112,6 +112,8 @@
to_chat(user, "[span_warning("\The [src] isn't bright enough to see anything!")] ")
return
var/render_list = list()//information will be packaged in a list for clean display to the user
switch(user.zone_selected)
if(BODY_ZONE_PRECISE_EYES)
if((M.head && M.head.flags_cover & HEADCOVERSEYES) || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSEYES) || (M.glasses && M.glasses.flags_cover & GLASSESCOVERSEYES))
@@ -119,24 +121,38 @@
return
var/obj/item/organ/internal/eyes/E = M.get_organ_slot(ORGAN_SLOT_EYES)
var/obj/item/organ/internal/brain = M.get_organ_slot(ORGAN_SLOT_BRAIN)
if(!E)
to_chat(user, span_warning("[M] doesn't have any eyes!"))
return
M.flash_act(visual = TRUE, length = (user.combat_mode) ? 2.5 SECONDS : 1 SECONDS) // Apply a 1 second flash effect to the target. The duration increases to 2.5 Seconds if you have combat mode on.
if(M == user) //they're using it on themselves
if(M.flash_act(visual = 1))
M.visible_message(span_notice("[M] directs [src] to [M.p_their()] eyes."), span_notice("You wave the light in front of your eyes! Trippy!"))
user.visible_message(span_warning("[user] shines [src] into [M.p_their()] eyes."), ignored_mobs = user)
render_list += span_info("You direct [src] to into your eyes:\n")
if(M.is_blind())
render_list += "<span class='notice ml-1'>You're not entirely certain what you were expecting...</span>\n"
else
M.visible_message(span_notice("[M] directs [src] to [M.p_their()] eyes."), span_notice("You wave the light in front of your eyes."))
render_list += "<span class='notice ml-1'>Trippy!</span>\n"
else
user.visible_message(span_warning("[user] directs [src] to [M]'s eyes."), \
span_danger("You direct [src] to [M]'s eyes."))
if(M.stat == DEAD || (M.is_blind()) || !M.flash_act(visual = 1)) //mob is dead or fully blind
to_chat(user, span_warning("[M]'s pupils don't react to the light!"))
else if(M.dna && M.dna.check_mutation(/datum/mutation/human/xray)) //mob has X-ray vision
to_chat(user, span_danger("[M]'s pupils give an eerie glow!"))
else //they're okay!
to_chat(user, span_notice("[M]'s pupils narrow."))
user.visible_message(span_warning("[user] directs [src] to [M]'s eyes."), ignored_mobs = user)
render_list += span_info("You direct [src] to [M]'s eyes:\n")
if(M.stat == DEAD || M.is_blind())
render_list += "<span class='danger ml-1'>[M.p_their(TRUE)] pupils don't react to the light!</span>\n"//mob is dead
else if(brain.damage > 20)
render_list += "<span class='danger ml-1'>[M.p_their(TRUE)] pupils contract unevenly!</span>\n"//mob has sustained damage to their brain
else
render_list += "<span class='notice ml-1'>[M.p_their(TRUE)] pupils narrow.</span>\n"//they're okay :D
if(M.dna && M.dna.check_mutation(/datum/mutation/human/xray))
render_list += "<span class='danger ml-1'>[M.p_their(TRUE)] pupils give an eerie glow!</span>\n"//mob has X-ray vision
//display our packaged information in an examine block for easy reading
to_chat(user, examine_block(jointext(render_list, "")), type = MESSAGE_TYPE_INFO)
if(BODY_ZONE_PRECISE_MOUTH)
@@ -144,8 +160,6 @@
to_chat(user, span_warning("You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSMOUTH) ? "helmet" : "mask"] first!"))
return
var/their = M.p_their()
var/list/mouth_organs = new
for(var/obj/item/organ/organ as anything in M.organs)
if(organ.zone == BODY_ZONE_PRECISE_MOUTH)
@@ -166,7 +180,7 @@
for(var/datum/action/item_action/hands_free/activate_pill/AP in M.actions)
pill_count++
if(M == user)
if(M == user)//if we're looking on our own mouth
var/can_use_mirror = FALSE
if(isturf(user.loc))
var/obj/structure/mirror/mirror = locate(/obj/structure/mirror, user.loc)
@@ -181,27 +195,57 @@
if(WEST)
can_use_mirror = mirror.pixel_x < 0
M.visible_message(span_notice("[M] directs [src] to [their] mouth."), \
span_notice("You point [src] into your mouth."))
M.visible_message(span_notice("[M] directs [src] to [ M.p_their()] mouth."), ignored_mobs = user)
render_list += span_info("You point [src] into your mouth:\n")
if(!can_use_mirror)
to_chat(user, span_notice("You can't see anything without a mirror."))
return
if(organ_count)
to_chat(user, span_notice("Inside your mouth [organ_count > 1 ? "are" : "is"] [organ_list]."))
render_list += "<span class='notice ml-1'>Inside your mouth [organ_count > 1 ? "are" : "is"] [organ_list].</span>\n"
else
to_chat(user, span_notice("There's nothing inside your mouth."))
render_list += "<span class='notice ml-1'>There's nothing inside your mouth.</span>\n"
if(pill_count)
to_chat(user, span_notice("You have [pill_count] implanted pill[pill_count > 1 ? "s" : ""]."))
render_list += "<span class='notice ml-1'>You have [pill_count] implanted pill[pill_count > 1 ? "s" : ""].</span>\n"
else
user.visible_message(span_notice("[user] directs [src] to [M]'s mouth."),\
span_notice("You direct [src] to [M]'s mouth."))
else //if we're looking in someone elses mouth
user.visible_message(span_notice("[user] directs [src] to [M]'s mouth."), ignored_mobs = user)
render_list += span_info("You point [src] into [M]'s mouth:\n")
if(organ_count)
to_chat(user, span_notice("Inside [their] mouth [organ_count > 1 ? "are" : "is"] [organ_list]."))
render_list += "<span class='notice ml-1'>Inside [ M.p_their()] mouth [organ_count > 1 ? "are" : "is"] [organ_list].</span>\n"
else
to_chat(user, span_notice("[M] doesn't have any organs in [their] mouth."))
render_list += "<span class='notice ml-1'>[M] doesn't have any organs in [ M.p_their()] mouth.</span>\n"
if(pill_count)
to_chat(user, span_notice("[M] has [pill_count] pill[pill_count > 1 ? "s" : ""] implanted in [their] teeth."))
render_list += "<span class='notice ml-1'>[M] has [pill_count] pill[pill_count > 1 ? "s" : ""] implanted in [ M.p_their()] teeth.</span>\n"
//assess any suffocation damage
var/hypoxia_status = M.getOxyLoss() > 20
if(M == user)
if(hypoxia_status)
render_list += "<span class='danger ml-1'>Your lips appear blue!</span>\n"//you have suffocation damage
else
render_list += "<span class='notice ml-1'>Your lips appear healthy.</span>\n"//you're okay!
else
if(hypoxia_status)
render_list += "<span class='danger ml-1'>[M.p_their(TRUE)] lips appear blue!</span>\n"//they have suffocation damage
else
render_list += "<span class='notice ml-1'>[M.p_their(TRUE)] lips appear healthy.</span>\n"//they're okay!
//assess blood level
if(M == user)
render_list += span_info("You press a finger to your gums:\n")
else
render_list += span_info("You press a finger to [M.p_their()] gums:\n")
if(M.blood_volume <= BLOOD_VOLUME_SAFE && M.blood_volume > BLOOD_VOLUME_OKAY)
render_list += "<span class='danger ml-1'>Color returns slowly!</span>\n"//low blood
else if(M.blood_volume <= BLOOD_VOLUME_OKAY)
render_list += "<span class='danger ml-1'>Color does not return!</span>\n"//critical blood
else
render_list += "<span class='notice ml-1'>Color returns quickly.</span>\n"//they're okay :D
//display our packaged information in an examine block for easy reading
to_chat(user, examine_block(jointext(render_list, "")), type = MESSAGE_TYPE_INFO)
else
return ..()
+104 -12
View File
@@ -185,26 +185,118 @@
var/mob/living/carbon/carbon_patient = M
var/body_part = parse_zone(user.zone_selected)
var/oxy_loss = carbon_patient.getOxyLoss()
var/heart_strength = span_danger("no")
var/lung_strength = span_danger("no")
var/heart_strength
var/pulse_pressure
var/obj/item/organ/internal/heart/heart = carbon_patient.get_organ_slot(ORGAN_SLOT_HEART)
var/obj/item/organ/internal/lungs/lungs = carbon_patient.get_organ_slot(ORGAN_SLOT_LUNGS)
var/obj/item/organ/internal/liver/liver = carbon_patient.get_organ_slot(ORGAN_SLOT_LIVER)
var/obj/item/organ/internal/appendix/appendix = carbon_patient.get_organ_slot(ORGAN_SLOT_APPENDIX)
if(carbon_patient.stat != DEAD && !(HAS_TRAIT(carbon_patient, TRAIT_FAKEDEATH)))
if(istype(heart))
heart_strength = (heart.beating ? "a healthy" : span_danger("an unstable"))
if(istype(lungs))
lung_strength = ((carbon_patient.failed_last_breath || carbon_patient.losebreath) ? span_danger("strained") : "healthy")
var/render_list = list()//information will be packaged in a list for clean display to the user
user.visible_message(span_notice("[user] places [src] against [carbon_patient]'s [body_part] and listens attentively."), ignored_mobs = user)
//determine what specific action we're taking
switch (body_part)
if(BODY_ZONE_CHEST)//Listening to the chest
user.visible_message(span_notice("[user] places [src] against [carbon_patient]'s [body_part] and listens attentively."), ignored_mobs = user)
if(!user.can_hear())
to_chat(user, span_notice("You place [src] against [carbon_patient]'s [body_part]. Fat load of good it does you though, since you can't hear"))
return
else
render_list += span_info("You place [src] against [carbon_patient]'s [body_part]:\n")
var/diagnosis = (body_part == BODY_ZONE_CHEST ? "You hear [heart_strength] pulse and [lung_strength] respiration" : "You faintly hear [heart_strength] pulse")
if(!user.can_hear())
diagnosis = "Fat load of good it does you though, since you can't hear"
//assess breathing
if(!lungs)//sanity check, enusure patient actually has lungs
render_list += "<span class='danger ml-1'>[M] doesn't have any lungs!</span>\n"
else
if(carbon_patient.stat == DEAD || (HAS_TRAIT(carbon_patient, TRAIT_FAKEDEATH)) || (HAS_TRAIT(carbon_patient, TRAIT_NOBREATH))|| carbon_patient.failed_last_breath || carbon_patient.losebreath)//If pt is dead or otherwise not breathing
render_list += "<span class='danger ml-1'>[M.p_theyre(TRUE)] not breathing!</span>\n"
else if(lungs.damage > 10)//if breathing, check for lung damage
render_list += "<span class='danger ml-1'>You hear fluid in [M.p_their()] lungs!</span>\n"
else if(oxy_loss > 10)//if they have suffocation damage
render_list += "<span class='danger ml-1'>[M.p_theyre(TRUE)] breathing heavily!</span>\n"
else
render_list += "<span class='notice ml-1'>[M.p_theyre(TRUE)] breathing normally.</span>\n"//they're okay :D
to_chat(user, span_notice("You place [src] against [carbon_patient]'s [body_part]. [diagnosis]."))
//assess heart
if(body_part == BODY_ZONE_CHEST)//if we're listening to the chest
if(!heart)//sanity check, ensure the patient actually has a heart
render_list += "<span class='danger ml-1'>[M] doesn't have a heart!</span>\n"
else
if(!heart.beating || carbon_patient.stat == DEAD)
render_list += "<span class='danger ml-1'>You don't hear a heartbeat!</span>\n"//they're dead or their heart isn't beating
else if(heart.damage > 10 || carbon_patient.blood_volume <= BLOOD_VOLUME_OKAY)
render_list += "<span class='danger ml-1'>You hear a weak heartbeat.</span>\n"//their heart is damaged, or they have critical blood
else
render_list += "<span class='notice ml-1'>You hear a healthy heartbeat.</span>\n"//they're okay :D
if(BODY_ZONE_PRECISE_GROIN)//If we're targeting the groin
render_list += span_info("You carefully press down on [carbon_patient]'s abdomen:\n")
user.visible_message(span_notice("[user] presses their hands against [carbon_patient]'s abdomen."), ignored_mobs = user)
//assess abdominal organs
if(body_part == BODY_ZONE_PRECISE_GROIN)
var/appendix_okay = TRUE
var/liver_okay = TRUE
if(!liver)//sanity check, ensure the patient actually has a liver
render_list += "<span class='danger ml-1'>[M] doesn't have a liver!</span>\n"
liver_okay = FALSE
else
if(liver.damage > 10)
render_list += "<span class='danger ml-1'>[M.p_their(TRUE)] liver feels firm.</span>\n"//their liver is damaged
liver_okay = FALSE
if(!appendix)//sanity check, ensure the patient actually has an appendix
render_list += "<span class='danger ml-1'>[M] doesn't have an appendix!</span>\n"
appendix_okay = FALSE
else
if(appendix.damage > 10 && carbon_patient.stat == CONSCIOUS)
render_list += "<span class='danger ml-1'>[M] screams when you lift your hand from [M.p_their()] appendix!</span>\n"//scream if their appendix is damaged and they're awake
M.emote("scream")
appendix_okay = FALSE
if(liver_okay && appendix_okay)//if they have all their organs and have no detectable damage
render_list += "<span class='notice ml-1'>You don't find anything abnormal.</span>\n"//they're okay :D
if(BODY_ZONE_PRECISE_EYES)
balloon_alert(user, "can't do that!")
return
if(BODY_ZONE_PRECISE_MOUTH)
balloon_alert(user, "can't do that!")
return
else//targeting an extremity or the head
if(body_part == BODY_ZONE_HEAD)
render_list += span_info("You carefully press your fingers to [carbon_patient]'s neck:\n")
user.visible_message(span_notice("[user] presses their fingers against [carbon_patient]'s neck."), ignored_mobs = user)
else
render_list += span_info("You carefully press your fingers to [carbon_patient]'s [body_part]:\n")
user.visible_message(span_notice("[user] presses their fingers against [carbon_patient]'s [body_part]."), ignored_mobs = user)
//assess pulse (heart & blood level)
if(!heart)//sanity check, ensure the patient actually has a heart
render_list += "<span class='danger ml-1'>[M] doesn't have a heart!</span>\n"
else
if(!heart.beating || carbon_patient.blood_volume <= BLOOD_VOLUME_OKAY || carbon_patient.stat == DEAD)
render_list += "<span class='danger ml-1'>You can't find a pulse!</span>\n"//they're dead, their heart isn't beating, or they have critical blood
else
if(heart.damage > 10)
heart_strength = span_danger("irregular")//their heart is damaged
else
heart_strength = span_notice("regular")//they're okay :D
if(carbon_patient.blood_volume <= BLOOD_VOLUME_SAFE && carbon_patient.blood_volume > BLOOD_VOLUME_OKAY)
pulse_pressure = span_danger("thready")//low blood
else
pulse_pressure = span_notice("strong")//they're okay :D
render_list += "<span class='notice ml-1'>[M.p_their(TRUE)] pulse is [pulse_pressure] and [heart_strength].</span>\n"
//display our packaged information in an examine block for easy reading
to_chat(user, examine_block(jointext(render_list, "")), type = MESSAGE_TYPE_INFO)
///////////
//SCARVES//