diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 5d390a8035b..5e60408645a 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -16,6 +16,8 @@ remove_on_fullheal = TRUE heal_flag_necessary = HEAL_CC_STATUS var/needs_update_stat = FALSE + /// Suffixes attached to the force_say when applied, uses the "hurt" suffixes by default + var/list/alter_phrases /datum/status_effect/incapacitating/on_creation(mob/living/new_owner, set_duration) if(isnum(set_duration)) @@ -31,6 +33,15 @@ owner.update_stat() return ..() +/datum/status_effect/incapacitating/on_apply() + SHOULD_CALL_PARENT(TRUE) + . = ..() + if(!.) + return + + var/mob/living/carbon/human/human = owner + if(istype(human)) + human.force_say(alter_phrases, immediate = TRUE) //STUN /datum/status_effect/incapacitating/stun @@ -79,6 +90,7 @@ //PARALYZED /datum/status_effect/incapacitating/paralyzed id = "paralyzed" + alter_phrases = list("") // "Why am I about to be froz-" /datum/status_effect/incapacitating/paralyzed/on_apply() . = ..() @@ -134,6 +146,7 @@ alert_type = /atom/movable/screen/alert/status_effect/asleep needs_update_stat = TRUE tick_interval = 2 SECONDS + alter_phrases = list("Zzz...", "ZZz...", "ZZZ...", "zzZ...", "zZZ...", "ZzZ...", "zzz...", "zZz...", "mimimimimimi...") /datum/status_effect/incapacitating/sleeping/on_apply() . = ..() diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index ab217c39956..0fff7037fea 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -314,8 +314,6 @@ structure_check() searches for nearby cultist structures required for the invoca if(check_holidays(APRIL_FOOLS) && prob(10)) convertee.Paralyze(10 SECONDS) - if(istype(human_convertee)) - human_convertee.force_say() convertee.say("You son of a bitch! I'm in.", forced = "That son of a bitch! They're in. (April Fools)") else diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 4eb5bbb169a..efbeca7b1ce 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -22,6 +22,8 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift) human_heart?.beat = BEAT_NONE human_heart?.Stop() + force_say(immediate = TRUE) + . = ..() if(client && !HAS_TRAIT(src, TRAIT_SUICIDED) && !(client in GLOB.dead_players_during_shift)) diff --git a/code/modules/tgui_input/say_modal/modal.dm b/code/modules/tgui_input/say_modal/modal.dm index 8407d8da38d..054c656d2e9 100644 --- a/code/modules/tgui_input/say_modal/modal.dm +++ b/code/modules/tgui_input/say_modal/modal.dm @@ -33,6 +33,10 @@ var/window_open /// What text was present in the say box the last time save_text was called var/saved_text = "" + /// What channel was in use in the say box the last time save_text was called + var/saved_channel + /// Speech suffuxes used for force_say after "-". Defaults to hurt_phrases + var/list/alter_phrases /** Creates the new input window to exist in the background. */ /datum/tgui_say/New(client/client, id) diff --git a/code/modules/tgui_input/say_modal/speech.dm b/code/modules/tgui_input/say_modal/speech.dm index b5b7099868e..d27e7cfc0fd 100644 --- a/code/modules/tgui_input/say_modal/speech.dm +++ b/code/modules/tgui_input/say_modal/speech.dm @@ -9,9 +9,11 @@ */ /datum/tgui_say/proc/alter_entry(payload) var/entry = payload["entry"] + var/list/phrases = alter_phrases || hurt_phrases + /// No OOC leaks if(!entry || payload["channel"] == OOC_CHANNEL || payload["channel"] == ME_CHANNEL) - return pick(hurt_phrases) + return pick(phrases) /// Random trimming for larger sentences if(length(entry) > 50) entry = trim(entry, rand(40, 50)) @@ -19,7 +21,7 @@ /// Otherwise limit trim to just last letter if(length(entry) > 1) entry = trim(entry, length(entry)) - return entry + "-" + pick(hurt_phrases) + return entry + "-" + pick(phrases) /** * Delegates the speech to the proper channel. @@ -42,19 +44,34 @@ client.mob.me_verb(entry) return TRUE if(OOC_CHANNEL) - client.ooc(entry) + ASYNC + client.ooc(entry) return TRUE if(ADMIN_CHANNEL) - SSadmin_verbs.dynamic_invoke_verb(client, /datum/admin_verb/cmd_admin_say, entry) + INVOKE_ASYNC(SSadmin_verbs, TYPE_PROC_REF(/datum/controller/subsystem/admin_verbs, dynamic_invoke_verb), client, /datum/admin_verb/cmd_admin_say, entry) return TRUE return FALSE /** * Force say handler. * Sends a message to the say modal to send its current value. + * Arguments: + * alter_phrases - Optional list of alternate suffixes to blurt out + * immediate - If [TRUE], the say must be invoked inline due to side effects that may cause the mob to be unable to speak */ -/datum/tgui_say/proc/force_say() - window.send_message("force") +/datum/tgui_say/proc/force_say(list/alter_phrases = null, immediate = FALSE) + src.alter_phrases = alter_phrases + if(immediate) + if(!saved_text) + saved_text = "" + if(!saved_channel) + saved_channel = SAY_CHANNEL + + handle_entry("force", list("type" = "force", "entry" = saved_text, "channel" = saved_channel)) + window.send_message("close") + else + window.send_message("force") + stop_typing() /** @@ -66,11 +83,14 @@ /** * Makes the player force say what's in their current input box. + * Arguments: + * alter_phrases - Optional list of alternate suffixes to blurt out + * immediate - If [TRUE], the say must be invoked inline due to side effects that may cause the mob to be unable to speak */ -/mob/living/carbon/human/proc/force_say() +/mob/living/carbon/human/proc/force_say(list/alter_phrases = null, immediate = FALSE) if(stat != CONSCIOUS || !client?.tgui_say?.window_open) return FALSE - client.tgui_say.force_say() + client.tgui_say.force_say(alter_phrases, immediate) if(client.typing_indicators) log_speech_indicators("[key_name(client)] FORCED to stop typing, indicators enabled.") else @@ -118,5 +138,6 @@ var/target_channel = payload["channel"] if(target_channel == SAY_CHANNEL || target_channel == RADIO_CHANNEL) saved_text = payload["entry"] // only save IC text + saved_channel = target_channel return TRUE return FALSE diff --git a/tgui/packages/tgui-say/TguiSay.tsx b/tgui/packages/tgui-say/TguiSay.tsx index 0fdcab89d7c..37bcdfded7b 100644 --- a/tgui/packages/tgui-say/TguiSay.tsx +++ b/tgui/packages/tgui-say/TguiSay.tsx @@ -194,6 +194,7 @@ export function TguiSay() { // Handles typing indicators if (channelIterator.current.isVisible() && newPrefix !== ':b ') { messages.current.typingMsg(); + messages.current.saveText(newValue, iterator.current()); } setValue(newValue); @@ -262,6 +263,7 @@ export function TguiSay() { Byond.subscribeTo('force', handleForceSay); Byond.subscribeTo('open', handleOpen); Byond.subscribeTo('save', handleSaveText); + Byond.subscribeTo('close', handleClose); }, []); /** Value has changed, we need to check if the size of the window is ok */ diff --git a/tgui/packages/tgui-say/timers.ts b/tgui/packages/tgui-say/timers.ts index 6b4a9f46f08..c9ca98b4c23 100644 --- a/tgui/packages/tgui-say/timers.ts +++ b/tgui/packages/tgui-say/timers.ts @@ -17,11 +17,10 @@ export const byondMessages = { 1 * SECONDS, true, ), - saveText: debounce( + saveText: throttle( (entry: string, channel: Channel) => Byond.sendMessage('save', { entry, channel }), 1 * SECONDS, - true, ), // Throttle: Prevents spamming the server typingMsg: throttle(() => Byond.sendMessage('typing'), 4 * SECONDS),