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 3478d7b243f..e411cdfba92 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -158,9 +158,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 45b0839d2ae..01145cd32ea 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -314,9 +314,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 5bb10e71aea..8d46812e226 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_SMALL_VOICE "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 64249fa3085..16ea8a4fbda 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 72e50014ca2..159152d11f5 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -270,17 +270,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 @@ -366,9 +378,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 768fe0374d5..ee54ef4a8f7 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 aab5a55aa4a..2b5eaf99c6d 100644 --- a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm +++ b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm @@ -48,12 +48,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 0f6a0c1a133..1169895e713 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 78519109101..9383dcc3d0a 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 2cb15da2b81..bbe0927520b 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 79391916de4..8f1398ce244 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 29481da7496..a797c2c8679 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 7b00a7d3365..506f4ab1523 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -289,8 +289,8 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) GLOB.vending_machines_to_restock -= src 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_skyrat/modules/cortical_borer/code/cortical_borer.dm b/modular_skyrat/modules/cortical_borer/code/cortical_borer.dm index 6454b7e2757..54420c1c839 100644 --- a/modular_skyrat/modules/cortical_borer/code/cortical_borer.dm +++ b/modular_skyrat/modules/cortical_borer/code/cortical_borer.dm @@ -383,7 +383,7 @@ 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 = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null, 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_skyrat/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm b/modular_skyrat/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm index 7ac643b363d..ee44cf44996 100644 --- a/modular_skyrat/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm +++ b/modular_skyrat/modules/modular_implants/code/soulcatcher/soulcatcher_mob.dm @@ -31,7 +31,7 @@ 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, sanitize, datum/language/language, ignore_spam, forced, filterproof, message_range, datum/saymode/saymode, message_mods = list()) message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) if(!message) return FALSE diff --git a/tgstation.dme b/tgstation.dme index 53bc22c6b6a..989cf11c004 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1573,6 +1573,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"