Files
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

236 lines
9.6 KiB
Plaintext

/atom
/// If non-null, overrides a/an/some in all cases
var/article
/// Text that appears preceding the name in [/atom/proc/examine_title]
var/examine_thats = "That's"
/**
* Called when a mob examines this atom: [/mob/verb/examinate]
*
* Default behaviour is to get the name and icon of the object and its reagents where
* the [TRANSPARENT] flag is set on the reagents holder
*
* Produces a signal [COMSIG_ATOM_EXAMINE], for modifying the list returned from this proc
*/
/atom/proc/examine(mob/user)
. = list()
. += get_name_chaser(user)
if(desc)
. += "<i>[desc]</i>"
var/list/tags_list = examine_tags(user)
var/list/post_descriptor = examine_post_descriptor(user)
var/post_desc_string = length(post_descriptor) ? " [jointext(post_descriptor, " ")]" : ""
if (length(tags_list))
var/tag_string = list()
for (var/atom_tag in tags_list)
tag_string += (isnull(tags_list[atom_tag]) ? atom_tag : span_tooltip(tags_list[atom_tag], atom_tag))
// some regex to ensure that we don't add another "and" if the final element's main text (not tooltip) has one
tag_string = english_list(tag_string, and_text = (findtext(tag_string[length(tag_string)], regex(@">.*?and .*?<"))) ? " " : " and ")
. += "[p_They()] [p_are()] a [tag_string] [examine_descriptor(user)][post_desc_string]."
else if(post_desc_string)
. += "[p_They()] [p_are()] a [examine_descriptor(user)][post_desc_string]."
if(reagents)
var/user_sees_reagents = user.can_see_reagents()
var/reagent_sigreturn = SEND_SIGNAL(src, COMSIG_ATOM_REAGENT_EXAMINE, user, ., user_sees_reagents)
if(!(reagent_sigreturn & STOP_GENERIC_REAGENT_EXAMINE))
if(reagents.flags & TRANSPARENT)
if(reagents.total_volume)
. += "It contains <b>[reagents.total_volume]</b> units of various reagents[user_sees_reagents ? ":" : "."]"
if(user_sees_reagents || (reagent_sigreturn & ALLOW_GENERIC_REAGENT_EXAMINE)) //Show each individual reagent for detailed examination
for(var/datum/reagent/current_reagent as anything in reagents.reagent_list)
. += "&bull; [round(current_reagent.volume, CHEMICAL_VOLUME_ROUNDING)] units of [current_reagent.name]"
if(reagents.is_reacting)
. += span_warning("It is currently reacting!")
. += span_notice("The solution's pH is [round(reagents.ph, 0.01)] and has a temperature of [reagents.chem_temp]K.")
else
. += "It contains:<br>Nothing."
else if(reagents.flags & AMOUNT_VISIBLE)
if(reagents.total_volume)
. += span_notice("It has [reagents.total_volume] unit\s left.")
else
. += span_danger("It's empty.")
if(HAS_TRAIT(user, TRAIT_KEEN_NOSE))
var/sniff_text = get_sniff_examine(user)
if(sniff_text)
. += sniff_text
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, user, .)
/// Returns an examine string describing what the contents of this atom smell like
/atom/proc/get_sniff_examine(mob/living/carbon/sniffer)
if(!istype(sniffer) || HAS_TRAIT(sniffer, TRAIT_ANOSMIA))
return
if(!is_open_container() || !reagents?.total_volume)
return
if(!sniffer.is_holding(src))
return
if(!sniffer.get_bodypart(BODY_ZONE_HEAD)) // Need a nose to smell
return
if(sniffer.is_mouth_covered())
return span_warning("You can't get a whiff of [src] with your face covered.")
var/smell_message = generate_reagents_taste_message(reagents.reagent_list, sniffer, 10)
return span_notice("You catch a whiff of [src]. It smells like [smell_message].")
/**
* A list of "tags" displayed after atom's description in examine.
* This should return an assoc list of tags -> tooltips for them. If item is null, then no tooltip is assigned.
*
* * TGUI tooltips (not the main text) in chat cannot use HTML stuff at all, so
* trying something like `<b><big>ffff</big></b>` will not work for tooltips.
*
* For example:
* ```byond
* . = list()
* .["small"] = "It is a small item."
* .["fireproof"] = "It is made of fire-retardant materials."
* .["and conductive"] = "It's made of conductive materials and whatnot. Blah blah blah." // having "and " in the end tag's main text/key works too!
* ```
* will result in
*
* It is a *small*, *fireproof* *and conductive* item.
*
* where "item" is pulled from [/atom/proc/examine_descriptor]
*/
/atom/proc/examine_tags(mob/user)
. = list()
if(abstract_type == type)
.[span_hypnophrase("abstract")] = "This is an abstract concept, you should report this to a strange entity called GITHUB!"
if(resistance_flags & INDESTRUCTIBLE)
.["indestructible"] = "It is extremely robust! It'll probably withstand anything that could happen to it!"
else
if(resistance_flags & LAVA_PROOF)
.["lava-proof"] = "It is made of an extremely heat-resistant material, it'd probably be able to withstand lava!"
if(resistance_flags & (ACID_PROOF | UNACIDABLE))
.["acid-proof"] = "It looks pretty robust! It'd probably be able to withstand acid!"
if(resistance_flags & FREEZE_PROOF)
.["freeze-proof"] = "It is made of cold-resistant materials."
if(resistance_flags & FIRE_PROOF)
.["fire-proof"] = "It is made of fire-retardant materials."
if(resistance_flags & SHUTTLE_CRUSH_PROOF)
.["crush-proof"] = "It is extremely solid. It should be able to withstand being run over by a shuttle!"
if(resistance_flags & BOMB_PROOF)
.["bomb-proof"] = "It looks like it could survive an explosion!"
if(resistance_flags & FLAMMABLE)
.["flammable"] = "It looks like it could easily catch on fire."
if(flags_1 & HOLOGRAM_1)
.["holographic"] = "It looks like a hologram."
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE_TAGS, user, .)
/// What this atom should be called in examine tags
/atom/proc/examine_descriptor(mob/user)
return "object"
/// Returns a list of strings to be displayed after the descriptor
/atom/proc/examine_post_descriptor(mob/user)
. = list()
if(!custom_materials)
return
var/mats_list = list()
for(var/custom_material in custom_materials)
var/datum/material/current_material = SSmaterials.get_material(custom_material)
mats_list += span_tooltip("It is made out of [current_material.name].", current_material.name)
. += "made of [english_list(mats_list)]"
/**
* Called when a mob examines (shift click or verb) this atom twice (or more) within EXAMINE_MORE_WINDOW (default 1 second)
*
* This is where you can put extra information on something that may be superfluous or not important in critical gameplay
* moments, while allowing people to manually double-examine to take a closer look
*
* Produces a signal [COMSIG_ATOM_EXAMINE_MORE]
*/
/atom/proc/examine_more(mob/user)
SHOULD_CALL_PARENT(TRUE)
RETURN_TYPE(/list)
. = list()
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE_MORE, user, .)
SEND_SIGNAL(user, COMSIG_MOB_EXAMINING_MORE, src, .)
if (!length(custom_materials) || (material_flags & MATERIAL_NO_DESCRIPTORS) || !HAS_TRAIT(user, TRAIT_RESEARCH_SCANNER))
return
for (var/datum/material/material as anything in custom_materials)
var/list/material_string = list()
for (var/prop_id in material.mat_properties)
var/datum/material_property/property = SSmaterials.properties[prop_id]
var/prop_value = material.get_property(prop_id)
if (isnull(prop_value)) // Error?
continue
var/descriptor = property?.get_descriptor(prop_value)
var/tooltip_hint = property?.get_tooltip(prop_value)
if (descriptor) // Overriden derivative property?
material_string += span_tooltip("[property]: [tooltip_hint]", descriptor)
if (length(material_string))
. += span_info("[capitalize(material.name)] is [english_list(material_string)].")
/**
* Get the name of this object for examine
*
* You can override what is returned from this proc by registering to listen for the
* [COMSIG_ATOM_GET_EXAMINE_NAME] signal
*/
/atom/proc/get_examine_name(mob/user)
var/list/override = list(article, null, "<em>[get_visible_name()]</em>")
SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override)
if(!isnull(override[EXAMINE_POSITION_ARTICLE]))
override -= null // IF there is no "before", don't try to join it
return jointext(override, " ")
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]"
/mob/living/get_examine_name(mob/user)
var/visible_name = get_visible_name()
var/list/name_override = list(visible_name)
if(SEND_SIGNAL(user, COMSIG_LIVING_PERCEIVE_EXAMINE_NAME, src, visible_name, name_override) & COMPONENT_EXAMINE_NAME_OVERRIDEN)
return name_override[1]
return visible_name
/// 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] ":""]<em>[get_examine_name(user)]</em>"
/**
* Returns an extended list of examine strings for any contained ID cards.
*
* Arguments:
* * user - The user who is doing the examining.
*/
/atom/proc/get_id_examine_strings(mob/user)
. = list()
///Used to insert text after the name but before the description in examine()
/atom/proc/get_name_chaser(mob/user, list/name_chaser = list())
return name_chaser
/**
* Used by mobs to determine the name for someone wearing a mask, or with a disfigured or missing face.
* By default just returns the atom's name.
*
* * add_id_name - If TRUE, ID information such as honorifics or name (if mismatched) are appended
* * force_real_name - If TRUE, will always return real_name and add (as face_name/id_name) if it doesn't match their appearance
*/
/atom/proc/get_visible_name(add_id_name = TRUE, force_real_name = FALSE)
return name