mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 16:05:07 +00:00
## About The Pull Request
Port of my changes from [Monkestation/Monkestation2.0@`c54fc87`
(#7295)](c54fc872bf)
This nukes `/obj/item/bodypart/proc/get_modified_bleed_rate()`, instead
everything just uses the `cache_bleed_rate` var - `refresh_bleed_rate()`
will now take into account body position and grasping in the same way
`get_modified_bleed_rate` did.
(Un)grasping a limb already called `refresh_bleed_rate()` anyways, so
the only other change needed was making
`COMSIG_LIVING_SET_BODY_POSITION` also call it.
647 lines
28 KiB
Plaintext
647 lines
28 KiB
Plaintext
/// Adds a newline to the examine list if the above entry is not empty and it is not the first element in the list
|
|
#define ADD_NEWLINE_IF_NECESSARY(list) if(length(list) > 0 && list[length(list)]) { list += "" }
|
|
#define CARBON_EXAMINE_EMBEDDING_MAX_DIST 4
|
|
|
|
/mob/living/carbon/human/get_examine_icon(mob/user)
|
|
return null
|
|
|
|
/mob/living/carbon/examine(mob/user)
|
|
if(HAS_TRAIT(src, TRAIT_UNKNOWN))
|
|
return list(span_warning("You're struggling to make out any details..."))
|
|
|
|
var/t_He = p_They()
|
|
var/t_His = p_Their()
|
|
var/t_his = p_their()
|
|
var/t_him = p_them()
|
|
var/t_has = p_have()
|
|
var/t_is = p_are()
|
|
|
|
. = list()
|
|
. += get_clothing_examine_info(user)
|
|
// give us some space between clothing examine and the rest
|
|
ADD_NEWLINE_IF_NECESSARY(.)
|
|
|
|
var/appears_dead = FALSE
|
|
var/just_sleeping = FALSE
|
|
|
|
if(!appears_alive())
|
|
appears_dead = TRUE
|
|
|
|
var/obj/item/clothing/glasses/shades = get_item_by_slot(ITEM_SLOT_EYES)
|
|
var/are_we_in_weekend_at_bernies = shades?.tint && buckled && istype(buckled, /obj/vehicle/ridden/wheelchair)
|
|
|
|
if(isliving(user) && (HAS_MIND_TRAIT(user, TRAIT_NAIVE) || are_we_in_weekend_at_bernies))
|
|
just_sleeping = TRUE
|
|
|
|
if(!just_sleeping)
|
|
// since this is relatively important and giving it space makes it easier to read
|
|
ADD_NEWLINE_IF_NECESSARY(.)
|
|
if(HAS_TRAIT(src, TRAIT_SUICIDED))
|
|
. += span_warning("[t_He] appear[p_s()] to have committed suicide... there is no hope of recovery.")
|
|
|
|
. += generate_death_examine_text()
|
|
|
|
//Status effects
|
|
var/list/status_examines = get_status_effect_examinations()
|
|
if (length(status_examines))
|
|
. += status_examines
|
|
|
|
if(get_bodypart(BODY_ZONE_HEAD) && !get_organ_by_type(/obj/item/organ/brain))
|
|
. += span_deadsay("It appears that [t_his] brain is missing...")
|
|
|
|
var/list/disabled = list()
|
|
for(var/obj/item/bodypart/body_part as anything in bodyparts)
|
|
if(body_part.bodypart_disabled)
|
|
disabled += body_part
|
|
for(var/obj/item/embedded as anything in body_part.embedded_objects)
|
|
if(embedded.get_embed().stealthy_embed)
|
|
continue
|
|
var/harmless = embedded.get_embed().is_harmless()
|
|
var/stuck_wordage = harmless ? "stuck to" : "embedded in"
|
|
var/embed_line = "\a [embedded]"
|
|
if (get_dist(src, user) <= CARBON_EXAMINE_EMBEDDING_MAX_DIST)
|
|
embed_line = "<a href='byond://?src=[REF(src)];embedded_object=[REF(embedded)];embedded_limb=[REF(body_part)]'>\a [embedded]</a>"
|
|
var/embed_text = "[t_He] [t_has] [icon2html(embedded, user)] [embed_line] [stuck_wordage] [t_his] [body_part.plaintext_zone]!"
|
|
if (harmless)
|
|
. += span_italics(span_notice(embed_text))
|
|
else
|
|
. += span_boldwarning(embed_text)
|
|
|
|
for(var/datum/wound/iter_wound as anything in body_part.wounds)
|
|
. += span_danger(iter_wound.get_examine_description(user))
|
|
|
|
for(var/obj/item/bodypart/body_part as anything in disabled)
|
|
var/damage_text
|
|
if(HAS_TRAIT(body_part, TRAIT_DISABLED_BY_WOUND))
|
|
continue // skip if it's disabled by a wound (cuz we'll be able to see the bone sticking out!)
|
|
if(body_part.get_damage() < body_part.max_damage) //we don't care if it's stamcritted
|
|
damage_text = "limp and lifeless"
|
|
else
|
|
damage_text = (body_part.brute_dam >= body_part.burn_dam) ? body_part.heavy_brute_msg : body_part.heavy_burn_msg
|
|
. += span_boldwarning("[capitalize(t_his)] [body_part.plaintext_zone] looks [damage_text]!")
|
|
|
|
//stores missing limbs
|
|
var/l_limbs_missing = 0
|
|
var/r_limbs_missing = 0
|
|
for(var/missing_limb in get_missing_limbs())
|
|
if(missing_limb == BODY_ZONE_HEAD)
|
|
. += span_deadsay("<B>[t_His] [parse_zone(missing_limb)] is missing!</B>")
|
|
continue
|
|
if(missing_limb == BODY_ZONE_L_ARM || missing_limb == BODY_ZONE_L_LEG)
|
|
l_limbs_missing++
|
|
else if(missing_limb == BODY_ZONE_R_ARM || missing_limb == BODY_ZONE_R_LEG)
|
|
r_limbs_missing++
|
|
|
|
. += span_boldwarning("[capitalize(t_his)] [parse_zone(missing_limb)] is missing!")
|
|
|
|
if(l_limbs_missing >= 2 && r_limbs_missing == 0)
|
|
. += span_tinydanger("[t_He] look[p_s()] all right now...")
|
|
else if(l_limbs_missing == 0 && r_limbs_missing >= 2)
|
|
. += span_tinydanger("[t_He] really keep[p_s()] to the left...")
|
|
else if(l_limbs_missing >= 2 && r_limbs_missing >= 2)
|
|
. += span_tinydanger("[t_He] [p_do()]n't seem all there...")
|
|
|
|
if(!(user == src && has_status_effect(/datum/status_effect/grouped/screwy_hud/fake_healthy))) //fake healthy
|
|
var/temp
|
|
if(user == src && has_status_effect(/datum/status_effect/grouped/screwy_hud/fake_crit))//fake damage
|
|
temp = 50
|
|
else
|
|
temp = getBruteLoss()
|
|
var/list/damage_desc = get_majority_bodypart_damage_desc()
|
|
if(temp)
|
|
if(temp < 25)
|
|
. += span_danger("[t_He] [t_has] minor [damage_desc[BRUTE]].")
|
|
else if(temp < 50)
|
|
. += span_danger("[t_He] [t_has] <b>moderate</b> [damage_desc[BRUTE]]!")
|
|
else
|
|
. += span_bolddanger("[t_He] [t_has] severe [damage_desc[BRUTE]]!")
|
|
|
|
temp = getFireLoss()
|
|
if(temp)
|
|
if(temp < 25)
|
|
. += span_danger("[t_He] [t_has] minor [damage_desc[BURN]].")
|
|
else if (temp < 50)
|
|
. += span_danger("[t_He] [t_has] <b>moderate</b> [damage_desc[BURN]]!")
|
|
else
|
|
. += span_bolddanger("[t_He] [t_has] severe [damage_desc[BURN]]!")
|
|
|
|
if(pulledby?.grab_state)
|
|
. += span_warning("[t_He] [t_is] restrained by [pulledby]'s grip.")
|
|
|
|
if(nutrition < NUTRITION_LEVEL_STARVING - 50)
|
|
. += span_warning("[t_He] [t_is] severely malnourished.")
|
|
else if(nutrition >= NUTRITION_LEVEL_FAT)
|
|
if(user.nutrition < NUTRITION_LEVEL_STARVING - 50)
|
|
. += span_hypnophrase("[t_He] [t_is] plump and delicious looking - Like a fat little piggy. A tasty piggy.")
|
|
else
|
|
. += "<b>[t_He] [t_is] quite chubby.</b>"
|
|
switch(disgust)
|
|
if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS)
|
|
. += "[t_He] look[p_s()] a bit grossed out."
|
|
if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED)
|
|
. += "[t_He] look[p_s()] really grossed out."
|
|
if(DISGUST_LEVEL_DISGUSTED to INFINITY)
|
|
. += "[t_He] look[p_s()] extremely disgusted."
|
|
|
|
var/apparent_blood_volume = blood_volume
|
|
if(HAS_TRAIT(src, TRAIT_USES_SKINTONES) && ishuman(src))
|
|
var/mob/living/carbon/human/husrc = src // gross istypesrc but easier than refactoring even further for now
|
|
if(husrc.skin_tone == "albino")
|
|
apparent_blood_volume -= (BLOOD_VOLUME_NORMAL * 0.25) // knocks you down a few pegs
|
|
switch(apparent_blood_volume)
|
|
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
|
|
. += span_warning("[t_He] [t_has] pale skin.")
|
|
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
|
|
. += span_boldwarning("[t_He] look[p_s()] like pale death.")
|
|
if(-INFINITY to BLOOD_VOLUME_BAD)
|
|
. += span_deadsay("<b>[t_He] resemble[p_s()] a crushed, empty juice pouch.</b>")
|
|
|
|
if(is_bleeding())
|
|
var/list/obj/item/bodypart/bleeding_limbs = list()
|
|
var/list/obj/item/bodypart/grasped_limbs = list()
|
|
|
|
for(var/obj/item/bodypart/body_part as anything in bodyparts)
|
|
if(body_part.cached_bleed_rate)
|
|
bleeding_limbs += body_part.plaintext_zone
|
|
if(body_part.grasped_by)
|
|
grasped_limbs += body_part.plaintext_zone
|
|
|
|
if(LAZYLEN(bleeding_limbs))
|
|
var/bleed_text = "<b>"
|
|
if(appears_dead)
|
|
bleed_text += "<span class='deadsay'>"
|
|
bleed_text += "Blood is visible in [t_his] open "
|
|
else
|
|
bleed_text += "<span class='warning'>"
|
|
bleed_text += "[t_He] [t_is] bleeding from [t_his] "
|
|
|
|
bleed_text += english_list(bleeding_limbs, and_text = " and ")
|
|
|
|
if(appears_dead)
|
|
bleed_text += ", but it has pooled and is not flowing."
|
|
else
|
|
if(HAS_TRAIT(src, TRAIT_BLOODY_MESS))
|
|
bleed_text += " incredibly quickly"
|
|
bleed_text += "!"
|
|
|
|
if(appears_dead)
|
|
bleed_text += "<span class='deadsay'>"
|
|
else
|
|
bleed_text += "<span class='warning'>"
|
|
bleed_text += "</b>"
|
|
|
|
. += bleed_text
|
|
if(LAZYLEN(grasped_limbs))
|
|
for(var/grasped_part in grasped_limbs)
|
|
. += "[t_He] [t_is] holding [t_his] [grasped_part] to slow the bleeding!"
|
|
|
|
if(reagents.has_reagent(/datum/reagent/teslium, needs_metabolizing = TRUE))
|
|
. += span_smallnoticeital("[t_He] [t_is] emitting a gentle blue glow!") // this should be signalized
|
|
|
|
if(just_sleeping)
|
|
. += span_notice("[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.")
|
|
|
|
else if(!appears_dead)
|
|
var/mob/living/living_user = user
|
|
if(src != user)
|
|
if(HAS_TRAIT(user, TRAIT_EMPATH))
|
|
if (combat_mode)
|
|
. += "[t_He] seem[p_s()] to be on guard."
|
|
if (getOxyLoss() >= 10)
|
|
. += "[t_He] seem[p_s()] winded."
|
|
if (getToxLoss() >= 10)
|
|
. += "[t_He] seem[p_s()] sickly."
|
|
if(mob_mood.sanity <= SANITY_DISTURBED)
|
|
. += "[t_He] seem[p_s()] distressed."
|
|
living_user.add_mood_event("empath", /datum/mood_event/sad_empath, src)
|
|
if(is_blind())
|
|
. += "[t_He] appear[p_s()] to be staring off into space."
|
|
if (HAS_TRAIT(src, TRAIT_DEAF))
|
|
. += "[t_He] appear[p_s()] to not be responding to noises."
|
|
if (bodytemperature > dna.species.bodytemp_heat_damage_limit)
|
|
. += "[t_He] [t_is] flushed and wheezing."
|
|
if (bodytemperature < dna.species.bodytemp_cold_damage_limit)
|
|
. += "[t_He] [t_is] shivering."
|
|
if(HAS_TRAIT(src, TRAIT_EVIL))
|
|
. += "[t_His] eyes radiate with a unfeeling, cold detachment. There is nothing but darkness within [t_his] soul."
|
|
if(living_user.mind?.holy_role >= HOLY_ROLE_PRIEST)
|
|
. += span_warning("PERFECT FOR SMITING!!")
|
|
else
|
|
living_user.add_mood_event("encountered_evil", /datum/mood_event/encountered_evil)
|
|
living_user.set_jitter_if_lower(15 SECONDS)
|
|
|
|
if(HAS_TRAIT(user, TRAIT_SPIRITUAL) && mind?.holy_role)
|
|
. += "[t_He] [t_has] a holy aura about [t_him]."
|
|
living_user.add_mood_event("religious_comfort", /datum/mood_event/religiously_comforted)
|
|
|
|
switch(stat)
|
|
if(UNCONSCIOUS, HARD_CRIT)
|
|
. += span_notice("[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.")
|
|
if(SOFT_CRIT)
|
|
. += span_notice("[t_He] [t_is] barely conscious.")
|
|
if(CONSCIOUS)
|
|
if(HAS_TRAIT(src, TRAIT_DUMB))
|
|
. += "[t_He] [t_has] a stupid expression on [t_his] face."
|
|
var/obj/item/organ/brain/brain = get_organ_by_type(/obj/item/organ/brain)
|
|
if(brain && isnull(ai_controller))
|
|
var/npc_message = ""
|
|
if(HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE))
|
|
npc_message = "Soul is pending..."
|
|
else if(!key)
|
|
npc_message = "[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely."
|
|
else if(!client)
|
|
npc_message ="[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon."
|
|
if(npc_message)
|
|
// give some space since this is usually near the end
|
|
ADD_NEWLINE_IF_NECESSARY(.)
|
|
. += span_deadsay(npc_message)
|
|
|
|
var/scar_severity = 0
|
|
for(var/datum/scar/scar as anything in all_scars)
|
|
if(scar.is_visible(user))
|
|
scar_severity += scar.severity
|
|
|
|
if(scar_severity >= 1)
|
|
// give some space since this is even more usually near the end
|
|
ADD_NEWLINE_IF_NECESSARY(.)
|
|
switch(scar_severity)
|
|
if(1 to 4)
|
|
. += span_tinynoticeital("[t_He] [t_has] visible scarring, you can look again to take a closer look...")
|
|
if(5 to 8)
|
|
. += span_smallnoticeital("[t_He] [t_has] several bad scars, you can look again to take a closer look...")
|
|
if(9 to 11)
|
|
. += span_notice("<i>[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...</i>")
|
|
if(12 to INFINITY)
|
|
. += span_notice("<b><i>[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...</i></b>")
|
|
|
|
if(HAS_TRAIT(src, TRAIT_HUSK))
|
|
. += span_warning("This body has been reduced to a grotesque husk.")
|
|
if(HAS_MIND_TRAIT(user, TRAIT_MORBID))
|
|
if(HAS_TRAIT(src, TRAIT_DISSECTED))
|
|
. += span_notice("[t_He] appear[p_s()] to have been dissected. Useless for examination... <b><i>for now.</i></b>")
|
|
if(HAS_TRAIT(src, TRAIT_SURGICALLY_ANALYZED))
|
|
. += span_notice("A skilled hand has mapped this one's internal intricacies. It will be far easier to perform future experimentations upon [user.p_them()]. <b><i>Exquisite.</i></b>")
|
|
if(isliving(user) && HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FITNESS))
|
|
. += compare_fitness(user)
|
|
|
|
var/hud_info = get_hud_examine_info(user)
|
|
if(length(hud_info))
|
|
. += hud_info
|
|
|
|
if(isobserver(user))
|
|
ADD_NEWLINE_IF_NECESSARY(.)
|
|
. += "<b>Quirks:</b> [get_quirk_string(FALSE, CAT_QUIRK_ALL)]"
|
|
|
|
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, user, .)
|
|
if(length(.))
|
|
.[1] = "<span class='info'>" + .[1]
|
|
.[length(.)] += "</span>"
|
|
return .
|
|
|
|
/mob/living/carbon/examine_more(mob/user)
|
|
. = ..()
|
|
if(HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
|
|
return
|
|
for(var/datum/scar/iter_scar as anything in all_scars)
|
|
if(iter_scar.is_visible(user))
|
|
. += iter_scar.get_examine_description(user)
|
|
|
|
/**
|
|
* Shows any and all examine text related to any status effects the user has.
|
|
*/
|
|
/mob/living/proc/get_status_effect_examinations()
|
|
var/list/examine_list = list()
|
|
|
|
for(var/datum/status_effect/effect as anything in status_effects)
|
|
var/effect_text = effect.get_examine_text()
|
|
if(!effect_text)
|
|
continue
|
|
|
|
examine_list += effect_text
|
|
|
|
if(!length(examine_list))
|
|
return
|
|
|
|
return examine_list.Join("<br>")
|
|
|
|
/// Returns death message for mob examine text
|
|
/mob/living/carbon/proc/generate_death_examine_text()
|
|
var/mob/dead/observer/ghost = get_ghost(TRUE, TRUE)
|
|
var/t_He = p_They()
|
|
var/t_his = p_their()
|
|
var/t_is = p_are()
|
|
//This checks to see if the body is revivable
|
|
var/obj/item/organ/brain = get_organ_by_type(/obj/item/organ/brain)
|
|
if(brain && HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE))
|
|
return span_deadsay("[t_He] [t_is] limp and unresponsive; but [t_his] soul might yet come back...")
|
|
var/client_like = client || HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE)
|
|
var/valid_ghost = ghost?.can_reenter_corpse && ghost?.client
|
|
var/valid_soul = brain || !HAS_TRAIT(src, TRAIT_FAKE_SOULLESS)
|
|
if((brain && client_like) || (valid_ghost && valid_soul))
|
|
return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life...")
|
|
return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life and [t_his] soul has departed...")
|
|
|
|
/// Returns a list of "damtype" => damage description based off of which bodypart description is most common
|
|
/mob/living/carbon/proc/get_majority_bodypart_damage_desc()
|
|
var/list/seen_damage = list() // This looks like: ({Damage type} = list({Damage description for that damage type} = {number of times it has appeared}, ...), ...)
|
|
var/list/most_seen_damage = list() // This looks like: ({Damage type} = {Frequency of the most common description}, ...)
|
|
var/list/final_descriptions = list() // This looks like: ({Damage type} = {Most common damage description for that type}, ...)
|
|
for(var/obj/item/bodypart/part as anything in bodyparts)
|
|
for(var/damage_type in part.damage_examines)
|
|
var/damage_desc = part.damage_examines[damage_type]
|
|
if(!seen_damage[damage_type])
|
|
seen_damage[damage_type] = list()
|
|
|
|
if(!seen_damage[damage_type][damage_desc])
|
|
seen_damage[damage_type][damage_desc] = 1
|
|
else
|
|
seen_damage[damage_type][damage_desc] += 1
|
|
|
|
if(seen_damage[damage_type][damage_desc] > most_seen_damage[damage_type])
|
|
most_seen_damage[damage_type] = seen_damage[damage_type][damage_desc]
|
|
final_descriptions[damage_type] = damage_desc
|
|
return final_descriptions
|
|
|
|
/// Coolects examine information about the mob's clothing and equipment
|
|
/mob/living/carbon/proc/get_clothing_examine_info(mob/living/user)
|
|
. = list()
|
|
var/obscured = check_obscured_slots()
|
|
var/t_He = p_They()
|
|
var/t_His = p_Their()
|
|
var/t_his = p_their()
|
|
var/t_has = p_have()
|
|
var/t_is = p_are()
|
|
//head
|
|
if(head && !(obscured & ITEM_SLOT_HEAD) && !HAS_TRAIT(head, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] wearing [head.examine_title(user)] on [t_his] head."
|
|
//back
|
|
if(back && !HAS_TRAIT(back, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [back.examine_title(user)] on [t_his] back."
|
|
//Hands
|
|
for(var/obj/item/held_thing in held_items)
|
|
if((held_thing.item_flags & (ABSTRACT|HAND_ITEM)) || HAS_TRAIT(held_thing, TRAIT_EXAMINE_SKIP))
|
|
continue
|
|
. += "[t_He] [t_is] holding [held_thing.examine_title(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
|
|
for(var/obj/item/bodypart/arm/part in bodyparts)
|
|
if(!(part.bodypart_flags & BODYPART_PSEUDOPART))
|
|
continue
|
|
var/obj/item/corresponding_item = get_item_for_held_index(part.held_index) || part
|
|
. += "[t_He] [t_has] a [corresponding_item.examine_title(user)] in place of [t_his] [initial(part.plaintext_zone)]."
|
|
//gloves
|
|
if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !HAS_TRAIT(gloves, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [gloves.examine_title(user)] on [t_his] hands."
|
|
else if(GET_ATOM_BLOOD_DECAL_LENGTH(src) && num_hands)
|
|
var/list/blood_stains = GET_ATOM_BLOOD_DECALS(src)
|
|
var/datum/blood_type/blood_type = blood_stains[blood_stains[length(blood_stains)]]
|
|
var/blood_descriptior = "blood"
|
|
if(istype(blood_type))
|
|
blood_descriptior = LOWER_TEXT(blood_type.get_blood_name())
|
|
. += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a "][blood_descriptior]-stained hand[num_hands > 1 ? "s" : ""]!")
|
|
//handcuffed?
|
|
if(handcuffed)
|
|
var/cables_or_cuffs = istype(handcuffed, /obj/item/restraints/handcuffs/cable) ? "restrained with cable" : "handcuffed"
|
|
. += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] [cables_or_cuffs]!")
|
|
//shoes
|
|
if(shoes && !(obscured & ITEM_SLOT_FEET) && !HAS_TRAIT(shoes, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] wearing [shoes.examine_title(user)] on [t_his] feet."
|
|
//mask
|
|
if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !HAS_TRAIT(wear_mask, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [wear_mask.examine_title(user)] on [t_his] face."
|
|
if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !HAS_TRAIT(wear_neck, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] wearing [wear_neck.examine_title(user)] around [t_his] neck."
|
|
//eyes
|
|
if(!(obscured & ITEM_SLOT_EYES) )
|
|
if(glasses && !HAS_TRAIT(glasses, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [glasses.examine_title(user)] covering [t_his] eyes."
|
|
else if(HAS_TRAIT(src, TRAIT_UNNATURAL_RED_GLOWY_EYES))
|
|
. += span_warning("<B>[t_His] eyes are glowing with an unnatural red aura!</B>")
|
|
else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES))
|
|
. += span_warning("<B>[t_His] eyes are bloodshot!</B>")
|
|
//ears
|
|
if(ears && !(obscured & ITEM_SLOT_EARS) && !HAS_TRAIT(ears, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [ears.examine_title(user)] on [t_his] ears."
|
|
|
|
// Yes there's a lot of copypasta here, we can improve this later when carbons are less dumb in general
|
|
/mob/living/carbon/human/get_clothing_examine_info(mob/living/user)
|
|
. = list()
|
|
var/obscured = check_obscured_slots()
|
|
var/t_He = p_They()
|
|
var/t_His = p_Their()
|
|
var/t_his = p_their()
|
|
var/t_has = p_have()
|
|
var/t_is = p_are()
|
|
|
|
//uniform
|
|
if(w_uniform && !(obscured & ITEM_SLOT_ICLOTHING) && !HAS_TRAIT(w_uniform, TRAIT_EXAMINE_SKIP))
|
|
//accessory
|
|
var/accessory_message = ""
|
|
if(istype(w_uniform, /obj/item/clothing/under))
|
|
var/obj/item/clothing/under/undershirt = w_uniform
|
|
var/list/accessories = undershirt.list_accessories_with_icon(user)
|
|
if(length(accessories))
|
|
accessory_message = " with [english_list(accessories)] attached"
|
|
|
|
. += "[t_He] [t_is] wearing [w_uniform.examine_title(user)][accessory_message]."
|
|
//head
|
|
if(head && !(obscured & ITEM_SLOT_HEAD) && !HAS_TRAIT(head, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] wearing [head.examine_title(user)] on [t_his] head."
|
|
//mask
|
|
if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !HAS_TRAIT(wear_mask, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [wear_mask.examine_title(user)] on [t_his] face."
|
|
//neck
|
|
if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !HAS_TRAIT(wear_neck, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] wearing [wear_neck.examine_title(user)] around [t_his] neck."
|
|
//eyes
|
|
if(!(obscured & ITEM_SLOT_EYES) )
|
|
if(glasses && !HAS_TRAIT(glasses, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [glasses.examine_title(user)] covering [t_his] eyes."
|
|
else if(HAS_TRAIT(src, TRAIT_UNNATURAL_RED_GLOWY_EYES))
|
|
. += span_warning("<B>[t_His] eyes are glowing with an unnatural red aura!</B>")
|
|
else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES))
|
|
. += span_warning("<B>[t_His] eyes are bloodshot!</B>")
|
|
//ears
|
|
if(ears && !(obscured & ITEM_SLOT_EARS) && !HAS_TRAIT(ears, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [ears.examine_title(user)] on [t_his] ears."
|
|
//suit/armor
|
|
if(wear_suit && !HAS_TRAIT(wear_suit, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] wearing [wear_suit.examine_title(user)]."
|
|
//suit/armor storage
|
|
if(s_store && !(obscured & ITEM_SLOT_SUITSTORE) && !HAS_TRAIT(s_store, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] carrying [s_store.examine_title(user)] on [t_his] [wear_suit.name]."
|
|
//back
|
|
if(back && !HAS_TRAIT(back, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [back.examine_title(user)] on [t_his] back."
|
|
//ID
|
|
if(wear_id && !HAS_TRAIT(wear_id, TRAIT_EXAMINE_SKIP))
|
|
var/obj/item/card/id/id = wear_id.GetID()
|
|
if(id && get_dist(user, src) <= ID_EXAMINE_DISTANCE)
|
|
var/id_href = "<a href='byond://?src=[REF(src)];see_id=1;id_ref=[REF(id)];id_name=[id.registered_name];examine_time=[world.time]'>[wear_id.examine_title(user)]</a>"
|
|
. += "[t_He] [t_is] wearing [id_href]."
|
|
|
|
else
|
|
. += "[t_He] [t_is] wearing [wear_id.examine_title(user)]."
|
|
//Hands
|
|
for(var/obj/item/held_thing in held_items)
|
|
if((held_thing.item_flags & (ABSTRACT|HAND_ITEM)) || HAS_TRAIT(held_thing, TRAIT_EXAMINE_SKIP))
|
|
continue
|
|
. += "[t_He] [t_is] holding [held_thing.examine_title(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
|
|
for(var/obj/item/bodypart/arm/part in bodyparts)
|
|
if(!(part.bodypart_flags & BODYPART_PSEUDOPART))
|
|
continue
|
|
var/obj/item/corresponding_item = get_item_for_held_index(part.held_index) || part
|
|
. += "[t_He] [t_has] [corresponding_item.examine_title(user)] in place of [t_his] [initial(part.plaintext_zone)]."
|
|
//gloves
|
|
if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !HAS_TRAIT(gloves, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [gloves.examine_title(user)] on [t_his] hands."
|
|
else if(GET_ATOM_BLOOD_DECAL_LENGTH(src) || blood_in_hands)
|
|
if(num_hands)
|
|
. += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a "]blood-stained hand[num_hands > 1 ? "s" : ""]!")
|
|
//handcuffed?
|
|
if(handcuffed)
|
|
var/cables_or_cuffs = istype(handcuffed, /obj/item/restraints/handcuffs/cable) ? "restrained with cable" : "handcuffed"
|
|
. += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] [cables_or_cuffs]!")
|
|
//belt
|
|
if(belt && !(obscured & ITEM_SLOT_BELT) && !HAS_TRAIT(belt, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_has] [belt.examine_title(user)] about [t_his] waist."
|
|
//shoes
|
|
if(shoes && !(obscured & ITEM_SLOT_FEET) && !HAS_TRAIT(shoes, TRAIT_EXAMINE_SKIP))
|
|
. += "[t_He] [t_is] wearing [shoes.examine_title(user)] on [t_his] feet."
|
|
|
|
/// Collects info displayed about any HUDs the user has when examining src
|
|
/mob/living/carbon/proc/get_hud_examine_info(mob/living/user)
|
|
return
|
|
|
|
/mob/living/carbon/human/get_hud_examine_info(mob/living/user)
|
|
. = list()
|
|
|
|
var/perpname = get_face_name(get_id_name(""))
|
|
var/title = ""
|
|
if(perpname && (HAS_TRAIT(user, TRAIT_SECURITY_HUD) || HAS_TRAIT(user, TRAIT_MEDICAL_HUD)) && (user.stat == CONSCIOUS || isobserver(user)) && user != src)
|
|
var/datum/record/crew/target_record = find_record(perpname)
|
|
if(target_record)
|
|
. += "Rank: [target_record.rank]"
|
|
. += "<a href='byond://?src=[REF(src)];hud=1;photo_front=1;examine_time=[world.time]'>\[Front photo\]</a><a href='byond://?src=[REF(src)];hud=1;photo_side=1;examine_time=[world.time]'>\[Side photo\]</a>"
|
|
if(HAS_TRAIT(user, TRAIT_MEDICAL_HUD) && HAS_TRAIT(user, TRAIT_SECURITY_HUD))
|
|
title = separator_hr("Medical & Security Analysis")
|
|
. += get_medhud_examine_info(user, target_record)
|
|
. += get_sechud_examine_info(user, target_record)
|
|
|
|
else if(HAS_TRAIT(user, TRAIT_MEDICAL_HUD))
|
|
title = separator_hr("Medical Analysis")
|
|
. += get_medhud_examine_info(user, target_record)
|
|
|
|
else if(HAS_TRAIT(user, TRAIT_SECURITY_HUD))
|
|
title = separator_hr("Security Analysis")
|
|
. += get_sechud_examine_info(user, target_record)
|
|
|
|
// applies the separator correctly without an extra line break
|
|
if(title && length(.))
|
|
.[1] = title + .[1]
|
|
return .
|
|
|
|
/// Collects information displayed about src when examined by a user with a medical HUD.
|
|
/mob/living/carbon/proc/get_medhud_examine_info(mob/living/user, datum/record/crew/target_record)
|
|
. = list()
|
|
|
|
var/list/cybers = list()
|
|
for(var/obj/item/organ/cyberimp/cyberimp in organs)
|
|
if(IS_ROBOTIC_ORGAN(cyberimp) && !(cyberimp.organ_flags & ORGAN_HIDDEN))
|
|
cybers += cyberimp.examine_title(user)
|
|
if(length(cybers))
|
|
. += "<span class='notice ml-1'>Detected cybernetic modifications:</span>"
|
|
. += "<span class='notice ml-2'>[english_list(cybers, and_text = ", and")]</span>"
|
|
if(target_record)
|
|
. += "<a href='byond://?src=[REF(src)];hud=m;physical_status=1;examine_time=[world.time]'>\[[target_record.physical_status]\]</a>"
|
|
. += "<a href='byond://?src=[REF(src)];hud=m;mental_status=1;examine_time=[world.time]'>\[[target_record.mental_status]\]</a>"
|
|
else
|
|
. += "\[Record Missing\]"
|
|
. += "\[Record Missing\]"
|
|
. += "<a href='byond://?src=[REF(src)];hud=m;evaluation=1;examine_time=[world.time]'>\[Medical evaluation\]</a>"
|
|
. += "<a href='byond://?src=[REF(src)];hud=m;quirk=1;examine_time=[world.time]'>\[See quirks\]</a>"
|
|
|
|
/// Collects information displayed about src when examined by a user with a security HUD.
|
|
/mob/living/carbon/proc/get_sechud_examine_info(mob/living/user, datum/record/crew/target_record)
|
|
. = list()
|
|
|
|
var/wanted_status = WANTED_NONE
|
|
var/security_note = "None."
|
|
|
|
if(target_record)
|
|
wanted_status = target_record.wanted_status
|
|
if(target_record.security_note)
|
|
security_note = target_record.security_note
|
|
if(ishuman(user))
|
|
. += "Criminal status: <a href='byond://?src=[REF(src)];hud=s;status=1;examine_time=[world.time]'>\[[wanted_status]\]</a>"
|
|
else
|
|
. += "Criminal status: [wanted_status]"
|
|
. += "Important Notes: [security_note]"
|
|
. += "Security record: <a href='byond://?src=[REF(src)];hud=s;view=1;examine_time=[world.time]'>\[View\]</a>"
|
|
if(ishuman(user))
|
|
. += "<a href='byond://?src=[REF(src)];hud=s;add_citation=1;examine_time=[world.time]'>\[Add citation\]</a>\
|
|
<a href='byond://?src=[REF(src)];hud=s;add_crime=1;examine_time=[world.time]'>\[Add crime\]</a>\
|
|
<a href='byond://?src=[REF(src)];hud=s;add_note=1;examine_time=[world.time]'>\[Add note\]</a>"
|
|
|
|
/mob/living/carbon/human/examine_more(mob/user)
|
|
. = ..()
|
|
|
|
if(istype(w_uniform, /obj/item/clothing/under) && !(check_obscured_slots() & ITEM_SLOT_ICLOTHING) && !HAS_TRAIT(w_uniform, TRAIT_EXAMINE_SKIP))
|
|
var/obj/item/clothing/under/undershirt = w_uniform
|
|
if(undershirt.has_sensor == BROKEN_SENSORS)
|
|
. += list(span_notice("\The [undershirt]'s medical sensors are sparking."))
|
|
|
|
if(HAS_TRAIT(src, TRAIT_UNKNOWN) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
|
|
return
|
|
|
|
var/limbs_text = get_mismatched_limb_text()
|
|
if(LAZYLEN(limbs_text))
|
|
. += limbs_text
|
|
|
|
var/agetext = get_age_text()
|
|
if(agetext)
|
|
. += agetext
|
|
|
|
/// Reports all body parts which are mismatched with the user's species
|
|
/mob/living/carbon/human/proc/get_mismatched_limb_text()
|
|
var/list/covered = get_covered_body_zones()
|
|
var/list/texts = list()
|
|
for(var/obj/item/bodypart/part as anything in bodyparts)
|
|
var/part_id = part.limb_id
|
|
var/obj/item/bodypart/expected_part = dna?.species?.bodypart_overrides[part.body_zone]
|
|
var/expected_id = initial(expected_part?.limb_id)
|
|
// only report abnormal bodyparts
|
|
if(part_id == expected_id)
|
|
continue
|
|
// same shape bodyparts are concealed by clothing
|
|
// this means you can see ex. digitigrade legs through clothes
|
|
// but you can't see ex. cybernetic legs through clothes
|
|
if(part.bodyshape == initial(expected_part?.bodyshape) && (part.body_zone in covered))
|
|
continue
|
|
texts += span_notice("[p_They()] [p_have()] \a [part].")
|
|
|
|
return texts
|
|
|
|
/// Reports how old the mob appears to be
|
|
/mob/living/carbon/human/proc/get_age_text()
|
|
if((wear_mask?.flags_inv & HIDEFACE) || (head?.flags_inv & HIDEFACE))
|
|
return
|
|
|
|
var/age_text
|
|
switch(age)
|
|
if(-INFINITY to 25)
|
|
age_text = "very young"
|
|
if(26 to 35)
|
|
age_text = "of adult age"
|
|
if(36 to 55)
|
|
age_text = "middle-aged"
|
|
if(56 to 75)
|
|
age_text = "rather old"
|
|
if(76 to 100)
|
|
age_text = "very old"
|
|
if(101 to INFINITY)
|
|
age_text = "withering away"
|
|
|
|
return span_notice("[p_They()] appear[p_s()] to be [age_text].")
|
|
|
|
#undef ADD_NEWLINE_IF_NECESSARY
|
|
#undef CARBON_EXAMINE_EMBEDDING_MAX_DIST
|