diff --git a/code/__DEFINES/emotes.dm b/code/__DEFINES/emotes.dm index 99704553f99..5880f171d7f 100644 --- a/code/__DEFINES/emotes.dm +++ b/code/__DEFINES/emotes.dm @@ -37,9 +37,12 @@ // Each mob can only play an emote with audio every AUDIO_EMOTE_COOLDOWN seconds, unless a given emote overrides its own audio cooldown. -/// Default cooldown for audio that comes from emotes. +/// Default cooldown for emote-emitted audio. #define AUDIO_EMOTE_COOLDOWN (5 SECONDS) +/// Cooldown for emotes that are emitted unintentionally, to prevent them from getting audibly spammy. +#define AUDIO_EMOTE_UNINTENTIONAL_COOLDOWN (2 SECONDS) + // Emote parameter types // If nothing is passed in for a target, this can determine the possible target. diff --git a/code/datums/emote.dm b/code/datums/emote.dm index 1b2edf9e488..d4a7663ee76 100644 --- a/code/datums/emote.dm +++ b/code/datums/emote.dm @@ -97,6 +97,10 @@ var/cooldown = DEFAULT_EMOTE_COOLDOWN /// How long is the cooldown on the audio of the emote, if it has one? var/audio_cooldown = AUDIO_EMOTE_COOLDOWN + /// If the emote is triggered unintentionally, how long would that cooldown be? + var/unintentional_audio_cooldown = AUDIO_EMOTE_UNINTENTIONAL_COOLDOWN + /// If true, an emote will completely bypass any cooldown when called unintentionally. Necessary for things like deathgasp. + var/bypass_unintentional_cooldown = FALSE /// How loud is the audio emote? var/volume = 50 @@ -184,7 +188,7 @@ var/sound_volume = get_volume(user) // If our sound emote is forced by code, don't worry about cooldowns at all. if(tmp_sound && should_play_sound(user, intentional) && sound_volume > 0) - if(!intentional || user.start_audio_emote_cooldown(audio_cooldown)) + if(bypass_unintentional_cooldown || user.start_audio_emote_cooldown(intentional, intentional ? audio_cooldown : unintentional_audio_cooldown)) play_sound_effect(user, intentional, tmp_sound, sound_volume) if(msg) @@ -303,7 +307,7 @@ return TRUE // if our emote would play sound but another audio emote is on cooldown, prevent this emote from being used. // Note that this only applies to intentional emotes - if(get_sound(user) && should_play_sound(user, intentional) && !user.can_use_audio_emote()) + if(get_sound(user) && should_play_sound(user, intentional) && !user.can_use_audio_emote(intentional)) return FALSE var/cooldown_in_use if(!isnull(user.emote_cooldown_override)) diff --git a/code/modules/mob/living/carbon/human/human_emote.dm b/code/modules/mob/living/carbon/human/human_emote.dm index ee9d2730220..344aafb872d 100644 --- a/code/modules/mob/living/carbon/human/human_emote.dm +++ b/code/modules/mob/living/carbon/human/human_emote.dm @@ -99,6 +99,7 @@ vary = TRUE age_based = TRUE cooldown = 5 SECONDS + unintentional_audio_cooldown = 3.5 SECONDS mob_type_blacklist_typecache = list( /mob/living/carbon/human/monkey, // screech instead /mob/living/silicon // Robot sounds diff --git a/code/modules/mob/living/living_emote.dm b/code/modules/mob/living/living_emote.dm index 7270e45f6e4..62df24c9b5c 100644 --- a/code/modules/mob/living/living_emote.dm +++ b/code/modules/mob/living/living_emote.dm @@ -71,6 +71,7 @@ cooldown = 10 SECONDS volume = 40 unintentional_stat_allowed = DEAD + bypass_unintentional_cooldown = TRUE // again, this absolutely MUST play when a user dies, if it can. message = "seizes up and falls limp, their eyes dead and lifeless..." message_alien = "seizes up and falls limp, their eyes dead and lifeless..." message_robot = "shudders violently for a moment before falling still, its eyes slowly darkening." diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index adc24e80b39..171a50e1c30 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1542,7 +1542,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!is_component_functioning("power cell") || !cell || !cell.charge) - if(!start_audio_emote_cooldown(10 SECONDS)) + if(!start_audio_emote_cooldown(TRUE, 10 SECONDS)) to_chat(src, "The low-power capacitor for your speaker system is still recharging, please try again later.") return visible_message("The power warning light on [src] flashes urgently.",\ diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 1882292a24c..278e424fcbb 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -102,12 +102,14 @@ /// Cooldown on audio effects from emotes. var/audio_emote_cd_status = EMOTE_READY + /// Cooldown on audio effects from unintentional emotes. + var/audio_emote_unintentional_cd_status = EMOTE_READY + /// Override for cooldowns on non-audio emotes. Should be a number in deciseconds. var/emote_cooldown_override = null /// Tracks last uses of emotes for cooldown purposes var/list/emotes_used - var/list/emotes_on_cooldown var/job = null //Living diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 5ac82f4e641..584b619b0b5 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -755,8 +755,9 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM /** * Helper proc to determine if a mob can use emotes that make sound or not. */ -/mob/proc/can_use_audio_emote() - switch(audio_emote_cd_status) +/mob/proc/can_use_audio_emote(intentional) + var/emote_status = intentional ? audio_emote_cd_status : audio_emote_unintentional_cd_status + switch(emote_status) if(EMOTE_INFINITE) // Spam those emotes return TRUE if(EMOTE_ADMIN_BLOCKED) // Cooldown emotes were disabled by an admin, prevent use @@ -769,24 +770,37 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM CRASH("Invalid emote type") /** - * # Start the cooldown for an emote that plays audio. + * Start the cooldown for an emote that plays audio. * - * * cooldown: The amount of time that should be waited before any other audio emote can fire. + * Arguments: + * * intentional - Whether or not the user deliberately triggered this emote. + * * cooldown - The amount of time that should be waited before any other audio emote can fire. */ -/mob/proc/start_audio_emote_cooldown(cooldown = AUDIO_EMOTE_COOLDOWN) - if(!can_use_audio_emote()) +/mob/proc/start_audio_emote_cooldown(intentional, cooldown = AUDIO_EMOTE_COOLDOWN) + if(!can_use_audio_emote(intentional)) return FALSE - if(audio_emote_cd_status == EMOTE_READY) - audio_emote_cd_status = EMOTE_ON_COOLDOWN // Starting cooldown - addtimer(CALLBACK(src, .proc/on_audio_emote_cooldown_end), cooldown) + var/cooldown_source = intentional ? audio_emote_cd_status : audio_emote_unintentional_cd_status + + if(cooldown_source == EMOTE_READY) + // we do have to juggle between cooldowns a little bit, but this lets us keep them on separate cooldowns so + // a user screaming every five seconds doesn't prevent them from sneezing. + if(intentional) + audio_emote_cd_status = EMOTE_ON_COOLDOWN // Starting cooldown + else + audio_emote_unintentional_cd_status = EMOTE_ON_COOLDOWN + addtimer(CALLBACK(src, .proc/on_audio_emote_cooldown_end, intentional), cooldown) return TRUE // proceed with emote -/mob/proc/on_audio_emote_cooldown_end() - if(audio_emote_cd_status == EMOTE_ON_COOLDOWN) - // only reset emotes that probably weren't set by an admin - audio_emote_cd_status = EMOTE_READY +/mob/proc/on_audio_emote_cooldown_end(intentional) + if(intentional) + if(audio_emote_cd_status == EMOTE_ON_COOLDOWN) + // only reset to ready if we're in a cooldown state + audio_emote_cd_status = EMOTE_READY + else + if(audio_emote_unintentional_cd_status == EMOTE_ON_COOLDOWN) + audio_emote_unintentional_cd_status = EMOTE_READY /proc/stat_to_text(stat) switch(stat)