diff --git a/code/__DEFINES/chat.dm b/code/__DEFINES/chat.dm index c3fe46b0cd496..f4f88df6c706e 100644 --- a/code/__DEFINES/chat.dm +++ b/code/__DEFINES/chat.dm @@ -45,5 +45,7 @@ #define debug_world_log(msg) if (GLOB.Debug2) log_world("DEBUG: [msg]") /// Adds a generic box around whatever message you're sending in chat. Really makes things stand out. #define examine_block(str) ("
" + str + "
") +/// Makes a horizontal line with text in the middle +#define separator_hr(str) ("
" + str + "
") /// Emboldens runechat messages #define RUNECHAT_BOLD(str) "+[str]+" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 3af767dd392af..3a4e34463a27e 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -1034,3 +1034,7 @@ GLOBAL_LIST_INIT(layers_to_offset, list( #define BUTT_SPRITE_PLASMA "plasma" #define BUTT_SPRITE_FUZZY "fuzzy" #define BUTT_SPRITE_SLIME "slime" + +/// Distance which you can see someone's ID card +/// Short enough that you can inspect over tables (bartender checking age) +#define ID_EXAMINE_DISTANCE 3 diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm index 259a13e1e9afe..a4f81a168e610 100644 --- a/code/__DEFINES/span.dm +++ b/code/__DEFINES/span.dm @@ -117,19 +117,22 @@ #define span_secradio(str) ("" + str + "") #define span_servradio(str) ("" + str + "") #define span_singing(str) ("" + str + "") +#define span_slightly_larger(str) ("" + str + "") #define span_slime(str) ("" + str + "") #define span_small(str) ("" + str + "") +#define span_smalldanger(str) ("" + str + "") #define span_smallnotice(str) ("" + str + "") #define span_smallnoticeital(str) ("" + str + "") #define span_soapbox(str) ("" + str + "") +#define span_spiderbreacher(str) ("" + str + "") #define span_spiderbroodmother(str) ("" + str + "") #define span_spiderscout(str) ("" + str + "") -#define span_spiderbreacher(str) ("" + str + "") #define span_subheader_announcement_text(str) ("" + str + "") #define span_suicide(str) ("" + str + "") #define span_suppradio(str) ("" + str + "") #define span_syndradio(str) ("" + str + "") #define span_tape_recorder(str) ("" + str + "") +#define span_tinydanger(str) ("" + str + "") #define span_tinynotice(str) ("" + str + "") #define span_tinynoticeital(str) ("" + str + "") #define span_unconscious(str) ("" + str + "") diff --git a/code/datums/elements/ai_held_item.dm b/code/datums/elements/ai_held_item.dm index 053a1827fb23d..185e45da9aa2b 100644 --- a/code/datums/elements/ai_held_item.dm +++ b/code/datums/elements/ai_held_item.dm @@ -54,7 +54,7 @@ var/obj/item/carried_item = get_held_item(source) if (!carried_item) return - examine_text += span_notice("[source.p_They()] [source.p_are()] carrying [carried_item.get_examine_string(user)].") + examine_text += span_notice("[source.p_They()] [source.p_are()] carrying [carried_item.examine_title(user)].") /// If we died, drop anything we were carrying /datum/element/ai_held_item/proc/on_death(mob/living/ol_yeller) diff --git a/code/datums/elements/dextrous.dm b/code/datums/elements/dextrous.dm index 681e2a9488d8c..240cfc88494d3 100644 --- a/code/datums/elements/dextrous.dm +++ b/code/datums/elements/dextrous.dm @@ -69,5 +69,5 @@ for(var/obj/item/held_item in examined.held_items) if(held_item.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM)) continue - examine_list += span_info("[examined.p_They()] [examined.p_have()] [held_item.get_examine_string(user)] in [examined.p_their()] \ + examine_list += span_info("[examined.p_They()] [examined.p_have()] [held_item.examine_title(user)] in [examined.p_their()] \ [examined.get_held_index_name(examined.get_held_index_of_item(held_item))].") diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm index 46c31c4578d1d..c2ca39b638726 100644 --- a/code/datums/status_effects/debuffs/fire_stacks.dm +++ b/code/datums/status_effects/debuffs/fire_stacks.dm @@ -136,6 +136,12 @@ /// Type of mob light emitter we use when on fire var/moblight_type = /obj/effect/dummy/lighting_obj/moblight/fire +/datum/status_effect/fire_handler/fire_stacks/get_examine_text() + if(owner.on_fire) + return + + return "[owner.p_They()] [owner.p_are()] covered in something flammable." + /datum/status_effect/fire_handler/fire_stacks/proc/owner_touched_sparks() SIGNAL_HANDLER @@ -294,6 +300,9 @@ enemy_types = list(/datum/status_effect/fire_handler/fire_stacks) stack_modifier = -1 +/datum/status_effect/fire_handler/wet_stacks/get_examine_text() + return "[owner.p_They()] look[owner.p_s()] a little soaked." + /datum/status_effect/fire_handler/wet_stacks/tick(seconds_between_ticks) adjust_stacks(-0.5 * seconds_between_ticks) if(stacks <= 0) diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm index 43385b47180ae..45635f5a70a20 100644 --- a/code/datums/wounds/bones.dm +++ b/code/datums/wounds/bones.dm @@ -147,14 +147,26 @@ if(1 to 6) victim.bleed(blood_bled, TRUE) if(7 to 13) - victim.visible_message("A thin stream of blood drips from [victim]'s mouth from the blow to [victim.p_their()] chest.", span_danger("You cough up a bit of blood from the blow to your chest."), vision_distance=COMBAT_MESSAGE_RANGE) + victim.visible_message( + span_smalldanger("A thin stream of blood drips from [victim]'s mouth from the blow to [victim.p_their()] chest."), + span_danger("You cough up a bit of blood from the blow to your chest."), + vision_distance = COMBAT_MESSAGE_RANGE, + ) victim.bleed(blood_bled, TRUE) if(14 to 19) - victim.visible_message("Blood spews out of [victim]'s mouth from the blow to [victim.p_their()] chest!", span_danger("You spit out a string of blood from the blow to your chest!"), vision_distance=COMBAT_MESSAGE_RANGE) + victim.visible_message( + span_smalldanger("Blood spews out of [victim]'s mouth from the blow to [victim.p_their()] chest!"), + span_danger("You spit out a string of blood from the blow to your chest!"), + vision_distance = COMBAT_MESSAGE_RANGE, + ) new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) victim.bleed(blood_bled) if(20 to INFINITY) - victim.visible_message(span_danger("Blood spurts out of [victim]'s mouth from the blow to [victim.p_their()] chest!"), span_danger("You choke up on a spray of blood from the blow to your chest!"), vision_distance=COMBAT_MESSAGE_RANGE) + victim.visible_message( + span_danger("Blood spurts out of [victim]'s mouth from the blow to [victim.p_their()] chest!"), + span_bolddanger("You choke up on a spray of blood from the blow to your chest!"), + vision_distance = COMBAT_MESSAGE_RANGE, + ) victim.bleed(blood_bled) new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) victim.add_splatter_floor(get_step(victim.loc, victim.dir)) diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm index 4454b0024e441..618a2e7fe3b2e 100644 --- a/code/datums/wounds/pierce.dm +++ b/code/datums/wounds/pierce.dm @@ -42,14 +42,26 @@ if(1 to 6) victim.bleed(blood_bled, TRUE) if(7 to 13) - victim.visible_message("Blood droplets fly from the hole in [victim]'s [limb.plaintext_zone].", span_danger("You cough up a bit of blood from the blow to your [limb.plaintext_zone]."), vision_distance=COMBAT_MESSAGE_RANGE) + victim.visible_message( + span_smalldanger("Blood droplets fly from the hole in [victim]'s [limb.plaintext_zone]."), + span_danger("You cough up a bit of blood from the blow to your [limb.plaintext_zone]."), + vision_distance = COMBAT_MESSAGE_RANGE, + ) victim.bleed(blood_bled, TRUE) if(14 to 19) - victim.visible_message("A small stream of blood spurts from the hole in [victim]'s [limb.plaintext_zone]!", span_danger("You spit out a string of blood from the blow to your [limb.plaintext_zone]!"), vision_distance=COMBAT_MESSAGE_RANGE) + victim.visible_message( + span_smalldanger("A small stream of blood spurts from the hole in [victim]'s [limb.plaintext_zone]!"), + span_danger("You spit out a string of blood from the blow to your [limb.plaintext_zone]!"), + vision_distance = COMBAT_MESSAGE_RANGE, + ) new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) victim.bleed(blood_bled) if(20 to INFINITY) - victim.visible_message(span_danger("A spray of blood streams from the gash in [victim]'s [limb.plaintext_zone]!"), span_danger("You choke up on a spray of blood from the blow to your [limb.plaintext_zone]!"), vision_distance=COMBAT_MESSAGE_RANGE) + victim.visible_message( + span_danger("A spray of blood streams from the gash in [victim]'s [limb.plaintext_zone]!"), + span_bolddanger("You choke up on a spray of blood from the blow to your [limb.plaintext_zone]!"), + vision_distance = COMBAT_MESSAGE_RANGE, + ) victim.bleed(blood_bled) new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) victim.add_splatter_floor(get_step(victim.loc, victim.dir)) diff --git a/code/game/atom/atom_examine.dm b/code/game/atom/atom_examine.dm index ab94c73856365..0a2a51dc297d4 100644 --- a/code/game/atom/atom_examine.dm +++ b/code/game/atom/atom_examine.dm @@ -1,6 +1,14 @@ /atom - ///If non-null, overrides a/an/some in all cases + /// If non-null, overrides a/an/some in all cases var/article + /// Text that appears preceding the name in examine() + var/examine_thats = "That's" + +/mob/living/carbon/human + examine_thats = "This is" + +/mob/living/silicon/robot + examine_thats = "This is" /** * Called when a mob examines (shift click or verb) this atom @@ -11,15 +19,10 @@ * Produces a signal [COMSIG_ATOM_EXAMINE] */ /atom/proc/examine(mob/user) - var/examine_string = get_examine_string(user, thats = TRUE) - if(examine_string) - . = list("[examine_string].") - else - . = list() - + . = list() . += get_name_chaser(user) if(desc) - . += desc + . += "[desc]" if(custom_materials) var/list/materials_list = list() @@ -75,7 +78,7 @@ * [COMSIG_ATOM_GET_EXAMINE_NAME] signal */ /atom/proc/get_examine_name(mob/user) - var/list/override = list(article, null, "[name]") + var/list/override = list(article, null, "[get_visible_name()]") SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) if(!isnull(override[EXAMINE_POSITION_ARTICLE])) @@ -84,11 +87,24 @@ if(!isnull(override[EXAMINE_POSITION_BEFORE])) override -= null // There is no article, don't try to join it return "\a [jointext(override, " ")]" - return "\a [src]" + return "\a [src]" + +/mob/living/get_examine_name(mob/user) + return get_visible_name() -///Generate the full examine string of this atom (including icon for goonchat) -/atom/proc/get_examine_string(mob/user, thats = FALSE) - return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]" +/// Icon displayed in examine +/atom/proc/get_examine_icon(mob/user) + return icon2html(src, user) + +/** + * Formats the atom's name into a string for use in examine (as the "title" of the atom) + * + * * user - the mob examining the atom + * * thats - whether to include "That's", or similar (mobs use "This is") before the name + */ +/atom/proc/examine_title(mob/user, thats = FALSE) + var/examine_icon = get_examine_icon(user) + return "[examine_icon ? "[examine_icon] " : ""][thats ? "[examine_thats] ":""][get_examine_name(user)]" /** * Returns an extended list of examine strings for any contained ID cards. @@ -98,7 +114,6 @@ */ /atom/proc/get_id_examine_strings(mob/user) . = list() - return ///Used to insert text after the name but before the description in examine() /atom/proc/get_name_chaser(mob/user, list/name_chaser = list()) diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 8490f30f1bb75..1f20ea9bc3e39 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -174,10 +174,10 @@ /obj/item/card/id/get_id_examine_strings(mob/user) . = ..() - . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "bigicon")]") + . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "hugeicon")]") -/obj/item/card/id/get_examine_string(mob/user, thats = FALSE) - return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" +/obj/item/card/id/get_examine_icon(mob/user) + return icon2html(get_cached_flat_icon(), user) /** * Helper proc, checks whether the ID card can hold any given set of wildcards. @@ -1924,10 +1924,10 @@ /obj/item/card/cardboard/get_id_examine_strings(mob/user) . = ..() - . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "bigicon")]") + . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "hugeicon")]") -/obj/item/card/cardboard/get_examine_string(mob/user, thats = FALSE) - return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" +/obj/item/card/cardboard/get_examine_icon(mob/user) + return icon2html(get_cached_flat_icon(), user) /obj/item/card/cardboard/examine(mob/user) . = ..() diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index 26df57683aa26..40ca839cb9a15 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -397,7 +397,7 @@ var/cyberimp_detect for(var/obj/item/organ/internal/cyberimp/cyberimp in carbontarget.organs) if(IS_ROBOTIC_ORGAN(cyberimp) && !(cyberimp.organ_flags & ORGAN_HIDDEN)) - cyberimp_detect += "[!cyberimp_detect ? "[cyberimp.get_examine_string(user)]" : ", [cyberimp.get_examine_string(user)]"]" + cyberimp_detect += "[!cyberimp_detect ? "[cyberimp.examine_title(user)]" : ", [cyberimp.examine_title(user)]"]" if(cyberimp_detect) render_list += "Detected cybernetic modifications:\n" render_list += "[cyberimp_detect]\n" diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm index e8c1fc6d19245..b4cd92665de23 100644 --- a/code/game/objects/items/storage/wallets.dm +++ b/code/game/objects/items/storage/wallets.dm @@ -111,10 +111,8 @@ cached_flat_icon = getFlatIcon(src) return cached_flat_icon -/obj/item/storage/wallet/get_examine_string(mob/user, thats = FALSE) - if(front_id) - return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" //displays all overlays in chat - return ..() +/obj/item/storage/wallet/get_examine_icon(mob/user) + return icon2html(get_cached_flat_icon(), user) /obj/item/storage/wallet/proc/update_label() if(front_id) @@ -175,4 +173,3 @@ /obj/item/storage/wallet/money/PopulateContents() for(var/iteration in 1 to pick(3, 4)) new /obj/item/holochip(src, rand(50, 450)) - diff --git a/code/modules/antagonists/changeling/powers/transform.dm b/code/modules/antagonists/changeling/powers/transform.dm index 1e1ef9e65d8e2..852e8fca53c8c 100644 --- a/code/modules/antagonists/changeling/powers/transform.dm +++ b/code/modules/antagonists/changeling/powers/transform.dm @@ -156,10 +156,10 @@ /obj/item/changeling/id/get_id_examine_strings(mob/user) . = ..() - . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "bigicon")]") + . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "hugeicon")]") -/obj/item/changeling/id/get_examine_string(mob/user, thats = FALSE) - return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" //displays all overlays in chat +/obj/item/changeling/id/get_examine_icon(mob/user) + return icon2html(get_cached_flat_icon(), user) //Change our DNA to that of somebody we've absorbed. /datum/action/changeling/transform/sting_action(mob/living/carbon/human/user) diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index c3505b4285dc9..fa7813f0f4695 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -310,7 +310,7 @@ /obj/item/clothing/under/proc/list_accessories_with_icon(mob/user) var/list/all_accessories = list() for(var/obj/item/clothing/accessory/attached as anything in attached_accessories) - all_accessories += attached.get_examine_string(user) + all_accessories += attached.examine_title(user) return all_accessories diff --git a/code/modules/mob/living/basic/cult/constructs/_construct.dm b/code/modules/mob/living/basic/cult/constructs/_construct.dm index 1c5e91b50c263..01004685607a0 100644 --- a/code/modules/mob/living/basic/cult/constructs/_construct.dm +++ b/code/modules/mob/living/basic/cult/constructs/_construct.dm @@ -106,7 +106,7 @@ return FALSE to_chat(src, span_bold(playstyle_string)) -/mob/living/basic/construct/examine(mob/user) +/mob/living/basic/construct/get_examine_name(mob/user) var/text_span switch(theme) if(THEME_CULT) @@ -115,13 +115,20 @@ text_span = "purple" if(THEME_HOLY) text_span = "blue" - . = list("This is [icon2html(src, user)] \a [src]!\n[desc]") + + if(!text_span) + return ..() + + return "[..()]" + +/mob/living/basic/construct/examine(mob/user) + . = list() if(health < maxHealth) if(health >= maxHealth/2) . += span_warning("[p_They()] look[p_s()] slightly dented.") else . += span_warning(span_bold("[p_They()] look[p_s()] severely dented!")) - . += "" + return . /mob/living/basic/construct/narsie_act() diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm index 983ade8de0bcc..12e3125581f14 100644 --- a/code/modules/mob/living/basic/drone/_drone.dm +++ b/code/modules/mob/living/basic/drone/_drone.dm @@ -276,21 +276,21 @@ return icon('icons/mob/butts.dmi', BUTT_SPRITE_DRONE) /mob/living/basic/drone/examine(mob/user) - . = list("This is [icon2html(src, user)] \a [src]!") + . = list() //Hands for(var/obj/item/held_thing in held_items) if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM)) continue - . += "It has [held_thing.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(held_thing))]." + . += "It has [held_thing.examine_title(user)] in its [get_held_index_name(get_held_index_of_item(held_thing))]." //Internal storage if(internal_storage && !(internal_storage.item_flags & ABSTRACT)) - . += "It is holding [internal_storage.get_examine_string(user)] in its internal storage." + . += "It is holding [internal_storage.examine_title(user)] in its internal storage." //Cosmetic hat - provides no function other than looks if(head && !(head.item_flags & ABSTRACT)) - . += "It is wearing [head.get_examine_string(user)] on its head." + . += "It is wearing [head.examine_title(user)] on its head." //Braindead if(!client && stat != DEAD) @@ -313,8 +313,6 @@ . += span_deadsay("A message repeatedly flashes on its display: \"REBOOT -- REQUIRED\".") else . += span_deadsay("A message repeatedly flashes on its display: \"ERROR -- OFFLINE\".") - . += "" - /mob/living/basic/drone/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //Secbots won't hunt maintenance drones. return -10 diff --git a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm index ae9cbfbeecbc8..2b9d50f085493 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm @@ -28,7 +28,7 @@ . = ..() if(isnull(internal_storage) || (internal_storage.item_flags & ABSTRACT)) return - . += span_info("It is holding [internal_storage.get_examine_string(user)] in its internal storage.") + . += span_info("It is holding [internal_storage.examine_title(user)] in its internal storage.") /mob/living/basic/guardian/dextrous/recall_effects() . = ..() diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 6d6d7ada73ceb..7b9743d104258 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -1,4 +1,19 @@ +/// 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 += "" } + +/mob/living/carbon/human/get_examine_name(mob/user) + if(!HAS_TRAIT(user, TRAIT_PROSOPAGNOSIA)) + return ..() + + return "Unknown" + +/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() @@ -6,157 +21,541 @@ var/t_has = p_have() var/t_is = p_are() - . = list("This is [icon2html(src, user)] \a [src]!") - var/obscured = check_obscured_slots() - - if (handcuffed) - . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] handcuffed!") - if (head) - . += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head. " - if(wear_mask && !(obscured & ITEM_SLOT_MASK)) - . += "[t_He] [t_is] wearing [wear_mask.get_examine_string(user)] on [t_his] face." - if(wear_neck && !(obscured & ITEM_SLOT_NECK)) - . += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck." - - for(var/obj/item/held_thing in held_items) - if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM)) - continue - . += "[t_He] [t_is] holding [held_thing.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]." + . = list() + . += get_clothing_examine_info(user) + // give us some space between clothing examine and the rest + ADD_NEWLINE_IF_NECESSARY(.) - if (back) - . += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back." var/appears_dead = FALSE - if (stat == DEAD) + var/just_sleeping = FALSE + + if(!appears_alive()) appears_dead = TRUE - if(get_organ_by_type(/obj/item/organ/internal/brain)) - . += span_deadsay("[t_He] [t_is] limp and unresponsive, with no signs of life.") - else if(get_bodypart(BODY_ZONE_HEAD)) - . += span_deadsay("It appears that [t_his] brain is missing...") - - var/list/msg = list("") - for(var/obj/item/bodypart/bodypart as anything in bodyparts) - for(var/obj/item/embedded_item as anything in bodypart.embedded_objects) - if(embedded_item.is_embed_harmless()) - msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] stuck to [t_his] [bodypart.name]!\n" - else - msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] embedded in [t_his] [bodypart.name]!\n" - for(var/datum/wound/bodypart_wound as anything in bodypart.wounds) - msg += "[bodypart_wound.get_examine_description(user)]\n" - for(var/obj/item/bodypart/disabled_limb as anything in get_disabled_limbs()) + 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/internal/brain)) + . += span_deadsay("It appears that [t_his] brain is missing...") + + var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + var/list/disabled = list() + for(var/obj/item/bodypart/body_part as anything in bodyparts) + if(body_part.bodypart_disabled) + disabled += body_part + missing -= body_part.body_zone + for(var/obj/item/embedded as anything in body_part.embedded_objects) + var/stuck_wordage = embedded.is_embed_harmless() ? "stuck to" : "embedded in" + . += span_boldwarning("[t_He] [t_has] [icon2html(embedded, user)] \a [embedded] [stuck_wordage] [t_his] [body_part.plaintext_zone]!") + + 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 - damage_text = (disabled_limb.brute_dam >= disabled_limb.burn_dam) ? disabled_limb.heavy_brute_msg : disabled_limb.heavy_burn_msg - msg += "[t_His] [disabled_limb.name] is [damage_text]!\n" + 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] is [damage_text]!") - for(var/obj/item/bodypart/missing_limb as anything in get_missing_limbs()) - if(missing_limb == BODY_ZONE_HEAD) - msg += "[span_deadsay("[t_His] [parse_zone(missing_limb)] is missing!")]\n" + //stores missing limbs + var/l_limbs_missing = 0 + var/r_limbs_missing = 0 + for(var/gone in missing) + if(gone == BODY_ZONE_HEAD) + . += span_deadsay("[t_His] [parse_zone(gone)] is missing!") continue - msg += "[span_warning("[t_His] [parse_zone(missing_limb)] is missing!")]\n" + if(gone == BODY_ZONE_L_ARM || gone == BODY_ZONE_L_LEG) + l_limbs_missing++ + else if(gone == BODY_ZONE_R_ARM || gone == BODY_ZONE_R_LEG) + r_limbs_missing++ + . += span_boldwarning("[capitalize(t_his)] [parse_zone(gone)] 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...") - var/temp = getBruteLoss() 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) - msg += "[t_He] [t_has] minor bruising.\n" - else if (temp < 50) - msg += "[t_He] [t_has] moderate bruising!\n" + if(temp < 25) + . += span_danger("[t_He] [t_has] minor [damage_desc[BRUTE]].") + else if(temp < 50) + . += span_danger("[t_He] [t_has] moderate [damage_desc[BRUTE]]!") else - msg += "[t_He] [t_has] severe bruising!\n" + . += span_bolddanger("[t_He] [t_has] severe [damage_desc[BRUTE]]!") temp = getFireLoss() if(temp) - if (temp < 25) - msg += "[t_He] [t_has] minor burns.\n" + if(temp < 25) + . += span_danger("[t_He] [t_has] minor [damage_desc[BURN]].") else if (temp < 50) - msg += "[t_He] [t_has] moderate burns!\n" + . += span_danger("[t_He] [t_has] moderate [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 + . += "[t_He] [t_is] quite chubby." + 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("[t_He] resemble[p_s()] a crushed, empty juice pouch.") + + 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.get_modified_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 = "" + if(appears_dead) + bleed_text += "" + bleed_text += "Blood is visible in [t_his] open" else - msg += "[t_He] [t_has] severe burns!\n" + bleed_text += "" + bleed_text += "[t_He] [t_is] bleeding from [t_his] " - if(HAS_TRAIT(src, TRAIT_DUMB)) - msg += "[t_He] seem[p_s()] to be clumsy and unable to think.\n" + bleed_text += english_list(bleeding_limbs, and_text = " and ") - if(has_status_effect(/datum/status_effect/fire_handler/fire_stacks)) - msg += "[t_He] [t_is] covered in something flammable.\n" - if(has_status_effect(/datum/status_effect/fire_handler/wet_stacks)) - msg += "[t_He] look[p_s()] a little soaked.\n" + 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(pulledby?.grab_state) - msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n" + if(appears_dead) + bleed_text += "" + else + bleed_text += "" + bleed_text += "" + + . += 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(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) - var/scar_severity = 0 - for(var/i in all_scars) - var/datum/scar/S = i - if(S.is_visible(user)) - scar_severity += S.severity - - switch(scar_severity) - if(1 to 4) - msg += "[span_tinynoticeital("[t_He] [t_has] visible scarring, you can look again to take a closer look...")]\n" - if(5 to 8) - msg += "[span_smallnoticeital("[t_He] [t_has] several bad scars, you can look again to take a closer look...")]\n" - if(9 to 11) - msg += "[span_notice("[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...")]\n" - if(12 to INFINITY) - msg += "[span_notice("[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...")]\n" - - msg += "" - - . += msg.Join("") - - if(!appears_dead) switch(stat) - if(SOFT_CRIT) - . += "[t_His] breathing is shallow and labored." if(UNCONSCIOUS, HARD_CRIT) - . += "[t_He] [t_is]n't responding to anything around [t_him] and seems to be asleep." - - var/trait_exam = common_trait_examine() - if (!isnull(trait_exam)) - . += trait_exam - - if(mob_mood) - switch(mob_mood.shown_mood) - if(-INFINITY to MOOD_SAD4) - . += "[t_He] look[p_s()] depressed." - if(MOOD_SAD4 to MOOD_SAD3) - . += "[t_He] look[p_s()] very sad." - if(MOOD_SAD3 to MOOD_SAD2) - . += "[t_He] look[p_s()] a bit down." - if(MOOD_HAPPY2 to MOOD_HAPPY3) - . += "[t_He] look[p_s()] quite happy." - if(MOOD_HAPPY3 to MOOD_HAPPY4) - . += "[t_He] look[p_s()] very happy." - if(MOOD_HAPPY4 to INFINITY) - . += "[t_He] look[p_s()] ecstatic." - . += "" + . += 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." + if(get_organ_by_type(/obj/item/organ/internal/brain) && isnull(ai_controller)) + var/npc_message = "" + 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("[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...") + if(12 to INFINITY) + . += span_notice("[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...") + + 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("[user.p_They()] appear[user.p_s()] to have been dissected. Useless for examination... for now.") + 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()]. Exquisite.") + if(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(.) + . += "Quirks: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]" SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, user, .) + if(length(.)) + .[1] = "" + .[1] + .[length(.)] += "" + return . + +/** + * 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("
") + +/// 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 + if(get_organ_by_type(/obj/item/organ/internal/brain) && (client || HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE) || (ghost?.can_reenter_corpse && ghost?.client))) + return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life...") + else + 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) && !(head.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_is] wearing [head.examine_title(user)] on [t_his] head." + //back + if(back && !(back.item_flags & 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|EXAMINE_SKIP|HAND_ITEM)) + 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))]." + //gloves + if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_has] [gloves.examine_title(user)] on [t_his] hands." + else if(GET_ATOM_BLOOD_DNA_LENGTH(src)) + 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]!") + //shoes + if(shoes && !(obscured & ITEM_SLOT_FEET) && !(shoes.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_is] wearing [shoes.examine_title(user)] on [t_his] feet." + //mask + if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !(wear_mask.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_has] [wear_mask.examine_title(user)] on [t_his] face." + if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !(wear_neck.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_is] wearing [wear_neck.examine_title(user)] around [t_his] neck." + //eyes + if(!(obscured & ITEM_SLOT_EYES) ) + if(glasses && !(glasses.item_flags & 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("[t_His] eyes are glowing with an unnatural red aura!") + else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES)) + . += span_warning("[t_His] eyes are bloodshot!") + //ears + if(ears && !(obscured & ITEM_SLOT_EARS) && !(ears.item_flags & 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) && !(w_uniform.item_flags & 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) && !(head.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_is] wearing [head.examine_title(user)] on [t_his] head." + //mask + if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !(wear_mask.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_has] [wear_mask.examine_title(user)] on [t_his] face." + //neck + if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !(wear_neck.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_is] wearing [wear_neck.examine_title(user)] around [t_his] neck." + //eyes + if(!(obscured & ITEM_SLOT_EYES) ) + if(glasses && !(glasses.item_flags & 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("[t_His] eyes are glowing with an unnatural red aura!") + else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES)) + . += span_warning("[t_His] eyes are bloodshot!") + //ears + if(ears && !(obscured & ITEM_SLOT_EARS) && !(ears.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_has] [ears.examine_title(user)] on [t_his] ears." + //suit/armor + if(wear_suit && !(wear_suit.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_is] wearing [wear_suit.examine_title(user)]." + //suit/armor storage + if(s_store && !(obscured & ITEM_SLOT_SUITSTORE) && !(s_store.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_is] carrying [s_store.examine_title(user)] on [t_his] [wear_suit.name]." + //back + if(back && !(back.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_has] [back.examine_title(user)] on [t_his] back." + //ID + if(wear_id && !(wear_id.item_flags & EXAMINE_SKIP)) + var/obj/item/card/id/id = wear_id.GetID() + if(id && get_dist(user, src) <= ID_EXAMINE_DISTANCE) + var/id_href = "[wear_id.examine_title(user)]" + . += "[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|EXAMINE_SKIP|HAND_ITEM)) + 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))]." + //gloves + if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_has] [gloves.examine_title(user)] on [t_his] hands." + else if(GET_ATOM_BLOOD_DNA_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 && !(belt.item_flags & EXAMINE_SKIP)) + . += "[t_He] [t_has] [belt.examine_title(user)] about [t_his] waist." + //shoes + if(shoes && !(obscured & ITEM_SLOT_FEET) && !(shoes.item_flags & 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) + var/perpname = get_face_name(get_id_name("")) + 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]" + . += "\[Front photo\]\[Side photo\]" + if(HAS_TRAIT(user, TRAIT_MEDICAL_HUD) && HAS_TRAIT(user, TRAIT_SECURITY_HUD)) + . += 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)) + . += separator_hr("Medical Analysis") + . += get_medhud_examine_info(user, target_record) + + else if(HAS_TRAIT(user, TRAIT_SECURITY_HUD)) + . += separator_hr("Security Analysis") + . += get_sechud_examine_info(user, target_record) -/mob/living/carbon/examine_more(mob/user) +/// 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/internal/cyberimp/cyberimp in organs) + if(IS_ROBOTIC_ORGAN(cyberimp) && !(cyberimp.organ_flags & ORGAN_HIDDEN)) + cybers += cyberimp.examine_title(user) + if(length(cybers)) + . += "Detected cybernetic modifications:" + . += "[english_list(cybers, and_text = ", and")]" + if(target_record) + . += "\[[target_record.physical_status]\]" + . += "\[[target_record.mental_status]\]" + else + . += "\[Record Missing\]" + . += "\[Record Missing\]" + . += "\[Medical evaluation\]" + . += "\[See quirks\]" + . = jointext(., "
") + +/// 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: \[[wanted_status]\]" + else + . += "Criminal status: [wanted_status]" + . += "Important Notes: [security_note]" + . += "Security record: \[View\]" + if(ishuman(user)) + . += "\[Add citation\]\ + \[Add crime\]\ + \[Add note\]" + . = jointext(., "
") + +/mob/living/carbon/human/examine_more(mob/user) . = ..() - . += span_notice("You examine [src] closer, and note the following...") - - if(dna) //not all carbons have it. eg - xenos - //On closer inspection, this man isnt a man at all! - var/list/covered_zones = get_covered_body_zones() - for(var/obj/item/bodypart/part as anything in bodyparts) - if(part.body_zone in covered_zones) - continue - if(part.limb_id != dna.species.examine_limb_id) - . += "[span_info("[p_They()] [p_have()] \an [part.name].")]" - - var/list/visible_scars - for(var/i in all_scars) - var/datum/scar/S = i - if(S.is_visible(user)) - LAZYADD(visible_scars, S) - - for(var/i in visible_scars) - var/datum/scar/S = i - var/scar_text = S.get_examine_description(user) - if(scar_text) - . += "[scar_text]" + if((wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))) + return + if(HAS_TRAIT(src, TRAIT_UNKNOWN) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) + 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" + . += list(span_notice("[p_They()] appear[p_s()] to be [age_text].")) - return . +#undef ADD_NEWLINE_IF_NECESSARY diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm deleted file mode 100644 index 0c50e881635f6..0000000000000 --- a/code/modules/mob/living/carbon/human/examine.dm +++ /dev/null @@ -1,450 +0,0 @@ -/mob/living/carbon/human/examine(mob/user) -//this is very slightly better than it was because you can use it more places. still can't do \his[src] though. - 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() - var/obscure_name - var/obscure_examine - - if(isliving(user)) - var/mob/living/L = user - if(HAS_TRAIT(L, TRAIT_PROSOPAGNOSIA) || HAS_TRAIT(L, TRAIT_INVISIBLE_MAN)) - obscure_name = TRUE - if(HAS_TRAIT(src, TRAIT_UNKNOWN)) - obscure_name = TRUE - obscure_examine = TRUE - - . = list("This is [!obscure_name ? name : "Unknown"]!") - - if(obscure_examine) - return list("You're struggling to make out any details...") - - var/obscured = check_obscured_slots() - - //uniform - if(w_uniform && !(obscured & ITEM_SLOT_ICLOTHING) && !(w_uniform.item_flags & 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.get_examine_string(user)][accessory_message]." - //head - if(head && !(obscured & ITEM_SLOT_HEAD) && !(head.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head." - //suit/armor - if(wear_suit && !(wear_suit.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_is] wearing [wear_suit.get_examine_string(user)]." - //suit/armor storage - if(s_store && !(obscured & ITEM_SLOT_SUITSTORE) && !(s_store.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_is] carrying [s_store.get_examine_string(user)] on [t_his] [wear_suit.name]." - //back - if(back && !(back.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back." - - //Hands - for(var/obj/item/held_thing in held_items) - if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM)) - continue - . += "[t_He] [t_is] holding [held_thing.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]." - - //gloves - if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands." - else if(GET_ATOM_BLOOD_DNA_LENGTH(src)) - if(num_hands) - . += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a "]blood-stained hand[num_hands > 1 ? "s" : ""]!") - - //handcuffed? - if(handcuffed) - if(istype(handcuffed, /obj/item/restraints/handcuffs/cable)) - . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] restrained with cable!") - else - . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] handcuffed!") - - //belt - if(belt && !(belt.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_has] [belt.get_examine_string(user)] about [t_his] waist." - - //shoes - if(shoes && !(obscured & ITEM_SLOT_FEET) && !(shoes.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_is] wearing [shoes.get_examine_string(user)] on [t_his] feet." - - //mask - if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !(wear_mask.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_has] [wear_mask.get_examine_string(user)] on [t_his] face." - - if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !(wear_neck.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck." - - //eyes - if(!(obscured & ITEM_SLOT_EYES) ) - if(glasses && !(glasses.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes." - else if(HAS_TRAIT(src, TRAIT_UNNATURAL_RED_GLOWY_EYES)) - . += "[t_His] eyes are glowing with an unnatural red aura!" - else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES)) - . += "[t_His] eyes are bloodshot!" - - //ears - if(ears && !(obscured & ITEM_SLOT_EARS) && !(ears.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_has] [ears.get_examine_string(user)] on [t_his] ears." - - //ID - if(wear_id && !(wear_id.item_flags & EXAMINE_SKIP)) - . += "[t_He] [t_is] wearing [wear_id.get_examine_string(user)]." - - . += wear_id.get_id_examine_strings(user) - - //Status effects - var/list/status_examines = get_status_effect_examinations() - if (length(status_examines)) - . += status_examines - - var/appears_dead = FALSE - var/just_sleeping = FALSE - - if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) - appears_dead = TRUE - - var/obj/item/clothing/glasses/G = get_item_by_slot(ITEM_SLOT_EYES) - var/are_we_in_weekend_at_bernies = G?.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) - 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() - - if(get_bodypart(BODY_ZONE_HEAD) && !get_organ_by_type(/obj/item/organ/internal/brain)) - . += span_deadsay("It appears that [t_his] brain is missing...") - - var/list/msg = list() - - var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) - var/list/disabled = list() - for(var/X in bodyparts) - var/obj/item/bodypart/body_part = X - if(body_part.bodypart_disabled) - disabled += body_part - missing -= body_part.body_zone - for(var/obj/item/I in body_part.embedded_objects) - if(I.is_embed_harmless()) - msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] stuck to [t_his] [body_part.name]!\n" - else - msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] embedded in [t_his] [body_part.name]!\n" - - for(var/i in body_part.wounds) - var/datum/wound/iter_wound = i - msg += "[iter_wound.get_examine_description(user)]\n" - - for(var/X in disabled) - var/obj/item/bodypart/body_part = X - 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 - msg += "[capitalize(t_his)] [body_part.name] is [damage_text]!\n" - - //stores missing limbs - var/l_limbs_missing = 0 - var/r_limbs_missing = 0 - for(var/t in missing) - if(t == BODY_ZONE_HEAD) - msg += "[t_His] [parse_zone(t)] is missing!\n" - continue - if(t == BODY_ZONE_L_ARM || t == BODY_ZONE_L_LEG) - l_limbs_missing++ - else if(t == BODY_ZONE_R_ARM || t == BODY_ZONE_R_LEG) - r_limbs_missing++ - - msg += "[capitalize(t_his)] [parse_zone(t)] is missing!\n" - - if(l_limbs_missing >= 2 && r_limbs_missing == 0) - msg += "[t_He] look[p_s()] all right now.\n" - else if(l_limbs_missing == 0 && r_limbs_missing >= 2) - msg += "[t_He] really keep[p_s()] to the left.\n" - else if(l_limbs_missing >= 2 && r_limbs_missing >= 2) - msg += "[t_He] [p_do()]n't seem all there.\n" - - 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) - msg += "[t_He] [t_has] minor [damage_desc[BRUTE]].\n" - else if(temp < 50) - msg += "[t_He] [t_has] moderate [damage_desc[BRUTE]]!\n" - else - msg += "[t_He] [t_has] severe [damage_desc[BRUTE]]!\n" - - temp = getFireLoss() - if(temp) - if(temp < 25) - msg += "[t_He] [t_has] minor [damage_desc[BURN]].\n" - else if (temp < 50) - msg += "[t_He] [t_has] moderate [damage_desc[BURN]]!\n" - else - msg += "[t_He] [t_has] severe [damage_desc[BURN]]!\n" - - if(has_status_effect(/datum/status_effect/fire_handler/fire_stacks)) - msg += "[t_He] [t_is] covered in something flammable.\n" - if(has_status_effect(/datum/status_effect/fire_handler/wet_stacks)) - msg += "[t_He] look[p_s()] a little soaked.\n" - - - if(pulledby?.grab_state) - msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n" - - if(nutrition < NUTRITION_LEVEL_STARVING - 50) - msg += "[t_He] [t_is] severely malnourished.\n" - else if(nutrition >= NUTRITION_LEVEL_FAT) - if(user.nutrition < NUTRITION_LEVEL_STARVING - 50) - msg += "[t_He] [t_is] plump and delicious looking - Like a fat little piggy. A tasty piggy.\n" - else - msg += "[t_He] [t_is] quite chubby.\n" - switch(disgust) - if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS) - msg += "[t_He] look[p_s()] a bit grossed out.\n" - if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED) - msg += "[t_He] look[p_s()] really grossed out.\n" - if(DISGUST_LEVEL_DISGUSTED to INFINITY) - msg += "[t_He] look[p_s()] extremely disgusted.\n" - - var/apparent_blood_volume = blood_volume - if(HAS_TRAIT(src, TRAIT_USES_SKINTONES) && (skin_tone == "albino")) - apparent_blood_volume -= 150 // enough to knock you down one tier - switch(apparent_blood_volume) - if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - msg += "[t_He] [t_has] pale skin.\n" - if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) - msg += "[t_He] look[p_s()] like pale death.\n" - if(-INFINITY to BLOOD_VOLUME_BAD) - msg += "[span_deadsay("[t_He] resemble[p_s()] a crushed, empty juice pouch.")]\n" - - 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.get_modified_bleed_rate()) - bleeding_limbs += body_part - if(body_part.grasped_by) - grasped_limbs += body_part - - var/num_bleeds = LAZYLEN(bleeding_limbs) - - var/list/bleed_text - if(appears_dead) - bleed_text = list("Blood is visible in [t_his] open") - else - bleed_text = list("[t_He] [t_is] bleeding from [t_his]") - - switch(num_bleeds) - if(1 to 2) - bleed_text += " [bleeding_limbs[1].name][num_bleeds == 2 ? " and [bleeding_limbs[2].name]" : ""]" - if(3 to INFINITY) - for(var/i in 1 to (num_bleeds - 1)) - var/obj/item/bodypart/body_part = bleeding_limbs[i] - bleed_text += " [body_part.name]," - bleed_text += " and [bleeding_limbs[num_bleeds].name]" - - if(appears_dead) - bleed_text += ", but it has pooled and is not flowing.\n" - else - if(reagents.has_reagent(/datum/reagent/toxin/heparin, needs_metabolizing = TRUE)) - bleed_text += " incredibly quickly" - - bleed_text += "!\n" - - for(var/i in grasped_limbs) - var/obj/item/bodypart/grasped_part = i - bleed_text += "[t_He] [t_is] holding [t_his] [grasped_part.name] to slow the bleeding!\n" - - msg += bleed_text.Join() - - if(reagents.has_reagent(/datum/reagent/teslium, needs_metabolizing = TRUE)) - msg += "[t_He] [t_is] emitting a gentle blue glow!\n" - - if(just_sleeping) - msg += "[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.\n" - - if(!appears_dead) - var/mob/living/living_user = user - if(src != user) - if(HAS_TRAIT(user, TRAIT_EMPATH)) - if (combat_mode) - msg += "[t_He] seem[p_s()] to be on guard.\n" - if (getOxyLoss() >= 10) - msg += "[t_He] seem[p_s()] winded.\n" - if (getToxLoss() >= 10) - msg += "[t_He] seem[p_s()] sickly.\n" - if(mob_mood.sanity <= SANITY_DISTURBED) - msg += "[t_He] seem[p_s()] distressed.\n" - living_user.add_mood_event("empath", /datum/mood_event/sad_empath, src) - if(is_blind()) - msg += "[t_He] appear[p_s()] to be staring off into space.\n" - if (HAS_TRAIT(src, TRAIT_DEAF)) - msg += "[t_He] appear[p_s()] to not be responding to noises.\n" - if (bodytemperature > dna.species.bodytemp_heat_damage_limit) - msg += "[t_He] [t_is] flushed and wheezing.\n" - if (bodytemperature < dna.species.bodytemp_cold_damage_limit) - msg += "[t_He] [t_is] shivering.\n" - - msg += "" - - if(HAS_TRAIT(user, TRAIT_SPIRITUAL) && mind?.holy_role) - msg += "[t_He] [t_has] a holy aura about [t_him].\n" - living_user.add_mood_event("religious_comfort", /datum/mood_event/religiously_comforted) - - switch(stat) - if(UNCONSCIOUS, HARD_CRIT) - msg += "[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.\n" - if(SOFT_CRIT) - msg += "[t_He] [t_is] barely conscious.\n" - if(CONSCIOUS) - if(HAS_TRAIT(src, TRAIT_DUMB)) - msg += "[t_He] [t_has] a stupid expression on [t_his] face.\n" - if(get_organ_by_type(/obj/item/organ/internal/brain) && isnull(ai_controller)) - if(!key) - msg += "[span_deadsay("[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.")]\n" - else if(!client) - msg += "[span_deadsay("[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon.")]\n" - - var/scar_severity = 0 - for(var/i in all_scars) - var/datum/scar/S = i - if(S.is_visible(user)) - scar_severity += S.severity - - switch(scar_severity) - if(1 to 4) - msg += "[span_tinynoticeital("[t_He] [t_has] visible scarring, you can look again to take a closer look...")]\n" - if(5 to 8) - msg += "[span_smallnoticeital("[t_He] [t_has] several bad scars, you can look again to take a closer look...")]\n" - if(9 to 11) - msg += "[span_notice("[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...")]\n" - if(12 to INFINITY) - msg += "[span_notice("[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...")]\n" - msg += "" // closes info class - - if (length(msg)) - . += span_warning("[msg.Join("")]") - - var/trait_exam = common_trait_examine() - if (!isnull(trait_exam)) - . += trait_exam - - if(isliving(user)) - var/mob/living/privacy_invader = user - if(HAS_MIND_TRAIT(privacy_invader, TRAIT_MORBID)) - if(HAS_TRAIT(src, TRAIT_DISSECTED)) - msg += "[span_notice("[t_He] appears to have been dissected. Useless for examination... for now.")]\n" - if(HAS_TRAIT(src, TRAIT_SURGICALLY_ANALYZED)) - msg += "[span_notice("A skilled hand has mapped this one's internal intricacies. It will be far easier to perform future experimentations upon [t_him]. Exquisite.")]\n" - if(HAS_MIND_TRAIT(privacy_invader, TRAIT_EXAMINE_FITNESS)) - . += compare_fitness(user) - - var/perpname = get_face_name(get_id_name("")) - if(perpname && (HAS_TRAIT(user, TRAIT_SECURITY_HUD) || HAS_TRAIT(user, TRAIT_MEDICAL_HUD))) - var/datum/record/crew/target_record = find_record(perpname) - if(target_record) - . += "Rank: [target_record.rank]\n\[Front photo\]\[Side photo\]" - if(HAS_TRAIT(user, TRAIT_MEDICAL_HUD)) - var/cyberimp_detect - for(var/obj/item/organ/internal/cyberimp/cyberimp in organs) - if(IS_ROBOTIC_ORGAN(cyberimp) && !(cyberimp.organ_flags & ORGAN_HIDDEN)) - cyberimp_detect += "[!cyberimp_detect ? "[cyberimp.get_examine_string(user)]" : ", [cyberimp.get_examine_string(user)]"]" - if(cyberimp_detect) - . += "Detected cybernetic modifications:" - . += "[cyberimp_detect]" - if(target_record) - var/health_record = target_record.physical_status - . += "\[[health_record]\]" - health_record = target_record.mental_status - . += "\[[health_record]\]" - target_record = find_record(perpname) - if(target_record) - . += "\[Medical evaluation\]
" - . += "\[See quirks\]" - - if(HAS_TRAIT(user, TRAIT_SECURITY_HUD)) - if((user.stat == CONSCIOUS || isobserver(user)) && user != src) - //|| !user.canmove || user.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at. - var/wanted_status = WANTED_NONE - var/security_note = "None." - - target_record = find_record(perpname) - 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: \[[wanted_status]\]" - else - . += "Criminal status: [wanted_status]" - . += "Important Notes: [security_note]" - . += "Security record: \[View\]" - if(ishuman(user)) - . += jointext(list("\[Add citation\]", - "\[Add crime\]", - "\[Add note\]"), "") - if(isobserver(user)) - . += span_info("\nQuirks: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]") - . += "
" - - SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, 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("\n") - -/mob/living/carbon/human/examine_more(mob/user) - . = ..() - if ((wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (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" - . += list(span_notice("[p_They()] appear[p_s()] to be [age_text].")) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index cf73704600c7f..5f008af504590 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -88,6 +88,59 @@ /mob/living/carbon/human/Topic(href, href_list) + if(href_list["see_id"]) + var/mob/viewer = usr + var/can_see_still = (viewer in viewers(src)) + + var/obj/item/card/id/id = wear_id?.GetID() + var/same_id = id && (href_list["id_ref"] == REF(id) || href_list["id_name"] == id.registered_name) + if(!same_id && can_see_still) + to_chat(viewer, span_notice("[p_They()] [p_are()] no longer wearing that ID card.")) + return + + var/viable_time = can_see_still ? 3 MINUTES : 1 MINUTES // assuming 3min is the length of a hop line visit - give some leeway if they're still in sight + if(!same_id || (text2num(href_list["examine_time"]) + viable_time) < world.time) + to_chat(viewer, span_notice("You don't have that good of a memory. Examine [p_them()] again.")) + return + if(HAS_TRAIT(src, TRAIT_UNKNOWN)) + to_chat(viewer, span_notice("You can't make out that ID anymore.")) + return + if(get_dist(viewer, src) > ID_EXAMINE_DISTANCE + 1) // leeway + to_chat(viewer, span_notice("You can't make out that ID from here.")) + return + + var/id_name = id.registered_name + var/id_age = id.registered_age + var/id_job = id.assignment + // Should probably be recorded on the ID, but this is easier (albiet more restrictive) on chameleon ID users + var/datum/record/crew/record = find_record(id_name) + var/id_blood_type = record?.blood_type + var/id_gender = record?.gender + var/id_species = record?.species + var/id_icon = jointext(id.get_id_examine_strings(viewer), "") + // Fill in some blanks for chameleon IDs to maintain the illusion of a real ID + if(istype(id, /obj/item/card/id/advanced/chameleon)) + id_gender ||= gender + id_species ||= dna.species.name + id_blood_type ||= dna.blood_type + + var/id_examine = span_slightly_larger(separator_hr("This is [src]'s ID card.")) + id_examine += "
" + id_examine += "[id_icon]" + id_examine += "
" + id_examine += jointext(list( + "• Name: [id_name || "Unknown"]", + "• Job: [id_job || "Unassigned"]", + "• Age: [id_age || "Unknown"]", + "• Gender: [id_gender || "Unknown"]", + "• Blood Type: [id_blood_type || "?"]", + "• Species: [id_species || "Unknown"]", + ), "
") + id_examine += "
" // container + id_examine += "
" // text + + to_chat(viewer, examine_block(span_info(id_examine))) + ///////HUDs/////// if(href_list["hud"]) if(!ishuman(usr) && !isobserver(usr)) @@ -97,7 +150,7 @@ if(!HAS_TRAIT(human_or_ghost_user, TRAIT_SECURITY_HUD) && !HAS_TRAIT(human_or_ghost_user, TRAIT_MEDICAL_HUD)) return if((text2num(href_list["examine_time"]) + 1 MINUTES) < world.time) - to_chat(human_or_ghost_user, "[span_notice("It's too late to use this now!")]") + to_chat(human_or_ghost_user, span_notice("It's too late to use this now!")) return var/datum/record/crew/target_record = find_record(perpname) if(href_list["photo_front"] || href_list["photo_side"]) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index c561ca28999f1..84b2aa8534d7b 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -4,28 +4,6 @@ return FALSE return TRUE -///returns a list of "damtype" => damage description based off of which bodypart description is most common -///used in human examines -/mob/living/carbon/human/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 - //gets assignment from ID or ID inside PDA or PDA itself //Useful when player do something with computers /mob/living/carbon/human/proc/get_assignment(if_no_id = "No id", if_no_job = "No job", hand_first = TRUE) @@ -76,10 +54,10 @@ fake_name = "[fake_name]/[id_name]" else fake_name = id_name - if (HAS_TRAIT(src, TRAIT_UNKNOWN) || (!face_name && !id_name)) + if (HAS_TRAIT(src, TRAIT_UNKNOWN) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN) || (!face_name && !id_name)) fake_name = "Unknown" return "[real_name][fake_name ? " (as [fake_name])" : ""]" - if(HAS_TRAIT(src, TRAIT_UNKNOWN)) + if(HAS_TRAIT(src, TRAIT_UNKNOWN) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) return "Unknown" if(face_name) if(add_id_name && id_name && (id_name != face_name)) @@ -89,8 +67,12 @@ return id_name return "Unknown" -//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when Fluacided or when updating a human's name variable -/mob/living/carbon/human/proc/get_face_name(if_no_face = "Unknown") +/// Returns "Unknown" if facially disfigured and real_name if not. +/// Useful for setting name when Fluacided or when updating a human's name variable +/mob/living/carbon/proc/get_face_name(if_no_face = "Unknown") + return real_name + +/mob/living/carbon/human/get_face_name(if_no_face = "Unknown") if(HAS_TRAIT(src, TRAIT_UNKNOWN)) return if_no_face //We're Unknown, no face information for you for(var/obj/item/worn_item in get_equipped_items()) @@ -104,7 +86,10 @@ //gets name from ID or PDA itself, ID inside PDA doesn't matter //Useful when player is being seen by other mobs -/mob/living/carbon/human/proc/get_id_name(if_no_id = "Unknown") +/mob/living/carbon/proc/get_id_name(if_no_id = "Unknown") + return + +/mob/living/carbon/human/get_id_name(if_no_id = "Unknown") var/obj/item/storage/wallet/wallet = wear_id var/obj/item/modular_computer/pda = wear_id var/obj/item/card/id/id = wear_id @@ -247,18 +232,6 @@ WRITE_FILE(F["scar[char_index]-[scar_index]"], sanitize_text(valid_scars)) WRITE_FILE(F["current_scar_index"], sanitize_integer(scar_index)) -///Returns death message for mob examine text -/mob/living/carbon/human/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 - if(get_organ_by_type(/obj/item/organ/internal/brain) && (client || HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE) || (ghost?.can_reenter_corpse && ghost?.client))) - return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life...") - else - return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life and [t_his] soul has departed...") - ///copies over clothing preferences like underwear to another human /mob/living/carbon/human/proc/copy_clothing_prefs(mob/living/carbon/human/destination) destination.underwear = underwear diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index e838f202af47c..13dc53ff2a840 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -1,5 +1,5 @@ /mob/living/silicon/ai/examine(mob/user) - . = list("This is [icon2html(src, user)] [src]!") + . = list() if(stat == DEAD) . += span_deadsay("It appears to be powered-down.") . += span_notice("Its floor bolts are [is_anchored ? "tightened" : "loose"].") @@ -26,9 +26,5 @@ . += "The wireless networking light is blinking." else if (!shunted && !client) . += "[src]Core.exe has stopped responding! NTOS is searching for a solution to the problem..." - . += "" . += ..() - -/mob/living/silicon/ai/get_examine_string(mob/user, thats = FALSE) - return null diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 53869cf817fd0..3091881c56f51 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -1,10 +1,10 @@ /mob/living/silicon/robot/examine(mob/user) - . = list("This is [icon2html(src, user)] [src]!") + . = list() if(desc) . += "[desc]" var/model_name = model ? "\improper [model.name]" : "\improper Default" - . += "\nIt is currently \a \"[span_bold("[model_name]")]\"-type cyborg.\n" + . += "It is currently \a [model_name]-type cyborg." var/obj/act_module = get_active_held_item() if(act_module) @@ -14,13 +14,13 @@ if (getBruteLoss() < maxHealth*0.5) . += span_warning("It looks slightly dented.") else - . += span_warning("It looks severely dented!") + . += span_boldwarning("It looks severely dented!") if (getFireLoss() || getToxLoss()) var/overall_fireloss = getFireLoss() + getToxLoss() if (overall_fireloss < maxHealth * 0.5) . += span_warning("It looks slightly charred.") else - . += span_warning("It looks severely burnt and heat-warped!") + . += span_boldwarning("It looks severely burnt and heat-warped!") if (health < -maxHealth*0.5) . += span_warning("It looks barely operational.") if (fire_stacks < 0) @@ -46,9 +46,5 @@ . += span_warning("It doesn't seem to be responding.") if(DEAD) . += span_deadsay("It looks like its system is corrupted and requires a reset.") - . += "" . += ..() - -/mob/living/silicon/robot/get_examine_string(mob/user, thats = FALSE) - return null diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index f321a8396a379..8cffe1b766d60 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -523,29 +523,29 @@ return face_atom(examinify) - var/list/result + var/result_combined if(client) LAZYINITLIST(client.recent_examines) - var/ref_to_atom = ref(examinify) + var/ref_to_atom = REF(examinify) var/examine_time = client.recent_examines[ref_to_atom] if(examine_time && (world.time - examine_time < EXAMINE_MORE_WINDOW)) - result = examinify.examine_more(src) + var/list/result = examinify.examine_more(src) if(!length(result)) result += span_notice("You examine [examinify] closer, but find nothing of interest...") + result_combined = jointext(result, "
") + else - result = examinify.examine(src) - SEND_SIGNAL(src, COMSIG_MOB_EXAMINING, examinify, result) client.recent_examines[ref_to_atom] = world.time // set to when we last normal examine'd them addtimer(CALLBACK(src, PROC_REF(clear_from_recent_examines), ref_to_atom), RECENT_EXAMINE_MAX_WINDOW) handle_eye_contact(examinify) - else - result = examinify.examine(src) // if a tree is examined but no client is there to see it, did the tree ever really exist? - if(result.len) - for(var/i in 1 to (length(result) - 1)) - result[i] += "\n" + if(!result_combined) + var/list/result = examinify.examine(src) + var/atom_title = examinify.examine_title(src, thats = TRUE) + SEND_SIGNAL(src, COMSIG_MOB_EXAMINING, examinify, result) + result_combined = (atom_title ? "[span_slightly_larger(separator_hr("[atom_title]."))]" : "") + jointext(result, "
") - to_chat(src, examine_block("[result.Join()]")) + to_chat(src, examine_block(span_infoplain(result_combined))) SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, examinify) /mob/proc/blind_examine_check(atom/examined_thing) @@ -641,7 +641,7 @@ // check to see if their face is blocked or, if not, a signal blocks it if(examined_mob.is_face_visible() && SEND_SIGNAL(src, COMSIG_MOB_EYECONTACT, examined_mob, TRUE) != COMSIG_BLOCK_EYECONTACT) var/msg = span_smallnotice("You make eye contact with [examined_mob].") - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), src, msg), 3) // so the examine signal has time to fire and this will print after + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), src, msg), 0.3 SECONDS) // so the examine signal has time to fire and this will print after if(!imagined_eye_contact && is_face_visible() && SEND_SIGNAL(examined_mob, COMSIG_MOB_EYECONTACT, src, FALSE) != COMSIG_BLOCK_EYECONTACT) var/msg = span_smallnotice("[src] makes eye contact with you.") diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index a3fceb0493e8c..6cb7ad84902f3 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -402,15 +402,6 @@ /mob/proc/can_hear() return !HAS_TRAIT(src, TRAIT_DEAF) -/** - * Examine text for traits shared by multiple types. - * - * I wish examine was less copypasted. (oranges say, be the change you want to see buddy) - */ -/mob/proc/common_trait_examine() - if(HAS_TRAIT(src,TRAIT_HUSK)) - . += span_warning("This body has been reduced to a grotesque husk.") - /** * Get the list of keywords for policy config * diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 03503ac36d864..4f4a8fcc17016 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -251,7 +251,7 @@ /obj/item/modular_computer/get_id_examine_strings(mob/user) . = ..() if(computer_id_slot) - . += "\The [src] is displaying [computer_id_slot]." + . += "[src] is displaying [computer_id_slot]:" . += computer_id_slot.get_id_examine_strings(user) /obj/item/modular_computer/proc/print_text(text_to_print, paper_title = "") diff --git a/tgstation.dme b/tgstation.dme index 321bf996e6e21..6bf6b7550889a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5169,7 +5169,6 @@ #include "code\modules\mob\living\carbon\human\death.dm" #include "code\modules\mob\living\carbon\human\dummy.dm" #include "code\modules\mob\living\carbon\human\emote.dm" -#include "code\modules\mob\living\carbon\human\examine.dm" #include "code\modules\mob\living\carbon\human\human.dm" #include "code\modules\mob\living\carbon\human\human_context.dm" #include "code\modules\mob\living\carbon\human\human_defense.dm" diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index efd8b7df9e642..25f4eabd45d86 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -416,6 +416,15 @@ em { color: #c51e1e; } +.tinydanger { + color: #c51e1e; + font-size: 85%; +} + +.smalldanger { + color: #c51e1e; + font-size: 90%; +} .warning { color: #c51e1e; font-style: italic; @@ -760,6 +769,10 @@ em { font-size: 60%; } +.slightly_larger { + font-size: 115%; +} + .big { font-size: 185%; } @@ -950,7 +963,7 @@ em { } .ml-3 { - margin-left: 3em; + margin-left: 2.5em; } .examine_block { @@ -965,6 +978,46 @@ em { border-bottom: 1px dashed #fff; } +// Provides a horizontal bar with text in the middle +// I got this off stackoverflow +.separator { + display: flex; + align-items: center; + text-align: center; +} + +.separator::before, +.separator::after { + content: ''; + flex: 1; + border-bottom: 1px solid #a4bad6; +} + +.separator:not(:empty)::before { + margin-right: 0.25em; +} + +.separator:not(:empty)::after { + margin-left: 0.25em; +} + +// Used to display images besides text +.img_by_text_container { + display: flex; + align-items: center; + flex-wrap: wrap; +} + +.img_by_text_container img { + width: 3em; // a css guru can probably dehardcode this later + height: auto; + margin-right: 12px; +} + +.img_by_text_container .img_text { + flex-grow: 1; +} + $alert-stripe-colors: ( 'default': #00283a, 'green': #003d00, diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index bb0673869a0cb..b9caf24ddd81f 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -787,6 +787,10 @@ h2.alert { font-size: 60%; } +.slightly_larger { + font-size: 115%; +} + .big { font-size: 185%; } @@ -992,6 +996,46 @@ h2.alert { border-bottom: 1px dashed #000; } +// Provides a horizontal bar with text in the middle +// I got this off stackoverflow +.separator { + display: flex; + align-items: center; + text-align: center; +} + +.separator::before, +.separator::after { + content: ''; + flex: 1; + border-bottom: 1px solid #111a27; +} + +.separator:not(:empty)::before { + margin-right: 0.25em; +} + +.separator:not(:empty)::after { + margin-left: 0.25em; +} + +// Used to display images besides text +.img_by_text_container { + display: flex; + align-items: center; + flex-wrap: wrap; +} + +.img_by_text_container img { + width: 2.5em; // a css guru can probably dehardcode this later + height: auto; + margin-right: 12px; +} + +.img_by_text_container .img_text { + flex-grow: 1; +} + $alert-stripe-colors: ( 'default': #b3bfff, 'green': #adffad,