mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 07:46:20 +00:00
Refactors say modes and custom say verbs. Extends custom say verbs to more situations, forwards more spans. (#92127)
## About The Pull Request Oh man, so this entire pr started because of two things: 1. A kinda hacky fix to #92123 that got closed a good while ago. 2. A borg I know mentioning you can't do custom say verbs over robotic talk. Which subsequently led me down this rabbit hole of say modes and custom say verbs. So! The most wide-reaching thing this does is merge the custom say verb/radio emote logic that used to be specialcased in `compose_message(...)` into `say_quote(...)`, renaming this to `generate_messagepart(...)` with its new functionality. This means things that don't use the exact same chain as living things talking normally can still generate custom say verbs if given that message modifier. Then, we split up say modes into a "can we do this" and "try to do this" check to reduce conflicts (like #92123), and forward more of our data to the latter. This allows us to then edit the say modes to actually make use of that data, and with the previous addition of `generate_messagepart(...)` allow for custom say verbs to be used. In doing this I realized the logging was kind of awkward and all over the place, so we create the new logging helper `log_sayverb_talk(...)` which handles selecting how we should log things based on the given message modifiers. For better or worse I forgot about this pr for a few weeks, so I don't perfectly remember all the details, but those are the big key parts. ## Why It's Good For The Game Fixes #92123. I think custom say verbs are some of the best flavour we have for talking over radio, and any situation benefits from that being possible. It's great to be able to tap your microphone, and it's hilarious for an AI to be able to emote beaming an image directly into the heads of their borgs over robotic talk. The rest is mostly cleanup.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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("<b>\[Blob Telepathy\] [minion.real_name]</b> [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)
|
||||
|
||||
@@ -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("<b>[ling_sender.changelingID]:</b> [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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = "</span>"
|
||||
|
||||
//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 = " <span class='message'>[messagepart]</span></span>"
|
||||
|
||||
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, "<span class='red'>meme</span>"` part of the `Grey Tider says, "meme"`.
|
||||
* This proc is used to generate the 'message' part of a chat message.
|
||||
* Generates the `says, "<span class='red'>meme</span>"` 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 = "..."
|
||||
|
||||
|
||||
@@ -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("<b>\[Blob Telepathy\] [name](<font color=\"[blobstrain.color]\">[blobstrain.name]</font>)</b> [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("<b>\[Blob Telepathy\] [name](<font color=\"[blobstrain.color]\">[blobstrain.name]</font>)</b> [messagepart]"))
|
||||
relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, src, MESSAGE_TYPE_RADIO)
|
||||
|
||||
/mob/eye/blob/blob_act(obj/structure/blob/B)
|
||||
|
||||
@@ -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("<i>Drone Chat: [span_name("[name]")] <span class='message'>[say_quote(msg)]</span></i>", 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("<i>Drone Chat: [span_name("[name]")] <span class='message'>[message_part]</span></i>", TRUE)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, \
|
||||
<a href='byond://?src=[REF(M)];track=[html_encode(namepart)]'>[span_name("[namepart] ([designation])")]</a> \
|
||||
<span class='message'>[quoted_message]</span>\
|
||||
<a href='byond://?src=[REF(hearing_mob)];track=[html_encode(namepart)]'>[span_name("[namepart] ([designation])")]</a> \
|
||||
<span class='message'>[messagepart]</span>\
|
||||
"),
|
||||
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]")] <span class='message'>[quoted_message]</span>\
|
||||
[span_name("[namepart]")] <span class='message'>[messagepart]</span>\
|
||||
"),
|
||||
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]")] <span class='message'>[quoted_message]</span>\
|
||||
[span_name("[namepart]")] <span class='message'>[messagepart]</span>\
|
||||
"),
|
||||
type = MESSAGE_TYPE_RADIO,
|
||||
avoid_highlighting = src == M
|
||||
avoid_highlighting = (src == hearing_mob)
|
||||
)
|
||||
|
||||
/mob/living/silicon/binarycheck()
|
||||
|
||||
@@ -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 = "<span class='game'><span class='prefix'>DEAD:</span> <span class='name'>[name]</span>[alt_name]"
|
||||
var/rendered = " <span class='message'>[emoji_parse(spanned)]</span></span>"
|
||||
log_talk(message, LOG_SAY, tag="DEAD")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user