Let's you talk through action figures, plushies, and toy mechs with .l and .r. Also a big clean up of say because its support for non-mobs was lackluster. (#81848)

This commit is contained in:
MrMelbert
2024-03-07 10:21:12 -06:00
committed by GitHub
parent 0733065c4e
commit 47dc38fee2
34 changed files with 316 additions and 133 deletions

View File

@@ -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)

View File

@@ -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"

View File

@@ -55,11 +55,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"
@@ -75,8 +78,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

View File

@@ -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("<b>Do not commit suicide or put the body in a deadly position. Behave like you care about it as much as the owner.</b>"))
/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

View File

@@ -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,

View File

@@ -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)

View File

@@ -67,6 +67,7 @@
language = language,
forced = "[source]'s marionette",
saymode = saymode,
message_mods = list(MODE_RELAY = TRUE),
)
speech_args[SPEECH_RANGE] = WHISPER_RANGE

View File

@@ -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()

View File

@@ -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

View File

@@ -606,8 +606,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)

View File

@@ -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]

View File

@@ -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]"

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -21,7 +21,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)
@@ -31,7 +60,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)
@@ -60,7 +88,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.
@@ -77,7 +105,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
@@ -174,7 +203,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, "<span class='red'>meme</span>"` 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 = "..."

View File

@@ -286,7 +286,19 @@ GLOBAL_LIST_EMPTY(blob_nodes)
blob_points = clamp(blob_points + points, 0, max_blob_points)
hud_used.blobpwrdisplay.maptext = MAPTEXT("<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round(blob_points)]</font></div>")
/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

View File

@@ -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)

View File

@@ -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

View File

@@ -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))

View File

@@ -47,12 +47,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)
..()

View File

@@ -168,7 +168,19 @@
essencecolor = "#1D2953" //oh jeez you're dying
hud_used.healths.maptext = MAPTEXT("<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='[essencecolor]'>[essence]E</font></div>")
/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

View File

@@ -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()

View File

@@ -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))

View File

@@ -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")

View File

@@ -94,13 +94,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]]
@@ -206,7 +217,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)
@@ -222,6 +233,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)
@@ -229,8 +243,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)
@@ -426,38 +438,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).
*

View File

@@ -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 ..()

View File

@@ -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.*/

View File

@@ -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"

View File

@@ -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

View File

@@ -194,13 +194,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]"

View File

@@ -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)
. = ..()

View File

@@ -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

View File

@@ -1479,6 +1479,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"