diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm
index b98b8bd1b7f..f96d60cbbfa 100644
--- a/code/__DEFINES/span.dm
+++ b/code/__DEFINES/span.dm
@@ -151,6 +151,9 @@
#define span_warning(str) ("" + str + "")
#define span_yell(str) ("" + str + "")
#define span_yellowteamradio(str) ("" + str + "")
+#define span_notice_ml(text) "[text]"
+#define span_danger_ml(text) "[text]"
+#define span_info_ml(text) "[text]"
// Spans that use embedded tgui components:
// Sorted alphabetically
diff --git a/code/datums/quirks/negative_quirks/immunodeficiency.dm b/code/datums/quirks/negative_quirks/immunodeficiency.dm
index 99d6c9520cd..ca7c47db4c7 100644
--- a/code/datums/quirks/negative_quirks/immunodeficiency.dm
+++ b/code/datums/quirks/negative_quirks/immunodeficiency.dm
@@ -1,6 +1,6 @@
/datum/quirk/item_quirk/immunodeficiency
name = "Immunodeficiency"
- desc = "Whiether by chronic illness or genetic happenstance, your body is a 24/7 Bed and Breakfast for bacteria, viruses, and parasites of all kinds. Even with your prescribed immunity boosters, you'll fare worse than most others."
+ desc = "Whether by chronic illness or genetic happenstance, your body is a 24/7 Bed and Breakfast for bacteria, viruses, and parasites of all kinds. Even with your prescribed immunity boosters, you'll fare worse than most others."
icon = FA_ICON_MASK_FACE
value = -10
mob_trait = TRAIT_IMMUNODEFICIENCY
diff --git a/code/datums/quirks/positive_quirks/friendly.dm b/code/datums/quirks/positive_quirks/friendly.dm
index 26b42f554f9..6f23d657950 100644
--- a/code/datums/quirks/positive_quirks/friendly.dm
+++ b/code/datums/quirks/positive_quirks/friendly.dm
@@ -21,3 +21,4 @@
// Like, physically bigger.
holder_heart.reagents.add_reagent(/datum/reagent/consumable/nutriment/organ_tissue, 5)
holder_heart.transform = holder_heart.transform.Scale(1.5)
+ holder_heart.beat_noise += ". It radiates loving warmth" // wuv is a detectable diagnostic quality
diff --git a/code/datums/storage/subtypes/belts.dm b/code/datums/storage/subtypes/belts.dm
index d57cafee194..81b1abf9944 100644
--- a/code/datums/storage/subtypes/belts.dm
+++ b/code/datums/storage/subtypes/belts.dm
@@ -72,6 +72,8 @@
/obj/item/wrench/medical,
/obj/item/knife/ritual,
/obj/item/flesh_shears,
+ /obj/item/blood_scanner,
+ /obj/item/reflexhammer,
/obj/item/clothing/suit/toggle/labcoat/hospitalgown, //SKYRAT EDIT ADDITION - adds surgery gowns to belts
/obj/item/hypospray/mkii, //SKYRAT EDIT ADDITION - HYPOSPRAYS
/obj/item/storage/hypospraykit/, //SKYRAT EDIT ADDITION - HYPOSPRAYS
diff --git a/code/datums/storage/subtypes/medkits.dm b/code/datums/storage/subtypes/medkits.dm
index e638547f08b..c793469790c 100644
--- a/code/datums/storage/subtypes/medkits.dm
+++ b/code/datums/storage/subtypes/medkits.dm
@@ -25,6 +25,8 @@
/obj/item/implantcase,
/obj/item/implant,
/obj/item/implanter,
+ /obj/item/blood_scanner,
+ /obj/item/reflexhammer,
//stacks
/obj/item/stack/medical,
diff --git a/code/datums/storage/subtypes/others/misc.dm b/code/datums/storage/subtypes/others/misc.dm
index 154ac9944ea..35a124e94eb 100644
--- a/code/datums/storage/subtypes/others/misc.dm
+++ b/code/datums/storage/subtypes/others/misc.dm
@@ -38,6 +38,7 @@
/obj/item/stack/sticky_tape/surgical,
/obj/item/surgical_drapes,
/obj/item/surgicaldrill,
+ /obj/item/blood_scanner,
))
///Organ box
diff --git a/code/datums/storage/subtypes/pockets.dm b/code/datums/storage/subtypes/pockets.dm
index f5fa5991142..ec667357ed5 100644
--- a/code/datums/storage/subtypes/pockets.dm
+++ b/code/datums/storage/subtypes/pockets.dm
@@ -97,6 +97,7 @@
/obj/item/switchblade,
/obj/item/boxcutter,
/obj/item/pen,
+ /obj/item/flashlight/pen, //i mean cmon if a pen fits in there this does
/obj/item/scalpel,
/obj/item/dnainjector,
/obj/item/reagent_containers/syringe,
@@ -156,6 +157,7 @@
/obj/item/lipstick,
/obj/item/match,
/obj/item/pen,
+ /obj/item/flashlight/pen,
/obj/item/reagent_containers/cup/glass/flask,
/obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/hypospray/medipen,
diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm
index 2482d7997b3..305ce0adc14 100644
--- a/code/datums/wounds/burns.dm
+++ b/code/datums/wounds/burns.dm
@@ -53,7 +53,7 @@
if(HAS_TRAIT(victim, TRAIT_VIRUS_RESISTANCE))
sanitization += 0.9
- if(HAS_TRAIT(victim, TRAIT_IMMUNODEFICIENCY) && !HAS_TRAIT(victim, TRAIT_VIRUS_RESISTANCE))
+ if(HAS_TRAIT(victim, TRAIT_IMMUNODEFICIENCY))
infestation += 0.05
sanitization = max(sanitization - 0.15, 0)
if(infestation_rate <= 0.15 && prob(50))
diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm
index b7fb7017e8d..a83974517ed 100644
--- a/code/datums/wounds/pierce.dm
+++ b/code/datums/wounds/pierce.dm
@@ -232,6 +232,23 @@
return 0
return weight
+/datum/wound/pierce/bleed/moderate/needle_fail //for blood testamajig
+ name = "Pinprick Pierce"
+ desc = "Patient's skin has been deeply pierced, causing mild bleeding."
+ treat_text_short = "Apply bandaging or suturing."
+ examine_desc = "has a small red pinprick, gently bleeding"
+ initial_flow = 0.5 //very minor, mostly there as fluff and "dont do that idiot" reminder
+ gauzed_clot_rate = 0.1
+ clot_rate = 0.03 // will close quickly on its own
+ internal_bleeding_chance = 0
+ internal_bleeding_coefficient = 1
+ threshold_penalty = 5
+
+/datum/wound_pregen_data/flesh_pierce/open_puncture/pinprick
+ wound_path_to_generate = /datum/wound/pierce/bleed/moderate/needle_fail
+ can_be_randomly_generated = FALSE
+ abstract = FALSE
+
/datum/wound/pierce/bleed/moderate/projectile
name = "Minor Skin Penetration"
desc = "Patient's skin has been pierced through, causing severe bruising and minor internal bleeding in affected area."
@@ -349,6 +366,24 @@
return FALSE
return ..()
+/datum/wound/pierce/bleed/severe/magicalearpain //what happens if you try to listen to the heartbeat of a corrupt heart while not a heretic
+ name = "Bleeding Ears"
+ desc = "Patient's ears are bleeding heavily as blood seeps through the inner flesh of the ear through some unknown means."
+ examine_desc = "is covered in blood, black-purple fluid flowing from its ears"
+ occur_text = "is soaked as two spurts of black liquid spray from its ears"
+ internal_bleeding_chance = 0 // just your ears
+
+/datum/wound_pregen_data/flesh_pierce/open_puncture/magicalearpain
+ wound_path_to_generate = /datum/wound/pierce/bleed/severe/magicalearpain
+ viable_zones = list(BODY_ZONE_HEAD)
+ can_be_randomly_generated = FALSE
+
+/datum/wound/pierce/bleed/severe/magicalearpain/apply_wound(obj/item/bodypart/limb, silent, datum/wound/old_wound, smited, attack_direction, wound_source, replacing)
+ var/obj/item/organ/ears/ears = locate() in limb
+ if (!istype(ears))
+ return FALSE
+ . = ..()
+
/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."
diff --git a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
index fc113ef8a69..05414ef5fb5 100644
--- a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
@@ -261,7 +261,7 @@
desc = "Fish DNA infused on what once was a normal pair of lungs that now require spacemen to breathe water vapor, or keep themselves covered in water."
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "gills"
-
+ breath_noise = "the dribbling of water"
organ_traits = list(TRAIT_NODROWN)
// Seafood instead of meat, because it's a fish organ. Additionally gross for being gills
foodtype_flags = RAW | SEAFOOD | GORE | GROSS
diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
index 011b35ccd4c..71922f1b3a9 100644
--- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
@@ -61,6 +61,7 @@
post_init_icon_state = "heart"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = RAT_COLORS
+ beat_noise = "a fast-paced high-pitched pit-pat"
/obj/item/organ/heart/rat/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/items/devices/blood_scanner.dm b/code/game/objects/items/devices/blood_scanner.dm
new file mode 100644
index 00000000000..9b5c3b40075
--- /dev/null
+++ b/code/game/objects/items/devices/blood_scanner.dm
@@ -0,0 +1,95 @@
+///Pricks people with a big needle and gives the user a bunch of info about bloodtype, tox damage, other such stuff. if you fuck up it stabs the guy a bit.
+/obj/item/blood_scanner
+ name = "hemoanalytic scanner"
+ icon = 'icons/obj/devices/scanner.dmi'
+ icon_state = "bloodscanner"
+ inhand_icon_state = "healthanalyzer"
+ worn_icon_state = "healthanalyzer"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ desc = "This Me-Lo Tech branded medical device"
+ obj_flags = CONDUCTS_ELECTRICITY
+ item_flags = NOBLUDGEON
+ slot_flags = ITEM_SLOT_BELT
+ throwforce = 3
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2, /datum/material/glass=SMALL_MATERIAL_AMOUNT*2)
+
+/obj/item/blood_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!usable_check(person_scanning = user, scanee = interacting_with))
+ return NONE
+ var/mob/living/carbon/poked_guy = interacting_with
+ var/obj/item/bodypart/poked_bit = poked_guy.get_bodypart(check_zone(user.zone_selected))
+ if(!poked_bit)
+ return
+ user.visible_message(span_notice("[user] pricks [poked_guy] with [src]."), span_notice("You begin scanning [poked_guy] with [src]."), ignored_mobs = poked_guy)
+ to_chat(poked_guy, span_notice("[user] slides the needle of [src] into your [poked_bit] and begins pressing buttons."))
+ var/success = do_after(user, 2 SECONDS, poked_guy)
+ if(success)
+ scan_blood(scanner = user, scanned_person = poked_guy)
+ else
+ if(prob(25))
+ regret(stabber = user, stabbed = poked_guy, to_stab = poked_bit)
+ return
+
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/blood_scanner/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!usable_check(person_scanning = user, scanee = interacting_with))
+ return NONE
+ var/mob/living/carbon/poked_guy = interacting_with
+ var/obj/item/bodypart/poked_bit = poked_guy.get_bodypart(check_zone(user.zone_selected))
+ user.visible_message(span_notice("[user] pricks [poked_guy] with [src]."), span_notice("You begin scanning [poked_guy] with [src]."), ignored_mobs = poked_guy)
+ to_chat(poked_guy, span_notice("[user] slides the needle of [src] into your [poked_bit] and begins pressing buttons."))
+ var/success = do_after(user, 2 SECONDS, poked_guy)
+ if(success)
+ chemscan(user, poked_guy, reagent_types_to_check = /datum/reagent/medicine)
+ else
+ if(prob(25))
+ regret(stabber = user, stabbed = poked_guy, to_stab = poked_bit)
+ return
+
+ return ITEM_INTERACT_SUCCESS
+
+
+/obj/item/blood_scanner/proc/scan_blood(mob/living/scanner, mob/living/carbon/scanned_person)
+ var/render_list = list()
+ var/oxy_loss = scanned_person.getOxyLoss()
+ var/tox_loss = scanned_person.getToxLoss()
+ render_list += span_info("You read the [src]'s screen:\n")
+ render_list += "Blood Type: [scanned_person?.dna?.blood_type]\n"
+ if(oxy_loss > 50)//if they have knockout levels of suffocation damage
+ render_list += "Warning: Hypoxic blood oxygen levels.\n"
+ if(scanned_person.blood_volume <= BLOOD_VOLUME_SAFE)
+ render_list += "Warning: Dangerously low blood flow.\n"
+ if(tox_loss > 10)
+ render_list += "Warning: Toxic buildup detected in bloodstream.\n"
+ if(scanned_person.has_status_effect(/datum/status_effect/eigenstasium))
+ render_list += "Warning: Dimensional instability detected. Administer stabilizers.\n"
+ if(scanned_person.has_reagent(/datum/reagent/gold/cursed) || scanned_person.has_status_effect(/datum/status_effect/midas_blight))
+ render_list += "Warning: Hemo-aurificating hexes present.\n" //can it detect normal things? barely. But ancient greed-cursed magicks? Spot on.
+ if(HAS_TRAIT_FROM(scanned_person, TRAIT_NODEATH, /datum/reagent/inverse/penthrite))
+ render_list += "Warning: Frankensteinian revivification in progress.\n" //patient is currently an immortal drug-zombie
+ if(scanned_person.has_status_effect(/datum/status_effect/high_blood_pressure))
+ render_list += "Warning: Dangerously high blood pressure.\n"
+ if(HAS_TRAIT(scanned_person, TRAIT_IMMUNODEFICIENCY))
+ render_list += "Warning: Low immune cell concentration.\n" //it doe
+ to_chat(scanner, boxed_message(jointext(render_list, "")), type = MESSAGE_TYPE_INFO)
+
+/obj/item/blood_scanner/proc/regret(mob/living/stabber, mob/living/stabbed, obj/item/bodypart/to_stab)
+ to_stab?.force_wound_upwards(/datum/wound/pierce/bleed/moderate/needle_fail, wound_source = "idiot moved with a needle in them")
+ stabber.visible_message(span_warning("[src]'s needle is ripped out, tearing a hole in [stabbed]'s [to_stab]!"), span_warning("Damnit! The needle is torn out, making a tiny hole in [stabbed.p_their()] [to_stab]."), ignored_mobs = stabbed)
+ to_chat(stabbed, span_userdanger("OWWW! The needle of [src] is ripped out, tearing a small hole in your [to_stab]!"))
+
+/obj/item/blood_scanner/proc/usable_check(mob/living/person_scanning, atom/scanee)
+ if(!isliving(scanee))
+ return FALSE
+ if(!istype(scanee, /mob/living/carbon))
+ to_chat(person_scanning, span_warning("[src] is incompatible."))
+ return FALSE
+ if(!person_scanning.can_read(src) || person_scanning.is_blind())
+ to_chat(person_scanning, span_warning("You are unable to read [src]'s screen!"))
+ return FALSE
+ return TRUE
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 57ea76358b8..f43550aa134 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -111,53 +111,62 @@
user.visible_message(span_suicide("[user] is putting [src] close to [user.p_their()] eyes and turning it on! It looks like [user.p_theyre()] trying to commit suicide!"))
return FIRELOSS
-/obj/item/flashlight/proc/eye_examine(mob/living/carbon/human/M, mob/living/user)
+/obj/item/flashlight/proc/eye_examine(mob/living/carbon/human/patient, mob/living/user)
. = list()
- if((M.head && M.head.flags_cover & HEADCOVERSEYES) || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSEYES) || (M.glasses && M.glasses.flags_cover & GLASSESCOVERSEYES))
- to_chat(user, span_warning("You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSEYES) ? "helmet" : (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSEYES) ? "mask": "glasses"] first!"))
+ if((patient.head && patient.head.flags_cover & HEADCOVERSEYES) || (patient.wear_mask && patient.wear_mask.flags_cover & MASKCOVERSEYES) || (patient.glasses && patient.glasses.flags_cover & GLASSESCOVERSEYES))
+ to_chat(user, span_warning("You're going to need to remove that [(patient.head && patient.head.flags_cover & HEADCOVERSEYES) ? "helmet" : (patient.wear_mask && patient.wear_mask.flags_cover & MASKCOVERSEYES) ? "mask": "glasses"] first!"))
return
- var/obj/item/organ/eyes/E = M.get_organ_slot(ORGAN_SLOT_EYES)
- var/obj/item/organ/brain = M.get_organ_slot(ORGAN_SLOT_BRAIN)
- if(!E)
- to_chat(user, span_warning("[M] doesn't have any eyes!"))
+ var/obj/item/organ/eyes/eyes = patient.get_organ_slot(ORGAN_SLOT_EYES)
+ var/obj/item/organ/brain = patient.get_organ_slot(ORGAN_SLOT_BRAIN)
+ var/obj/item/organ/zombie_infection/tumor = patient.get_organ_slot(ORGAN_SLOT_ZOMBIE) //this slot only ever holds zombie tumors, so we can just check if this exists
+ var/braaaainz = tumor?.causes_damage //prevents steath tumors (admin bullshittery or romerol) from showing up to preserve stealthiness
+ if(!eyes)
+ to_chat(user, span_warning("[patient] 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.
+ patient.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
- user.visible_message(span_warning("[user] shines [src] into [M.p_their()] eyes."), ignored_mobs = user)
+ if(patient == user) //they're using it on themselves
+ user.visible_message(span_warning("[user] shines [src] into [patient.p_their()] eyes."), ignored_mobs = user)
. += span_info("You direct [src] to into your eyes:\n")
- if(M.is_blind())
- . += "You're not entirely certain what you were expecting...\n"
+ if(patient.is_blind())
+ . += span_notice_ml("You're not entirely certain what you were expecting...\n")
else
- . += "Trippy!\n"
+ . += span_notice_ml("Trippy!\n")
else
- user.visible_message(span_warning("[user] directs [src] to [M]'s eyes."), ignored_mobs = user)
- . += span_info("You direct [src] to [M]'s eyes:\n")
+ user.visible_message(span_warning("[user] directs [src] to [patient]'s eyes."), ignored_mobs = user)
+ . += span_info("You direct [src] to [patient]'s eyes:\n")
- if(M.stat == DEAD || M.is_blind() || M.get_eye_protection() > FLASH_PROTECTION_WELDER)
- . += "[M.p_Their()] pupils don't react to the light!\n"//mob is dead
+ if(patient.stat == DEAD || patient.is_blind() || patient.get_eye_protection() >= FLASH_PROTECTION_WELDER) //this used to be just > but literally nothing accessable in the game gave greater than welder without also covering eyes
+ . += span_danger_ml("[patient.p_Their()] [eyes.pupils_name] don't react to the light!\n")//mob is dead
else if(brain.damage > 20)
- . += "[M.p_Their()] pupils contract unevenly!\n"//mob has sustained damage to their brain
+ . += span_danger_ml("[patient.p_Their()] [eyes.pupils_name] contract unevenly!\n")//mob has sustained damage to their brain
else
- . += "[M.p_Their()] pupils narrow.\n"//they're okay :D
-
- if(M.dna && M.dna.check_mutation(/datum/mutation/xray))
- . += "[M.p_Their()] pupils give an eerie glow!\n"//mob has X-ray vision
+ . += span_notice_ml("[patient.p_Their()] [eyes.pupils_name] narrow.\n")//they're okay :D
+ if(HAS_TRAIT(patient, TRAIT_XRAY_VISION))
+ . += span_danger_ml("[patient.p_Their()] [eyes.pupils_name] give an eerie glow!\n")//mob has X-ray vision
+ if(eyes.penlight_message != /obj/item/organ/eyes::penlight_message) //prevent default eyes from cluttering text, if this still happens somehow it displays "default message please report"
+ . += span_notice_ml("[eyes.penlight_examine(user, src)]\n")
+ if(braaaainz)
+ . += span_danger_ml("[patient.p_Their()] eyes are webbed by fibrous black tendrils!\n")
+ if(patient.has_status_effect(/datum/status_effect/drugginess) || patient.has_status_effect(/datum/status_effect/trance))
+ . += span_danger_ml("[patient.p_Their()] [eyes.pupils_name] are responsive, but entirely unfocused.")
+ if(patient.has_status_effect(/datum/status_effect/stoned))
+ . += span_danger_ml("[patient.p_Their()] eyes are wide, lazy, and bloodshot.") //this shit is GAS, batman. *wheezing chuckle*
return .
-/obj/item/flashlight/proc/mouth_examine(mob/living/carbon/human/M, mob/living/user)
+/obj/item/flashlight/proc/mouth_examine(mob/living/carbon/human/patient, mob/living/user)
. = list()
- if(M.is_mouth_covered())
- to_chat(user, span_warning("You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSMOUTH) ? "helmet" : "mask"] first!"))
+ if(patient.is_mouth_covered())
+ to_chat(user, span_warning("You're going to need to remove that [(patient.head && patient.head.flags_cover & HEADCOVERSMOUTH) ? "helmet" : "mask"] first!"))
return
var/list/mouth_organs = list()
- for(var/obj/item/organ/organ as anything in M.organs)
+ for(var/obj/item/organ/organ as anything in patient.organs)
if(organ.zone == BODY_ZONE_PRECISE_MOUTH)
mouth_organs.Add(organ)
var/organ_list = ""
@@ -169,14 +178,14 @@
organ_list += ", and "
else
organ_list += ", "
- var/obj/item/organ/O = mouth_organs[I]
- organ_list += (O.gender == "plural" ? O.name : "\an [O.name]")
+ var/obj/item/organ/organ_to_display = mouth_organs[I]
+ organ_list += (organ_to_display.gender == "plural" ? organ_to_display.name : "\an [organ_to_display.name]")
var/pill_count = 0
- for(var/datum/action/item_action/activate_pill/AP in M.actions)
+ for(var/datum/action/item_action/activate_pill/AP in patient.actions)
pill_count++
- if(M == user)//if we're looking on our own mouth
+ if(patient == 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)
@@ -191,54 +200,55 @@
if(WEST)
can_use_mirror = mirror.pixel_x < 0
- M.visible_message(span_notice("[M] directs [src] to [ M.p_their()] mouth."), ignored_mobs = user)
- . += span_info("You point [src] into your mouth:\n")
+ patient.visible_message(span_notice("[patient] directs [src] to [patient.p_their()] mouth."), ignored_mobs = user)
+ . += span_info_ml("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)
- . += "Inside your mouth [organ_count > 1 ? "are" : "is"] [organ_list].\n"
+ . += span_notice_ml("Inside your mouth [organ_count > 1 ? "are" : "is"] [organ_list].\n")
else
- . += "There's nothing inside your mouth.\n"
+ . += span_notice_ml("There's nothing inside your mouth.\n")
if(pill_count)
- . += "You have [pill_count] implanted pill[pill_count > 1 ? "s" : ""].\n"
+ . += span_notice_ml("You have [pill_count] implanted pill[pill_count > 1 ? "s" : ""].\n")
else //if we're looking in someone elses mouth
- user.visible_message(span_notice("[user] directs [src] to [M]'s mouth."), ignored_mobs = user)
- . += span_info("You point [src] into [M]'s mouth:\n")
+ user.visible_message(span_notice("[user] directs [src] to [patient]'s mouth."), ignored_mobs = user)
+ . += span_info_ml("You point [src] into [patient]'s mouth:\n")
if(organ_count)
- . += "Inside [ M.p_their()] mouth [organ_count > 1 ? "are" : "is"] [organ_list].\n"
+ . += span_notice_ml("Inside [patient.p_their()] mouth [organ_count > 1 ? "are" : "is"] [organ_list].\n")
else
- . += "[M] doesn't have any organs in [ M.p_their()] mouth.\n"
+ . += span_notice_ml("[patient] doesn't have any organs in [patient.p_their()] mouth.\n")
if(pill_count)
- . += "[M] has [pill_count] pill[pill_count > 1 ? "s" : ""] implanted in [ M.p_their()] teeth.\n"
+ . += span_notice_ml("[patient] has [pill_count] pill[pill_count > 1 ? "s" : ""] implanted in [patient.p_their()] teeth.\n")
//assess any suffocation damage
- var/hypoxia_status = M.getOxyLoss() > 20
+ var/hypoxia_status = patient.getOxyLoss() > 20
- if(M == user)
+ if(patient == user)
if(hypoxia_status)
- . += "Your lips appear blue!\n"//you have suffocation damage
+ . += span_danger_ml("Your lips appear blue!\n")//you have suffocation damage
else
- . += "Your lips appear healthy.\n"//you're okay!
+ . += span_notice_ml("Your lips appear healthy.\n")//you're okay!
else
if(hypoxia_status)
- . += "[M.p_Their()] lips appear blue!\n"//they have suffocation damage
+ . += span_danger_ml("[patient.p_Their()] lips appear blue!\n")//they have suffocation damage
else
- . += "[M.p_Their()] lips appear healthy.\n"//they're okay!
+ . += span_notice_ml("[patient.p_Their()] lips appear healthy.\n")//they're okay!
//assess blood level
- if(M == user)
- . += span_info("You press a finger to your gums:\n")
+ if(patient == user)
+ . += span_info_ml("You press a finger to your gums:\n")
else
- . += span_info("You press a finger to [M.p_their()] gums:\n")
+ . += span_info_ml("You press a finger to [patient.p_their()] gums:\n")
- if(M.blood_volume <= BLOOD_VOLUME_SAFE && M.blood_volume > BLOOD_VOLUME_OKAY)
- . += "Color returns slowly!\n"//low blood
- else if(M.blood_volume <= BLOOD_VOLUME_OKAY)
- . += "Color does not return!\n"//critical blood
+ if(patient.blood_volume <= BLOOD_VOLUME_SAFE && patient.blood_volume > BLOOD_VOLUME_OKAY)
+ . += span_danger_ml("Color returns slowly!\n")//low blood
+ else if(patient.blood_volume <= BLOOD_VOLUME_OKAY)
+ . += span_danger_ml("Color does not return!\n")//critical blood
else
- . += "Color returns quickly.\n"//they're okay :D
+ . += span_notice_ml("Color returns quickly.\n")//they're okay :D
+
/obj/item/flashlight/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!ishuman(interacting_with))
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index 490c1b67934..5d09226154e 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -524,7 +524,7 @@
REMOVE_TRAIT(target, TRAIT_RECENTLY_TREATED, ANALYZER_TRAIT)
return TRUE
-/proc/chemscan(mob/living/user, mob/living/target)
+/proc/chemscan(mob/living/user, mob/living/target, reagent_types_to_check = null)
if(user.incapacitated)
return
@@ -538,6 +538,9 @@
var/datum/reagent/reagent = r
if(reagent.chemical_flags & REAGENT_INVISIBLE) //Don't show hidden chems on scanners
continue
+ if(reagent_types_to_check)
+ if(!istype(reagent, reagent_types_to_check))
+ continue
render_block += "[round(reagent.volume, 0.001)] units of [reagent.name][reagent.overdosed ? " - [span_bolddanger("OVERDOSING")]" : "."]
"
if(!length(render_block)) //If no VISIBLY DISPLAYED reagents are present, we report as if there is nothing.
@@ -555,6 +558,9 @@
var/datum/reagent/bit = bile
if(bit.chemical_flags & REAGENT_INVISIBLE)
continue
+ if(reagent_types_to_check)
+ if(!istype(bit, reagent_types_to_check))
+ continue
if(!belly.food_reagents[bit.type])
render_block += "[round(bit.volume, 0.001)] units of [bit.name][bit.overdosed ? " - [span_bolddanger("OVERDOSING")]" : "."]
"
else
diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm
index 82df5ddd144..1035a006a9c 100644
--- a/code/modules/antagonists/heretic/items/corrupted_organs.dm
+++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm
@@ -7,6 +7,8 @@
eye_color_left = COLOR_VOID_PURPLE
eye_color_right = COLOR_VOID_PURPLE
organ_flags = parent_type::organ_flags | ORGAN_HAZARDOUS
+ pupils_name = span_hypnophrase("pierced realities") //teeny tiny mansus portals, IN YOUR EYEBALLS (known to cause cancer in the state of california)
+ penlight_message = "ARE THE LOCK, THE LIGHT IS THE KEY! THE HIGHER I RISE, THE MORE I-"
/// The override images we are applying
var/list/hallucinations
@@ -39,6 +41,17 @@
organ_owner.client?.images -= hallucinations
QDEL_NULL(hallucinations)
+/obj/item/organ/eyes/corrupt/penlight_examine(mob/living/viewer, obj/item/examtool)
+ viewer.playsound_local(src, 'sound/effects/magic/magic_block_mind.ogg', 75, FALSE)
+ if(!viewer.is_blind() && !IS_HERETIC_OR_MONSTER(viewer))
+ to_chat(viewer, span_danger("Your eyes sizzle in their sockets as eldritch energies assault them!"))
+ viewer.emote("scream")
+ viewer.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus)
+ viewer.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic)
+ viewer.adjust_timed_status_effect(5 SECONDS, /datum/status_effect/temporary_blindness) //debounce basically.
+ var/obj/item/organ/eyes/parboiled = viewer.get_organ_slot(ORGAN_SLOT_EYES)
+ parboiled?.apply_organ_damage(40) //enough to blind, but not enough to blind *permanently*
+ return "[owner.p_Their()] eyes [span_hypnophrase(penlight_message)]"
/// Sometimes speak in incomprehensible tongues
/obj/item/organ/tongue/corrupt
@@ -49,7 +62,7 @@
/obj/item/organ/tongue/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
- AddElement(/datum/element/noticable_organ, "The inside of %PRONOUN_Their mouth is full of stars.", BODY_ZONE_PRECISE_MOUTH)
+ AddElement(/datum/element/noticable_organ, "The inside of %PRONOUN_their mouth is full of stars.", BODY_ZONE_PRECISE_MOUTH)
/obj/item/organ/tongue/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
@@ -183,6 +196,7 @@
/obj/item/organ/heart/corrupt
name = "corrupt heart"
desc = "What corruption is this spreading along with the blood?"
+ beat_noise = "THE THUMPTHUMPTHUMPING OF THE CHISEL ON THE GLASS. OPEN THE FUTURE SHATTER THE-"
organ_flags = parent_type::organ_flags | ORGAN_HAZARDOUS
/// How long until the next heart?
COOLDOWN_DECLARE(hand_cooldown)
@@ -198,12 +212,22 @@
fire_curse_hand(owner)
COOLDOWN_START(src, hand_cooldown, rand(6 SECONDS, 45 SECONDS)) // Wide variance to put you off guard
+/obj/item/organ/heart/corrupt/hear_beat_noise(mob/living/hearer)
+ hearer.playsound_local(src, 'sound/effects/magic/hereticknock.ogg', 75, FALSE)
+ if(!IS_HERETIC_OR_MONSTER(hearer))
+ hearer.emote("scream")
+ hearer.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus)
+ hearer.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic)
+ var/obj/item/bodypart/head/regret = hearer.get_bodypart(BODY_ZONE_HEAD)
+ regret?.force_wound_upwards(/datum/wound/pierce/bleed/severe/magicalearpain, wound_source = "stethoscoped a corrupted heart")
+ return "[owner.p_Their()] heart produces [span_hypnophrase(beat_noise)]"
/// Sometimes cough out some kind of dangerous gas
/obj/item/organ/lungs/corrupt
name = "corrupt lungs"
desc = "Some things SHOULD be drowned in tar."
organ_flags = parent_type::organ_flags | ORGAN_HAZARDOUS
+ breath_noise = "SECRET SONGS OF THE BREAKING OF THE MAKING OF THE WAKING FROM THE-"
/// How likely are we not to cough every time we take a breath?
var/cough_chance = 15
/// How much gas to emit?
@@ -233,6 +257,15 @@
var/turf/open/our_turf = get_turf(breather)
our_turf.assume_air(mix_to_spawn)
+/obj/item/organ/lungs/corrupt/hear_breath_noise(mob/living/hearer)
+ hearer.playsound_local(src, 'sound/effects/magic/voidblink.ogg', 75, FALSE)
+ if(!IS_HERETIC_OR_MONSTER(hearer))
+ hearer.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic)
+ hearer.emote("scream")
+ hearer.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus)
+ var/obj/item/organ/ears/regret = hearer.get_organ_slot(ORGAN_SLOT_EARS)
+ regret?.adjustEarDamage(10,20)
+ return "[owner.p_Their()] lungs emit [span_hypnophrase(breath_noise)]"
/// It's full of worms
/obj/item/organ/appendix/corrupt
diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm
index e2a9b684003..b50880e025b 100644
--- a/code/modules/antagonists/nightmare/nightmare_organs.dm
+++ b/code/modules/antagonists/nightmare/nightmare_organs.dm
@@ -82,6 +82,7 @@
icon_state = "demon_heart-on"
base_icon_state = "demon_heart"
+ beat_noise = "the writhing pulses of a fear given form" // evil schmeevil
color = COLOR_CRAYON_BLACK
decay_factor = 0
// No love is to be found in a heart so twisted.
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 454065d31b7..c7cd7fa100a 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -254,6 +254,8 @@
var/heart_strength
var/pulse_pressure
+ var/heart_noises = TRUE
+
var/obj/item/organ/heart/heart = carbon_patient.get_organ_slot(ORGAN_SLOT_HEART)
var/obj/item/organ/lungs/lungs = carbon_patient.get_organ_slot(ORGAN_SLOT_LUNGS)
var/obj/item/organ/liver/liver = carbon_patient.get_organ_slot(ORGAN_SLOT_LIVER)
@@ -272,6 +274,7 @@
render_list += span_info("You place [src] against [carbon_patient]'s [body_part]:\n")
//assess breathing
+ var/lung_noises = TRUE
if(isnull(lungs) \
|| carbon_patient.stat == DEAD \
|| (HAS_TRAIT(carbon_patient, TRAIT_FAKEDEATH)) \
@@ -279,21 +282,27 @@
|| carbon_patient.failed_last_breath \
|| carbon_patient.losebreath)//If pt is dead or otherwise not breathing
render_list += "[target.p_Theyre()] not breathing!\n"
+ lung_noises = FALSE
+
else if(lungs.damage > 10)//if breathing, check for lung damage
render_list += "You hear fluid in [target.p_their()] lungs!\n"
else if(oxy_loss > 10)//if they have suffocation damage
render_list += "[target.p_Theyre()] breathing heavily!\n"
else
render_list += "[target.p_Theyre()] breathing normally.\n"//they're okay :D
-
+ if(lung_noises)
+ render_list += "[lungs.hear_breath_noise(user)]\n"
//assess heart
if(body_part == BODY_ZONE_CHEST)//if we're listening to the chest
if(isnull(heart) || !heart.is_beating() || carbon_patient.stat == DEAD)
render_list += "You don't hear a heartbeat!\n"//they're dead or their heart isn't beating
+ heart_noises = FALSE
else if(heart.damage > 10 || carbon_patient.blood_volume <= BLOOD_VOLUME_OKAY)
render_list += "You hear a weak heartbeat.\n"//their heart is damaged, or they have critical blood
else
render_list += "You hear a healthy heartbeat.\n"//they're okay :D
+ if(heart_noises)
+ render_list += "[heart.hear_beat_noise(user)]\n"
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")
diff --git a/code/modules/research/designs/autolathe/medsci_designs.dm b/code/modules/research/designs/autolathe/medsci_designs.dm
index 7d0318396a3..7e804c21e6f 100644
--- a/code/modules/research/designs/autolathe/medsci_designs.dm
+++ b/code/modules/research/designs/autolathe/medsci_designs.dm
@@ -15,7 +15,7 @@
name = "Reflex Hammer"
id = "reflex_hammer"
build_type = PROTOLATHE | AWAY_LATHE | AUTOLATHE
- materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*0.75, /datum/material/plastic=SMALL_MATERIAL_AMOUNT*0.5)
+ materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT*0.75, /datum/material/plastic =SMALL_MATERIAL_AMOUNT*0.5)
build_path = /obj/item/reflexhammer
category = list(
RND_CATEGORY_INITIAL,
@@ -23,6 +23,18 @@
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE
+/datum/design/blood_scanner
+ name = "Hemoanalytic Scanner"
+ id = "blood_scanner"
+ build_type = PROTOLATHE | AWAY_LATHE | AUTOLATHE
+ materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT*2, /datum/material/glass =SMALL_MATERIAL_AMOUNT*0.5)
+ build_path = /obj/item/blood_scanner
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_MEDICAL
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE
+
/datum/design/stethoscope
name = "Stethoscope"
id = "stethoscope"
diff --git a/code/modules/research/techweb/nodes/medbay_nodes.dm b/code/modules/research/techweb/nodes/medbay_nodes.dm
index b590b9997f0..f6d5a79feb1 100644
--- a/code/modules/research/techweb/nodes/medbay_nodes.dm
+++ b/code/modules/research/techweb/nodes/medbay_nodes.dm
@@ -31,6 +31,7 @@
"xlarge_beaker",
"jerrycan",
"reflex_hammer",
+ "blood_scanner",
)
experiments_to_unlock = list(
/datum/experiment/autopsy/human,
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index d7fa2eec63f..87c3e5fad6a 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -59,6 +59,16 @@
/// Scarring on this organ
var/scarring = NONE
+ /// The (custom, sometimes) messages we get when we use a flashlight or penlight on these eyes.
+ /// Completely optional but good if you wanna be FANCY
+
+ /// this message should never show up for default eyes, do not change on default eyes.
+ var/penlight_message = "useless default please report"
+ /// what are the pupils called? eg. pupils, apertures, etc.
+ var/pupils_name = "pupils"
+ /// do these eyes have pupils (or equivalent) that react to light when penlighted.
+ var/light_reactive = TRUE
+
/obj/item/organ/eyes/Initialize(mapload)
. = ..()
if (blink_animation)
@@ -544,6 +554,10 @@
#undef BLINK_LOOPS
#undef ASYNC_BLINKING_BRAIN_DAMAGE
+/// by default, returns the eyes' penlight_message var as a notice span. May do other things when overridden, such as eldritch insanity, or eye damage, or whatnot. Whatever you want, really.
+/obj/item/organ/eyes/proc/penlight_examine(mob/living/viewer)
+ return span_notice("[owner.p_Their()] eyes [penlight_message].")
+
#define NIGHTVISION_LIGHT_OFF 0
#define NIGHTVISION_LIGHT_LOW 1
#define NIGHTVISION_LIGHT_MID 2
@@ -596,11 +610,17 @@
low_light_cutoff = list(0, 15, 20)
medium_light_cutoff = list(0, 20, 35)
high_light_cutoff = list(0, 40, 50)
+ pupils_name = "photosensory openings"
+ penlight_message = "are attached to fungal stalks"
/obj/item/organ/eyes/zombie
name = "undead eyes"
desc = "Somewhat counterintuitively, these half-rotten eyes actually have superior vision to those of a living human."
color_cutoffs = list(25, 35, 5)
+ penlight_message = "are rotten and decayed"
+
+/obj/item/organ/eyes/zombie/penlight_examine(mob/living/viewer, obj/item/examtool)
+ return span_danger(penlight_message)
/obj/item/organ/eyes/alien
name = "alien eyes"
@@ -620,6 +640,8 @@
organ_flags = ORGAN_MINERAL
color_cutoffs = list(10, 15, 5)
actions_types = list(/datum/action/cooldown/golem_ore_sight)
+ penlight_message = "glimmer, their crystaline structure refracting light inwards"
+ pupils_name = "lensing gems" ///given it says these are a "mineral lattice" that collects light i assume they work like artifical ruby laser foci
/// Send an ore detection pulse on a cooldown
/datum/action/cooldown/golem_ore_sight
@@ -642,6 +664,8 @@
icon_state = "eyes_cyber"
organ_flags = ORGAN_ROBOTIC
failing_desc = "seems to be broken."
+ pupils_name = "apertures"
+ penlight_message = "are cybernetic, click-whirring as they refocus"
/obj/item/organ/eyes/robotic/emp_act(severity)
. = ..()
@@ -660,6 +684,7 @@
eye_color_left = "#2f3032"
eye_color_right = "#2f3032"
flash_protect = FLASH_PROTECTION_SENSITIVE
+ penlight_message = "are low grade cybernetics, poorly compensating for the light"
/obj/item/organ/eyes/robotic/basic/emp_act(severity)
. = ..()
@@ -679,6 +704,7 @@
eye_color_left = "#3cb8a5"
eye_color_right = "#3cb8a5"
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
+ penlight_message = "replaced by small radiation emitters and detectors"
/obj/item/organ/eyes/robotic/xray/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
@@ -699,6 +725,8 @@
color_cutoffs = list(25, 8, 5)
sight_flags = SEE_MOBS
flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "slit aperatures"
+ penlight_message = "are cybernetic, with vertically slit metalic lenses."
/obj/item/organ/eyes/robotic/flashlight
name = "flashlight eyes"
@@ -710,6 +738,9 @@
flash_protect = FLASH_PROTECTION_WELDER
tint = INFINITY
var/obj/item/flashlight/eyelight/eye
+ light_reactive = FALSE
+ pupils_name = "flashlights"
+ penlight_message = "are actually two flashlights taped together. ...why"
/obj/item/organ/eyes/robotic/flashlight/Initialize(mapload)
. = ..()
@@ -740,6 +771,8 @@
eye_color_left = "#353845"
eye_color_right = "#353845"
flash_protect = FLASH_PROTECTION_WELDER
+ pupils_name = "flash shields"
+ penlight_message = "have polarized cybernetic lenses, blocking bright lights"
/obj/item/organ/eyes/robotic/shield/Initialize(mapload)
. = ..()
@@ -771,6 +804,7 @@
var/left_eye_color_string
/// The custom selected eye color for the right eye. Defaults to the mob's natural eye color
var/right_eye_color_string
+ penlight_message = "shine back with cybernetic LEDs"
/obj/item/organ/eyes/robotic/glow/Initialize(mapload)
. = ..()
@@ -1033,6 +1067,8 @@
blink_animation = FALSE
iris_overlay = null
flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "ommatidia" //yes i know compound eyes have no pupils shut up
+ penlight_message = "are bulbous and insectoid"
/obj/item/organ/eyes/robotic/moth
name = "robotic moth eyes"
@@ -1040,6 +1076,8 @@
icon_state = "eyes_moth_cyber"
eye_icon_state = "motheyes_cyber"
flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "aperture clusters"
+ penlight_message = "are metal hemispheres, resembling insect eyes"
/obj/item/organ/eyes/robotic/basic/moth
name = "basic robotic moth eyes"
@@ -1049,6 +1087,8 @@
eye_color_right = "#65686f"
blink_animation = FALSE
flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "aperture clusters"
+ penlight_message = "are metal hemispheres, resembling insect eyes"
/obj/item/organ/eyes/robotic/xray/moth
name = "moth x-ray eyes"
@@ -1059,6 +1099,7 @@
eye_color_right = "#3c4e52"
blink_animation = FALSE
flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "aperture clusters"
/obj/item/organ/eyes/robotic/shield/moth
name = "shielded robotic moth eyes"
@@ -1067,6 +1108,8 @@
eye_color_left = "#353845"
eye_color_right = "#353845"
blink_animation = FALSE
+ pupils_name = "aperture clusters"
+ penlight_message = "have shutters, protecting insectoid compound eyes."
/obj/item/organ/eyes/robotic/glow/moth
name = "high luminosity moth eyes"
@@ -1077,6 +1120,8 @@
blink_animation = FALSE
base_eye_state = "eyes_mothglow"
flash_protect = FLASH_PROTECTION_SENSITIVE
+ penlight_message = "are bulbous clusters of LEDs and cameras"
+ pupils_name = "aperture clusters"
/obj/item/organ/eyes/robotic/thermals/moth //we inherit flash weakness from thermals
name = "thermal moth eyes"
@@ -1085,6 +1130,8 @@
eye_color_left = "#901f38"
eye_color_right = "#901f38"
blink_animation = FALSE
+ pupils_name = "sensor clusters"
+ penlight_message = "are two clustered hemispheres of thermal sensors"
/obj/item/organ/eyes/snail
name = "snail eyes"
@@ -1092,6 +1139,8 @@
icon_state = "eyes_snail"
eye_icon_state = "snail_eyes"
blink_animation = FALSE
+ pupils_name = "eyestalks" //many species of snails can retract their eyes into their face! (my lame science excuse for not having better writing here)
+ penlight_message = "are sat upon retractable tentacles"
/obj/item/organ/eyes/jelly
name = "jelly eyes"
@@ -1100,12 +1149,16 @@
eye_icon_state = "jelleyes"
blink_animation = FALSE
iris_overlay = null
+ pupils_name = "lensing bubbles" //imagine a water lens physics demo but with goo. thats how these work.
+ penlight_message = "are three bubbles of refractive jelly"
/obj/item/organ/eyes/lizard
name = "reptile eyes"
desc = "A pair of reptile eyes with thin vertical slits for pupils."
icon_state = "lizard_eyes"
// synchronized_blinking = FALSE // BUBBER EDIT - REMOVE ASYNC BLINKING UNTIL https://github.com/tgstation/tgstation/issues/90269 is fixed
+ pupils_name = "slit pupils"
+ penlight_message = "have vertically slit pupils and tinted whites"
/obj/item/organ/eyes/night_vision/maintenance_adapted
name = "adapted eyes"
@@ -1120,6 +1173,13 @@
low_light_cutoff = list(5, 12, 20)
medium_light_cutoff = list(15, 20, 30)
high_light_cutoff = list(30, 35, 50)
+ penlight_message = "glow a foggy red, sizzling under the light"
+
+/obj/item/organ/eyes/night_vision/maintenance_adapted/penlight_examine(mob/living/viewer, obj/item/examtool)
+ if(!owner.is_blind())
+ to_chat(owner, span_danger("Your eyes sizzle agonizingly as light is shone on them!"))
+ apply_organ_damage(20 * examtool.light_power) //that's 0.5 lightpower for a penlight, so one penlight shining is equivalent to two seconds in a lit area
+ return span_danger("[owner.p_Their()] eyes [penlight_message].")
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
@@ -1146,3 +1206,4 @@
// eye_color_right = "#375846" BUBBER EDIT - ALLOWS EYE COLOR TO BE CHANGED AGAIN
// iris_overlay = null BUBBER EDIT - ALLOWS EYE COLOR TO BE CHANGED AGAIN
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
+ penlight_message = "are green and plant-like"
diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm
index d09a216722d..d0e386ac5a8 100644
--- a/code/modules/surgery/organs/internal/heart/_heart.dm
+++ b/code/modules/surgery/organs/internal/heart/_heart.dm
@@ -31,6 +31,7 @@
var/beat = BEAT_NONE
/// whether the heart's been operated on to fix some of its damages
var/operated = FALSE
+ var/beat_noise = "a rhythmic thumping"
/obj/item/organ/heart/update_icon_state()
. = ..()
@@ -149,12 +150,17 @@
return span_warning("[self_aware ? "Your heart hurts." : "It hurts, and your heart rate feels irregular."]")
return span_boldwarning("[self_aware ? "Your heart seriously hurts!" : "It seriously hurts, and your heart rate is all over the place."]")
+/// by default, returns the hearts beat_noise var as a notice span. May do other things when overridden, such as eldritch insanity or electrocution. Whatever you want, really.
+/obj/item/organ/heart/proc/hear_beat_noise(mob/living/hearer)
+ return span_notice("[owner.p_Their()] heart produces [beat_noise].")
+
/obj/item/organ/heart/cursed
name = "cursed heart"
desc = "A heart that, when inserted, will force you to pump it manually."
icon_state = "cursedheart-off"
base_icon_state = "cursedheart"
decay_factor = 0
+ beat_noise = "a pained screeching with every beat. It seems to lack any kind of rhythm"
var/pump_delay = 3 SECONDS
var/blood_loss = BLOOD_VOLUME_NORMAL * 0.2
var/heal_brute = 0
@@ -179,6 +185,9 @@
qdel(accursed.GetComponent(/datum/component/manual_heart))
+/obj/item/organ/heart/cursed/hear_beat_noise(mob/living/hearer)
+ return span_danger(beat_noise)
+
/obj/item/organ/heart/cybernetic
name = "basic cybernetic heart"
desc = "A basic electronic device designed to mimic the functions of an organic human heart."
@@ -187,7 +196,7 @@
organ_flags = ORGAN_ROBOTIC
maxHealth = STANDARD_ORGAN_THRESHOLD * 0.75 //This also hits defib timer, so a bit higher than its less important counterparts
failing_desc = "seems to be broken."
-
+ beat_noise = "a steady fsssh of hydraulics"
/// Whether or not we have a stabilization available. This prevents our owner from entering softcrit for an amount of time.
var/stabilization_available = FALSE
@@ -291,6 +300,7 @@
icon_state = "heart-c-s-on"
base_icon_state = "heart-c-s"
maxHealth = STANDARD_ORGAN_THRESHOLD*0.5
+ beat_noise = "a concerningly irregular hydraulic hum. You shouldn't touch this while it's running"
emp_vulnerability = 100
//surplus organs are so awful that they explode when removed, unless failing
@@ -298,10 +308,14 @@
. = ..()
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE)
+/obj/item/organ/heart/cybernetic/surplus/hear_beat_noise(mob/living/hearer)
+ return span_danger("[owner.p_Their()] heart produces [beat_noise].")
+
/obj/item/organ/heart/freedom
name = "heart of freedom"
desc = "This heart pumps with the passion to give... something freedom."
organ_flags = ORGAN_ROBOTIC //the power of freedom prevents heart attacks
+ beat_noise = "THE SOUND OF FREEDOM"
/// The cooldown until the next time this heart can give the host an adrenaline boost.
COOLDOWN_DECLARE(adrenaline_cooldown)
@@ -317,5 +331,6 @@
/obj/item/organ/heart/pod
name = "pod mitochondria"
desc = "This plant-like organ is the powerhouse of the podperson." // deliberate wording here
+ beat_noise = "the power of the podperson" // makes sense
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
color = COLOR_LIME
diff --git a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm
index eccb6b9f55a..10fbc5f2f30 100644
--- a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm
+++ b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm
@@ -7,6 +7,7 @@
name = "voltaic combat cyberheart"
desc = "A cutting-edge cyberheart, originally designed for Nanotrasen killsquad usage but later declassified for normal research. Voltaic technology allows the heart to keep the body upright in dire circumstances, alongside redirecting anomalous flux energy to fully shield the user from shocks and electro-magnetic pulses. Requires a refined Flux core as a power source."
icon_state = "anomalock_heart"
+ beat_noise = "an astonishing BZZZ of immense electrical power"
bleed_prevention = TRUE
toxification_probability = 0
@@ -211,4 +212,13 @@
icon_state = "anomalock_heart"
desc = "Voltaic energy is flooding your muscles, keeping your body upright. You have 30 seconds before it falters!"
+/obj/item/organ/heart/cybernetic/anomalock/hear_beat_noise(mob/living/hearer)
+ if(prob(1))
+ to_chat(hearer, span_danger("Yeah. Press a metal disk to the chest of a living arc flash hazard. See what that gets you.")) //the guy is LITERALLY sparking like a tesla coil.
+ else
+ to_chat(hearer, span_danger("An electrical arc strikes your stethoscope, conducting into you!"))
+ if(hearer.electrocute_act(15, "stethoscope", flags = SHOCK_NOGLOVES)) //the stethoscope is in your ears. (returns true if it does damage so we only scream in that case)
+ hearer.emote("scream")
+ return span_danger("[owner.p_Their()] heart produces [beat_noise].")
+
#undef DOAFTER_IMPLANTING_HEART
diff --git a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
index fef34e1abbd..fd149c8ede6 100644
--- a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
+++ b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
@@ -2,6 +2,7 @@
name = "crystal core"
icon_state = "ethereal_heart-on"
base_icon_state = "ethereal_heart"
+ beat_noise = "a pulsing crackle"
visual = TRUE //This is used by the ethereal species for color
desc = "A crystal-like organ that functions similarly to a heart for Ethereals. It can revive its owner."
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index 7b9fe2dbde2..2204a9c8b8f 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -104,6 +104,8 @@
var/crit_stabilizing_reagent = /datum/reagent/medicine/epinephrine
+ var/breath_noise = "steady in- and exhalation"
+
// assign the respiration_type
/obj/item/organ/lungs/Initialize(mapload)
. = ..()
@@ -868,6 +870,10 @@
return span_boldwarning("Your lungs feel extremely tight[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and every breath is a struggle"].")
return span_boldwarning("It feels extremely tight[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and every breath is a struggle"].")
+/// by default, returns the lungs' breath_noise var as a notice. called when stethoscope is used on chest, uses the return as a message for stethoscope user.
+/obj/item/organ/lungs/proc/hear_breath_noise(mob/living/hearer)
+ return span_notice("[owner.p_Their()] lungs emit [breath_noise].")
+
#define SMOKER_ORGAN_HEALTH (STANDARD_ORGAN_THRESHOLD * 0.75)
#define SMOKER_LUNG_HEALING (STANDARD_ORGAN_HEALING * 0.75)
@@ -876,7 +882,7 @@
desc = "A spongy rib-shaped mass for filtering plasma from the air."
icon_state = "lungs-plasma"
organ_traits = list(TRAIT_NOHUNGER) // A fresh breakfast of plasma is a great start to any morning.
-
+ breath_noise = "a crackle, like crushed foam"
safe_oxygen_min = 0 //We don't breathe this
safe_plasma_min = 4 //We breathe THIS!
safe_plasma_max = 0
@@ -885,14 +891,14 @@
name = "smoker plasma filter"
desc = "A plasma filter that look discolored, a result from smoking a lot."
icon_state = "lungs_plasma_smoker"
-
+ breath_noise = "a wheezing crackle, like crushed foam"
maxHealth = SMOKER_ORGAN_HEALTH
healing_factor = SMOKER_LUNG_HEALING
/obj/item/organ/lungs/slime
name = "slime vacuole"
desc = "A large organelle designed to store oxygen and other important gasses."
-
+ breath_noise = "a low burbling"
safe_plasma_max = 0 //We breathe this to gain POWER.
/obj/item/organ/lungs/slime/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather_slime)
@@ -905,7 +911,7 @@
name = "smoker lungs"
desc = "A pair of lungs that look sickly, a result from smoking a lot."
icon_state = "lungs_smoker"
-
+ breath_noise = "an unsteady, wheezing rhythm"
maxHealth = SMOKER_ORGAN_HEALTH
healing_factor = SMOKER_LUNG_HEALING
@@ -914,6 +920,7 @@
desc = "A basic cybernetic version of the lungs found in traditional humanoid entities."
failing_desc = "seems to be broken."
icon_state = "lungs-c"
+ breath_noise = "a steady whirr"
organ_flags = ORGAN_ROBOTIC
maxHealth = STANDARD_ORGAN_THRESHOLD * 0.5
var/emp_vulnerability = 80 //Chance of permanent effects if emp-ed.
@@ -954,6 +961,7 @@
name = "surplus prosthetic lungs"
desc = "Two fragile, inflatable sacks of air that only barely mimic the function of human lungs. \
Offer no protection against EMPs."
+ breath_noise = "a concerningly unstable scratchy whirr. You shouldn't touch this while it's running"
icon_state = "lungs-c-s"
maxHealth = 0.35 * STANDARD_ORGAN_THRESHOLD
emp_vulnerability = 100
@@ -963,10 +971,14 @@
. = ..()
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE)
+/obj/item/organ/lungs/cybernetic/surplus/hear_breath_noise(mob/living/hearer)
+ return span_danger("[owner.p_Their()] lungs emit [breath_noise].")
+
/obj/item/organ/lungs/lavaland
name = "blackened frilled lungs" // blackened from necropolis exposure
desc = "Exposure to the necropolis has mutated these lungs to breathe the air of Indecipheres, the lava-covered moon."
icon_state = "lungs-ashwalker"
+ breath_noise = "a throbbing smoke-like hiss"
// Normal oxygen is 21 kPa partial pressure, but SS13 humans can tolerate down
// to 16 kPa. So it follows that ashwalkers, as humanoids, follow the same rules.
@@ -1032,6 +1044,7 @@
name = "aeration reticulum"
desc = "These exotic lungs seem crunchier than most."
icon_state = "lungs_ethereal"
+ breath_noise = "a low fluorescent hum"
heat_level_1_threshold = FIRE_MINIMUM_TEMPERATURE_TO_SPREAD // 150C or 433k, in line with ethereal max safe body temperature
heat_level_2_threshold = 473
heat_level_3_threshold = 1073
@@ -1040,7 +1053,7 @@
name = "smoker aeration reticulum"
desc = "A pair of exotic lungs that look pale and sickly, a result from smoking a lot."
icon_state = "lungs_ethereal_smoker"
-
+ breath_noise = "a spotty hum, like a broken lightbulb"
maxHealth = SMOKER_ORGAN_HEALTH
healing_factor = SMOKER_LUNG_HEALING
@@ -1060,6 +1073,7 @@
/obj/item/organ/lungs/pod
name = "pod vacuole"
desc = "A large organelle designed to store oxygen and other important gasses."
+ breath_noise = "a humid hiss"
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
color = COLOR_LIME
diff --git a/icons/obj/devices/scanner.dmi b/icons/obj/devices/scanner.dmi
index b5902be55d6..0bd6b6cd1db 100644
Binary files a/icons/obj/devices/scanner.dmi and b/icons/obj/devices/scanner.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index dd370d240e8..f1f12f9f349 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2695,6 +2695,7 @@
#include "code\game\objects\items\devices\anomaly_releaser.dm"
#include "code\game\objects\items\devices\battle_royale.dm"
#include "code\game\objects\items\devices\beacon.dm"
+#include "code\game\objects\items\devices\blood_scanner.dm"
#include "code\game\objects\items\devices\broadcast_camera.dm"
#include "code\game\objects\items\devices\chameleonproj.dm"
#include "code\game\objects\items\devices\destabilizing_crystal.dm"