mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-18 13:43:27 +00:00
## About The Pull Request defaults: ```php /// How long is the shared emote cooldown triggered by this emote? var/general_emote_audio_cooldown = 2 SECONDS /// How long is the specific emote cooldown triggered by this emote? var/specific_emote_audio_cooldown = 5 SECONDS ``` specific cooldown only checks for the emote, while the general is a shared cooldown, gave laugh and scream longer specific cooldowns. ## Why It's Good For The Game The general emote cooldown fixed the problem of multiple emotes getting used at once, now we have a problem with people using the same emote too many times, I want to give the players ability to use sounds in their emotes, but spamming the same emote over and over again makes me regret it and hesitant about it, this way we can have the best of both worlds. ## Changelog 🆑 grungussuss sound: along with the shared emote cooldown, specific emote audio cooldown has returned. /🆑 # Conflicts: # code/datums/emotes.dm
456 lines
16 KiB
Plaintext
456 lines
16 KiB
Plaintext
/**
|
|
* # Emote
|
|
*
|
|
* Most of the text that's not someone talking is based off of this.
|
|
*
|
|
* Yes, the displayed message is stored on the datum, it would cause problems
|
|
* for emotes with a message that can vary, but that's handled differently in
|
|
* run_emote(), so be sure to use can_message_change if you plan to have
|
|
* different displayed messages from player to player.
|
|
*
|
|
*/
|
|
/datum/emote
|
|
/// What calls the emote.
|
|
var/key = ""
|
|
/// This will also call the emote.
|
|
var/key_third_person = ""
|
|
/// Needed for more user-friendly emote names, so emotes with keys like "aflap" will show as "flap angry". Defaulted to key.
|
|
var/name = ""
|
|
/// Message displayed when emote is used.
|
|
var/message = ""
|
|
/// Message displayed if the user is a mime.
|
|
var/message_mime = ""
|
|
/// Message displayed if the user is a grown alien.
|
|
var/message_alien = ""
|
|
/// Message displayed if the user is an alien larva.
|
|
var/message_larva = ""
|
|
/// Message displayed if the user is a robot.
|
|
var/message_robot = ""
|
|
/// Message displayed if the user is an AI.
|
|
var/message_AI = ""
|
|
/// Message displayed if the user is a monkey.
|
|
var/message_monkey = ""
|
|
/// Message to display if the user is a simple_animal or basic mob.
|
|
var/message_animal_or_basic = ""
|
|
/// Message with %t at the end to allow adding params to the message, like for mobs doing an emote relatively to something else.
|
|
var/message_param = ""
|
|
/// Whether the emote is visible and/or audible bitflag
|
|
var/emote_type = EMOTE_VISIBLE
|
|
/// Checks if the mob can use its hands before performing the emote.
|
|
var/hands_use_check = FALSE
|
|
/// Types that are allowed to use that emote.
|
|
var/list/mob_type_allowed_typecache = /mob
|
|
/// Types that are NOT allowed to use that emote.
|
|
var/list/mob_type_blacklist_typecache
|
|
/// Types that can use this emote regardless of their state.
|
|
var/list/mob_type_ignore_stat_typecache
|
|
/// Trait that is required to use this emote.
|
|
var/trait_required
|
|
/// In which state can you use this emote? (Check stat.dm for a full list of them)
|
|
var/stat_allowed = CONSCIOUS
|
|
/// Sound to play when emote is called.
|
|
var/sound
|
|
/// Does this emote vary in pitch?
|
|
var/vary = FALSE
|
|
/// Can only code call this event instead of the player.
|
|
var/only_forced_audio = FALSE
|
|
/// The cooldown between the uses of the emote.
|
|
var/cooldown = 0.8 SECONDS
|
|
/// Does this message have a message that can be modified by the user?
|
|
var/can_message_change = FALSE
|
|
/// How long is the shared emote cooldown triggered by this emote?
|
|
var/general_emote_audio_cooldown = 2 SECONDS
|
|
/// How long is the specific emote cooldown triggered by this emote?
|
|
var/specific_emote_audio_cooldown = 5 SECONDS
|
|
/// Does this emote's sound ignore walls?
|
|
var/sound_wall_ignore = FALSE
|
|
|
|
/datum/emote/New()
|
|
switch(mob_type_allowed_typecache)
|
|
if(/mob)
|
|
mob_type_allowed_typecache = GLOB.typecache_mob
|
|
if(/mob/living)
|
|
mob_type_allowed_typecache = GLOB.typecache_living
|
|
else
|
|
mob_type_allowed_typecache = typecacheof(mob_type_allowed_typecache)
|
|
|
|
mob_type_blacklist_typecache = typecacheof(mob_type_blacklist_typecache)
|
|
mob_type_ignore_stat_typecache = typecacheof(mob_type_ignore_stat_typecache)
|
|
|
|
if(!name)
|
|
name = key
|
|
|
|
/**
|
|
* Handles the modifications and execution of emotes.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is trying to send the emote.
|
|
* * params - Parameters added after the emote.
|
|
* * type_override - Override to the current emote_type.
|
|
* * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE).
|
|
*
|
|
*/
|
|
/datum/emote/proc/run_emote(mob/user, params, type_override, intentional = FALSE)
|
|
var/msg = select_message_type(user, message, intentional)
|
|
if(params && message_param)
|
|
msg = select_param(user, params)
|
|
|
|
msg = replace_pronoun(user, msg)
|
|
if(!msg)
|
|
return
|
|
|
|
user.log_message(msg, LOG_EMOTE)
|
|
|
|
var/tmp_sound = get_sound(user)
|
|
if(tmp_sound && should_play_sound(user, intentional) && TIMER_COOLDOWN_FINISHED(user, "general_emote_audio_cooldown") && TIMER_COOLDOWN_FINISHED(user, type))
|
|
TIMER_COOLDOWN_START(user, type, specific_emote_audio_cooldown)
|
|
TIMER_COOLDOWN_START(user, "general_emote_audio_cooldown", general_emote_audio_cooldown)
|
|
playsound(source = user,soundin = tmp_sound,vol = 50, vary = vary, ignore_walls = sound_wall_ignore)
|
|
|
|
var/is_important = emote_type & EMOTE_IMPORTANT
|
|
var/is_visual = emote_type & EMOTE_VISIBLE
|
|
var/is_audible = emote_type & EMOTE_AUDIBLE
|
|
var/space = should_have_space_before_emote(html_decode(msg)[1]) ? " " : "" // SKYRAT EDIT ADDITION
|
|
|
|
// Emote doesn't get printed to chat, runechat only
|
|
if(emote_type & EMOTE_RUNECHAT)
|
|
for(var/mob/viewer as anything in viewers(user))
|
|
if(isnull(viewer.client))
|
|
continue
|
|
if(!is_important && viewer != user && (!is_visual || !is_audible))
|
|
if(is_audible && !viewer.can_hear())
|
|
continue
|
|
if(is_visual && viewer.is_blind())
|
|
continue
|
|
// SKYRAT EDIT BEGIN - Pref checked emotes
|
|
if(!pref_check_emote(viewer))
|
|
continue
|
|
// SKYRAT EDIT END
|
|
if(user.runechat_prefs_check(viewer, EMOTE_MESSAGE))
|
|
viewer.create_chat_message(
|
|
speaker = user,
|
|
raw_message = msg,
|
|
runechat_flags = EMOTE_MESSAGE,
|
|
)
|
|
else if(is_important)
|
|
to_chat(viewer, span_emote("<b>[user]</b> [msg]"))
|
|
else if(is_audible && is_visual)
|
|
viewer.show_message(
|
|
span_emote("<b>[user]</b> [msg]"), MSG_AUDIBLE,
|
|
span_emote("You see how <b>[user]</b> [msg]"), MSG_VISUAL,
|
|
)
|
|
else if(is_audible)
|
|
viewer.show_message(span_emote("<b>[user]</b> [msg]"), MSG_AUDIBLE)
|
|
else if(is_visual)
|
|
viewer.show_message(span_emote("<b>[user]</b> [msg]"), MSG_VISUAL)
|
|
return // Early exit so no dchat message
|
|
|
|
// The emote has some important information, and should always be shown to the user
|
|
else if(is_important)
|
|
for(var/mob/viewer as anything in viewers(user))
|
|
// SKYRAT EDIT BEGIN - Pref checked emotes
|
|
if(!pref_check_emote(viewer))
|
|
continue
|
|
// SKYRAT EDIT END
|
|
to_chat(viewer, span_emote("<b>[user]</b> [msg]"))
|
|
if(user.runechat_prefs_check(viewer, EMOTE_MESSAGE))
|
|
viewer.create_chat_message(
|
|
speaker = user,
|
|
raw_message = msg,
|
|
runechat_flags = EMOTE_MESSAGE,
|
|
)
|
|
// Emotes has both an audible and visible component
|
|
// Prioritize audible, and provide a visible message if the user is deaf
|
|
else if(is_visual && is_audible)
|
|
user.audible_message(
|
|
message = msg,
|
|
deaf_message = span_emote("You see how <b>[user]</b> [msg]"),
|
|
self_message = msg,
|
|
audible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
|
|
separation = space, // SKYRAT EDIT ADDITION
|
|
pref_to_check = pref_to_check // SKYRAT EDIT ADDITION - Pref checked emotes
|
|
)
|
|
// Emote is entirely audible, no visible component
|
|
else if(is_audible)
|
|
user.audible_message(
|
|
message = msg,
|
|
self_message = msg,
|
|
audible_message_flags = EMOTE_MESSAGE,
|
|
separation = space, // SKYRAT EDIT ADDITION
|
|
pref_to_check = pref_to_check // SKYRAT EDIT ADDITION - Pref checked emotes
|
|
)
|
|
// Emote is entirely visible, no audible component
|
|
else if(is_visual)
|
|
user.visible_message(
|
|
message = msg,
|
|
self_message = msg,
|
|
visible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
|
|
separation = space, // SKYRAT EDIT ADDITION
|
|
pref_to_check = pref_to_check // SKYRAT EDIT ADDITION - Pref checked emotes
|
|
)
|
|
else
|
|
CRASH("Emote [type] has no valid emote type set!")
|
|
|
|
// SKYRAT EDIT -- BEGIN -- ADDITION -- AI QOL - RELAY EMOTES OVER HOLOPADS
|
|
var/obj/effect/overlay/holo_pad_hologram/hologram = GLOB.hologram_impersonators[user]
|
|
if(hologram)
|
|
if(is_important)
|
|
for(var/mob/living/viewer in viewers(world.view, hologram))
|
|
if(!pref_check_emote(viewer))
|
|
continue
|
|
to_chat(viewer, msg)
|
|
else if(is_visual && is_audible)
|
|
hologram.audible_message(
|
|
message = msg,
|
|
deaf_message = "<span class='emote'>You see how <b>[user]</b> [msg]</span>",
|
|
self_message = msg,
|
|
audible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
|
|
separation = space,
|
|
pref_to_check = pref_to_check,
|
|
)
|
|
else if(is_audible)
|
|
hologram.audible_message(
|
|
message = msg,
|
|
self_message = msg,
|
|
audible_message_flags = EMOTE_MESSAGE,
|
|
separation = space,
|
|
pref_to_check = pref_to_check,
|
|
)
|
|
else if(is_visual)
|
|
hologram.visible_message(
|
|
message = msg,
|
|
self_message = msg,
|
|
visible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
|
|
separation = space,
|
|
pref_to_check = pref_to_check,
|
|
)
|
|
// SKYRAT EDIT -- END
|
|
|
|
if(!isnull(user.client))
|
|
var/dchatmsg = "<b>[user]</b>[space][msg]" // SKYRAT EDIT - Better emotes - Original: var/dchatmsg = "<b>[user]</b> [msg]"
|
|
for(var/mob/ghost as anything in GLOB.dead_mob_list - viewers(get_turf(user)))
|
|
if(isnull(ghost.client) || isnewplayer(ghost))
|
|
continue
|
|
if(!(get_chat_toggles(ghost.client) & CHAT_GHOSTSIGHT))
|
|
continue
|
|
// SKYRAT EDIT BEGIN - Pref checked emotes
|
|
if(!pref_check_emote(ghost))
|
|
continue
|
|
// SKYRAT EDIT END
|
|
to_chat(ghost, span_emote("[FOLLOW_LINK(ghost, user)] [dchatmsg]"))
|
|
|
|
return
|
|
|
|
|
|
|
|
/**
|
|
* For handling emote cooldown, return true to allow the emote to happen.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is trying to send the emote.
|
|
* * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE).
|
|
*
|
|
* Returns FALSE if the cooldown is not over, TRUE if the cooldown is over.
|
|
*/
|
|
/datum/emote/proc/check_cooldown(mob/user, intentional)
|
|
if(SEND_SIGNAL(user, COMSIG_MOB_EMOTE_COOLDOWN_CHECK, src.key, intentional) & COMPONENT_EMOTE_COOLDOWN_BYPASS)
|
|
intentional = FALSE
|
|
|
|
if(!intentional)
|
|
return TRUE
|
|
|
|
if(user.emotes_used && user.emotes_used[src] + cooldown > world.time)
|
|
var/datum/emote/default_emote = /datum/emote
|
|
if(cooldown > initial(default_emote.cooldown)) // only worry about longer-than-normal emotes
|
|
// Original: to_chat(user, span_danger("You must wait another [DisplayTimeText(user.emotes_used[src] - world.time + cooldown)] before using that emote."))
|
|
user.balloon_alert(user, "on cooldown!") // BUBBER EDIT CHANGE
|
|
return FALSE
|
|
if(!user.emotes_used)
|
|
user.emotes_used = list()
|
|
user.emotes_used[src] = world.time
|
|
return TRUE
|
|
|
|
/**
|
|
* To get the sound that the emote plays, for special sound interactions depending on the mob.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is trying to send the emote.
|
|
*
|
|
* Returns the sound that will be made while sending the emote.
|
|
*/
|
|
/datum/emote/proc/get_sound(mob/living/user)
|
|
return sound //by default just return this var.
|
|
|
|
/**
|
|
* To replace pronouns in the inputed string with the user's proper pronouns.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is trying to send the emote.
|
|
* * msg - The string to modify.
|
|
*
|
|
* Returns the modified msg string.
|
|
*/
|
|
/datum/emote/proc/replace_pronoun(mob/user, msg)
|
|
if(findtext(msg, "their"))
|
|
msg = replacetext(msg, "their", user.p_their())
|
|
if(findtext(msg, "them"))
|
|
msg = replacetext(msg, "them", user.p_them())
|
|
if(findtext(msg, "they"))
|
|
msg = replacetext(msg, "they", user.p_they())
|
|
if(findtext(msg, "%s"))
|
|
msg = replacetext(msg, "%s", user.p_s())
|
|
return msg
|
|
|
|
/**
|
|
* Selects the message type to override the message with.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is trying to send the emote.
|
|
* * msg - The string to modify.
|
|
* * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE).
|
|
*
|
|
* Returns the new message, or msg directly, if no change was needed.
|
|
*/
|
|
/datum/emote/proc/select_message_type(mob/user, msg, intentional)
|
|
// Basically, we don't care that the others can use datum variables, because they're never going to change.
|
|
. = msg
|
|
if(!isliving(user))
|
|
return .
|
|
var/mob/living/living_user = user
|
|
|
|
if(HAS_MIND_TRAIT(user, TRAIT_MIMING) && message_mime)
|
|
. = message_mime
|
|
if(isalienadult(user) && message_alien)
|
|
. = message_alien
|
|
else if(islarva(user) && message_larva)
|
|
. = message_larva
|
|
else if(isAI(user) && message_AI)
|
|
. = message_AI
|
|
else if(ismonkey(user) && message_monkey)
|
|
. = message_monkey
|
|
else if((iscyborg(user) || (living_user.mob_biotypes & MOB_ROBOTIC)) && message_robot)
|
|
. = message_robot
|
|
else if(isanimal_or_basicmob(user) && message_animal_or_basic)
|
|
. = message_animal_or_basic
|
|
|
|
return .
|
|
|
|
/**
|
|
* Replaces the %t in the message in message_param by params.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is trying to send the emote.
|
|
* * params - Parameters added after the emote.
|
|
*
|
|
* Returns the modified string.
|
|
*/
|
|
/datum/emote/proc/select_param(mob/user, params)
|
|
return replacetext(message_param, "%t", params)
|
|
|
|
/**
|
|
* Check to see if the user is allowed to run the emote.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is trying to send the emote.
|
|
* * status_check - Bool that says whether we should check their stat or not.
|
|
* * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE).
|
|
* * params - Parameters added after the emote.
|
|
*
|
|
* Returns a bool about whether or not the user can run the emote.
|
|
*/
|
|
/datum/emote/proc/can_run_emote(mob/user, status_check = TRUE, intentional = FALSE, params)
|
|
if(trait_required && !HAS_TRAIT(user, trait_required))
|
|
return FALSE
|
|
if(!is_type_in_typecache(user, mob_type_allowed_typecache))
|
|
return FALSE
|
|
if(is_type_in_typecache(user, mob_type_blacklist_typecache))
|
|
return FALSE
|
|
if(status_check && !is_type_in_typecache(user, mob_type_ignore_stat_typecache))
|
|
if(user.stat > stat_allowed)
|
|
if(!intentional)
|
|
return FALSE
|
|
switch(user.stat)
|
|
if(SOFT_CRIT)
|
|
to_chat(user, span_warning("You cannot [key] while in a critical condition!"))
|
|
if(UNCONSCIOUS, HARD_CRIT)
|
|
to_chat(user, span_warning("You cannot [key] while unconscious!"))
|
|
if(DEAD)
|
|
to_chat(user, span_warning("You cannot [key] while dead!"))
|
|
return FALSE
|
|
if(hands_use_check && HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
|
|
if(!intentional)
|
|
return FALSE
|
|
to_chat(user, span_warning("You cannot use your hands to [key] right now!"))
|
|
return FALSE
|
|
|
|
if(HAS_TRAIT(user, TRAIT_EMOTEMUTE))
|
|
return FALSE
|
|
|
|
//SKYRAT EDIT BEGIN
|
|
if(allowed_species && ishuman(user))
|
|
var/mob/living/carbon/human/sender = user
|
|
if(sender.dna.species.type in allowed_species)
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
//SKYRAT EDIT END
|
|
return TRUE
|
|
|
|
/**
|
|
* Check to see if the user should play a sound when performing the emote.
|
|
*
|
|
* Arguments:
|
|
* * user - Person that is doing the emote.
|
|
* * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE).
|
|
*
|
|
* Returns a bool about whether or not the user should play a sound when performing the emote.
|
|
*/
|
|
/datum/emote/proc/should_play_sound(mob/user, intentional = FALSE)
|
|
if(emote_type & EMOTE_AUDIBLE && !hands_use_check)
|
|
if(HAS_TRAIT(user, TRAIT_MUTE))
|
|
return FALSE
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/loud_mouth = user
|
|
if(HAS_MIND_TRAIT(loud_mouth, TRAIT_MIMING)) // vow of silence prevents outloud noises
|
|
return FALSE
|
|
if(!loud_mouth.get_organ_slot(ORGAN_SLOT_TONGUE))
|
|
return FALSE
|
|
|
|
if(only_forced_audio && intentional)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* Allows the intrepid coder to send a basic emote
|
|
* Takes text as input, sends it out to those who need to know after some light parsing
|
|
* If you need something more complex, make it into a datum emote
|
|
* Arguments:
|
|
* * text - The text to send out
|
|
*
|
|
* Returns TRUE if it was able to run the emote, FALSE otherwise.
|
|
*/
|
|
/atom/proc/manual_emote(text)
|
|
if(!text)
|
|
CRASH("Someone passed nothing to manual_emote(), fix it")
|
|
|
|
log_message(text, LOG_EMOTE)
|
|
visible_message(text, visible_message_flags = EMOTE_MESSAGE)
|
|
return TRUE
|
|
|
|
/mob/manual_emote(text)
|
|
if (stat != CONSCIOUS)
|
|
return FALSE
|
|
. = ..()
|
|
if (!.)
|
|
return FALSE
|
|
if (!client)
|
|
return TRUE
|
|
var/ghost_text = "<b>[src]</b> [text]"
|
|
var/origin_turf = get_turf(src)
|
|
for(var/mob/ghost as anything in GLOB.dead_mob_list)
|
|
if(!ghost.client || isnewplayer(ghost))
|
|
continue
|
|
if(get_chat_toggles(ghost.client) & CHAT_GHOSTSIGHT && !(ghost in viewers(origin_turf, null)))
|
|
ghost.show_message("[FOLLOW_LINK(ghost, src)] [ghost_text]")
|
|
return TRUE
|