diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index 3ddd0eb8538..b1914cc966b 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -155,9 +155,9 @@ #define ZIMPACT_NO_SPIN (1<<2) /// From mob/living/try_speak(): (message, ignore_spam, forced) -#define COMSIG_LIVING_TRY_SPEECH "living_vocal_speech" - /// Return if the mob can speak the message, regardless of any other signal returns or checks. - #define COMPONENT_CAN_ALWAYS_SPEAK (1<<0) +#define COMSIG_MOB_TRY_SPEECH "living_vocal_speech" + /// Return to skip can_speak check, IE, forcing success. Overrides below. + #define COMPONENT_IGNORE_CAN_SPEAK (1<<0) /// Return if the mob cannot speak. #define COMPONENT_CANNOT_SPEAK (1<<1) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index e3c57b59f62..91dbba15ff4 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -311,9 +311,11 @@ // /obj/item/radio signals +///called from base of /obj/item/proc/talk_into(): (atom/movable/speaker, message, channel, list/spans, language, list/message_mods) +#define COMSIG_ITEM_TALK_INTO "item_talk_into" ///called from base of /obj/item/radio/proc/set_frequency(): (list/args) #define COMSIG_RADIO_NEW_FREQUENCY "radio_new_frequency" -///called from base of /obj/item/radio/proc/talk_into(): (atom/movable/M, message, channel) +///called from base of /obj/item/radio/talk_into(): (atom/movable/M, message, channel) #define COMSIG_RADIO_NEW_MESSAGE "radio_new_message" ///called from base of /obj/item/radio/proc/on_receive_messgae(): (list/data) #define COMSIG_RADIO_RECEIVE_MESSAGE "radio_receive_message" diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index 04b1aab8455..e007eaadc6d 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -59,11 +59,14 @@ #define MODE_MAFIA "mafia" +/// Applies singing characters to the message #define MODE_SING "sing" - +/// A custom say emote is being supplied [value = the emote] #define MODE_CUSTOM_SAY_EMOTE "custom_say" - +/// No message is following, just emote #define MODE_CUSTOM_SAY_ERASE_INPUT "erase_input" +/// Message is being relayed through another object +#define MODE_RELAY "relayed" //Spans. Robot speech, italics, etc. Applied in compose_message(). #define SPAN_ROBOT "robot" @@ -79,8 +82,11 @@ #define SPAN_HELIUM "small" //bitflag #defines for return value of the radio() proc. +/// Makes the message use italics #define ITALICS (1<<0) +/// Reduces the range of the message to 1 #define REDUCE_RANGE (1<<1) +/// Stops any actual message from being sent #define NOPASS (1<<2) /// Range to hear normal messages diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index adad6491cf4..3e2c91efb5d 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -171,7 +171,8 @@ to_chat(src, span_notice("As a split personality, you cannot do anything but observe. However, you will eventually gain control of your body, switching places with the current personality.")) to_chat(src, span_warning("Do not commit suicide or put the body in a deadly position. Behave like you care about it as much as the owner.")) -/mob/living/split_personality/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/split_personality/try_speak(message, ignore_spam, forced, filterproof) + SHOULD_CALL_PARENT(FALSE) to_chat(src, span_warning("You cannot speak, your other self is controlling your body!")) return FALSE diff --git a/code/datums/components/blob_minion.dm b/code/datums/components/blob_minion.dm index 8261a7ad1fd..fb92e1139cc 100644 --- a/code/datums/components/blob_minion.dm +++ b/code/datums/components/blob_minion.dm @@ -55,7 +55,7 @@ RegisterSignal(parent, COMSIG_ATOM_FIRE_ACT, PROC_REF(on_burned)) RegisterSignal(parent, COMSIG_ATOM_TRIED_PASS, PROC_REF(on_attempted_pass)) RegisterSignal(parent, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(on_space_move)) - RegisterSignal(parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(parent, COMSIG_MOB_CHANGED_TYPE, PROC_REF(on_transformed)) living_parent.update_appearance(UPDATE_ICON) GLOB.blob_telepathy_mobs |= parent @@ -73,7 +73,7 @@ COMSIG_ATOM_FIRE_ACT, COMSIG_ATOM_TRIED_PASS, COMSIG_ATOM_UPDATE_ICON, - COMSIG_LIVING_TRY_SPEECH, + COMSIG_MOB_TRY_SPEECH, COMSIG_MOB_CHANGED_TYPE, COMSIG_MOB_GET_STATUS_TAB_ITEMS, COMSIG_MOB_MIND_INITIALIZED, diff --git a/code/datums/components/chuunibyou.dm b/code/datums/components/chuunibyou.dm index dda1bdeed5a..57428bc4223 100644 --- a/code/datums/components/chuunibyou.dm +++ b/code/datums/components/chuunibyou.dm @@ -45,7 +45,7 @@ . = ..() RegisterSignal(parent, COMSIG_MOB_SPELL_PROJECTILE, PROC_REF(on_spell_projectile)) RegisterSignal(parent, COMSIG_MOB_PRE_INVOCATION, PROC_REF(on_pre_invocation)) - RegisterSignal(parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(parent, COMSIG_MOB_AFTER_SPELL_CAST, PROC_REF(on_after_spell_cast)) /datum/component/chuunibyou/UnregisterFromParent() @@ -53,7 +53,7 @@ UnregisterSignal(parent, list( COMSIG_MOB_SPELL_PROJECTILE, COMSIG_MOB_PRE_INVOCATION, - COMSIG_LIVING_TRY_SPEECH, + COMSIG_MOB_TRY_SPEECH, COMSIG_MOB_AFTER_SPELL_CAST, )) @@ -63,7 +63,7 @@ SIGNAL_HANDLER if(casting_spell) - return COMPONENT_CAN_ALWAYS_SPEAK + return COMPONENT_IGNORE_CAN_SPEAK ///signal sent when the parent casts a spell that has a projectile /datum/component/chuunibyou/proc/on_spell_projectile(mob/living/source, datum/action/cooldown/spell/spell, atom/cast_on, obj/projectile/to_fire) diff --git a/code/datums/components/marionette.dm b/code/datums/components/marionette.dm index a2f58031768..2777bbb5178 100644 --- a/code/datums/components/marionette.dm +++ b/code/datums/components/marionette.dm @@ -67,6 +67,7 @@ language = language, forced = "[source]'s marionette", saymode = saymode, + message_mods = list(MODE_RELAY = TRUE), ) speech_args[SPEECH_RANGE] = WHISPER_RANGE diff --git a/code/datums/components/sign_language.dm b/code/datums/components/sign_language.dm index 23e40258100..e8d22238705 100644 --- a/code/datums/components/sign_language.dm +++ b/code/datums/components/sign_language.dm @@ -79,7 +79,7 @@ carbon_parent.verb_yell = "emphatically signs" carbon_parent.bubble_icon = "signlang" RegisterSignal(carbon_parent, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(on_added_organ)) - RegisterSignal(carbon_parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(carbon_parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(carbon_parent, COMSIG_LIVING_TREAT_MESSAGE, PROC_REF(on_treat_living_message)) RegisterSignal(carbon_parent, COMSIG_MOVABLE_USING_RADIO, PROC_REF(on_using_radio)) RegisterSignal(carbon_parent, COMSIG_MOVABLE_SAY_QUOTE, PROC_REF(on_say_quote)) @@ -106,7 +106,7 @@ carbon_parent.bubble_icon = initial(carbon_parent.bubble_icon) UnregisterSignal(carbon_parent, list( COMSIG_CARBON_GAIN_ORGAN, - COMSIG_LIVING_TRY_SPEECH, + COMSIG_MOB_TRY_SPEECH, COMSIG_LIVING_TREAT_MESSAGE, COMSIG_MOVABLE_USING_RADIO, COMSIG_MOVABLE_SAY_QUOTE, @@ -125,7 +125,7 @@ var/obj/item/organ/internal/tongue/new_tongue = new_organ new_tongue.temp_say_mod = "signs" -/// Signal proc for [COMSIG_LIVING_TRY_SPEECH] +/// Signal proc for [COMSIG_MOB_TRY_SPEECH] /// Sign languagers can always speak regardless of they're mute (as long as they're not mimes) /datum/component/sign_language/proc/on_try_speech(mob/living/source, message, ignore_spam, forced) SIGNAL_HANDLER @@ -158,7 +158,7 @@ // Assuming none of the above fail, sign language users can speak // regardless of being muzzled or mute toxin'd or whatever. - return COMPONENT_CAN_ALWAYS_SPEAK + return COMPONENT_IGNORE_CAN_SPEAK /// Checks to see what state this person is in and if they are able to sign or not. /datum/component/sign_language/proc/check_signables_state() diff --git a/code/datums/elements/toy_talk.dm b/code/datums/elements/toy_talk.dm new file mode 100644 index 00000000000..8061eafaeb3 --- /dev/null +++ b/code/datums/elements/toy_talk.dm @@ -0,0 +1,28 @@ +/** + * Allows people to talk via the item with .l or .r + * + * Be sure to override [/atom/movable/proc/GetVoice] if you want the item's "voice" to not default to itself + */ +/datum/element/toy_talk + +/datum/element/toy_talk/Attach(datum/target) + . = ..() + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ITEM_TALK_INTO, PROC_REF(do_talk)) + +/datum/element/toy_talk/Detach(datum/source, ...) + . = ..() + UnregisterSignal(source, COMSIG_ITEM_TALK_INTO) + +/datum/element/toy_talk/proc/do_talk(obj/item/source, mob/speaker, message, channel, list/spans, language, list/message_mods) + SIGNAL_HANDLER + + if(!ismob(speaker) || message_mods[MODE_HEADSET] || message_mods[MODE_RELAY]) + return NONE + + message_mods[MODE_RELAY] = TRUE // Redundant (given NOPASS) but covers our bases + speaker.log_talk(message, LOG_SAY, tag = "toy talk ([source])") + source.say(message, language = language, sanitize = FALSE, message_mods = list(MODE_RELAY = TRUE)) + return NOPASS diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index ba9e4912aff..4e40fc10601 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -630,8 +630,23 @@ playsound(src, block_sound, BLOCK_SOUND_VOLUME, vary = TRUE) return TRUE -/obj/item/proc/talk_into(mob/M, input, channel, spans, datum/language/language, list/message_mods) - return ITALICS | REDUCE_RANGE +/** + * Handles someone talking INTO an item + * + * Commonly used by someone holding it and using .r or .l + * Also used by radios + * + * * speaker - the atom that is doing the talking + * * message - the message being spoken + * * channel - the channel the message is being spoken on, only really used for radios + * * spans - the spans of the message + * * language - the language the message is in + * * message_mods - any message mods that should be applied to the message + * + * Return a flag that modifies the original message + */ +/obj/item/proc/talk_into(atom/movable/speaker, message, channel, list/spans, datum/language/language, list/message_mods) + return SEND_SIGNAL(src, COMSIG_ITEM_TALK_INTO, speaker, message, channel, spans, language, message_mods) || (ITALICS|REDUCE_RANGE) /// Called when a mob drops an item. /obj/item/proc/dropped(mob/user, silent = FALSE) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index d57283d13e0..a3861199178 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -262,17 +262,29 @@ /obj/item/radio/talk_into(atom/movable/talking_movable, message, channel, list/spans, datum/language/language, list/message_mods) if(SEND_SIGNAL(talking_movable, COMSIG_MOVABLE_USING_RADIO, src) & COMPONENT_CANNOT_USE_RADIO) - return + return NONE if(SEND_SIGNAL(src, COMSIG_RADIO_NEW_MESSAGE, talking_movable, message, channel) & COMPONENT_CANNOT_USE_RADIO) - return + return NONE if(!spans) spans = list(talking_movable.speech_span) if(!language) language = talking_movable.get_selected_language() - INVOKE_ASYNC(src, PROC_REF(talk_into_impl), talking_movable, message, channel, spans.Copy(), language, message_mods) + INVOKE_ASYNC(src, PROC_REF(talk_into_impl), talking_movable, message, channel, LAZYLISTDUPLICATE(spans), language, LAZYLISTDUPLICATE(message_mods)) return ITALICS | REDUCE_RANGE +/** + * Handles talking into the radio + * + * Unlike most speech related procs, spans and message_mods are not guaranteed to be lists + * + * * talking_movable - the atom that is talking + * * message - the message to be spoken + * * channel - the channel to be spoken on + * * spans - the spans to be used, lazylist + * * language - the language to be spoken in. (Should) never be null + * * message_mods - the message mods to be used, lazylist + */ /obj/item/radio/proc/talk_into_impl(atom/movable/talking_movable, message, channel, list/spans, datum/language/language, list/message_mods) if(!on) return // the device has to be on @@ -358,9 +370,9 @@ /obj/item/radio/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) . = ..() - if(radio_freq || !broadcasting || get_dist(src, speaker) > canhear_range) + if(radio_freq || !broadcasting || get_dist(src, speaker) > canhear_range || message_mods[MODE_RELAY]) return - var/filtered_mods = list() + var/list/filtered_mods = list() if (message_mods[MODE_SING]) filtered_mods[MODE_SING] = message_mods[MODE_SING] diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 985d2761a02..c686eeb04a7 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -159,6 +159,9 @@ /obj/item/taperecorder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, list/message_mods = list(), message_range) . = ..() + if(message_mods[MODE_RELAY]) + return + if(mytape && recording) mytape.timestamp += mytape.used_capacity mytape.storedinfo += "\[[time2text(mytape.used_capacity,"mm:ss")]\] [raw_message]" diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index 90790a75ea2..e2d1b6f26e9 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -45,6 +45,7 @@ . = ..() AddComponent(/datum/component/squeak, squeak_override) AddElement(/datum/element/bed_tuckable, mapload, 6, -5, 90) + AddElement(/datum/element/toy_talk) //have we decided if Pinocchio goes in the blue or pink aisle yet? if(gender == NEUTER) diff --git a/code/game/objects/items/toy_mechs.dm b/code/game/objects/items/toy_mechs.dm index ce3ce4600c7..c50908738fe 100644 --- a/code/game/objects/items/toy_mechs.dm +++ b/code/game/objects/items/toy_mechs.dm @@ -53,6 +53,7 @@ /obj/item/toy/mecha/Initialize(mapload) . = ..() AddElement(/datum/element/series, /obj/item/toy/mecha, "Mini-Mecha action figures") + AddElement(/datum/element/toy_talk) combat_health = max_combat_health switch(special_attack_type) if(SPECIAL_ATTACK_DAMAGE) @@ -263,12 +264,8 @@ if(wins || losses) . += span_notice("This toy has [wins] wins, and [losses] losses.") -/** - * Override the say proc if they're mute - */ -/obj/item/toy/mecha/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - if(!quiet) - . = ..() +/obj/item/toy/mecha/can_speak(allow_mimes) + return !quiet && ..() /** * The 'master' proc of the mech battle. Processes the entire battle's events and makes sure it start and finishes correctly. diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index c91f6eb6c44..1d2b0bba7b1 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1013,6 +1013,7 @@ /obj/item/toy/figure/Initialize(mapload) . = ..() desc = "A \"Space Life\" brand [src]." + AddElement(/datum/element/toy_talk) /obj/item/toy/figure/attack_self(mob/user as mob) if(cooldown <= world.time) @@ -1245,13 +1246,9 @@ to_chat(user, span_notice("You name the dummy as \"[doll_name]\".")) name = "[initial(name)] - [doll_name]" -/obj/item/toy/dummy/talk_into(atom/movable/A, message, channel, list/spans, datum/language/language, list/message_mods) - var/mob/M = A - if (istype(M)) - M.log_talk(message, LOG_SAY, tag="dummy toy") - - say(message, language, sanitize = FALSE) - return NOPASS +/obj/item/toy/dummy/Initialize(mapload) + . = ..() + AddElement(/datum/element/toy_talk) /obj/item/toy/dummy/GetVoice() return doll_name diff --git a/code/game/say.dm b/code/game/say.dm index 54cebf5e775..5f03dd5bf44 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -27,7 +27,36 @@ GLOBAL_LIST_INIT(freqtospan, list( "[FREQ_CTF_YELLOW]" = "yellowteamradio" )) -/atom/movable/proc/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = FALSE, message_range = 7, datum/saymode/saymode = null) +/** + * What makes things... talk. + * + * * message - The message to say. + * * bubble_type - The type of speech bubble to use when talking + * * spans - A list of spans to attach to the message. Includes the atom's speech span by default + * * sanitize - Should we sanitize the message? Only set to FALSE if you have ALREADY sanitized it + * * language - The language to speak in. Defaults to the atom's selected language + * * ignore_spam - Should we ignore spam checks? + * * forced - What was it forced by? null if voluntary. (NOT a boolean!) + * * filterproof - Do we bypass the filter when checking the message? + * * message_range - The range of the message. Defaults to 7 + * * saymode - Saymode passed to the speech + * This is usually set automatically and is only relevant for living mobs. + * * message_mods - A list of message modifiers, i.e. whispering/singing. + * Most of these are set automatically but you can pass in your own pre-say. + */ +/atom/movable/proc/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!try_speak(message, ignore_spam, forced, filterproof)) return if(sanitize) @@ -37,7 +66,6 @@ GLOBAL_LIST_INIT(freqtospan, list( spans |= speech_span if(!language) language = get_selected_language() - var/list/message_mods = list() message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) send_speech(message, message_range, src, bubble_type, spans, language, message_mods, forced = forced) @@ -66,7 +94,7 @@ GLOBAL_LIST_INIT(freqtospan, list( * TRUE of FASE depending on if our movable can speak */ /atom/movable/proc/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE) - return TRUE + return can_speak() /** * Checks if our movable can currently speak, vocally, in general. @@ -83,7 +111,8 @@ GLOBAL_LIST_INIT(freqtospan, list( * if TRUE, we will check if the movable can speak REGARDLESS of if they have an active mime vow. */ /atom/movable/proc/can_speak(allow_mimes = FALSE) - return TRUE + SHOULD_BE_PURE(TRUE) + return !HAS_TRAIT(src, TRAIT_MUTE) /atom/movable/proc/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language, list/message_mods = list(), forced = FALSE, tts_message, list/tts_filter) var/found_client = FALSE @@ -180,7 +209,15 @@ GLOBAL_LIST_INIT(freqtospan, list( /atom/movable/proc/get_default_say_verb() return verb_say -/atom/movable/proc/say_quote(input, list/spans=list(speech_span), list/message_mods = list()) +/** + * This prock is used to generate a message for chat + * Generates the `says, "meme"` part of the `Grey Tider says, "meme"`. + * + * 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()) if(!input) input = "..." diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index e56426a89ad..401daa97ac5 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -286,7 +286,19 @@ GLOBAL_LIST_EMPTY(blob_nodes) blob_points = clamp(blob_points + points, 0, max_blob_points) hud_used.blobpwrdisplay.maptext = MAPTEXT("
[round(blob_points)]
") -/mob/camera/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/camera/blob/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if (!message) return diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm index 18c957c28fd..acefd0e3717 100644 --- a/code/modules/antagonists/disease/disease_mob.dm +++ b/code/modules/antagonists/disease/disease_mob.dm @@ -113,7 +113,19 @@ the new instance inside the host to be updated to the template's stats. for(var/datum/disease_ability/ability in purchased_abilities) . += span_notice("[ability.name]") -/mob/camera/disease/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/camera/disease/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!message) return if(sanitize) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index 52ef29f38e5..de0954c960e 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -35,7 +35,7 @@ /obj/item/assembly/voice/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) . = ..() - if(message_mods[WHISPER_MODE]) //Too quiet lad + if(message_mods[WHISPER_MODE] || message_mods[MODE_RELAY]) //Too quiet lad return FALSE if(speaker == src) return FALSE diff --git a/code/modules/mob/dead/observer/observer_say.dm b/code/modules/mob/dead/observer/observer_say.dm index e63839b123f..e43086349b3 100644 --- a/code/modules/mob/dead/observer/observer_say.dm +++ b/code/modules/mob/dead/observer/observer_say.dm @@ -9,7 +9,19 @@ mods[RADIO_EXTENSION] = GLOB.department_radio_keys[mods[RADIO_KEY]] return message -/mob/dead/observer/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/dead/observer/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) message = trim(message) //trim now and sanitize after checking for special admin radio keys var/list/filter_result = CAN_BYPASS_FILTER(src) ? null : is_ooc_filtered(message) @@ -27,7 +39,6 @@ if(!message) return - var/list/message_mods = list() message = get_message_mods(message, message_mods) if(client?.holder && (message_mods[RADIO_EXTENSION] == MODE_ADMIN || message_mods[RADIO_EXTENSION] == MODE_DEADMIN || (message_mods[RADIO_EXTENSION] == MODE_PUPPET && mind?.current))) message = trim_left(copytext_char(message, length(message_mods[RADIO_KEY]) + 2)) diff --git a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm index 1be2a870de3..1d22c5831f9 100644 --- a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm +++ b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm @@ -43,12 +43,11 @@ /mob/living/basic/migo/proc/update_dodge_chance(health_ratio) dodge_prob = LERP(50, 10, health_ratio) -/mob/living/basic/migo/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - ..() - if(stat) +/mob/living/basic/migo/send_speech(message_raw, message_range, obj/source, bubble_type, list/spans, datum/language/message_language, list/message_mods, forced, tts_message, list/tts_filter) + . = ..() + if(stat != CONSCIOUS) return - var/chosen_sound = pick(migo_sounds) - playsound(src, chosen_sound, 50, TRUE) + playsound(src, pick(migo_sounds), 50, TRUE) /mob/living/basic/migo/Life(seconds_per_tick = SSMOBS_DT, times_fired) ..() diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm index 5a901fa2c79..0a5239e1f6e 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm @@ -168,7 +168,19 @@ essencecolor = "#1D2953" //oh jeez you're dying hud_used.healths.maptext = MAPTEXT("
[essence]E
") -/mob/living/basic/revenant/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/basic/revenant/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!message) return diff --git a/code/modules/mob/living/basic/space_fauna/statue/statue.dm b/code/modules/mob/living/basic/space_fauna/statue/statue.dm index 814500674fc..3ddbc2364e8 100644 --- a/code/modules/mob/living/basic/space_fauna/statue/statue.dm +++ b/code/modules/mob/living/basic/space_fauna/statue/statue.dm @@ -54,7 +54,7 @@ /mob/living/basic/statue/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_UNOBSERVANT, INNATE_TRAIT) + add_traits(list(TRAIT_MUTE, TRAIT_UNOBSERVANT), INNATE_TRAIT) AddComponent(/datum/component/unobserved_actor, unobserved_flags = NO_OBSERVED_MOVEMENT | NO_OBSERVED_ATTACKS) var/static/list/innate_actions = list( @@ -69,14 +69,8 @@ /mob/living/basic/statue/med_hud_set_status() return //we're a statue we're invincible -/mob/living/basic/statue/can_speak(allow_mimes = FALSE) - return FALSE // We're a statue, of course we can't talk. - // Cannot talk -/mob/living/basic/statue/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - return - // Turn to dust when gibbed /mob/living/basic/statue/gib() diff --git a/code/modules/mob/living/brain/brain_say.dm b/code/modules/mob/living/brain/brain_say.dm index 79e8e1d3079..df3601c1bef 100644 --- a/code/modules/mob/living/brain/brain_say.dm +++ b/code/modules/mob/living/brain/brain_say.dm @@ -1,14 +1,25 @@ -/mob/living/brain/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterpoof = null, message_range = 7, datum/saymode/saymode = null) - if(!(container && istype(container, /obj/item/mmi))) - return //No MMI, can't speak, bucko./N - else - if(prob(emp_damage*4)) - if(prob(10))//10% chane to drop the message entirely - return - else - message = Gibberish(message, emp_damage >= 12)//scrambles the message, gets worse when emp_damage is higher +/mob/living/brain/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) + if(prob(emp_damage * 4)) + if(prob(10)) //10% chance to drop the message entirely + return + message = Gibberish(message, emp_damage >= 12)//scrambles the message, gets worse when emp_damage is higher + + return ..() - ..() +/mob/living/brain/can_speak(allow_mimes) + return istype(container, /obj/item/mmi) && ..() /mob/living/brain/radio(message, list/message_mods = list(), list/spans, language) if(message_mods[MODE_HEADSET] && istype(container, /obj/item/mmi)) diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm index 7b250ef7a03..ae41bad2cf3 100644 --- a/code/modules/mob/living/carbon/human/human_say.dm +++ b/code/modules/mob/living/carbon/human/human_say.dm @@ -1,6 +1,18 @@ -/mob/living/carbon/human/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced, filterproof, message_range, datum/saymode/saymode) +/mob/living/carbon/human/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!HAS_TRAIT(src, TRAIT_SPEAKS_CLEARLY)) var/static/regex/tongueless_lower = new("\[gdntke]+", "g") var/static/regex/tongueless_upper = new("\[GDNTKE]+", "g") diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 22f4b244324..ec94ca94b61 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -99,13 +99,24 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( return new_msg -/mob/living/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(sanitize) message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) if(!message || message == "") return - var/list/message_mods = list() var/original_message = message message = get_message_mods(message, message_mods) saymode = SSradio.saymodes[message_mods[RADIO_KEY]] @@ -211,7 +222,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( message = "[randomnote] [message] [randomnote]" spans |= SPAN_SINGING - if(LAZYACCESS(message_mods,WHISPER_MODE)) // whisper away + if(message_mods[WHISPER_MODE]) // whisper away spans |= SPAN_ITALICS if(!message) @@ -231,6 +242,9 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( return var/radio_return = radio(message, message_mods, spans, language)//roughly 27% of living/say()'s total cost + if(radio_return & NOPASS) + return TRUE + if(radio_return & ITALICS) spans |= SPAN_ITALICS if(radio_return & REDUCE_RANGE) @@ -238,8 +252,6 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( if(!message_mods[WHISPER_MODE]) message_mods[WHISPER_MODE] = MODE_WHISPER message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) - if(radio_return & NOPASS) - return TRUE //No screams in space, unless you're next to someone. var/turf/T = get_turf(src) @@ -435,38 +447,6 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( /mob/proc/binarycheck() return FALSE -/mob/living/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE) - if(!..()) - return FALSE - var/sigreturn = SEND_SIGNAL(src, COMSIG_LIVING_TRY_SPEECH, message, ignore_spam, forced) - if(sigreturn & COMPONENT_CAN_ALWAYS_SPEAK) - return TRUE - - if(sigreturn & COMPONENT_CANNOT_SPEAK) - return FALSE - - if(!can_speak()) - if(HAS_MIND_TRAIT(src, TRAIT_MIMING)) - to_chat(src, span_green("Your vow of silence prevents you from speaking!")) - else - to_chat(src, span_warning("You find yourself unable to speak!")) - return FALSE - - return TRUE - -/mob/living/can_speak(allow_mimes = FALSE) - if(!allow_mimes && HAS_MIND_TRAIT(src, TRAIT_MIMING)) - return FALSE - - if(HAS_TRAIT(src, TRAIT_MUTE)) - return FALSE - - if(is_muzzled()) - return FALSE - - return TRUE - - /** * Treats the passed message with things that may modify speech (stuttering, slurring etc). * diff --git a/code/modules/mob/living/silicon/ai/ai_say.dm b/code/modules/mob/living/silicon/ai/ai_say.dm index b7e42ea1df0..14eeb85162f 100644 --- a/code/modules/mob/living/silicon/ai/ai_say.dm +++ b/code/modules/mob/living/silicon/ai/ai_say.dm @@ -1,5 +1,17 @@ -/mob/living/silicon/ai/say(message, bubble_type,list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - if(parent && istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. +/mob/living/silicon/ai/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) + if(istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. return parent.say(arglist(args)) return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index e33578ecc55..41fdf26250a 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -56,11 +56,8 @@ M.take_damage(50, BRUTE, MELEE, 1) //Elites can't talk (normally)! -/mob/living/simple_animal/hostile/asteroid/elite/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - if(can_talk) - . = ..() - return TRUE - return FALSE +/mob/living/simple_animal/hostile/asteroid/elite/can_speak(allow_mimes) + return can_talk && ..() /*Basic setup for elite attacks, based on Whoneedspace's megafauna attack setup. While using this makes the system rely on OnFire, it still gives options for timers not tied to OnFire, and it makes using attacks consistent accross the board for player-controlled elites.*/ diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm index 9517bce4f92..77557879ccb 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm @@ -63,9 +63,11 @@ /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/become_ghost() icon_state = "herald_ghost" -/mob/living/simple_animal/hostile/asteroid/elite/herald/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/simple_animal/hostile/asteroid/elite/herald/send_speech(message_raw, message_range, obj/source, bubble_type, list/spans, datum/language/message_language, list/message_mods, forced, tts_message, list/tts_filter) . = ..() - playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) + if(stat != CONSCIOUS) + return + playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) /datum/action/innate/elite_attack/herald_trishot name = "Triple Shot" diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index 1f384878e8d..687f9ac7153 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -52,9 +52,6 @@ QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", 1, message, TRUE), SSspeech_controller) /mob/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE) - SHOULD_CALL_PARENT(TRUE) - if(!..()) - return FALSE var/list/filter_result var/list/soft_filter_result if(client && !forced && !filterproof) @@ -88,9 +85,31 @@ return FALSE if(client.handle_spam_prevention(message, MUTE_IC)) return FALSE - // Including can_speak() here would ignore COMPONENT_CAN_ALWAYS_SPEAK in /mob/living/try_speak() + + var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_TRY_SPEECH, message, ignore_spam, forced) + if(sigreturn & COMPONENT_IGNORE_CAN_SPEAK) + return TRUE + if(sigreturn & COMPONENT_CANNOT_SPEAK) + return FALSE + + if(!..()) // the can_speak check + if(HAS_MIND_TRAIT(src, TRAIT_MIMING)) + to_chat(src, span_green("Your vow of silence prevents you from speaking!")) + else + to_chat(src, span_warning("You find yourself unable to speak!")) + return FALSE + return TRUE +/mob/can_speak(allow_mimes = FALSE) + if(!allow_mimes && HAS_MIND_TRAIT(src, TRAIT_MIMING)) + return FALSE + + if(is_muzzled()) + return FALSE + + return ..() + ///Speak as a dead person (ghost etc) /mob/proc/say_dead(message) var/name = real_name diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 1defeecafe6..5d70f8d156d 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -216,13 +216,9 @@ return -/obj/item/bodypart/head/talk_into(mob/holder, message, channel, spans, datum/language/language, list/message_mods) - var/mob/headholder = holder - if(istype(headholder)) - headholder.log_talk(message, LOG_SAY, tag = "beheaded talk") - - say(message, language, sanitize = FALSE) - return NOPASS +/obj/item/bodypart/head/Initialize(mapload) + . = ..() + AddElement(/datum/element/toy_talk) /obj/item/bodypart/head/GetVoice() return "The head of [real_name]" diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 8a3ded30689..46e1d34c9ed 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -276,8 +276,8 @@ QDEL_NULL(sec_radio) return ..() -/obj/machinery/vending/can_speak() - return !shut_up +/obj/machinery/vending/can_speak(allow_mimes) + return is_operational && !shut_up && ..() /obj/machinery/vending/emp_act(severity) . = ..() diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index ed3b00c1194..308aea87420 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -20,16 +20,29 @@ new /obj/item/circuit_component/bci_core, ), SHELL_CAPACITY_SMALL, starting_circuit = circuit) -/obj/item/organ/internal/cyberimp/bci/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/obj/item/organ/internal/cyberimp/bci/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if (owner) // Otherwise say_dead will be called. // It's intentional that a circuit for a dead person does not speak from the shell. if (owner.stat == DEAD) return - owner.say(message, forced = "circuit speech") - else - return ..() + forced = "circuit speech" + return owner.say(arglist(args)) + + return ..() /obj/item/organ/internal/cyberimp/bci/proc/action_comp_registered(datum/source, obj/item/circuit_component/equipment_action/action_comp) SIGNAL_HANDLER diff --git a/modular_nova/modules/cortical_borer/code/cortical_borer.dm b/modular_nova/modules/cortical_borer/code/cortical_borer.dm index 56cca920a0c..ee716f9b533 100644 --- a/modular_nova/modules/cortical_borer/code/cortical_borer.dm +++ b/modular_nova/modules/cortical_borer/code/cortical_borer.dm @@ -393,7 +393,19 @@ GLOBAL_LIST_EMPTY(cortical_borers) //previously had borers unable to emote... but that means less RP, and we want that //borers should not be talking without a host at least -/mob/living/basic/cortical_borer/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/basic/cortical_borer/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!inside_human()) to_chat(src, span_warning("You are not able to speak without a host!")) return diff --git a/modular_nova/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm b/modular_nova/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm index 54f25fb210a..897b3cb086a 100644 --- a/modular_nova/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm +++ b/modular_nova/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm @@ -111,7 +111,19 @@ return_to_body() qdel(src) -/mob/living/soulcatcher_soul/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced, filterproof, message_range, datum/saymode/saymode) +/mob/living/soulcatcher_soul/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) if(!message || message == "") return diff --git a/tgstation.dme b/tgstation.dme index 92a9016dbd0..677471c3541 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1581,6 +1581,7 @@ #include "code\datums\elements\tenacious.dm" #include "code\datums\elements\tiny_mob_hunter.dm" #include "code\datums\elements\tool_flash.dm" +#include "code\datums\elements\toy_talk.dm" #include "code\datums\elements\turf_transparency.dm" #include "code\datums\elements\undertile.dm" #include "code\datums\elements\unfriend_attacker.dm"