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 1df2f39cc1b..378a86aceeb 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -105,7 +105,7 @@ /// Return to prevent the movable from talking into the radio. #define COMPONENT_CANNOT_USE_RADIO (1<<0) -/// Sent from /atom/movable/proc/say_quote() after say verb is chosen and before spans are applied. +/// Sent from /atom/movable/proc/generate_messagepart() generating a quoted message, after say verb is chosen and before spans are applied. #define COMSIG_MOVABLE_SAY_QUOTE "movable_say_quote" // Used to access COMSIG_MOVABLE_SAY_QUOTE argslist /// The index of args that corresponds to the actual message diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index 61c773b0b7c..1a3f3834eb0 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -52,7 +52,10 @@ #define MODE_KEY_PUPPET "j" #define MODE_ALIEN "alientalk" +#define MODE_KEY_ALIEN "a" + #define MODE_HOLOPAD "holopad" +#define MODE_KEY_HOLOPAD "h" #define MODE_CHANGELING "changeling" #define MODE_KEY_CHANGELING "g" @@ -125,6 +128,9 @@ #define MSG_AUDIBLE (1<<1) +// Say mode message handling return flags, exist for readability. +/// Say mode has handled the message. +#define SAYMODE_MESSAGE_HANDLED (1<<0) // Used in visible_message_flags, audible_message_flags and runechat_flags /// Automatically applies emote related spans/fonts/formatting to the message diff --git a/code/__HELPERS/chat.dm b/code/__HELPERS/chat.dm index 669a5f38fc4..5be492d9ef0 100644 --- a/code/__HELPERS/chat.dm +++ b/code/__HELPERS/chat.dm @@ -95,5 +95,10 @@ it will be sent to all connected chats. /// Sends a message to everyone within the list, as well as all observers. /proc/relay_to_list_and_observers(message, list/mob_list, source, message_type = null) for(var/mob/creature as anything in mob_list) - to_chat(creature, message, type = message_type) + to_chat( + creature, + message, + type = message_type, + avoid_highlighting = (creature == source), + ) send_to_observers(message, source) diff --git a/code/__HELPERS/logging/talk.dm b/code/__HELPERS/logging/talk.dm index 6d9c534287f..da5bebc17fc 100644 --- a/code/__HELPERS/logging/talk.dm +++ b/code/__HELPERS/logging/talk.dm @@ -1,3 +1,28 @@ +/** + * Helper for logging chat messages that may or may not have a custom say verb, + * or be a pure radio emote outright. + * + * This proc reads the `message_mods` to determine + * in what ways the given message should be logged, + * and forwards it to other logging procs as such. + * Arguments: + * * message - The message being logged + * * message_mods - A list of message modifiers, i.e. whispering/singing. + * * tag - tag that indicates the type of text(announcement, telepathy, etc) + * * log_globally - boolean checking whether or not we write this log to the log file + * * forced_by - source that forced the dialogue if any + */ +/atom/proc/log_sayverb_talk(message, list/message_mods = list(), tag = null, log_globally = TRUE, forced_by = null) + // If it's just the custom say verb, log it to emotes. + if(message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) + log_talk(message_mods[MODE_CUSTOM_SAY_EMOTE], LOG_RADIO_EMOTE, tag, log_globally, forced_by) + return + + if(message_mods[WHISPER_MODE]) + log_talk(message, LOG_WHISPER, tag, log_globally, forced_by, message_mods[MODE_CUSTOM_SAY_EMOTE]) + else + log_talk(message, LOG_SAY, tag, log_globally, forced_by, message_mods[MODE_CUSTOM_SAY_EMOTE]) + /** * Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) * diff --git a/code/controllers/subsystem/networks/radio.dm b/code/controllers/subsystem/networks/radio.dm index 578b69b0d0e..6d880cedeb3 100644 --- a/code/controllers/subsystem/networks/radio.dm +++ b/code/controllers/subsystem/networks/radio.dm @@ -11,6 +11,15 @@ SUBSYSTEM_DEF(radio) saymodes[SM.key] = SM return ..() +/// Gets the say mode associated with the given key, if available to the given user. +/datum/controller/subsystem/radio/proc/get_available_say_mode(mob/living/user, key) + var/datum/saymode/selected_saymode = SSradio.saymodes[key] + if(isnull(selected_saymode)) + return + if(!selected_saymode.can_be_used_by(user)) + return + return selected_saymode + /datum/controller/subsystem/radio/proc/add_object(obj/device, new_frequency as num, filter = null as text|null) var/f_text = num2text(new_frequency) var/datum/radio_frequency/frequency = frequencies[f_text] diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 449b0ec0657..617f590e5fb 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -223,7 +223,6 @@ /mob/eye/imaginary_friend/send_speech(message, range = IMAGINARY_FRIEND_SPEECH_RANGE, obj/source = src, bubble_type = bubble_icon, list/spans = list(), datum/language/message_language = null, list/message_mods = list(), forced = null) message = get_message_mods(message, message_mods) - message = capitalize(message) if(message_mods[RADIO_EXTENSION] == MODE_ADMIN) SSadmin_verbs.dynamic_invoke_verb(client, /datum/admin_verb/cmd_admin_say, message) @@ -236,6 +235,9 @@ if(check_emote(message, forced)) return + message = check_for_custom_say_emote(message, message_mods) + message = capitalize(message) + if(message_mods[MODE_SING]) var/randomnote = pick("♩", "♪", "♫") message = "[randomnote] [capitalize(message)] [randomnote]" @@ -246,21 +248,17 @@ var/eavesdrop_range = 0 - if (message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) - message = message_mods[MODE_CUSTOM_SAY_EMOTE] - log_message(message, LOG_RADIO_EMOTE) - else + if(!(message_mods[MODE_CUSTOM_SAY_ERASE_INPUT])) if(message_mods[WHISPER_MODE] == MODE_WHISPER) - log_talk(message, LOG_WHISPER, tag="imaginary friend", forced_by = forced, custom_say_emote = message_mods[MODE_CUSTOM_SAY_EMOTE]) spans |= SPAN_ITALICS eavesdrop_range = EAVESDROP_EXTRA_RANGE range = WHISPER_RANGE - else - log_talk(message, LOG_SAY, tag="imaginary friend", forced_by = forced, custom_say_emote = message_mods[MODE_CUSTOM_SAY_EMOTE]) - var/quoted_message = say_quote(apply_message_emphasis(message), spans, message_mods) - var/rendered = "[span_name("[name]")] [quoted_message]" - var/dead_rendered = "[span_name("[name] (Imaginary friend of [owner])")] [quoted_message]" + log_sayverb_talk(message, message_mods, tag = "imaginary friend", forced_by = forced) + + var/messagepart = generate_messagepart(message, spans, message_mods) + var/rendered = "[span_name("[name]")] [messagepart]" + var/dead_rendered = "[span_name("[name] (Imaginary friend of [owner])")] [messagepart]" var/language = message_language || owner.get_selected_language() Hear(rendered, src, language, message, null, null, null, spans, message_mods) // We always hear what we say diff --git a/code/datums/components/blob_minion.dm b/code/datums/components/blob_minion.dm index a17fd4b4a15..75eee8410d2 100644 --- a/code/datums/components/blob_minion.dm +++ b/code/datums/components/blob_minion.dm @@ -172,11 +172,17 @@ /// We only speak telepathically to blobs /datum/component/blob_minion/proc/on_try_speech(mob/living/minion, message, ignore_spam, forced) SIGNAL_HANDLER - minion.log_talk(message, LOG_SAY, tag = "blob hivemind telepathy") - var/spanned_message = minion.say_quote(message) + INVOKE_ASYNC(src, PROC_REF(send_blob_telepathy), minion, message) + return COMPONENT_CANNOT_SPEAK + +/datum/component/blob_minion/proc/send_blob_telepathy(mob/living/minion, message) + var/list/message_mods = list() + // Note: check_for_custom_say_emote can sleep. + var/adjusted_message = minion.check_for_custom_say_emote(message, message_mods) + minion.log_sayverb_talk(message, message_mods, tag = "blob hivemind telepathy") + var/spanned_message = minion.generate_messagepart(adjusted_message, message_mods = message_mods) var/rendered = span_blob("\[Blob Telepathy\] [minion.real_name] [spanned_message]") relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, minion, MESSAGE_TYPE_RADIO) - return COMPONENT_CANNOT_SPEAK /// Called when a blob minion is transformed into something else, hopefully a spore into a zombie /datum/component/blob_minion/proc/on_transformed(mob/living/minion, mob/living/replacement) diff --git a/code/datums/saymode.dm b/code/datums/saymode.dm index 5e67d960b42..951c805f0af 100644 --- a/code/datums/saymode.dm +++ b/code/datums/saymode.dm @@ -1,20 +1,40 @@ /datum/saymode + /// The symbol key used to enable this say mode. var/key + /// The corresponding say mode string. var/mode + /// Whether this say mode works with custom say emotes. + var/allows_custom_say_emotes = FALSE -//Return FALSE if you have handled the message. Otherwise, return TRUE and saycode will continue doing saycode things. -//user = whoever said the message -//message = the message -//language = the language. -/datum/saymode/proc/handle_message(mob/living/user, message, datum/language/language) +/// Checks whether this saymode can be used by the given user. May send feedback. +/datum/saymode/proc/can_be_used_by(mob/living/user) return TRUE +/** + * Handles actually modifying or forwarding our message. + * Returns `SAYMODE_[X]` flags. + * + * user - The living speaking using this say mode. + * message - The message to be said. + * spans - A list of spans to attach to the message. + * language - The language the message was said in. + * message_mods - A list of message modifiers, i.e. whispering/singing. + */ +/datum/saymode/proc/handle_message( + mob/living/user, + message, + list/spans = list(), + datum/language/language, + list/message_mods = list() +) + return NONE + + /datum/saymode/changeling key = MODE_KEY_CHANGELING mode = MODE_CHANGELING -/datum/saymode/changeling/handle_message(mob/living/user, message, datum/language/language) - //we can send the message +/datum/saymode/changeling/can_be_used_by(mob/living/user) if(!user.mind) return FALSE if(user.mind.has_antag_datum(/datum/antagonist/fallen_changeling)) @@ -26,11 +46,20 @@ if(HAS_TRAIT(user, TRAIT_CHANGELING_HIVEMIND_MUTE)) to_chat(user, span_warning("The poison in the air hinders our ability to interact with the hivemind.")) return FALSE + return TRUE - user.log_talk(message, LOG_SAY, tag="changeling [ling_sender.changelingID]") +/datum/saymode/changeling/handle_message/handle_message( + mob/living/user, + message, + list/spans = list(), + datum/language/language, + list/message_mods = list() +) + var/datum/antagonist/changeling/ling_sender = IS_CHANGELING(user) + user.log_talk(message, LOG_SAY, tag = "changeling [ling_sender.changelingID]") var/msg = span_changeling("[ling_sender.changelingID]: [message]") - //the recipients can receive the message + // Send the message to our other changelings. for(var/datum/antagonist/changeling/ling_receiver in GLOB.antagonists) if(!ling_receiver.owner) continue @@ -45,54 +74,96 @@ for(var/mob/dead/ghost as anything in GLOB.dead_mob_list) to_chat(ghost, "[FOLLOW_LINK(ghost, user)] [msg]", type = MESSAGE_TYPE_RADIO) - return FALSE + return SAYMODE_MESSAGE_HANDLED + /datum/saymode/xeno - key = "a" + key = MODE_KEY_ALIEN mode = MODE_ALIEN + allows_custom_say_emotes = TRUE -/datum/saymode/xeno/handle_message(mob/living/user, message, datum/language/language) - if(user.hivecheck()) - user.alien_talk(message) - return FALSE +/datum/saymode/xeno/can_be_used_by(mob/living/user) + if(!user.hivecheck()) + return FALSE + return TRUE + +/datum/saymode/xeno/handle_message/handle_message( + mob/living/user, + message, + list/spans = list(), + datum/language/language, + list/message_mods = list() +) + user.alien_talk(message, spans, message_mods) + return SAYMODE_MESSAGE_HANDLED /datum/saymode/vocalcords key = MODE_KEY_VOCALCORDS mode = MODE_VOCALCORDS -/datum/saymode/vocalcords/handle_message(mob/living/user, message, datum/language/language) - if(iscarbon(user)) - var/mob/living/carbon/C = user - var/obj/item/organ/vocal_cords/V = C.get_organ_slot(ORGAN_SLOT_VOICE) - if(V?.can_speak_with()) - V.handle_speech(message) //message - V.speak_with(message) //action - return FALSE +/datum/saymode/vocalcords/can_be_used_by(mob/living/user) + if(!iscarbon(user)) + return FALSE + return TRUE + +/datum/saymode/vocalcords/handle_message/handle_message( + mob/living/user, + message, + list/spans = list(), + datum/language/language, + list/message_mods = list() +) + var/mob/living/carbon/carbon_user = user + var/obj/item/organ/vocal_cords/our_vocal_cords = carbon_user.get_organ_slot(ORGAN_SLOT_VOICE) + if(our_vocal_cords?.can_speak_with()) + our_vocal_cords.handle_speech(message) //message + our_vocal_cords.speak_with(message) //action + return SAYMODE_MESSAGE_HANDLED /datum/saymode/binary //everything that uses .b (silicons, drones) key = MODE_KEY_BINARY mode = MODE_BINARY + allows_custom_say_emotes = TRUE -/datum/saymode/binary/handle_message(mob/living/user, message, datum/language/language) +/datum/saymode/binary/can_be_used_by(mob/living/user) + if(!isdrone(user) && !user.binarycheck()) + return FALSE + return TRUE + +/datum/saymode/binary/handle_message/handle_message( + mob/living/user, + message, + list/spans = list(), + datum/language/language, + list/message_mods = list() +) if(isdrone(user)) var/mob/living/basic/drone/drone_user = user - drone_user.drone_chat(message) - return FALSE - if(user.binarycheck()) - user.robot_talk(message) - return FALSE - return FALSE + drone_user.drone_chat(message, spans, message_mods) + else if(user.binarycheck()) + user.robot_talk(message, spans, message_mods) + return SAYMODE_MESSAGE_HANDLED /datum/saymode/holopad - key = "h" + key = MODE_KEY_HOLOPAD mode = MODE_HOLOPAD + allows_custom_say_emotes = TRUE -/datum/saymode/holopad/handle_message(mob/living/user, message, datum/language/language) - if(isAI(user)) - var/mob/living/silicon/ai/AI = user - AI.holopad_talk(message, language) +/datum/saymode/holopad/can_be_used_by(mob/living/user) + if(!isAI(user)) return FALSE return TRUE + +/datum/saymode/holopad/handle_message/handle_message( + mob/living/user, + message, + list/spans = list(), + datum/language/language, + list/message_mods = list() +) + var/mob/living/silicon/ai/ai_user = user + ai_user.holopad_talk(message, spans, language, message_mods) + return SAYMODE_MESSAGE_HANDLED diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 99ad58a5f49..b7cc52ca076 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -592,7 +592,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ holocall_to_update.user.Hear(message, speaker, message_language, raw_message, radio_freq, radio_freq_name, radio_freq_color, spans, message_mods, message_range = INFINITY) if(outgoing_call?.hologram && speaker == outgoing_call.user) - outgoing_call.hologram.say(raw_message, sanitize = FALSE) + outgoing_call.hologram.say(raw_message, spans = spans, sanitize = FALSE, language = message_language, message_mods = message_mods) if(record_mode && speaker == record_user) record_message(speaker, raw_message, message_language) diff --git a/code/game/say.dm b/code/game/say.dm index a9554944715..d5b42d3905f 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -68,7 +68,8 @@ GLOBAL_LIST_INIT(freqtospan, list( return spans |= speech_span language ||= get_selected_language() - message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) + if(!message_mods[SAY_MOD_VERB]) + message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) send_speech(message, message_range, src, bubble_type, spans, language, message_mods, forced = forced) /// Called when this movable hears a message from a source. @@ -173,18 +174,15 @@ GLOBAL_LIST_INIT(freqtospan, list( //End name span. var/endspanpart = "" - //Message - var/messagepart + // Language icon. var/languageicon = "" - if(message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) - messagepart = message_mods[MODE_CUSTOM_SAY_EMOTE] - else - messagepart = speaker.say_quote(raw_message, spans, message_mods) - + if(!message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) var/datum/language/dialect = GLOB.language_datum_instances[message_language] if(istype(dialect) && dialect.display_icon(src)) languageicon = "[dialect.get_icon()] " + // The actual message part. + var/messagepart = speaker.generate_messagepart(raw_message, spans, message_mods) messagepart = " [messagepart]" return "[spanpart1][spanpart2][freqpart][languageicon][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_language, raw_message, radio_freq)][endspanpart][messagepart]" @@ -225,14 +223,20 @@ GLOBAL_LIST_INIT(freqtospan, list( return verb_say /** - * This prock is used to generate a message for chat - * Generates the `says, "meme"` part of the `Grey Tider says, "meme"`. + * This proc is used to generate the 'message' part of a chat message. + * Generates the `says, "meme"` part of the `Grey Tider says, "meme"`, + * or the `taps their microphone.` part of `Grey Tider taps their microphone.`. * * input - The message to be said * spans - A list of spans to attach to the message. Includes the atom's speech span by default * message_mods - A list of message modifiers, i.e. whispering/singing */ -/atom/movable/proc/say_quote(input, list/spans = list(speech_span), list/message_mods = list()) +/atom/movable/proc/generate_messagepart(input, list/spans = list(speech_span), list/message_mods = list()) + // If we only care about the emote part, early return. + if(message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) + return apply_message_emphasis(message_mods[MODE_CUSTOM_SAY_EMOTE]) + + // Otherwise, we format our full quoted message. if(!input) input = "..." diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index 2761077d86a..037c2169972 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -321,10 +321,11 @@ GLOBAL_LIST_EMPTY(blob_nodes) if (!message) return - src.log_talk(message, LOG_SAY) - - var/message_a = say_quote(message) - var/rendered = span_big(span_blob("\[Blob Telepathy\] [name]([blobstrain.name]) [message_a]")) + var/list/message_mods = list() + var/adjusted_message = check_for_custom_say_emote(message, message_mods) + log_sayverb_talk(message, message_mods, tag = "blob hivemind telepathy") + var/messagepart = generate_messagepart(adjusted_message, message_mods = message_mods) + var/rendered = span_big(span_blob("\[Blob Telepathy\] [name]([blobstrain.name]) [messagepart]")) relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, src, MESSAGE_TYPE_RADIO) /mob/eye/blob/blob_act(obj/structure/blob/B) diff --git a/code/modules/mob/living/basic/drone/drone_say.dm b/code/modules/mob/living/basic/drone/drone_say.dm index af0bef41bb1..3a006d8d1b2 100644 --- a/code/modules/mob/living/basic/drone/drone_say.dm +++ b/code/modules/mob/living/basic/drone/drone_say.dm @@ -9,18 +9,24 @@ * * exact_faction_match - Passed to [/mob/proc/faction_check_atom] */ /proc/_alert_drones(msg, dead_can_hear = FALSE, atom/source, mob/living/faction_checked_mob, exact_faction_match) - if (dead_can_hear && source) - for (var/mob/dead_mob in GLOB.dead_mob_list) + if(dead_can_hear && source) + for(var/mob/dead_mob in GLOB.dead_mob_list) var/link = FOLLOW_LINK(dead_mob, source) to_chat(dead_mob, "[link] [msg]") for(var/global_drone in GLOB.drones_list) var/mob/living/basic/drone/drone = global_drone - if(istype(drone) && drone.stat != DEAD) - if(faction_checked_mob) - if(drone.faction_check_atom(faction_checked_mob, exact_faction_match)) - to_chat(drone, msg) - else - to_chat(drone, msg) + if(!istype(drone)) + continue + if(drone.stat == DEAD) + continue + if(faction_checked_mob && !drone.faction_check_atom(faction_checked_mob, exact_faction_match)) + continue + to_chat( + drone, + msg, + type = MESSAGE_TYPE_RADIO, + avoid_highlighting = (drone == source), + ) @@ -39,5 +45,7 @@ * * Shares the same radio code with binary */ -/mob/living/basic/drone/proc/drone_chat(msg) - alert_drones("Drone Chat: [span_name("[name]")] [say_quote(msg)]", TRUE) +/mob/living/basic/drone/proc/drone_chat(message, list/spans = list(), list/message_mods = list()) + log_sayverb_talk(message, message_mods, tag = "drone chat") + var/message_part = generate_messagepart(message, spans, message_mods) + alert_drones("Drone Chat: [span_name("[name]")] [message_part]", TRUE) diff --git a/code/modules/mob/living/carbon/alien/alien_say.dm b/code/modules/mob/living/carbon/alien/alien_say.dm index 43317ff59e7..21655bfa162 100644 --- a/code/modules/mob/living/carbon/alien/alien_say.dm +++ b/code/modules/mob/living/carbon/alien/alien_say.dm @@ -1,10 +1,10 @@ -/mob/living/proc/alien_talk(message, shown_name = real_name, big_voice = FALSE) - src.log_talk(message, LOG_SAY) +/mob/living/proc/alien_talk(message, list/spans = list(), list/message_mods = list(), shown_name = real_name, big_voice = FALSE) + log_sayverb_talk(message, message_mods, tag = "alien hivemind") message = trim(message) if(!message) return - var/message_a = say_quote(message) + var/message_a = generate_messagepart(message, spans, message_mods) var/hivemind_spans = "alien" if(big_voice) hivemind_spans += " big" @@ -16,8 +16,8 @@ var/link = FOLLOW_LINK(player, src) to_chat(player, "[link] [rendered]", type = MESSAGE_TYPE_RADIO) -/mob/living/carbon/alien/adult/royal/queen/alien_talk(message, shown_name = name) - ..(message, shown_name, TRUE) +/mob/living/carbon/alien/adult/royal/queen/alien_talk(message, list/spans = list(), list/message_mods = list(), shown_name = name, big_voice = TRUE) + ..(message, spans, message_mods, shown_name, TRUE) /mob/living/carbon/hivecheck() var/obj/item/organ/alien/hivenode/N = get_organ_by_type(/obj/item/organ/alien/hivenode) diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index edb2081bf6b..12f97e9a4ce 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -121,8 +121,8 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( var/original_message = message message = get_message_mods(message, message_mods) - saymode = SSradio.saymodes[message_mods[RADIO_KEY]] - if (!forced && !saymode) + saymode = SSradio.get_available_say_mode(src, message_mods[RADIO_KEY]) + if(!forced && (isnull(saymode) || saymode.allows_custom_say_emotes)) message = check_for_custom_say_emote(message, message_mods) if(!message) @@ -176,15 +176,10 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( var/succumbed = FALSE - // If there's a custom say emote it gets logged differently. - if(message_mods[MODE_CUSTOM_SAY_EMOTE]) - log_message(message_mods[MODE_CUSTOM_SAY_EMOTE], LOG_RADIO_EMOTE) - // If it's not erasing the input portion, then something is being said and this isn't a pure custom say emote. if(!message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) if(message_mods[WHISPER_MODE] == MODE_WHISPER) message_range = 1 - log_talk(message, LOG_WHISPER, forced_by = forced, custom_say_emote = message_mods[MODE_CUSTOM_SAY_EMOTE]) if(stat == HARD_CRIT) var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health) // If we cut our message short, abruptly end it with a-.. @@ -194,8 +189,8 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( last_words = message message_mods[WHISPER_MODE] = MODE_WHISPER_CRIT succumbed = TRUE - else - log_talk(message, LOG_SAY, forced_by = forced, custom_say_emote = message_mods[MODE_CUSTOM_SAY_EMOTE]) + + log_sayverb_talk(message, message_mods, forced_by = forced) #ifdef UNIT_TESTS // Saves a ref() to our arglist specifically. @@ -241,7 +236,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( message = autopunct_bare(message) // SKYRAT EDIT ADDITION END //This is before anything that sends say a radio message, and after all important message type modifications, so you can scumb in alien chat or something - if(saymode && !saymode.handle_message(src, message, language)) + if(saymode && (saymode.handle_message(src, message, spans, language, message_mods) & SAYMODE_MESSAGE_HANDLED)) return var/radio_return = radio(message, message_mods, spans, language)//roughly 27% of living/say()'s total cost diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 00cff3fe3ae..1cdfa46c511 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -798,7 +798,7 @@ /mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) var/raw_translation = translate_language(speaker, message_language, raw_message, spans, message_mods) var/atom/movable/source = speaker.GetSource() || speaker // is the speaker virtual/radio - var/treated_message = source.say_quote(raw_translation, spans, message_mods) + var/treated_message = source.generate_messagepart(raw_translation, spans, message_mods) var/start = "Relayed Speech: " var/namepart diff --git a/code/modules/mob/living/silicon/ai/ai_say.dm b/code/modules/mob/living/silicon/ai/ai_say.dm index 8e673bbe722..ca59835483b 100644 --- a/code/modules/mob/living/silicon/ai/ai_say.dm +++ b/code/modules/mob/living/silicon/ai/ai_say.dm @@ -42,25 +42,24 @@ return FALSE //For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(message, language) +/mob/living/silicon/ai/proc/holopad_talk(message, list/spans = list(), language, list/message_mods = list()) message = trim(message) if (!message) return var/obj/machinery/holopad/active_pad = current - if(istype(active_pad) && active_pad.masters[src])//If there is a hologram and its master is the user. - var/obj/effect/overlay/holo_pad_hologram/ai_holo = active_pad.masters[src] - var/turf/padturf = get_turf(active_pad) - var/padloc - if(padturf) - padloc = AREACOORD(padturf) - else - padloc = "(UNKNOWN)" - src.log_talk(message, LOG_SAY, tag="HOLOPAD in [padloc]") - ai_holo.say(message, sanitize = FALSE, language = language) - else + // Only continue if there is a hologram and its master is the user. + if(!istype(active_pad) || !active_pad.masters[src]) to_chat(src, span_alert("No holopad connected.")) + return + + var/obj/effect/overlay/holo_pad_hologram/ai_holo = active_pad.masters[src] + var/turf/pad_turf = get_turf(active_pad) + var/pad_loc = pad_turf ? AREACOORD(pad_turf) : "(UNKNOWN)" + + log_sayverb_talk(message, message_mods, tag = "HOLOPAD in [pad_loc]") + ai_holo.say(message, spans = spans, sanitize = FALSE, language = language, message_mods = message_mods) /* SKYRAT EDIT REMOVAL - MOVED TO: MODULAR_SKYRAT/MODULES/ALT_VOX/CODE/VOX_PROCS.DM // Make sure that the code compiles with AI_VOX undefined diff --git a/code/modules/mob/living/silicon/silicon_say.dm b/code/modules/mob/living/silicon/silicon_say.dm index d2b83890171..7e806bbdf8c 100644 --- a/code/modules/mob/living/silicon/silicon_say.dm +++ b/code/modules/mob/living/silicon/silicon_say.dm @@ -1,8 +1,8 @@ -/mob/living/proc/robot_talk(message) - log_talk(message, LOG_SAY, tag="binary") +/mob/living/proc/robot_talk(message, list/spans = list(), list/message_mods = list()) + log_sayverb_talk(message, message_mods, tag="binary") var/designation = "Default Cyborg" - var/spans = list(SPAN_ROBOT) + spans |= SPAN_ROBOT if(issilicon(src)) var/mob/living/silicon/player = src @@ -15,9 +15,10 @@ // AIs are loud and ugly spans |= SPAN_COMMAND - var/quoted_message = say_quote( + var/messagepart = generate_messagepart( message, - spans + spans, + message_mods, ) var/namepart = name @@ -31,31 +32,31 @@ namepart = brain.mainframe.name designation = brain.mainframe.job - for(var/mob/M in GLOB.player_list) - if(M.binarycheck()) - if(isAI(M)) + for(var/mob/hearing_mob in GLOB.player_list) + if(hearing_mob.binarycheck()) + if(isAI(hearing_mob)) to_chat( - M, + hearing_mob, span_binarysay("\ Robotic Talk, \ - [span_name("[namepart] ([designation])")] \ - [quoted_message]\ + [span_name("[namepart] ([designation])")] \ + [messagepart]\ "), type = MESSAGE_TYPE_RADIO, - avoid_highlighting = src == M + avoid_highlighting = (src == hearing_mob) ) else to_chat( - M, + hearing_mob, span_binarysay("\ Robotic Talk, \ - [span_name("[namepart]")] [quoted_message]\ + [span_name("[namepart]")] [messagepart]\ "), type = MESSAGE_TYPE_RADIO, - avoid_highlighting = src == M + avoid_highlighting = (src == hearing_mob) ) - if(isobserver(M)) + if(isobserver(hearing_mob)) var/following = src // If the AI talks on binary chat, we still want to follow @@ -65,17 +66,17 @@ var/mob/living/silicon/ai/ai = src following = ai.eyeobj - var/follow_link = FOLLOW_LINK(M, following) + var/follow_link = FOLLOW_LINK(hearing_mob, following) to_chat( - M, + hearing_mob, span_binarysay("\ [follow_link] \ Robotic Talk, \ - [span_name("[namepart]")] [quoted_message]\ + [span_name("[namepart]")] [messagepart]\ "), type = MESSAGE_TYPE_RADIO, - avoid_highlighting = src == M + avoid_highlighting = (src == hearing_mob) ) /mob/living/silicon/binarycheck() diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index 8dce7302089..90f84cfe7da 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -162,7 +162,7 @@ if(name != real_name) alt_name = " (died as [real_name])" - var/spanned = say_quote(apply_message_emphasis(message)) + var/spanned = generate_messagepart(message) var/source = "DEAD: [name][alt_name]" var/rendered = " [emoji_parse(spanned)]" log_talk(message, LOG_SAY, tag="DEAD") diff --git a/code/modules/mob/say_readme.md b/code/modules/mob/say_readme.md index 5284012de29..e39dfb9fbcf 100644 --- a/code/modules/mob/say_readme.md +++ b/code/modules/mob/say_readme.md @@ -53,7 +53,7 @@ global procs languages live either in datum/languages_holder or in the mind. verb_say/verb_ask/verb_exclaim/verb_yell/verb_sing - These determine what the verb is for their respective action. Used in say_quote(). + These determine what the verb is for their respective action. Used in generate_messagepart(). say(message, bubble_type, var/list/spans, sanitize, datum/language/language, ignore_spam, forced) Say() is the "mother-proc". It calls all the other procs required for speaking, but does little itself. @@ -82,8 +82,8 @@ global procs Modifies the message by comparing the languages of the speaker with the languages of the hearer. Called on the hearer. - say_quote(input, spans, list/message_mods) - Adds a verb and quotes to a message. Also attaches span classes to a message. + generate_messagepart(input, spans, list/message_mods) + Either adds a lone custom say verb, or a verb and quotes to a message. Also attaches span classes to a message. Verbs are determined by verb_say/verb_ask/verb_yell/verb_sing variables. Called on the speaker. /mob diff --git a/code/modules/mod/mod_link.dm b/code/modules/mod/mod_link.dm index 85ff92cf2e7..e44c923403e 100644 --- a/code/modules/mod/mod_link.dm +++ b/code/modules/mod/mod_link.dm @@ -122,7 +122,7 @@ . = ..() if(speaker != wearer && speaker != ai_assistant) return - mod_link.visual.say(raw_message, sanitize = FALSE, message_range = 2) + mod_link.visual.say(raw_message, spans = spans, sanitize = FALSE, language = message_language, message_range = 2, message_mods = message_mods) /obj/item/mod/control/proc/on_overlay_change(atom/source, cache_index, overlay) SIGNAL_HANDLER @@ -306,7 +306,7 @@ . = ..() if(speaker != loc) return - mod_link.visual.say(raw_message, sanitize = FALSE, message_range = 3) + mod_link.visual.say(raw_message, spans = spans, sanitize = FALSE, language = message_language, message_range = 3, message_mods = message_mods) /obj/item/clothing/neck/link_scryer/proc/on_overlay_change(atom/source, cache_index, overlay) SIGNAL_HANDLER