From 7c0973ccf4dd4e9bd829e4969f476995cd798d16 Mon Sep 17 00:00:00 2001
From: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Date: Fri, 12 Sep 2025 10:11:06 -0500
Subject: [PATCH] Refactors names and voices / potted plants no longer conceal
voice (just appearance) / honorifics show on examine (#92781)
## About The Pull Request
Closes #92778
Closes #86829
1. Splits `TRAIT_UNKNOWN` into `TRAIT_UNKNOWN_APPEARANCE` and
`TRAIT_UNKNOWN_VOICE`
2. Renames some stuff like `getvoice` and `getspecialvoice`
3. Gets rid some crummy signals around `get_visible_name` and
`get_voice`
4. Heads now apply the disfigured trait when relevant (rather than
snowflake checking for damage amount)
5. Ling voice refactored into using special voice (it was only used by a
viro symptom anyways; I don't anticipate this overlap being problematic)
6. Mask voice changer refactored into a trait
## Why It's Good For The Game
Potted plants shouldn't have magical voice concealing powers -
especially not over radio, but not over in person either. It's a damn
plant
So I addressed this by refactoring our face and voice system. Overall
things should be a lot cleaner and easier to use.
## Changelog
:cl: Melbert
refactor: Refactored a lot of code relating to human face and voice, ie,
what shows up in examine and in say. Report anything odd when examining
people, with ID cards, when talking over radio, or when disguised
refcator: Refactored how you get disfigured when your head's super
damaged
refactor: Refactored ling mimic voice and traitor voice changer
del: Potted plants no longer hide voice. They still hide appearance,
though
qol: Honorifics now show in examine / in world, rather than only when
speaking.
/:cl:
---
.../signals_atom/signals_atom_movable.dm | 5 --
code/__DEFINES/say.dm | 3 -
code/__DEFINES/surgery.dm | 6 +-
code/__DEFINES/traits/declarations.dm | 6 +-
code/__HELPERS/pronouns.dm | 2 +-
code/_globalvars/traits/_traits.dm | 4 +-
code/_globalvars/traits/admin_tooling.dm | 4 +-
code/datums/chatmessage.dm | 2 +-
code/datums/components/callouts.dm | 2 +-
code/datums/components/tactical.dm | 4 +-
.../diseases/advance/symptoms/voice_change.dm | 4 +-
.../quirks/negative_quirks/prosopagnosia.dm | 4 +-
code/game/data_huds.dm | 2 +-
code/game/objects/items/cards_ids.dm | 82 +++++++----------
.../objects/items/devices/taperecorder.dm | 2 +-
code/game/objects/items/emags.dm | 6 ++
code/game/objects/items/toys.dm | 2 +-
code/game/say.dm | 26 ++----
.../changeling/powers/mimic_voice.dm | 16 ++--
.../antagonists/heretic/magic/shadow_cloak.dm | 4 +-
.../heretic/status_effects/buffs.dm | 4 +-
code/modules/assembly/voice.dm | 4 +-
.../chameleon/generic_chameleon_clothing.dm | 13 +--
.../generic_skillchips/point.dm | 2 +-
code/modules/mob/living/carbon/examine.dm | 4 +-
code/modules/mob/living/carbon/human/human.dm | 2 +-
.../mob/living/carbon/human/human_defines.dm | 3 +-
.../mob/living/carbon/human/human_helpers.dm | 90 ++++++++++---------
.../mob/living/carbon/human/human_say.dm | 46 +++-------
.../mob/living/carbon/human/init_signals.dm | 18 +---
code/modules/mob/living/living.dm | 2 +-
code/modules/mob/living/living_say.dm | 2 +-
code/modules/mob/living/silicon/ai/ai.dm | 12 +--
.../modules/mob/living/silicon/silicon_say.dm | 2 +-
code/modules/mod/modules/modules_antag.dm | 2 +-
code/modules/religion/burdened/psyker.dm | 2 +-
code/modules/surgery/bodyparts/_bodyparts.dm | 26 ++++--
code/modules/surgery/bodyparts/head.dm | 14 +--
code/modules/wiremod/components/atom/hear.dm | 2 +-
39 files changed, 205 insertions(+), 231 deletions(-)
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
index 1ea6ef446de..75b809b5403 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
@@ -116,11 +116,6 @@
#define MOVABLE_SAY_QUOTE_MESSAGE_SPANS 2
#define MOVABLE_SAY_QUOTE_MESSAGE_MODS 3
-/// Sent from /atom/movable/proc/compose_message() after the name part: (list/stored_name, visible_name)
-#define COMSIG_MOVABLE_MESSAGE_GET_NAME_PART "movable_message_get_name_part"
- ///The index of the name part
- #define NAME_PART_INDEX 1
-
/// From /datum/element/immerse/proc/add_submerge_overlay(): (visual_overlay)
#define COMSIG_MOVABLE_EDIT_UNIQUE_IMMERSE_OVERLAY "movable_edit_unique_submerge_overlay"
/// From base of area/Exited(): (area/left, direction)
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index 097a2592e64..ecbbb3ec04b 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -143,6 +143,3 @@
///Defines for priorities for the bubble_icon_override comp
#define BUBBLE_ICON_PRIORITY_ACCESSORY 2
#define BUBBLE_ICON_PRIORITY_ORGAN 1
-
-/// Sent from /atom/movable/proc/compose_message() to find an honorific. Compatible with NAME_PART_INDEX: (list/stored_name, mob/living/carbon/carbon_human)
-#define COMSIG_ID_GET_HONORIFIC "id_get_honorific"
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index 108fc197404..9d73f12a980 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -80,8 +80,10 @@
#define HEAD_EYEHOLES (1<<5)
/// Head can have debrain overlay
#define HEAD_DEBRAIN (1<<6)
-/// All head flags, default for most heads
-#define HEAD_ALL_FEATURES (HEAD_HAIR|HEAD_FACIAL_HAIR|HEAD_LIPS|HEAD_EYESPRITES|HEAD_EYECOLOR|HEAD_EYEHOLES|HEAD_DEBRAIN)
+/// Head will never be disfigured by damage
+#define HEAD_NO_DISFIGURE (1<<7)
+/// Default for most heads
+#define HEAD_DEFAULT_FEATURES (HEAD_HAIR|HEAD_FACIAL_HAIR|HEAD_LIPS|HEAD_EYESPRITES|HEAD_EYECOLOR|HEAD_EYEHOLES|HEAD_DEBRAIN)
/// Return value when the surgery step fails :(
#define SURGERY_STEP_FAIL -1
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 47fd640341b..cb522ac9947 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -619,7 +619,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BLIND_TOOL "blind_tool"
/// The person with this trait always appears as 'unknown'.
-#define TRAIT_UNKNOWN "unknown"
+#define TRAIT_UNKNOWN_APPEARANCE "unknown_appearance"
+/// The person with this trait always talks as 'unknown'
+#define TRAIT_UNKNOWN_VOICE "unknown_voice"
+/// Spoken voice always matches any worn ID. If no worn ID, defaults to actual name.
+#define TRAIT_VOICE_MATCHES_ID "voice_matches_id"
/// If the mob has this trait and die, their bomb implant doesn't detonate automatically. It must be consciously activated.
#define TRAIT_PREVENT_IMPLANT_AUTO_EXPLOSION "prevent_implant_auto_explosion"
diff --git a/code/__HELPERS/pronouns.dm b/code/__HELPERS/pronouns.dm
index cc907f1bc49..cc1f357fcf6 100644
--- a/code/__HELPERS/pronouns.dm
+++ b/code/__HELPERS/pronouns.dm
@@ -411,7 +411,7 @@
return gender
/mob/living/carbon/human/get_visible_gender()
- if(HAS_TRAIT(src, TRAIT_UNKNOWN))
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
return PLURAL
// If both face and uniform are hidden, can't assess gender
if(obscured_slots & (HIDEFACE|HIDEJUMPSUIT) == (HIDEFACE|HIDEJUMPSUIT))
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 1e661883b23..bba6f9e0287 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -589,7 +589,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE" = TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE,
"TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE,
"TRAIT_UNINTELLIGIBLE_SPEECH" = TRAIT_UNINTELLIGIBLE_SPEECH,
- "TRAIT_UNKNOWN" = TRAIT_UNKNOWN,
+ "TRAIT_UNKNOWN_APPEARANCE" = TRAIT_UNKNOWN_APPEARANCE,
+ "TRAIT_UNKNOWN_VOICE" = TRAIT_UNKNOWN_VOICE,
"TRAIT_UNNATURAL_RED_GLOWY_EYES" = TRAIT_UNNATURAL_RED_GLOWY_EYES,
"TRAIT_UNOBSERVANT" = TRAIT_UNOBSERVANT,
"TRAIT_UNSTABLE" = TRAIT_UNSTABLE,
@@ -602,6 +603,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE,
"TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE,
"TRAIT_VIRUS_RESISTANCE" = TRAIT_VIRUS_RESISTANCE,
+ "TRAIT_VOICE_MATCHES_ID" = TRAIT_VOICE_MATCHES_ID,
"TRAIT_VORACIOUS" = TRAIT_VORACIOUS,
"TRAIT_WAS_EVOLVED" = TRAIT_WAS_EVOLVED,
"TRAIT_WATER_ADAPTATION" = TRAIT_WATER_ADAPTATION,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index c81ee3cd4d5..40e82c58bef 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -315,7 +315,8 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE" = TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE,
"TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE,
"TRAIT_UNINTELLIGIBLE_SPEECH" = TRAIT_UNINTELLIGIBLE_SPEECH,
- "TRAIT_UNKNOWN" = TRAIT_UNKNOWN,
+ "TRAIT_UNKNOWN_APPEARANCE" = TRAIT_UNKNOWN_APPEARANCE,
+ "TRAIT_UNKNOWN_VOICE" = TRAIT_UNKNOWN_VOICE,
"TRAIT_UNNATURAL_RED_GLOWY_EYES" = TRAIT_UNNATURAL_RED_GLOWY_EYES,
"TRAIT_UNOBSERVANT" = TRAIT_UNOBSERVANT,
"TRAIT_UNSTABLE" = TRAIT_UNSTABLE,
@@ -326,6 +327,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE,
"TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE,
"TRAIT_VIRUS_RESISTANCE" = TRAIT_VIRUS_RESISTANCE,
+ "TRAIT_VOICE_MATCHES_ID" = TRAIT_VOICE_MATCHES_ID,
"TRAIT_VORACIOUS" = TRAIT_VORACIOUS,
"TRAIT_WATER_ADAPTATION" = TRAIT_WATER_ADAPTATION,
"TRAIT_WATER_HATER" = TRAIT_WATER_HATER,
diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm
index ff86cb7c013..6f0b4579771 100644
--- a/code/datums/chatmessage.dm
+++ b/code/datums/chatmessage.dm
@@ -161,7 +161,7 @@
if(HAS_TRAIT(target, TRAIT_SIGN_LANG))
chat_color_name_to_use = target.get_visible_name(add_id_name = FALSE) // use face name for signers too
else
- chat_color_name_to_use = target.GetVoice() // for everything else, use the target's voice name
+ chat_color_name_to_use = target.get_voice() // for everything else, use the target's voice name
// Calculate target color if not already present
if (!target.chat_color || target.chat_color_name != chat_color_name_to_use)
diff --git a/code/datums/components/callouts.dm b/code/datums/components/callouts.dm
index f69fa39fe66..c6b27bf17dc 100644
--- a/code/datums/components/callouts.dm
+++ b/code/datums/components/callouts.dm
@@ -139,7 +139,7 @@
if (isnull(creator))
return
icon_state = callout::icon_state
- color = colorize_string(creator.GetVoice(), 2, 0.9)
+ color = colorize_string(creator.get_voice(), 2, 0.9)
update_appearance()
var/turf/target_loc = get_turf(target)
animate(src, pixel_x = (target_loc.x - loc.x) * ICON_SIZE_X + target.pixel_x, pixel_y = (target_loc.y - loc.y) * ICON_SIZE_Y + target.pixel_y, time = 0.2 SECONDS, easing = EASE_OUT)
diff --git a/code/datums/components/tactical.dm b/code/datums/components/tactical.dm
index 80ea33f2445..732e43ee766 100644
--- a/code/datums/components/tactical.dm
+++ b/code/datums/components/tactical.dm
@@ -45,7 +45,7 @@
RegisterSignal(user, COMSIG_HUMAN_GET_VISIBLE_NAME, PROC_REF(on_name_inquiry))
RegisterSignal(user, COMSIG_HUMAN_GET_FORCED_NAME, PROC_REF(on_name_inquiry))
// This forces a name update on the user, so we don't need to call name update ourselves
- ADD_TRAIT(user, TRAIT_UNKNOWN, REF(src))
+ ADD_TRAIT(user, TRAIT_UNKNOWN_APPEARANCE, REF(src))
current_slot = slot
@@ -106,7 +106,7 @@
))
current_slot = null
user.remove_alt_appearance("sneaking_mission[REF(src)]")
- REMOVE_TRAIT(user, TRAIT_UNKNOWN, REF(src))
+ REMOVE_TRAIT(user, TRAIT_UNKNOWN_APPEARANCE, REF(src))
///Checks if a mob is holding us, and if so we will modify our appearance to properly match w/ the mob.
/datum/component/tactical/proc/tactical_update(obj/item/source)
diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm
index 92583e1fca7..ec224147974 100644
--- a/code/datums/diseases/advance/symptoms/voice_change.dm
+++ b/code/datums/diseases/advance/symptoms/voice_change.dm
@@ -54,7 +54,7 @@
else
if(ishuman(M))
var/mob/living/carbon/human/H = M
- H.SetSpecialVoice(H.generate_random_mob_name())
+ H.override_voice = H.generate_random_mob_name()
if(scramble_language && !current_language) // Last part prevents rerolling language with small amounts of cure.
current_language = pick(subtypesof(/datum/language) - /datum/language/common)
H.add_blocked_language(subtypesof(/datum/language) - current_language, source = LANGUAGE_VOICECHANGE)
@@ -64,7 +64,7 @@
..()
if(ishuman(A.affected_mob))
var/mob/living/carbon/human/H = A.affected_mob
- H.UnsetSpecialVoice()
+ H.override_voice = ""
if(scramble_language)
A.affected_mob.remove_blocked_language(subtypesof(/datum/language), source = LANGUAGE_VOICECHANGE)
A.affected_mob.remove_all_languages(LANGUAGE_VOICECHANGE) // In case someone managed to get more than one anyway.
diff --git a/code/datums/quirks/negative_quirks/prosopagnosia.dm b/code/datums/quirks/negative_quirks/prosopagnosia.dm
index 3911cb45056..bb70a41c1dd 100644
--- a/code/datums/quirks/negative_quirks/prosopagnosia.dm
+++ b/code/datums/quirks/negative_quirks/prosopagnosia.dm
@@ -21,7 +21,7 @@
if(!ishuman(examined) || source == examined)
return NONE
- var/id_name = examined.get_id_name("")
+ var/id_name = examined.get_id_name("", honorifics = TRUE)
name_override[1] = id_name ? "[id_name]?" : "Unknown"
return COMPONENT_EXAMINE_NAME_OVERRIDEN
@@ -31,6 +31,6 @@
if(!ishuman(hovered) || source == hovered)
return NONE
- var/id_name = hovered.get_id_name("")
+ var/id_name = hovered.get_id_name("", honorifics = TRUE)
returned_name[1] = id_name ? "[id_name]?" : "Unknown"
return SCREENTIP_NAME_SET
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index 04ba0e52439..959716d230d 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -266,7 +266,7 @@ Security HUDs! Basic mode shows only the job.
SIGNAL_HANDLER
var/sechud_icon_state = wear_id?.get_sechud_job_icon_state()
- if(!sechud_icon_state || HAS_TRAIT(src, TRAIT_UNKNOWN))
+ if(!sechud_icon_state || HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
sechud_icon_state = "hudno_id"
set_hud_image_state(ID_HUD, sechud_icon_state)
sec_hud_set_security_status()
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 599d9fa7ad6..8288de30e16 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -43,6 +43,10 @@
. = ..()
cached_flat_icon = null
+/// Called to get what name this card represents
+/obj/item/card/proc/get_displayed_name(honorifics = FALSE)
+ return null
+
/// If no cached_flat_icon exists, this proc creates it and crops it. This proc then returns the cached_flat_icon. Intended for use displaying ID card icons in chat.
/obj/item/card/proc/get_cached_flat_icon()
if(!cached_flat_icon)
@@ -146,7 +150,7 @@
SSid_access.apply_trim_to_card(src, trim)
else
update_label()
- update_icon()
+ update_appearance()
// Apply any active RETA grants to this new ID card
// This will only do something if there are active grants, so it's safe to call always
@@ -156,7 +160,6 @@
register_context()
RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, PROC_REF(update_in_wallet))
- RegisterSignal(src, COMSIG_ID_GET_HONORIFIC, PROC_REF(return_message_name_part))
if(prob(1))
ADD_TRAIT(src, TRAIT_TASTEFULLY_THICK_ID_CARD, ROUNDSTART_TRAIT)
@@ -188,19 +191,25 @@
UnregisterSignal(user, COMSIG_MOVABLE_POINTED)
return ..()
-/obj/item/card/id/proc/return_message_name_part(datum/source, list/stored_name, mob/living/carbon/carbon_human)
- SIGNAL_HANDLER
- var/voice_name = carbon_human.GetVoice()
- var/end_string = ""
- var/return_string = ""
- if(carbon_human.name != voice_name)
- end_string += " (as [registered_name])"
- if(trim && honorific_position != HONORIFIC_POSITION_NONE && (carbon_human.name == voice_name)) //The voice and name are the same, so we display the title.
- return_string += honorific_title
- else
- return_string += voice_name //Name on the ID ain't the same as the speaker, so we display their real name with no title.
- return_string += end_string
- stored_name[NAME_PART_INDEX] = return_string
+/obj/item/card/id/equipped(mob/user, slot, initial = FALSE)
+ . = ..()
+ if(!(slot & ITEM_SLOT_ID))
+ return
+ if(ishuman(user))
+ var/mob/living/carbon/human/as_human = user
+ as_human.update_visible_name()
+
+/obj/item/card/id/dropped(mob/user, silent = FALSE)
+ . = ..()
+ if(ishuman(user))
+ var/mob/living/carbon/human/as_human = user
+ as_human.update_visible_name()
+
+/// Getter for the registered name, with optional honorifics
+/obj/item/card/id/get_displayed_name(honorifics = FALSE)
+ if(honorifics && honorific_position != HONORIFIC_POSITION_NONE && honorific_title)
+ return honorific_title
+ return registered_name
/obj/item/card/id/proc/on_loc_equipped(datum/source, mob/equipper, slot)
SIGNAL_HANDLER
@@ -214,7 +223,7 @@
/obj/item/card/id/proc/on_pointed(mob/living/user, atom/pointed, obj/effect/temp_visual/point/point)
SIGNAL_HANDLER
- if ((!big_pointer && !pointer_color) || HAS_TRAIT(user, TRAIT_UNKNOWN))
+ if ((!big_pointer && !pointer_color) || HAS_TRAIT(user, TRAIT_UNKNOWN_APPEARANCE))
return
if (point.icon_state != /obj/effect/temp_visual/point::icon_state) //it differs from the original icon_state already.
return
@@ -650,7 +659,7 @@
switch(var_name)
if(NAMEOF(src, assignment), NAMEOF(src, registered_name), NAMEOF(src, registered_age))
update_label()
- update_icon()
+ update_appearance()
if(NAMEOF(src, trim))
if(ispath(trim))
SSid_access.apply_trim_to_card(src, trim)
@@ -945,6 +954,10 @@
name = "[name_string] ([assignment_string])"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/human = loc
+ human.update_visible_name()
+
/// Re-generates the honorific title. Returns the compiled honorific_title value
/obj/item/card/id/proc/update_honorific()
switch(honorific_position)
@@ -1879,7 +1892,7 @@
REMOVE_TRAIT(src, TRAIT_MAGNETIC_ID_CARD, CHAMELEON_ITEM_TRAIT)
user.log_message("reset \the [initial(name)] named \"[src]\" to default.", LOG_GAME)
update_label()
- update_icon()
+ update_appearance()
forged = FALSE
to_chat(user, span_notice("You successfully reset the ID card."))
return
@@ -1939,7 +1952,7 @@
ADD_TRAIT(src, TRAIT_MAGNETIC_ID_CARD, CHAMELEON_ITEM_TRAIT)
update_label()
- update_icon()
+ update_appearance()
forged = TRUE
to_chat(user, span_notice("You successfully forge the ID card."))
user.log_message("forged \the [initial(name)] with name \"[registered_name]\", occupation \"[assignment]\" and trim \"[trim?.assignment]\".", LOG_GAME)
@@ -2057,35 +2070,8 @@
. = ..()
register_context()
-/obj/item/card/cardboard/equipped(mob/user, slot, initial = FALSE)
- . = ..()
- if(slot != ITEM_SLOT_ID)
- return
- RegisterSignal(user, COMSIG_HUMAN_GET_VISIBLE_NAME, PROC_REF(return_visible_name))
- RegisterSignal(user, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, PROC_REF(return_message_name_part))
- if(ishuman(user))
- var/mob/living/carbon/human/as_human = user
- as_human.update_visible_name()
-
-/obj/item/card/cardboard/dropped(mob/user, silent = FALSE)
- . = ..()
- UnregisterSignal(user, list(COMSIG_HUMAN_GET_VISIBLE_NAME, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART))
- if(ishuman(user))
- var/mob/living/carbon/human/as_human = user
- as_human.update_visible_name()
-
-/obj/item/card/cardboard/proc/return_visible_name(mob/living/carbon/human/source, list/identity)
- SIGNAL_HANDLER
- identity[VISIBLE_NAME_ID] = scribbled_name
-
-/obj/item/card/cardboard/proc/return_message_name_part(mob/living/carbon/human/source, list/stored_name, visible_name)
- SIGNAL_HANDLER
- if(visible_name)
- return
- var/voice_name = source.GetVoice()
- if(source.name != voice_name)
- voice_name += " (as [scribbled_name])"
- stored_name[NAME_PART_INDEX] = voice_name
+/obj/item/card/cardboard/get_displayed_name(honorifics = FALSE)
+ return scribbled_name
/obj/item/card/cardboard/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(user.can_write(tool, TRUE))
diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm
index 5dfe8ebce47..c5160b0d705 100644
--- a/code/game/objects/items/devices/taperecorder.dm
+++ b/code/game/objects/items/devices/taperecorder.dm
@@ -159,7 +159,7 @@
return
mytape.timestamp += mytape.used_capacity
- mytape.storedinfo += "\[[time2text(mytape.used_capacity,"mm:ss", NO_TIMEZONE)]\] [speaker.GetVoice()]: [raw_message]"
+ mytape.storedinfo += "\[[time2text(mytape.used_capacity,"mm:ss", NO_TIMEZONE)]\] [speaker.get_voice()]: [raw_message]"
/obj/item/taperecorder/verb/record()
diff --git a/code/game/objects/items/emags.dm b/code/game/objects/items/emags.dm
index 9c0a95877aa..219e1a3e144 100644
--- a/code/game/objects/items/emags.dm
+++ b/code/game/objects/items/emags.dm
@@ -17,6 +17,9 @@
var/prox_check = TRUE //If the emag requires you to be in range
var/type_blacklist //List of types that require a specialized emag
+/obj/item/card/emag/get_displayed_name(honorifics = FALSE)
+ return name // That's Grey Tider (as "cryptographic sequencer")
+
/obj/item/card/emag/attack_self(mob/user) //for traitors with balls of plastitanium
if(Adjacent(user))
user.visible_message(span_notice("[user] shows you: [icon2html(src, viewers(user))] [name]."), span_notice("You show [src]."))
@@ -99,6 +102,9 @@
/// Whether we are exploding
var/exploding = FALSE
+/obj/item/card/emagfake/get_displayed_name(honorifics = FALSE)
+ return name // That's Grey Tider (as "cryptographic sequencer")
+
/obj/item/card/emagfake/attack_self(mob/user) //for assistants with balls of plasteel
if(Adjacent(user))
user.visible_message(span_notice("[user] shows you: [icon2html(src, viewers(user))] [name]."), span_notice("You show [src]."))
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index a980ce95718..1e2405c126d 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1448,7 +1448,7 @@
. = ..()
AddElement(/datum/element/toy_talk)
-/obj/item/toy/dummy/GetVoice()
+/obj/item/toy/dummy/get_voice()
return doll_name
/obj/item/toy/seashell
diff --git a/code/game/say.dm b/code/game/say.dm
index 85848ee0f48..423a39498f3 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -149,21 +149,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
//Radio freq/name display
var/freqpart = radio_freq ? "\[[get_radio_name(radio_freq, radio_freq_name)]\] " : ""
//Speaker name
- var/namepart
- var/list/stored_name = list(null)
-
- if(iscarbon(speaker)) //First, try to pull the modified title from a carbon's ID. This will override both visual and audible names.
- var/mob/living/carbon/carbon_human = speaker
- var/obj/item/id_slot = carbon_human.get_item_by_slot(ITEM_SLOT_ID)
- if(id_slot)
- var/obj/item/card/id/id_card = id_slot?.GetID()
- if(id_card)
- SEND_SIGNAL(id_card, COMSIG_ID_GET_HONORIFIC, stored_name, carbon_human)
-
- if(!stored_name[NAME_PART_INDEX]) //Otherwise, we just use whatever the name signal gives us.
- SEND_SIGNAL(speaker, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, stored_name, visible_name)
-
- namepart = stored_name[NAME_PART_INDEX] || "[speaker.GetVoice()]"
+ var/namepart = speaker.get_message_voice(visible_name)
//End name span.
var/endspanpart = ""
@@ -333,9 +319,15 @@ GLOBAL_LIST_INIT(freqtospan, list(
return "2"
return "0"
-/atom/proc/GetVoice()
+/// Get what this atom sounds like when speaking
+/atom/proc/get_voice()
return "[src]" //Returns the atom's name, prepended with 'The' if it's not a proper noun
+/// Get what this atom appears like in chat when speaking
+/// visible_name - If TRUE, returns the visible name rather than the voice
+/atom/proc/get_message_voice(visible_name)
+ return visible_name ? get_visible_name() : get_voice()
+
//HACKY VIRTUALSPEAKER STUFF BEYOND THIS POINT
//these exist mostly to deal with the AIs hrefs and job stuff.
@@ -357,7 +349,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/virtualspeaker)
radio = _radio
source = M
if(istype(M))
- name = radio.anonymize ? "Unknown" : M.GetVoice()
+ name = radio.anonymize ? "Unknown" : M.get_voice()
verb_say = M.get_default_say_verb()
verb_ask = M.verb_ask
verb_exclaim = M.verb_exclaim
diff --git a/code/modules/antagonists/changeling/powers/mimic_voice.dm b/code/modules/antagonists/changeling/powers/mimic_voice.dm
index 99f54304580..e1d895d2b14 100644
--- a/code/modules/antagonists/changeling/powers/mimic_voice.dm
+++ b/code/modules/antagonists/changeling/powers/mimic_voice.dm
@@ -8,11 +8,11 @@
req_human = TRUE
// Fake Voice
-/datum/action/changeling/mimicvoice/sting_action(mob/user)
+/datum/action/changeling/mimicvoice/sting_action(mob/living/carbon/human/user)
var/datum/antagonist/changeling/changeling = IS_CHANGELING(user)
- if(changeling.mimicing)
- changeling.mimicing = ""
+ if(user.override_voice)
changeling.chem_recharge_slowdown -= 0.25
+ user.override_voice = ""
to_chat(user, span_notice("We return our vocal glands to their original position."))
return
@@ -20,16 +20,16 @@
if(!mimic_voice)
return
..()
- changeling.mimicing = mimic_voice
changeling.chem_recharge_slowdown += 0.25
+ user.override_voice = mimic_voice
to_chat(user, span_notice("We shape our glands to take the voice of [mimic_voice], this will slow down regenerating chemicals while active."))
to_chat(user, span_notice("Use this power again to return to our original voice and return chemical production to normal levels."))
return TRUE
-/datum/action/changeling/mimicvoice/Remove(mob/user)
+/datum/action/changeling/mimicvoice/Remove(mob/living/carbon/human/user)
var/datum/antagonist/changeling/changeling = IS_CHANGELING(user)
- if(changeling?.mimicing)
- changeling.chem_recharge_slowdown = max(0, changeling.chem_recharge_slowdown - 0.25)
- changeling.mimicing = ""
+ if(user.override_voice)
+ changeling?.chem_recharge_slowdown = max(0, changeling.chem_recharge_slowdown - 0.25)
+ user.override_voice = ""
to_chat(user, span_notice("Our vocal glands return to their original position."))
. = ..()
diff --git a/code/modules/antagonists/heretic/magic/shadow_cloak.dm b/code/modules/antagonists/heretic/magic/shadow_cloak.dm
index 0012495867a..825e1874c78 100644
--- a/code/modules/antagonists/heretic/magic/shadow_cloak.dm
+++ b/code/modules/antagonists/heretic/magic/shadow_cloak.dm
@@ -141,7 +141,7 @@
animate(cloak_image, alpha = 255, 0.2 SECONDS)
owner.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, id, cloak_image)
// Add the relevant traits and modifiers
- owner.add_traits(list(TRAIT_UNKNOWN, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
+ owner.add_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
owner.add_movespeed_modifier(/datum/movespeed_modifier/shadow_cloak)
owner.add_actionspeed_modifier(/datum/actionspeed_modifier/shadow_cloak)
// Register signals to cause effects
@@ -157,7 +157,7 @@
owner.remove_alt_appearance(id)
QDEL_NULL(cloak_image)
// Remove traits and modifiers
- owner.remove_traits(list(TRAIT_UNKNOWN, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
+ owner.remove_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
owner.remove_movespeed_modifier(/datum/movespeed_modifier/shadow_cloak)
owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/shadow_cloak)
// Clear signals
diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm
index ab327817735..3da3c43e1eb 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -345,11 +345,11 @@
alert_type = /atom/movable/screen/alert/status_effect/moon_grasp_hide
/datum/status_effect/moon_grasp_hide/on_apply()
- owner.add_traits(list(TRAIT_UNKNOWN, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
+ owner.add_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
return TRUE
/datum/status_effect/moon_grasp_hide/on_remove()
- owner.remove_traits(list(TRAIT_UNKNOWN, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
+ owner.remove_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
/atom/movable/screen/alert/status_effect/moon_grasp_hide
name = "Blessing of The Moon"
diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm
index ffe5eab54ce..62375dc0643 100644
--- a/code/modules/assembly/voice.dm
+++ b/code/modules/assembly/voice.dm
@@ -62,7 +62,7 @@
listening = FALSE
say("Activation message is '[recorded]'.", sanitize = FALSE, language = message_language)
if(RECOGNIZER_MODE)
- recorded = speaker.GetVoice()
+ recorded = speaker.get_voice()
listening = FALSE
say("Your voice pattern is saved.", language = message_language)
if(VOICE_SENSOR_MODE)
@@ -81,7 +81,7 @@
if(raw_message == recorded)
return TRUE
if(RECOGNIZER_MODE)
- if(speaker.GetVoice() == recorded)
+ if(speaker.get_voice() == recorded)
return TRUE
if(VOICE_SENSOR_MODE)
if(length(raw_message))
diff --git a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
index 3749f84d670..a8f03b0b3f9 100644
--- a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
+++ b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
@@ -185,12 +185,15 @@ do { \
w_class = WEIGHT_CLASS_SMALL
actions_types = list(/datum/action/item_action/chameleon/change/mask)
action_slots = ALL
- /// Is our voice changer enabled or disabled?
- var/voice_change = TRUE
+ clothing_traits = list(TRAIT_VOICE_MATCHES_ID)
/obj/item/clothing/mask/chameleon/attack_self(mob/user)
- voice_change = !voice_change
- to_chat(user, span_notice("The voice changer is now [voice_change ? "on" : "off"]!"))
+ var/was_on = (TRAIT_VOICE_MATCHES_ID in clothing_traits)
+ if(was_on)
+ attach_clothing_traits(TRAIT_VOICE_MATCHES_ID)
+ else
+ detach_clothing_traits(TRAIT_VOICE_MATCHES_ID)
+ to_chat(user, span_notice("The voice changer is now [was_on ? "off" : "on"]!"))
/obj/item/clothing/mask/chameleon/broken
@@ -203,7 +206,7 @@ do { \
item_flags = DROPDEL
//Same as the drone chameleon hat, undroppable and no protection
armor_type = /datum/armor/none
- voice_change = FALSE
+ clothing_traits = null
/obj/item/clothing/mask/chameleon/drone/Initialize(mapload)
. = ..()
diff --git a/code/modules/library/skill_learning/generic_skillchips/point.dm b/code/modules/library/skill_learning/generic_skillchips/point.dm
index 8b26e413b7e..23971af80e0 100644
--- a/code/modules/library/skill_learning/generic_skillchips/point.dm
+++ b/code/modules/library/skill_learning/generic_skillchips/point.dm
@@ -26,7 +26,7 @@
/obj/item/skillchip/big_pointer/proc/fancier_pointer(mob/living/user, atom/pointed, obj/effect/temp_visual/point/point)
SIGNAL_HANDLER
- if(HAS_TRAIT(user, TRAIT_UNKNOWN))
+ if(HAS_TRAIT(user, TRAIT_UNKNOWN_APPEARANCE))
return
point.cut_overlays()
var/datum/action/change_pointer_color/action = locate() in actions
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index a562204f65e..d7d3df762be 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -6,7 +6,7 @@
return null
/mob/living/carbon/examine(mob/user)
- if(HAS_TRAIT(src, TRAIT_UNKNOWN))
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
return list(span_warning("You're struggling to make out any details..."))
var/t_He = p_They()
@@ -587,7 +587,7 @@
if(undershirt.has_sensor == BROKEN_SENSORS)
. += list(span_notice("\The [undershirt]'s medical sensors are sparking."))
- if(HAS_TRAIT(src, TRAIT_UNKNOWN) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
return
var/limbs_text = get_mismatched_limb_text()
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 79da995c053..c40e6ab1b7b 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -105,7 +105,7 @@
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))
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
to_chat(viewer, span_notice("You can't make out that ID anymore."))
return
if(!isobserver(viewer) && get_dist(viewer, src) > ID_EXAMINE_DISTANCE + 1) // leeway, ignored if the viewer is a ghost
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 78d4db6fa0a..7b455134bc5 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -70,7 +70,8 @@
var/obj/item/l_store = null
var/obj/item/s_store = null
- var/special_voice = "" // For changing our voice. Used by a symptom.
+ /// Allows for special overrides of voice
+ var/override_voice = ""
var/datum/physiology/physiology
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 5a84b21169a..1bcdb336d9f 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -8,7 +8,7 @@
//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)
var/obj/item/card/id/id = get_idcard(hand_first)
- if(HAS_TRAIT(src, TRAIT_UNKNOWN))
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
return if_no_id
if(id)
. = id.assignment
@@ -25,7 +25,7 @@
//Useful when player do something with computers
/mob/living/carbon/human/proc/get_authentification_name(if_no_id = "Unknown")
var/obj/item/card/id/id = get_idcard(FALSE)
- if(HAS_TRAIT(src, TRAIT_UNKNOWN))
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
return if_no_id
if(id)
return id.registered_name
@@ -48,64 +48,66 @@
var/force_set = LAZYACCESS(identity, VISIBLE_NAME_FORCED)
if(force_set) // our name is overriden by something
return signal_face // no need to null-check, because force_set will always set a signal_face
- var/face_name = !isnull(signal_face) ? signal_face : get_face_name("")
- var/id_name = !isnull(signal_id) ? signal_id : get_id_name("")
- if(force_real_name)
- var/fake_name
- if (face_name && face_name != real_name)
- fake_name = face_name
- if(add_id_name && id_name && id_name != real_name)
- if (!isnull(fake_name) && id_name != face_name)
- fake_name = "[fake_name]/[id_name]"
- else
- fake_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) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
- return "Unknown"
- if(face_name)
- if(add_id_name && id_name && (id_name != face_name))
- return "[face_name] (as [id_name])"
- return face_name
- if(id_name)
- 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
+ var/face_name = isnull(signal_face) ? get_face_name("") : signal_face
+ var/id_name = isnull(signal_id) ? get_id_name("", honorifics = TRUE) : signal_id
+
+ // We need to account for real name
+ if(force_real_name)
+ var/disguse_name = get_visible_name(add_id_name = TRUE, force_real_name = FALSE)
+ return "[real_name][disguse_name == real_name ? "" : " (as [disguse_name])"]"
+
+ // We're just some unknown guy
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
+ return "Unknown"
+
+ // We have a face and an ID
+ if(face_name && id_name)
+ var/normal_id_name = get_id_name("") // need to check base ID name to avoid "John (as Captain John)"
+ if(normal_id_name == face_name)
+ return id_name // (this turns "John" into "Captain John")
+ if(add_id_name)
+ return "[face_name] (as [id_name])"
+
+ // Just go down the list of stuff we recorded
+ return face_name || id_name || "Unknown"
+
+/**
+ * Gets what the face of this mob looks like
+ *
+ * * if_no_face - What to return if we have no face or our face is obscured/disfigured
+ */
/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))
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
return if_no_face //We're Unknown, no face information for you
if(obscured_slots & HIDEFACE)
return if_no_face
var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD)
- if(isnull(head) || (HAS_TRAIT(src, TRAIT_DISFIGURED)) || (head.brutestate + head.burnstate) > 2 || !real_name || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) //disfigured. use id-name if possible
+ if(isnull(head) || !real_name || HAS_TRAIT(src, TRAIT_DISFIGURED) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) //disfigured. use id-name if possible
return if_no_face
return real_name
-//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/proc/get_id_name(if_no_id = "Unknown")
+/**
+ * Gets whatever name is in our ID or PDA
+ *
+ * * if_no_id - What to return if we have no ID or PDA
+ * * honorifics - Whether to include honorifics in the returned name (if the found ID has any set)
+ */
+/mob/living/carbon/proc/get_id_name(if_no_id = "Unknown", honorifics = FALSE)
return
-/mob/living/carbon/human/get_id_name(if_no_id = "Unknown")
- if(HAS_TRAIT(src, TRAIT_UNKNOWN))
- . = if_no_id //You get NOTHING, no id name, good day sir
+/mob/living/carbon/human/get_id_name(if_no_id = "Unknown", honorifics = FALSE)
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
var/list/identity = list(null, null, null)
SEND_SIGNAL(src, COMSIG_HUMAN_GET_FORCED_NAME, identity)
- if(identity[VISIBLE_NAME_FORCED])
- return identity[VISIBLE_NAME_FACE] // to return forced names when unknown, instead of ID
- else
- var/obj/item/card/id/id = astype(wear_id, /obj/item/card/id) \
- || astype(wear_id, /obj/item/storage/wallet)?.front_id \
- || astype(wear_id, /obj/item/modular_computer)?.stored_id
- . = id?.registered_name
- if(!.)
- . = if_no_id //to prevent null-names making the mob unclickable
+ return identity[VISIBLE_NAME_FORCED] ? identity[VISIBLE_NAME_FACE] : if_no_id
+
+ // either results in an ID card, a generic card, or null
+ var/obj/item/card/id = wear_id?.GetID() || astype(wear_id, /obj/item/card)
+ return id?.get_displayed_name(honorifics) || if_no_id
/mob/living/carbon/human/get_idcard(hand_first = TRUE)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm
index 053582174d1..a962b5d33cc 100644
--- a/code/modules/mob/living/carbon/human/human_say.dm
+++ b/code/modules/mob/living/carbon/human/human_say.dm
@@ -29,40 +29,22 @@
return "gurgles"
return tongue.temp_say_mod || tongue.say_mod || ..()
-/mob/living/carbon/human/GetVoice()
- if(HAS_TRAIT(src, TRAIT_UNKNOWN))
- return ("Unknown")
-
- if(istype(wear_mask, /obj/item/clothing/mask/chameleon))
- var/obj/item/clothing/mask/chameleon/V = wear_mask
- if(V.voice_change && wear_id)
- var/obj/item/card/id/idcard = wear_id.GetID()
- if(istype(idcard))
- return idcard.registered_name
- else
- return real_name
- else
- return real_name
-
- if(mind)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling?.mimicing)
- return changeling.mimicing
- if(GetSpecialVoice())
- return GetSpecialVoice()
+/mob/living/carbon/human/get_voice()
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_VOICE))
+ return "Unknown"
+ var/id_name = get_id_name("")
+ if(HAS_TRAIT(src, TRAIT_VOICE_MATCHES_ID) && id_name)
+ return id_name
+ if(override_voice)
+ return override_voice
+ if(real_name == id_name) // Allows for "Captain John" to have the voice "Captain Join" and not "John"
+ return get_id_name("", honorifics = TRUE)
return real_name
-/mob/living/carbon/human/proc/SetSpecialVoice(new_voice)
- if(new_voice)
- special_voice = new_voice
- return
-
-/mob/living/carbon/human/proc/UnsetSpecialVoice()
- special_voice = ""
- return
-
-/mob/living/carbon/human/proc/GetSpecialVoice()
- return special_voice
+/mob/living/carbon/human/get_message_voice(visible_name)
+ . = ..()
+ if(. != name)
+ . += " (as [get_id_name("Unknown", honorifics = TRUE)])"
/mob/living/carbon/human/binarycheck()
if(stat >= SOFT_CRIT)
diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm
index 06016b7a07c..827716b91a6 100644
--- a/code/modules/mob/living/carbon/human/init_signals.dm
+++ b/code/modules/mob/living/carbon/human/init_signals.dm
@@ -1,10 +1,9 @@
/mob/living/carbon/human/register_init_signals()
. = ..()
- RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_UNKNOWN), SIGNAL_REMOVETRAIT(TRAIT_UNKNOWN)), PROC_REF(update_ID_card))
+ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_UNKNOWN_APPEARANCE), SIGNAL_REMOVETRAIT(TRAIT_UNKNOWN_APPEARANCE)), PROC_REF(update_ID_card))
RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_DWARF), SIGNAL_REMOVETRAIT(TRAIT_DWARF)), PROC_REF(on_dwarf_trait))
RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_TOO_TALL), SIGNAL_REMOVETRAIT(TRAIT_TOO_TALL)), PROC_REF(on_tootall_trait))
- RegisterSignal(src, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, PROC_REF(get_name_part))
RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT)), PROC_REF(on_fat))
RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_NOHUNGER), SIGNAL_REMOVETRAIT(TRAIT_NOHUNGER)), PROC_REF(on_nohunger))
@@ -33,21 +32,6 @@
SIGNAL_HANDLER
update_mob_height()
-///From compose_message(). Snowflake code converted into its own signal proc
-/mob/living/carbon/human/proc/get_name_part(datum/source, list/stored_name, visible_name)
- SIGNAL_HANDLER
- /**
- * For if the message can be seen but not heard, shows our visible identity (like when using sign language)
- * Also used by hallucinations, so it doesn't give source's identity away.
- */
- if(visible_name)
- stored_name[NAME_PART_INDEX] = get_visible_name()
- return
- var/voice_name = GetVoice()
- if(name != voice_name)
- voice_name += " (as [get_id_name("Unknown")])"
- stored_name[NAME_PART_INDEX] = voice_name
-
/mob/living/carbon/human/proc/on_fat(datum/source)
SIGNAL_HANDLER
hud_used?.hunger?.update_hunger_bar()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index d8352bacb74..bab3c20164b 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2953,7 +2953,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
/// Create a report string about how strong this person looks, generated in a somewhat arbitrary fashion
/mob/living/proc/compare_fitness(mob/living/scouter)
- if (HAS_TRAIT(src, TRAIT_UNKNOWN))
+ if (HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
return span_warning("It's impossible to tell whether this person lifts.")
var/our_fitness_level = calculate_fitness()
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index 69c548ab937..d1c35cf77a3 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -424,7 +424,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
if(!M.client.prefs.read_preference(/datum/preference/toggle/enable_runechat) || (SSlag_switch.measures[DISABLE_RUNECHAT] && !HAS_TRAIT(src, TRAIT_BYPASS_MEASURES)))
speech_bubble_recipients.Add(M.client)
found_client = TRUE
- if(SStts.tts_enabled && voice && found_client && !message_mods[MODE_CUSTOM_SAY_ERASE_INPUT] && !HAS_TRAIT(src, TRAIT_SIGN_LANG) && !HAS_TRAIT(src, TRAIT_UNKNOWN))
+ if(SStts.tts_enabled && voice && found_client && !message_mods[MODE_CUSTOM_SAY_ERASE_INPUT] && !HAS_TRAIT(src, TRAIT_SIGN_LANG) && !HAS_TRAIT(src, TRAIT_UNKNOWN_VOICE))
var/tts_message_to_use = tts_message
if(!tts_message_to_use)
tts_message_to_use = message_raw
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 32811c61f80..a0c117593de 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -810,14 +810,14 @@
var/treated_message = source.generate_messagepart(raw_translation, spans, message_mods)
var/start = "Relayed Speech: "
- var/namepart
- var/list/stored_name = list(null)
- SEND_SIGNAL(speaker, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, stored_name, FALSE)
- namepart = stored_name[NAME_PART_INDEX] || "[speaker.GetVoice()]"
+ var/namepart = speaker.get_message_voice()
var/hrefpart = ""
var/jobpart = "Unknown"
- if(!HAS_TRAIT(speaker, TRAIT_UNKNOWN)) //don't fetch the speaker's job in case they have something that conseals their identity completely
+ // if voice is concealed, job is concealed
+ // on the other hand we don't care about TRAIT_UNKNOWN_APPEARANCE
+ // (AI can associate voice -> name -> crew record -> job)
+ if(!HAS_TRAIT(speaker, TRAIT_UNKNOWN_VOICE))
if (isliving(speaker))
var/mob/living/living_speaker = speaker
if(living_speaker.job)
@@ -1092,7 +1092,7 @@
. = ..()
.[/datum/job/ai::title] = minutes
-/mob/living/silicon/ai/GetVoice()
+/mob/living/silicon/ai/get_voice()
. = ..()
if(ai_voicechanger && ai_voicechanger.changing_voice)
return ai_voicechanger.say_name
diff --git a/code/modules/mob/living/silicon/silicon_say.dm b/code/modules/mob/living/silicon/silicon_say.dm
index 7e806bbdf8c..c3edff2b94b 100644
--- a/code/modules/mob/living/silicon/silicon_say.dm
+++ b/code/modules/mob/living/silicon/silicon_say.dm
@@ -24,7 +24,7 @@
var/namepart = name
// If carbon, use voice to account for voice changers
if(iscarbon(src))
- namepart = GetVoice()
+ namepart = get_voice()
// AI in carbon body should still have its real name
var/obj/item/organ/brain/cybernetic/ai/brain = get_organ_slot(ORGAN_SLOT_BRAIN)
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
index 32b11f23c59..cd2e8ce06d4 100644
--- a/code/modules/mod/modules/modules_antag.dm
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -419,7 +419,7 @@
incompatible_modules = list(/obj/item/mod/module/infiltrator, /obj/item/mod/module/welding/syndicate, /obj/item/mod/module/welding, /obj/item/mod/module/headprotector)
required_slots = list(ITEM_SLOT_FEET, ITEM_SLOT_HEAD, ITEM_SLOT_OCLOTHING)
/// List of traits added when the suit is activated
- var/list/traits_to_add = list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN, TRAIT_HEAD_INJURY_BLOCKED)
+ var/list/traits_to_add = list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_HEAD_INJURY_BLOCKED)
/obj/item/mod/module/infiltrator/on_install()
. = ..()
diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm
index 409ead91fdd..6d10524ccba 100644
--- a/code/modules/religion/burdened/psyker.dm
+++ b/code/modules/religion/burdened/psyker.dm
@@ -40,7 +40,7 @@
is_dimorphic = FALSE
should_draw_greyscale = FALSE
bodypart_traits = list(TRAIT_DISFIGURED, TRAIT_BALD, TRAIT_SHAVED)
- head_flags = HEAD_DEBRAIN
+ head_flags = HEAD_DEBRAIN | HEAD_NO_DISFIGURE // ignore disfigurement by damage, as we're always disfigured
/// flavorful variant of psykerizing that deals damage and sends messages before calling psykerize()
/mob/living/carbon/human/proc/slow_psykerize(blind_them = FALSE)
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 38731efab0a..1dc9ee3069b 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -194,7 +194,7 @@
var/unarmed_sharpness = NONE
/// Traits that are given to the holder of the part. This does not update automatically on life(), only when the organs are initially generated or inserted!
- var/list/bodypart_traits = list()
+ var/list/bodypart_traits
/// The name of the trait source that the organ gives. Should not be altered during the events of gameplay, and will cause problems if it is.
var/bodypart_trait_source = BODYPART_TRAIT
/// List of the above datums which have actually been instantiated, managed automatically
@@ -811,15 +811,15 @@
if(speed_modifier)
old_owner.update_bodypart_speed_modifier()
- if(length(bodypart_traits))
+ if(LAZYLEN(bodypart_traits))
old_owner.remove_traits(bodypart_traits, bodypart_trait_source)
UnregisterSignal(old_owner, list(
SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE),
- SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE),
+ SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE),
SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD),
SIGNAL_ADDTRAIT(TRAIT_NOBLOOD),
- ))
+ ))
UnregisterSignal(old_owner, list(COMSIG_ATOM_RESTYLE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_LIVING_SET_BODY_POSITION))
@@ -831,7 +831,7 @@
if(speed_modifier)
owner.update_bodypart_speed_modifier()
- if(length(bodypart_traits))
+ if(LAZYLEN(bodypart_traits))
owner.add_traits(bodypart_traits, bodypart_trait_source)
if(initial(can_be_disabled))
@@ -872,7 +872,7 @@
item_flags &= ~ABSTRACT
REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
- if(!length(bodypart_traits))
+ if(!LAZYLEN(bodypart_traits))
return
owner.remove_traits(bodypart_traits, bodypart_trait_source)
@@ -1488,3 +1488,17 @@
return "metal"
return "error"
+
+/// Add a trait to the bodypart traits list, then applies the trait if necessary
+/obj/item/bodypart/proc/add_bodypart_trait(new_trait)
+ LAZYOR(bodypart_traits, new_trait)
+ if(isnull(owner))
+ return
+ ADD_TRAIT(owner, new_trait, bodypart_trait_source)
+
+/// Remove a trait from the bodypart traits list, then removes the trait if necessary
+/obj/item/bodypart/proc/remove_bodypart_trait(old_trait)
+ LAZYREMOVE(bodypart_traits, old_trait)
+ if(isnull(owner))
+ return
+ REMOVE_TRAIT(owner, old_trait, bodypart_trait_source)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 347eb6428d4..0214198ad8f 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -34,7 +34,7 @@
/// Replacement name
var/real_name = ""
/// Flags related to appearance, such as hair, lips, etc
- var/head_flags = HEAD_ALL_FEATURES
+ var/head_flags = HEAD_DEFAULT_FEATURES
/// Hair style
var/hairstyle = "Bald"
@@ -210,18 +210,20 @@
. = ..()
AddElement(/datum/element/toy_talk)
-/obj/item/bodypart/head/GetVoice()
+/obj/item/bodypart/head/get_voice()
return "The head of [real_name]"
/obj/item/bodypart/head/update_bodypart_damage_state()
- if (!ishuman(owner))
+ if (head_flags & HEAD_NO_DISFIGURE)
return ..()
+
var/old_states = brutestate + burnstate
. = ..()
var/new_states = brutestate + burnstate
- var/mob/living/carbon/human/as_human = owner
- if ((old_states >= HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES && new_states < HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES) || (old_states < HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES && new_states >= HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES))
- as_human.update_visible_name()
+ if(new_states >= HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES)
+ add_bodypart_trait(TRAIT_DISFIGURED)
+ else if(old_states >= HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES)
+ remove_bodypart_trait(TRAIT_DISFIGURED)
/obj/item/bodypart/head/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
diff --git a/code/modules/wiremod/components/atom/hear.dm b/code/modules/wiremod/components/atom/hear.dm
index c846c4c7f83..4dc8812efc0 100644
--- a/code/modules/wiremod/components/atom/hear.dm
+++ b/code/modules/wiremod/components/atom/hear.dm
@@ -53,6 +53,6 @@
if(message_language)
language_port.set_output(initial(message_language.name))
speaker_port.set_output(speaker)
- speaker_name.set_output(speaker.GetVoice())
+ speaker_name.set_output(speaker.get_voice())
trigger_port.set_output(COMPONENT_SIGNAL)
return TRUE