From 3b44a8b15c6caf3fad22ead4eccb65ff6ae8bff5 Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:45:34 +0200 Subject: [PATCH] Adds cardboard IDs to the game: The broke man's agent ID. (#76682) ## About The Pull Request This PR adds a new craftable item to the game that, in a way, works somewhat like ID cards, as in it gives the wearer an identity of some sort, and that can be modified similardly to the agent ID, but... It doesn't provide access. It doesn't trick security bots and turrets. It doesn't work with chameleon masks. It doesn't have a bank account. It doesn't fit inside wallets or PDAs. It doesn't show a trim (it's just cosmetic) on the security HUD. It doesn't look like an ID card. (It does however, synergizes well with sign language and face-covering mask, but in the face of all the things id doesn't do, should I change that? idk) Basically, it's not an ID, it's just a piece of cardboard with name and job written on it. ![aww](https://github.com/tgstation/tgstation/assets/42542238/b33ddb38-a11d-41d9-8085-2c719a2c4d48) ![(null)scrnshot1](https://github.com/tgstation/tgstation/assets/42542238/00a53379-70f6-4105-9cca-cff75d0e4144) ## Why It's Good For The Game Often, player shenanigeans rely on ID cards with gimmicky names and jobs to advertise themselves or provide a (feeble) disguise for it. The idea is to provide players a cheap tool for their tomfoolery, that doesn't get much in the way of balance. Compared to actual IDs, cardboard IDs' only advantage is the fact they're more easily produceable. Also this PR converts a bit of snowflakey code into signals, and fixes the name part in hallucination messages being shown "Unknown" while the speaker is wearing a mask but also an ID. ## Changelog :cl: add: Added cardboard IDs to the game. They can be crafted with a cardboard sheet and wirecutters and modified with a writing tool. While worn, these will modify the visible name of the wearer just like actual IDs, though they aren't real IDs and won't work as such. /:cl: --- .../signals_atom/signals_atom_movable.dm | 5 + .../signals/signals_mob/signals_mob_carbon.dm | 7 + code/datums/components/crafting/misc.dm | 10 + code/game/objects/items/cards_ids.dm | 194 ++++++++++++++++-- code/game/objects/items/emags.dm | 6 - code/game/say.dm | 16 +- .../equipment/gear/abductor_clothing.dm | 9 +- code/modules/hallucination/fake_chat.dm | 4 +- .../mob/living/carbon/human/human_defines.dm | 2 - .../mob/living/carbon/human/human_helpers.dm | 10 +- .../mob/living/carbon/human/human_say.dm | 4 - .../mob/living/carbon/human/init_signals.dm | 16 ++ code/modules/mob/living/living_say.dm | 2 +- code/modules/mob/living/silicon/ai/ai.dm | 5 +- code/modules/mob/mob.dm | 17 +- .../inhands/equipment/idcards_lefthand.dmi | Bin 419 -> 532 bytes .../inhands/equipment/idcards_righthand.dmi | Bin 433 -> 546 bytes icons/obj/card.dmi | Bin 15541 -> 18245 bytes 18 files changed, 245 insertions(+), 62 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 dc4d5a25ea0..07954aab2f5 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -104,3 +104,8 @@ #define MOVABLE_SAY_QUOTE_MESSAGE 1 #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 diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index f72e47db97a..d73b5e5daf0 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -131,6 +131,13 @@ ///from /mob/living/carbon/human/proc/force_say(): () #define COMSIG_HUMAN_FORCESAY "human_forcesay" +///from /mob/living/carbon/human/get_visible_name(), not sent if the mob has TRAIT_UNKNOWN: (identity) +#define COMSIG_HUMAN_GET_VISIBLE_NAME "human_get_visible_name" + //Index for the name of the face + #define VISIBLE_NAME_FACE 1 + //Index for the name of the id + #define VISIBLE_NAME_ID 2 + // Mob transformation signals ///Called when a human turns into a monkey, from /mob/living/carbon/proc/finish_monkeyize() #define COMSIG_HUMAN_MONKEYIZE "human_monkeyize" diff --git a/code/datums/components/crafting/misc.dm b/code/datums/components/crafting/misc.dm index dd850874d68..264ff981565 100644 --- a/code/datums/components/crafting/misc.dm +++ b/code/datums/components/crafting/misc.dm @@ -34,3 +34,13 @@ ) tool_paths = list(/obj/item/stamp/head/captain) category = CAT_MISC + +/datum/crafting_recipe/cardboard_id + name = "Cardboard ID Card" + tool_behaviors = list(TOOL_WIRECUTTER) + result = /obj/item/card/cardboard + time = 4 SECONDS + reqs = list( + /obj/item/stack/sheet/cardboard = 1, + ) + category = CAT_MISC diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index c4821a30523..ddaf61f3095 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -28,14 +28,28 @@ name = "card" desc = "Does card things." icon = 'icons/obj/card.dmi' + inhand_icon_state = "card-id" + lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' w_class = WEIGHT_CLASS_TINY - - var/list/files = list() + /// Cached icon that has been built for this card. Intended to be displayed in chat. Cardboards IDs and actual IDs use it. + var/icon/cached_flat_icon /obj/item/card/suicide_act(mob/living/carbon/user) user.visible_message(span_suicide("[user] begins to swipe [user.p_their()] neck with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")) return BRUTELOSS +/obj/item/card/update_overlays() + . = ..() + cached_flat_icon = 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) + cached_flat_icon = getFlatIcon(src) + cached_flat_icon.Crop(ID_ICON_BORDERS) + return cached_flat_icon + /* * ID CARDS */ @@ -45,17 +59,11 @@ name = "retro identification card" desc = "A card used to provide ID and determine access across the station." icon_state = "card_grey" - inhand_icon_state = "card-id" worn_icon_state = "nothing" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' slot_flags = ITEM_SLOT_ID armor_type = /datum/armor/card_id resistance_flags = FIRE_PROOF | ACID_PROOF - /// Cached icon that has been built for this card. Intended for use in chat. - var/icon/cached_flat_icon - /// The name registered on the card (for example: Dr Bryan See) var/registered_name = null /// Linked bank account. @@ -136,18 +144,6 @@ . = ..() . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "bigicon")]") -/obj/item/card/id/update_overlays() - . = ..() - - cached_flat_icon = null - -/// If no cached_flat_icon exists, this proc creates it and crops it. This proc then returns the cached_flat_icon. Intended only for use displaying ID card icons in chat. -/obj/item/card/id/proc/get_cached_flat_icon() - if(!cached_flat_icon) - cached_flat_icon = getFlatIcon(src) - cached_flat_icon.Crop(ID_ICON_BORDERS) - return cached_flat_icon - /obj/item/card/id/get_examine_string(mob/user, thats = FALSE) return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" @@ -1194,8 +1190,6 @@ desc = "You are a number, you are not a free man." icon_state = "card_prisoner" inhand_icon_state = "orange-id" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' registered_name = "Scum" registered_age = null trim = /datum/id_trim/job/prisoner @@ -1648,3 +1642,159 @@ #undef INTERN_THRESHOLD_FALLBACK_HOURS #undef ID_ICON_BORDERS #undef HOLOPAY_PROJECTION_INTERVAL + +#define INDEX_NAME_COLOR 1 +#define INDEX_ASSIGNMENT_COLOR 2 +#define INDEX_TRIM_COLOR 3 + +/** + * A fake ID card any silly-willy can craft with wirecutters, cardboard and a writing utensil + * Beside the gimmick of changing the visible name when worn, they do nothing. They cannot have an account. + * They don't fit in PDAs nor wallets, They have no access. They won't trick securitrons. They won't work with chameleon masks. + * Etcetera etcetera. Furthermore, talking, or getting examined on will pretty much give it away. + */ +/obj/item/card/cardboard + name = "cardboard identification card" + desc = "A card used to provice ID and det- Heeeey, wait a second, this is just a piece of cut cardboard!" + icon_state = "cardboard_id" + inhand_icon_state = "cardboard-id" + worn_icon_state = "nothing" + resistance_flags = FLAMMABLE + slot_flags = ITEM_SLOT_ID + ///The "name" of the "owner" of this "ID" + var/scribbled_name + ///The assignment written on this card. + var/scribbled_assignment + ///An icon state used as trim. + var/scribbled_trim + ///The colors for each of the above variables, for when overlays are updated. + var/details_colors = list("#000000", "#000000", "#000000") + +/obj/item/card/cardboard/equipped(mob/user, slot, initial = FALSE) + . = ..() + if(slot == ITEM_SLOT_ID) + 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)) + +/obj/item/card/cardboard/dropped(mob/user, silent = FALSE) + . = ..() + UnregisterSignal(user, list(COMSIG_HUMAN_GET_VISIBLE_NAME, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART)) + +/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/attackby(obj/item/item, mob/living/user, params) + if(user.can_write(item, TRUE)) + INVOKE_ASYNC(src, PROC_REF(modify_card), user, item) + return TRUE + return ..() + +///Lets the user write a name, assignment or trim on the card, or reset it. Only the name is important for the component. +/obj/item/card/cardboard/proc/modify_card(mob/living/user, obj/item/item) + if(!user.mind) + return + var/popup_input = tgui_input_list(user, "What To Change", "Cardboard ID", list("Name", "Assignment", "Trim", "Reset")) + if(!after_input_check(user, item, popup_input)) + return + switch(popup_input) + if("Name") + var/input_name = tgui_input_text(user, "What name would you like to put on this card?", "Cardboard card name", scribbled_name || (ishuman(user) ? user.real_name : user.name), MAX_NAME_LEN) + input_name = sanitize_name(input_name, allow_numbers = TRUE) + if(!after_input_check(user, item, input_name, scribbled_name)) + return + scribbled_name = input_name + var/list/details = item.get_writing_implement_details() + details_colors[INDEX_NAME_COLOR] = details["color"] || "#000000" + if("Assignment") + var/input_assignment = tgui_input_text(user, "What assignment would you like to put on this card?", "Cardboard card job ssignment", scribbled_assignment || "Assistant", MAX_NAME_LEN) + if(!after_input_check(user, item, input_assignment, scribbled_assignment)) + return + scribbled_assignment = input_assignment + var/list/details = item.get_writing_implement_details() + details_colors[INDEX_ASSIGNMENT_COLOR] = details["color"] || "#000000" + if("Trim") + var/static/list/possible_trims + if(!possible_trims) + possible_trims = list() + for(var/trim_path in typesof(/datum/id_trim)) + var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path] + if(trim?.trim_state && trim.assignment) + possible_trims |= replacetext(trim.trim_state, "trim_", "") + sortTim(possible_trims, GLOBAL_PROC_REF(cmp_typepaths_asc)) + var/input_trim = tgui_input_list(user, "Select trim to apply to your card.\nNote: This will not grant any trim accesses.", "Forge Trim", possible_trims) + if(!input_trim || !after_input_check(user, item, input_trim, scribbled_trim)) + return + scribbled_trim = "cardboard_[input_trim]" + var/list/details = item.get_writing_implement_details() + details_colors[INDEX_TRIM_COLOR] = details["color"] || "#000000" + if("Reset") + scribbled_name = null + scribbled_assignment = null + scribbled_trim = null + details_colors = list("#000000", "#000000", "#000000") + + update_appearance() + +///Checks that the conditions to be able to modify the cardboard card are still present after user input calls. +/obj/item/card/cardboard/proc/after_input_check(mob/living/user, obj/item/item, input, value) + if(!input || (value && input == value)) + return FALSE + if(QDELETED(user) || QDELETED(item) || QDELETED(src) || user.incapacitated() || !user.is_holding(item) || !user.CanReach(src) || !user.can_write(item)) + return FALSE + return TRUE + +/obj/item/card/cardboard/attack_self(mob/user) + if(!Adjacent(user)) + return + user.visible_message(span_notice("[user] shows you: [icon2html(src, viewers(user))] [name]."), span_notice("You show \the [name].")) + add_fingerprint(user) + +/obj/item/card/cardboard/update_name() + . = ..() + if(!scribbled_name) + name = initial(name) + return + name = "[scribbled_name]'s ID Card ([scribbled_assignment])" + +/obj/item/card/cardboard/update_overlays() + . = ..() + if(scribbled_name) + var/mutable_appearance/name_overlay = mutable_appearance(icon, "cardboard_name") + name_overlay.color = details_colors[INDEX_NAME_COLOR] + . += name_overlay + if(scribbled_assignment) + var/mutable_appearance/assignment_overlay = mutable_appearance(icon, "cardboard_assignment") + assignment_overlay.color = details_colors[INDEX_ASSIGNMENT_COLOR] + . += assignment_overlay + if(scribbled_trim) + var/mutable_appearance/frame_overlay = mutable_appearance(icon, "cardboard_frame") + frame_overlay.color = details_colors[INDEX_TRIM_COLOR] + . += frame_overlay + var/mutable_appearance/trim_overlay = mutable_appearance(icon, scribbled_trim) + trim_overlay.color = details_colors[INDEX_TRIM_COLOR] + . += trim_overlay + +/obj/item/card/cardboard/get_id_examine_strings(mob/user) + . = ..() + . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "bigicon")]") + +/obj/item/card/cardboard/get_examine_string(mob/user, thats = FALSE) + return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" + +/obj/item/card/cardboard/examine(mob/user) + . = ..() + . += span_notice("You could use a pen or crayon to forge a name, assignment or trim.") + +#undef INDEX_NAME_COLOR +#undef INDEX_ASSIGNMENT_COLOR +#undef INDEX_TRIM_COLOR diff --git a/code/game/objects/items/emags.dm b/code/game/objects/items/emags.dm index 84eb1d50139..56da7757c0a 100644 --- a/code/game/objects/items/emags.dm +++ b/code/game/objects/items/emags.dm @@ -11,9 +11,6 @@ desc = "It's a card with a magnetic strip attached to some circuitry." name = "cryptographic sequencer" icon_state = "emag" - inhand_icon_state = "card-id" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' item_flags = NO_MAT_REDEMPTION | NOBLUDGEON slot_flags = ITEM_SLOT_ID worn_icon_state = "emag" @@ -40,11 +37,8 @@ desc = "It's a card with a magnetic strip attached to some circuitry. Closer inspection shows that this card is a poorly made replica, with a \"Donk Co.\" logo stamped on the back." name = "cryptographic sequencer" icon_state = "emag" - inhand_icon_state = "card-id" slot_flags = ITEM_SLOT_ID worn_icon_state = "emag" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' /obj/item/card/emagfake/attack_self(mob/user) //for assistants with balls of plasteel if(Adjacent(user)) diff --git a/code/game/say.dm b/code/game/say.dm index 1a5c6525741..528c81c656d 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -104,7 +104,7 @@ GLOBAL_LIST_INIT(freqtospan, list( if(voice && found_client) INVOKE_ASYNC(SStts, TYPE_PROC_REF(/datum/controller/subsystem/tts, queue_tts_message), src, html_decode(tts_message_to_use), message_language, voice, filter.Join(","), listened, message_range = range, pitch = pitch, silicon = tts_silicon_voice_effect) -/atom/movable/proc/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), face_name = FALSE, visible_name = FALSE) +/atom/movable/proc/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), visible_name = FALSE) //This proc uses [] because it is faster than continually appending strings. Thanks BYOND. //Basic span var/spanpart1 = "" @@ -113,13 +113,11 @@ GLOBAL_LIST_INIT(freqtospan, list( //Radio freq/name display var/freqpart = radio_freq ? "\[[get_radio_name(radio_freq)]\] " : "" //Speaker name - var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]" - if(face_name && ishuman(speaker)) - var/mob/living/carbon/human/H = speaker - namepart = "[H.get_face_name()]" //So "fake" speaking like in hallucinations does not give the speaker away if disguised - else if(visible_name && ishuman(speaker)) - var/mob/living/carbon/human/human_speaker = speaker - namepart = "[human_speaker.get_visible_name()]" //For if the message can be seen but not heard, shows "speaker"'s visible identity (like when using sign language) + var/namepart + var/list/stored_name = list(null) + SEND_SIGNAL(speaker, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, stored_name, visible_name) + namepart = stored_name[NAME_PART_INDEX] || "[speaker.GetVoice()]" + //End name span. var/endspanpart = "" @@ -236,8 +234,6 @@ GLOBAL_LIST_INIT(freqtospan, list( /atom/movable/proc/GetVoice() return "[src]" //Returns the atom's name, prepended with 'The' if it's not a proper noun -/atom/movable/proc/get_alt_name() - //HACKY VIRTUALSPEAKER STUFF BEYOND THIS POINT //these exist mostly to deal with the AIs hrefs and job stuff. diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm index 064b77cb518..f8bef933790 100644 --- a/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm @@ -94,13 +94,18 @@ if(ishuman(loc)) var/mob/living/carbon/human/wearer = loc new /obj/effect/temp_visual/dir_setting/ninja/cloak(get_turf(wearer), wearer.dir) - wearer.name_override = disguise.name + RegisterSignal(wearer, COMSIG_HUMAN_GET_VISIBLE_NAME, PROC_REF(return_disguise_name)) wearer.icon = disguise.icon wearer.icon_state = disguise.icon_state wearer.cut_overlays() wearer.add_overlay(disguise.overlays) wearer.update_held_items() +/obj/item/clothing/suit/armor/abductor/vest/proc/return_disguise_name(mob/living/carbon/human/source, list/identity) + SIGNAL_HANDLER + identity[VISIBLE_NAME_FACE] = disguise.name + identity[VISIBLE_NAME_ID] = "" + /obj/item/clothing/suit/armor/abductor/vest/proc/DeactivateStealth() if(!stealth_active) return @@ -108,7 +113,7 @@ if(ishuman(loc)) var/mob/living/carbon/human/wearer = loc new /obj/effect/temp_visual/dir_setting/ninja(get_turf(wearer), wearer.dir) - wearer.name_override = null + UnregisterSignal(wearer, COMSIG_HUMAN_GET_VISIBLE_NAME) wearer.cut_overlays() wearer.regenerate_icons() diff --git a/code/modules/hallucination/fake_chat.dm b/code/modules/hallucination/fake_chat.dm index 47a99d2088b..049a337c110 100644 --- a/code/modules/hallucination/fake_chat.dm +++ b/code/modules/hallucination/fake_chat.dm @@ -34,7 +34,7 @@ humans += crew_mind.current if(humans.len) speaker = pick(humans) - + if(!speaker) return @@ -87,7 +87,7 @@ hallucinator.create_chat_message(speaker, understood_language, chosen, spans) // And actually show them the message, for real. - var/message = hallucinator.compose_message(speaker, understood_language, chosen, is_radio ? "[FREQ_COMMON]" : null, spans, face_name = TRUE) + var/message = hallucinator.compose_message(speaker, understood_language, chosen, is_radio ? "[FREQ_COMMON]" : null, spans, visible_name = TRUE) to_chat(hallucinator, message) // Then clean up. diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 26b070dc5c6..36dcc80fa12 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -61,8 +61,6 @@ var/special_voice = "" // For changing our voice. Used by a symptom. - var/name_override //For temporary visible name changes - var/datum/physiology/physiology var/list/datum/bioware/biowares diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index f61d19ff318..805dc01249a 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -58,12 +58,14 @@ //repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a separate proc as it'll be useful elsewhere /mob/living/carbon/human/get_visible_name() - var/face_name = get_face_name("") - var/id_name = get_id_name("") if(HAS_TRAIT(src, TRAIT_UNKNOWN)) return "Unknown" - if(name_override) - return name_override + var/list/identity = list(null, null) + SEND_SIGNAL(src, COMSIG_HUMAN_GET_VISIBLE_NAME, identity) + var/signal_face = LAZYACCESS(identity, VISIBLE_NAME_FACE) + var/signal_id = LAZYACCESS(identity, VISIBLE_NAME_ID) + var/face_name = !isnull(signal_face) ? signal_face : get_face_name("") + var/id_name = !isnull(signal_id) ? signal_id : get_id_name("") if(face_name) if(id_name && (id_name != face_name)) return "[face_name] (as [id_name])" diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm index 7089e5ed048..27298f466dd 100644 --- a/code/modules/mob/living/carbon/human/human_say.dm +++ b/code/modules/mob/living/carbon/human/human_say.dm @@ -82,7 +82,3 @@ return ITALICS | REDUCE_RANGE return FALSE - -/mob/living/carbon/human/get_alt_name() - if(name != GetVoice()) - return " (as [get_id_name("Unknown")])" diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm index 44a377c2b3d..9a4a55bb7ac 100644 --- a/code/modules/mob/living/carbon/human/init_signals.dm +++ b/code/modules/mob/living/carbon/human/init_signals.dm @@ -3,6 +3,7 @@ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_UNKNOWN), SIGNAL_REMOVETRAIT(TRAIT_UNKNOWN)), PROC_REF(on_unknown_trait)) RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_DWARF), SIGNAL_REMOVETRAIT(TRAIT_DWARF)), PROC_REF(on_dwarf_trait)) + RegisterSignal(src, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, PROC_REF(get_name_part)) /// Gaining or losing [TRAIT_UNKNOWN] updates our name and our sechud /mob/living/carbon/human/proc/on_unknown_trait(datum/source) @@ -22,3 +23,18 @@ passtable_on(src, TRAIT_DWARF) else passtable_off(src, TRAIT_DWARF) + +///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 diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 62a3f10f6b8..5c06bc69e39 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -288,7 +288,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args) if(HAS_TRAIT(speaker, TRAIT_SIGN_LANG)) //Checks if speaker is using sign language - deaf_message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods, FALSE, TRUE) + deaf_message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods, TRUE) if(speaker != src) if(!radio_freq) //I'm about 90% sure there's a way to make this less cluttered diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 4fa56abf9b2..c0f093c38c6 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -893,7 +893,10 @@ var/treated_message = source.say_quote(raw_translation, spans, message_mods) var/start = "Relayed Speech: " - var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]" + 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/hrefpart = "" var/jobpart = "Unknown" diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9e1e3ecf138..5e2ef600a67 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1234,10 +1234,16 @@ * Proc that returns TRUE if the mob can write using the writing_instrument, FALSE otherwise. * * This proc a side effect, outputting a message to the mob's chat with a reason if it returns FALSE. + * Unless silent_if_not_writing_tool is TRUE. In that case it'll be silent if it isn't a writing implement/tool/instrument w/e. */ -/mob/proc/can_write(obj/item/writing_instrument) - if(!istype(writing_instrument)) - to_chat(src, span_warning("You can't write with the [writing_instrument]!")) +/mob/proc/can_write(obj/item/writing_instrument, silent_if_not_writing_tool = FALSE) + if(!writing_instrument) + return FALSE + + var/pen_info = writing_instrument.get_writing_implement_details() + if(!pen_info || (pen_info["interaction_mode"] != MODE_WRITING)) + if(!silent_if_not_writing_tool) + to_chat(src, span_warning("You can't write with the [writing_instrument]!")) return FALSE if(HAS_MIND_TRAIT(src, TRAIT_MIMING) && !istype(writing_instrument, /obj/item/toy/crayon/mime)) @@ -1252,11 +1258,6 @@ to_chat(src, span_warning("It's too dark in here to write anything!")) return FALSE - var/pen_info = writing_instrument.get_writing_implement_details() - if(!pen_info || (pen_info["interaction_mode"] != MODE_WRITING)) - to_chat(src, span_warning("You can't write with the [writing_instrument]!")) - return FALSE - if(has_gravity()) return TRUE diff --git a/icons/mob/inhands/equipment/idcards_lefthand.dmi b/icons/mob/inhands/equipment/idcards_lefthand.dmi index 53b40584e7baefc80f16a7d0e0b5f15c486d3e4e..90b96bd4d47a6f1fd643341e7bfeac9e06446589 100644 GIT binary patch delta 448 zcmV;x0YCnu1C#`RiBL{Q4GJ0x0000DNk~Le0001>0001h1Oos70Det>C;$KeCs0gO zMF0Q*NJvPds-xL`Kr783#sDYym;k%GyJSTtZc8kia!r3!E>SfZWnmF;00001bW%=J z06^y0W&i*Hi+WU8bVOxyV{&P5bZKvH004NLQ&wA^03DD^%RtjhF8}}m1W80eRA_8xFRQR8*<)zUw7M2 zc4DwRWJ&jh?hPfPc+n--~g#7Bga3MIqiIwkGH^11{RnL9t`za<^ q`O=RTEo~zw_=b%d4QcNNk?jjmNgIEz5m3p0>36sY+Dq$C1-LR^8ghlj`FRg1OH z%HOPYIL4s&ZV}S-r9_w?zg< z|3j6tyC$xdWn*|TvGa@P@3X0Aj&${^`hR6iuCTfrbEId*jU`FD976W}1-iA)=lAaf zy~>sRGY((=`A{j%CG@D6=w;SLhiVxZUV!9uD_8SJ9KQVY9Z>G58H$wt&lUVP4qyKI h5+ub3k@|2vPMD4N^DPyfmq1rDc)I$ztaD0e0syleeT@JB diff --git a/icons/mob/inhands/equipment/idcards_righthand.dmi b/icons/mob/inhands/equipment/idcards_righthand.dmi index 19a6f77c283811d2f1c2e6ddc8724942f368a39a..a4b0ec4bb746c3da99bc44bb7ddeb7169f197ea9 100644 GIT binary patch delta 462 zcmV;<0Wtou1EK_fiBL{Q4GJ0x0000DNk~Le0001>0001h1Oos70Det>C;$KeCs0gO zMgRZ*NJvPds-xL`Kr783#sDYym;k%GyJSTtZc8kia!pY+8GlqRm}bcP00001bW%=J z06^y0W&i*Hi+WU8bVOxyV{&P5bZKvH004NLQ&wA^03DD^%RtjhF8}}m5=lfsRA_v<016vW*nka?NVGKS zsa2MsBot~(uqu^!KWFZEc{=Co?w5CeGS$f>2_b}#e@3cpC#)Ynw%;$HasZ%7Csgb)%)Odf5}OCT{@5(z^*7WLcfiTkteS3? zTcd>broRE}EGc_;XRhW!c%dcxULaQH#s6O#sB~S07*qoM6N<$ Ef**CiS^xk5 delta 322 zcmZ3)vXMEgGr-TCmrII^fq{Y7)59eQNH>5m3p0>36sY+Dq$C1-LR|m<|L@`9v3S*D z?X&VXYaNa;sQ;M3uzUAz-SgsSfod2_g8YIR9G=}s19Dm>rdHM;T^tm7@alQiH<}FV zKG^kbd~=}}D7D?w#W5tK@$EE2u0sw2uEt6mrwU98`^YAC&2yr31%qnqTC;ig&)sc2 zA8|wHWcA%Y{MP+zm#t!8nAZB&@B2sYO=S!3eSErth41GUPSIJLSHxzhZ&ki}=|HRR zAI|#!j0_A8fg%O+JzfbXl9%VMxDe<1)@k;Yz7TI_@ojh585lOGaqZxL=k;Kkf9Z+~ zu`WQ#t6<48xMZBygPH!j0~XeI+-m(6W*wx<8*`&r-r)%QQVE_f%LT7Q0{y_?>FVdQ I&MBb@0BY!h{r~^~ diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi index 95a5504f18c0582fa59aff92e70b693107d1549b..a5c4e8283010bdfc5380be68f7b38d3c83327dff 100644 GIT binary patch literal 18245 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 E08|J5+5i9m literal 15541 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