Files
MrMelbert e4f533111f Deafness is now solely tracked by trait (#95029)
## About The Pull Request

Deletes `can_hear`, replaces it with trait-checking deafness.

The only two non-trait sources of deafness (hardcrit and lacking ears)
were refactored into using the trait.

## Why It's Good For The Game

Many places inconsistently check for the deaf trait rather than use
can_hear which meant behavior was not consistent.
Some code would treat "do we lack ears?" as being deaf, some would not. 

This unifies all the behavior so being deaf means you're deaf
everywhere.

It also means we can now easily react to gaining and losing deafness via
signal, where before we could not react to it without hooking the trait,
organ remove, AND stat change. Which no one did, of course, because who
would ever think to do that?

## Changelog

🆑 Melbert
refactor: Refactored how deafness is tracked. Please report any weird
interactions with sounds, like messages or sfx being missing.
fix: Lacking ears and being in hard crit now consistently treats you as
"being deaf". This affects a few minor interactions like empath, the
jukebox, and sleeping.
/🆑
2026-02-05 20:19:56 -05:00

59 lines
2.3 KiB
Plaintext

/**
* Component that allows for highlighting of words or phrases in chat based on regular expressions.
*
* Hooks into the parent's COMSIG_MOVABLE_HEAR signal to wrap every regex match in the message
* between <span class=''></span> tags with the provided span class. This modifies the output that
* is sent to the parent's chat window.
*
* Removal of this component should be done by calling [GetComponents(/datum/component/codeword_hearing)]
* on the parent and then iterating through all components calling [delete_if_from_source(source)].
*/
/datum/component/codeword_hearing
dupe_mode = COMPONENT_DUPE_ALLOWED
/// Regex for matching words or phrases you want highlighted.
var/regex/replace_regex
/// The <span class=''> to use for highlighting matches.
var/span_class
/// The source of this component. Used to identify the source in delete_if_from_source since this component is COMPONENT_DUPE_ALLOWED.
var/source
/datum/component/codeword_hearing/Initialize(regex/codeword_regex, highlight_span_class, component_source)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
replace_regex = codeword_regex
span_class = highlight_span_class
source = component_source
return ..()
/datum/component/codeword_hearing/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_HEAR, PROC_REF(handle_hearing))
/datum/component/codeword_hearing/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_MOVABLE_HEAR)
/// Callback for COMSIG_MOVABLE_HEAR which highlights syndicate code phrases in chat.
/datum/component/codeword_hearing/proc/handle_hearing(datum/source, list/hearing_args)
SIGNAL_HANDLER
var/mob/living/owner = parent
if(!istype(owner))
return
// don't skip codewords when owner speaks
if(HAS_TRAIT(owner, TRAIT_DEAF) || !owner.has_language(hearing_args[HEARING_LANGUAGE]))
return
var/message = hearing_args[HEARING_RAW_MESSAGE]
message = replace_regex.Replace(message, "<span class='[span_class]'>$1</span>")
hearing_args[HEARING_RAW_MESSAGE] = message
/// Since a parent can have multiple of these components on them simultaneously, this allows a datum to delete components from a specific source.
/datum/component/codeword_hearing/proc/delete_if_from_source(component_source)
if(source == component_source)
qdel(src)
return TRUE
return FALSE