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

<img width="347" height="39" alt="image"
src="https://github.com/user-attachments/assets/c50bd1ff-8c00-47a7-a31a-617fae2adc5b"
/>

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

🆑 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.
/🆑
This commit is contained in:
MrMelbert
2025-09-12 17:11:06 +02:00
committed by GitHub
parent 743d83c637
commit 7c0973ccf4
39 changed files with 205 additions and 231 deletions
@@ -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)
-3
View File
@@ -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"
+4 -2
View File
@@ -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
+5 -1
View File
@@ -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"
+1 -1
View File
@@ -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))
+3 -1
View File
@@ -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,
+3 -1
View File
@@ -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,
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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)
@@ -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.
@@ -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
+1 -1
View File
@@ -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()
+34 -48
View File
@@ -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))
@@ -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()
+6
View File
@@ -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]."))
+1 -1
View File
@@ -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
+9 -17
View File
@@ -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 = "</span>"
@@ -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
@@ -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 <b>[mimic_voice]</b>, 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."))
. = ..()
@@ -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
@@ -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"
+2 -2
View File
@@ -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))
@@ -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)
. = ..()
@@ -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
+2 -2
View File
@@ -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()
@@ -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
@@ -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
@@ -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)
. = ..()
@@ -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)
@@ -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()
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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
+6 -6
View File
@@ -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 = "<a href='byond://?src=[REF(src)];track=[html_encode(namepart)]'>"
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
@@ -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)
+1 -1
View File
@@ -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()
. = ..()
+1 -1
View File
@@ -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)
+20 -6
View File
@@ -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)
+8 -6
View File
@@ -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'
+1 -1
View File
@@ -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