Files
Aurora.3/code/game/atom/atom_examine.dm
Batrachophreno 7d058fc613 Jukebox TGUI update, Earphones autoplay, sound keys refactor (#21630)
V2 of [previous music playing
PR](https://github.com/Aurorastation/Aurora.3/pull/21466). TLDR no
longer uses the connect_range component for implementation because it
turned out a bit too inflexible for overlapping music players.

Removes a NanoUI template for the [TGUI
update](https://github.com/Aurorastation/Aurora.3/pull/21046).

New changelog:
  - refactor: "Ported Jukebox's NanoUI interface to TGUI."
- refactor: "Ported Jukebox audio playing functionality to a component."
- refactor: "Sound keys refactored from singletons to datums, along with
larger breakout of sound.dm to allow for easier SFX updates in future."
  - code_imp: "Expanded track datums to include track lengths."
- code_imp: "Reorganized music file folders for more intuitive access."
  - rscadd: "Earphone status feedback text now includes track length."
  - rscadd: "Added autoplay functionality to earphones."
- bugfix: "Fixed earphones' 'Previous Song' verb not sending you to the
end of the playlist when used while the first track is selected."
- bugfix: "Fixed gain adjustment for 'Konyang-1' (-23 dB -> standard
-9.8 dB)."
- bugfix: "Fixed y-offset of audioconsole-running overlay animation to
line up with the actual screen."

---------

Co-authored-by: VMSolidus <evilexecutive@gmail.com>
2026-01-22 16:54:40 +00:00

315 lines
12 KiB
Plaintext

/**
* Called when a mob examines (shift click or verb) this atom
*
* Any overrides must either call parent (preferable), or return `TRUE` and set the `SHOULD_CALL_PARENT(FALSE)` (discouraged)
*
* There is no need to check the return value of `..()`, this is only used by the calling `examinate()` proc to validate the call chain.
*
* Default behaviour is to call `get_examine_text()` and send the result to the mob
*
* **You should usually be overriding `get_examine_text()`, unless you need special examine behaviour**
*
* Produces a signal [COMSIG_ATOM_EXAMINE]
*
* - user - The mob performing the examine
* - distance - The distance in tiles from `user` to `src`
* - is_adjacent - Whether `user` is adjacent to `src`
* - infix String - String that is appended immediately after the atom's name
* - suffix String - Additional string appended after the atom's name and infix
* - show_extended Boolean - Whether to show extended examination text or not. Only passed to get_examine_text()
*
* Returns boolean - The caller always expects TRUE
*/
/atom/proc/examine(mob/user, distance, is_adjacent, infix = "", suffix = "", show_extended)
SHOULD_CALL_PARENT(TRUE)
SHOULD_NOT_SLEEP(TRUE)
var/list/examine_strings = get_examine_text(user, distance, is_adjacent, infix, suffix, show_extended)
if(!length(examine_strings))
crash_with("Examine called with no examine strings on [src].")
to_chat(user, EXAMINE_BLOCK(examine_strings.Join("\n")))
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, examine_strings.Join("\n"))
return TRUE
/**
* Called to get the examine text info of an atom
*
* This proc is what you should usually override to get things to show up inside the examine box
*
* - user - The mob performing the examine
* - distance - The distance in tiles from `user` to `src`
* - is_adjacent - Whether `user` is adjacent to `src`
* - infix String - String that is appended immediately after the atom's name
* - suffix String - Additional string appended after the atom's name and infix
* - show_extended Boolean - Whether to show extended examination text or not. If FALSE, will only show the existence of this text.
*
* Returns a `/list` of strings
*/
/**
* The structure of an object's examine box is as follows:
* [ Name ] [ Size ]
* [ Damage/Condition ]
* [ Description ]
* [ Extended Description*** ]
* [ Mechanics*** ]
* [ Assembly/Disassembly*** ]
* [ Upgrades*** ]
* [ Antagonist Interactions*** ]
* [ Status Feedback ]
*
* Blocks marked with *** are collapsed by default.
*/
/atom/proc/get_examine_text(mob/user, distance, is_adjacent, infix = "", suffix = "", show_extended)
SHOULD_CALL_PARENT(TRUE)
SHOULD_NOT_SLEEP(TRUE)
update_desc_blocks(user, distance, is_adjacent)
. = list()
var/f_name = "\a [src]. [infix]"
if(src.blood_DNA && !istype(src, /obj/effect/decal))
if(gender == PLURAL)
f_name = "some "
else
f_name = "a "
if(blood_color != COLOR_IPC_BLOOD && blood_color != COLOR_OIL)
f_name += "<span class='danger'>blood-stained</span> [name][infix]!"
else
f_name += "oil-stained [name][infix]."
// Object name. I.e. "This is an Object. It is a normal-sized item."
. += "[icon2html(src, user)] That's [f_name] [suffix]"
// Object description.
if(src.desc)
. += src.desc
// Returns a SPAN_* based on health, if configured.
var/list/condition_hints = src.condition_hints()
if(length(condition_hints))
. += condition_hints
// Extra object descriptions examination code.
if(show_extended)
// If the item has a extended description, show it.
if(desc_extended)
. += desc_extended
// If the item has a description regarding game mechanics, show it.
if(desc_mechanics)
. += FONT_SMALL(SPAN_NOTICE("<b>Mechanics</b>"))
. += FONT_SMALL("[desc_mechanics]")
// If the item has a description with assembly/disassembly instructions, show it.
if(desc_build)
. += FONT_SMALL(SPAN_NOTICE("<b>Assembly/Disassembly</b>"))
// Not a span because desc_build can use both NOTICE and ALERT.
. += FONT_SMALL("[desc_build]")
// If the item has a description about its upgrade components and what they do, show it.
// This one doesnt come prepended with a hyphen because theyre added when the desc is dynamically built.
if(desc_upgrade)
. += FONT_SMALL("<b>Upgrades</b>")
. += FONT_SMALL("[desc_upgrade]")
// If the item has an antagonist description and the user is an antagonist/ghost, show it.
if(desc_antag && (player_is_antag(user.mind) || isghost(user) || isstoryteller(user)))
. += FONT_SMALL(SPAN_ALERT("<b>Antagonism</b>"))
. += FONT_SMALL("[desc_antag]")
else
// Checks if the object has a extended description, a mechanics description, and/or an antagonist description (and if the user is an antagonist).
if(desc_extended || desc_mechanics || desc_build || desc_upgrade || (desc_antag && player_is_antag(user.mind)))
// If any of the above are true, show that the object has more information available.
. += FONT_SMALL(SPAN_NOTICE("\[?\] This object has additional examine information available:"))
// If the item has a extended description, show that it is available.
if(desc_extended)
. += FONT_SMALL("- <b>Extended Description</b>")
// If the item has a description regarding game mechanics, show that it is available.
if(desc_mechanics)
. += FONT_SMALL(SPAN_NOTICE("- <b>Mechanics</b>"))
// If the item has a description regarding game mechanics, show that it is available.
if(desc_build)
. += FONT_SMALL(SPAN_NOTICE("- <b>Assembly/Disassembly</b>"))
// If the item has a description regarding game mechanics, show that it is available.
if(desc_upgrade)
. += FONT_SMALL(SPAN_NOTICE("- <b>Upgrades</b>"))
// If the item has an antagonist description and the user is an antagonist/ghost, show that it is available.
if(desc_antag && (player_is_antag(user.mind) || isghost(user) || isstoryteller(user)))
. += FONT_SMALL(SPAN_ALERT("- <b>Antagonist Interactions</b>"))
. += FONT_SMALL(SPAN_NOTICE("<a href='byond://?src=[REF(src)];examine_fluff=1'>\[Show in Chat\]</a>"))
// If the item has any feedback text, show it.
if(desc_feedback)
. += "</br>[desc_feedback]"
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(H.glasses)
H.glasses.glasses_examine_atom(src, H)
/**
* Used to check if "examine_fluff" from the HTML link in examine() is true, i.e. if it was clicked.
*/
/atom/Topic(href, href_list)
. = ..()
if (.)
return
if(href_list["examine_fluff"])
examinate(usr, src, show_extended = TRUE)
var/client/usr_client = usr.client
var/list/paramslist = list()
if(href_list["statpanel_item_click"])
switch(href_list["statpanel_item_click"])
if("left")
paramslist[LEFT_CLICK] = "1"
if("right")
paramslist[RIGHT_CLICK] = "1"
if("middle")
paramslist[MIDDLE_CLICK] = "1"
else
return
if(href_list["statpanel_item_shiftclick"])
paramslist[SHIFT_CLICK] = "1"
if(href_list["statpanel_item_ctrlclick"])
paramslist[CTRL_CLICK] = "1"
if(href_list["statpanel_item_altclick"])
paramslist[ALT_CLICK] = "1"
var/mouseparams = list2params(paramslist)
usr_client.Click(src, loc, null, mouseparams)
return TRUE
/**
* Builds the text block variables for get_examine_text
* Builds the text block variables for get_examine_text.
* Objects themselves are responsible for handling their own logic to build these hints.
* See mecha.dm for an example of a system that relays hints from contained items to a parent.
*/
/atom/proc/update_desc_blocks(mob/user, distance, is_adjacent)
var/list/mechanics_hints = mechanics_hints(user, distance, is_adjacent)
var/list/assembly_hints = assembly_hints(user, distance, is_adjacent)
var/list/disassembly_hints = disassembly_hints(user, distance, is_adjacent)
var/list/upgrade_hints = upgrade_hints(user, distance, is_adjacent)
var/list/antagonist_hints = antagonist_hints(user, distance, is_adjacent)
var/list/feedback_hints = feedback_hints(user, distance, is_adjacent)
// A little ugly but it works.
var/first_line
desc_mechanics = ""
if(length(mechanics_hints))
first_line = TRUE
for(var/mechanics_hint in mechanics_hints)
if(!first_line)
desc_mechanics += "</br>"
first_line = FALSE
desc_mechanics += SPAN_NOTICE("[mechanics_hint]")
desc_build = ""
if(length(assembly_hints) || length(disassembly_hints))
first_line = TRUE
for(var/assembly_hint in assembly_hints)
if(!first_line)
desc_build += "</br>"
first_line = FALSE
desc_build += SPAN_NOTICE("[assembly_hint]")
// Make sure line breaks work reliably whether or not there's only assembly, only disassembly, or both types available.
if (length(assembly_hints) && length(disassembly_hints))
desc_build += "</br>"
first_line = TRUE
for(var/disassembly_hint in disassembly_hints)
if(!first_line)
desc_build += "</br>"
first_line = FALSE
desc_build += SPAN_ALERT("[disassembly_hint]")
desc_upgrade = ""
if(length(upgrade_hints))
first_line = TRUE
for(var/upgrade_hint in upgrade_hints)
if(!first_line)
desc_upgrade += "<br>"
desc_upgrade += "[upgrade_hint]"
first_line = FALSE
desc_antag = ""
if(length(antagonist_hints))
first_line = TRUE
for(var/antagonist_hint in antagonist_hints)
if(!first_line)
desc_antag += "</br>"
first_line = FALSE
desc_antag += SPAN_WARNING("[antagonist_hint]")
desc_feedback = ""
if(length(feedback_hints))
first_line = TRUE
for(var/feedback_hint in feedback_hints)
if(!first_line)
desc_feedback += "</br>"
first_line = FALSE
desc_feedback += "[feedback_hint]"
/**
* Should return a list() of SPAN_* strings in whatever format you like.
* Accepted style is SPAN_NOTICE for minor damage and SPAN_ALERT for anything worse. If the object's destruction
* could have major adverse consequences, you might use SPAN_DANGER for critical damage.
*/
/atom/proc/condition_hints(mob/user, distance, is_adjacent)
. = list()
/**
* Should return a list() of regular strings.
*/
/atom/proc/mechanics_hints(mob/user, distance, is_adjacent)
. = list()
/*
* Children of assembly_hints() and disassembly_hints() should check the current state of the object, whether it
* has any eligible steps in its assembly or disassembly respectively, and if so, return hints to that end.
*
* It should be used to suggest steps toward or away from a completed 'form' of the object.
* For example, a table whose surface can be carpeted would have carpeting instructions in assembly_hints().
* However, an IV drip which can have a gas tank attached to it would not have that described in assembly_hints(),
* as the IV drip itself is already 'completed,' and a gas tank is effectively just a swappable slot item for it.
*
* Look at existing objects' implementations and use your best judgement, or ask in Discord if need be!
*/
/**
* Should return a list() of regular strings.
* See the large comment block right above this in 'code/game/atom/atom_examine.dm' for nuanced details.
*/
/atom/proc/assembly_hints(mob/user, distance, is_adjacent)
. = list()
/**
* Should return a list() of regular strings.
* See the large comment block right above this in 'code/game/atom/atom_examine.dm' for nuanced details.
*/
/atom/proc/disassembly_hints(mob/user, distance, is_adjacent)
. = list()
/**
* Should return a list() of regular strings.
*/
/atom/proc/upgrade_hints(mob/user, distance, is_adjacent)
. = list()
/**
* Should return a list() of regular strings.
*/
/atom/proc/antagonist_hints(mob/user, distance, is_adjacent)
. = list()
/**
* Should return a list() of regular strings. It will accept SPAN_* strings, though for consistency's sake please
* use SPAN_ALERT or SPAN_DANGER for negative/bad feedback.
*
* For feedback data returning variable number values or important bools, please bold important var values for easy readability.
* Reference 'code/game/machinery/iv_drip.dm' for how bolding is used.
*/
/atom/proc/feedback_hints(mob/user, distance, is_adjacent)
. = list()