diff --git a/beestation.dme b/beestation.dme
index 5b300777e55a9..c0fa325b7febb 100644
--- a/beestation.dme
+++ b/beestation.dme
@@ -2585,6 +2585,7 @@
#include "code\modules\elevator\elevator_interface.dm"
#include "code\modules\elevator\elevator_segment.dm"
#include "code\modules\emoji\emoji_parse.dm"
+#include "code\modules\emote_panel\emote_panel.dm"
#include "code\modules\error_handler\error_handler.dm"
#include "code\modules\error_handler\error_viewer.dm"
#include "code\modules\events\_event.dm"
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index dbd4f1aefcf54..67b47015d3249 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -107,11 +107,23 @@
//Used in visible_message_flags, audible_message_flags and message_mods
#define CHATMESSAGE_EMOTE "emotemessage"
+/// By default, self_message will respect the visual / audible component of the message.
+/// Meaning that if the message is visual, and sourced from a blind mob, they will not see it.
+/// This flag skips that behavior, and will always show the self message to the mob.
+#define ALWAYS_SHOW_SELF_MESSAGE "showselfmessage"
+
///How far away blind people can see visible messages from
#define BLIND_TEXT_DIST 2
-// Emote flags
-
+// Bitflags for emotes, used in var/emote_type of the emote datum
+/// Is the emote audible
#define EMOTE_AUDIBLE (1<<0)
-#define EMOTE_ANIMATED (1<<1)
+/// Is the emote visible
+#define EMOTE_VISIBLE (1<<1)
+/// Is it an emote that should be shown regardless of blindness/deafness
+#define EMOTE_IMPORTANT (1<<2)
+/// Emote only prints to runechat, not to the chat window
+#define EMOTE_RUNECHAT (1<<3)
+// Animated emote bitflag
+#define EMOTE_ANIMATED (1<<4)
diff --git a/code/__DEFINES/species.dm b/code/__DEFINES/species.dm
index 1bd7b69f2f158..b743ca3356bb6 100644
--- a/code/__DEFINES/species.dm
+++ b/code/__DEFINES/species.dm
@@ -112,12 +112,16 @@
// Sounds used by species for "nasal/lungs" emotes - the DEFAULT being used mainly by humans, lizards, and ethereals becase biology idk
#define SPECIES_DEFAULT_COUGH_SOUND(user) user.gender == FEMALE ? pick(\
- 'sound/emotes/female/female_cough_1.ogg',\
- 'sound/emotes/female/female_cough_2.ogg',\
- 'sound/emotes/female/female_cough_3.ogg') : pick(\
- 'sound/emotes/male/male_cough_1.ogg',\
- 'sound/emotes/male/male_cough_2.ogg',\
- 'sound/emotes/male/male_cough_3.ogg')
+ 'sound/emotes/female/female_cough_1.ogg',\
+ 'sound/emotes/female/female_cough_2.ogg',\
+ 'sound/emotes/female/female_cough_3.ogg',\
+ 'sound/emotes/female/female_cough_4.ogg',\
+ 'sound/emotes/female/female_cough_5.ogg',\
+ 'sound/emotes/female/female_cough_6.ogg',\
+ 'sound/emotes/female/female_cough_7.ogg') : pick(\
+ 'sound/emotes/male/male_cough_1.ogg',\
+ 'sound/emotes/male/male_cough_2.ogg',\
+ 'sound/emotes/male/male_cough_3.ogg')
#define SPECIES_DEFAULT_GASP_SOUND(user) user.gender == FEMALE ? pick(\
'sound/emotes/female/gasp_f1.ogg',\
'sound/emotes/female/gasp_f2.ogg',\
@@ -132,5 +136,15 @@
'sound/emotes/male/gasp_m5.ogg',\
'sound/emotes/male/gasp_m6.ogg')
#define SPECIES_DEFAULT_SIGH_SOUND(user) user.gender == FEMALE ? 'sound/emotes/female/female_sigh.ogg' : 'sound/emotes/male/male_sigh.ogg'
-#define SPECIES_DEFAULT_SNEEZE_SOUND(user) user.gender == FEMALE ? 'sound/emotes/female/female_sneeze.ogg' : 'sound/emotes/male/male_sneeze.ogg'
+#define SPECIES_DEFAULT_SNEEZE_SOUND(user) user.gender == FEMALE ? pick(\
+ 'sound/emotes/female/female_sneeze1.ogg',\
+ 'sound/emotes/female/female_sneeze2.ogg') : pick(\
+ 'sound/emotes/male/male_sneeze1.ogg',\
+ 'sound/emotes/male/male_sneeze2.ogg')
#define SPECIES_DEFAULT_SNIFF_SOUND(user) user.gender == FEMALE ? 'sound/emotes/female/female_sniff.ogg' : 'sound/emotes/male/male_sniff.ogg'
+#define SPECIES_DEFAULT_GIGGLE_SOUND(user) user.gender == FEMALE ? pick(\
+ 'sound/emotes/female/female_giggle_1.ogg',\
+ 'sound/emotes/female/female_giggle_2.ogg') : pick(\
+ 'sound/emotes/male/male_giggle_1.ogg',\
+ 'sound/emotes/male/male_giggle_2.ogg',\
+ 'sound/emotes/male/male_giggle_3.ogg')
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index d6e721d5ce87f..2d121bb1829d5 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -300,3 +300,10 @@ DEFINE_BITFIELD(mecha_flags, list(
"IS_ENCLOSED" = IS_ENCLOSED,
"HAS_LIGHTS" = HAS_LIGHTS,
))
+
+DEFINE_BITFIELD(emote_flags, list(
+ "EMOTE_AUDIBLE" = EMOTE_AUDIBLE,
+ "EMOTE_VISIBLE" = EMOTE_VISIBLE,
+ "EMOTE_IMPORTANT" = EMOTE_IMPORTANT,
+ "EMOTE_ANIMATED" = EMOTE_ANIMATED,
+))
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 09e74a3e7b511..7eeed5c3d7f8d 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -1,6 +1,7 @@
/datum/emote
var/key = "" //What calls the emote
var/key_third_person = "" //This will also call the emote
+ var/name = "" // Needed for more user-friendly emote names, so emotes with keys like "aflap" will show as "flap angry". Defaulted to key.
var/message = "" //Message displayed when emote is used
var/message_mime = "" //Message displayed if the user is a mime
var/message_alien = "" //Message displayed if the user is a grown alien
@@ -12,8 +13,8 @@
var/message_insect = "" //Message to display if the user is a moth, apid or flyperson
var/message_simple = "" //Message to display if the user is a simple_animal
var/message_param = "" //Message to display if a param was given
- /// Emote flags (EMOTE_AUDIBLE and EMOTE_ANIMATED)
- var/emote_type = 0
+ /// Whether the emote is visible and/or audible bitflag
+ var/emote_type = NONE
/// Checks if the mob can use its hands before performing the emote.
var/hands_use_check = FALSE
var/muzzle_ignore = FALSE //Will only work if the emote is EMOTE_AUDIBLE
@@ -57,7 +58,11 @@
mob_type_blacklist_typecache = typecacheof(mob_type_blacklist_typecache)
mob_type_ignore_stat_typecache = typecacheof(mob_type_ignore_stat_typecache)
+ if(!name)
+ name = key
+
/datum/emote/proc/run_emote(mob/user, params, type_override, intentional = FALSE)
+ SHOULD_CALL_PARENT(TRUE)
if(!can_run_emote(user, TRUE, intentional))
return FALSE
@@ -88,22 +93,81 @@
var/space = should_have_space_before_emote(html_decode(msg)[1]) ? " " : ""
msg = punctuate(msg)
- var/dchatmsg = "[user][space][msg]"
+ var/is_important = emote_type & EMOTE_IMPORTANT
+ var/is_visual = emote_type & EMOTE_VISIBLE
+ var/is_audible = emote_type & EMOTE_AUDIBLE
- for(var/mob/M in GLOB.dead_mob_list)
- if(!M.client || isnewplayer(M))
- continue
- var/T = get_turf(user)
- if(M.stat == DEAD && M?.client.prefs?.read_player_preference(/datum/preference/toggle/chat_ghostsight) && !(M in viewers(T, null)))
- if(user.mind || M.client.prefs?.read_player_preference(/datum/preference/toggle/chat_followghostmindless))
- M.show_message("[FOLLOW_LINK(M, user)] [dchatmsg]")
- else
- M.show_message("[dchatmsg]")
+ // 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
+ if(user.runechat_prefs_check(viewer, CHATMESSAGE_EMOTE))
+ create_chat_message(
+ speaker = user,
+ raw_message = msg,
+ message_mods = list(CHATMESSAGE_EMOTE = TRUE),
+ )
+ else if(is_important)
+ to_chat(viewer, "[user] [msg]")
+ else if(is_audible && is_visual)
+ viewer.show_message(
+ "[user] [msg]", MSG_AUDIBLE,
+ "You see how [user] [msg]", MSG_VISUAL,
+ )
+ else if(is_audible)
+ viewer.show_message("[user] [msg]", MSG_AUDIBLE)
+ else if(is_visual)
+ viewer.show_message("[user] [msg]", MSG_VISUAL)
+ return TRUE // Early exit so no dchat message
- if(emote_type & EMOTE_AUDIBLE)
- user.audible_message(msg, audible_message_flags = list(CHATMESSAGE_EMOTE = TRUE), separation = space)
+ // 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))
+ to_chat(viewer, "[user] [msg]")
+ if(user.runechat_prefs_check(viewer, list(CHATMESSAGE_EMOTE = TRUE)))
+ create_chat_message(user, null, list(viewer), msg, null, list(CHATMESSAGE_EMOTE = TRUE))
+ // 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 = "You see how [user] [msg]",
+ self_message = msg,
+ audible_message_flags = list(CHATMESSAGE_EMOTE = TRUE, ALWAYS_SHOW_SELF_MESSAGE = TRUE),
+ separation = space
+ )
+ // Emote is entirely audible, no visible component
+ else if(is_audible)
+ user.audible_message(
+ message = msg,
+ self_message = msg,
+ audible_message_flags = list(CHATMESSAGE_EMOTE = TRUE),
+ separation = space
+ )
+ // Emote is entirely visible, no audible component
+ else if(is_visual)
+ user.visible_message(
+ message = msg,
+ self_message = msg,
+ visible_message_flags = list(CHATMESSAGE_EMOTE = TRUE, ALWAYS_SHOW_SELF_MESSAGE = TRUE),
+ )
else
- user.visible_message(msg, visible_message_flags = list(CHATMESSAGE_EMOTE = TRUE), separation = space)
+ CRASH("Emote [type] has no valid emote type set!")
+
+ if(!isnull(user.client))
+ var/dchatmsg = "[user] [msg]"
+ for(var/mob/ghost as anything in GLOB.dead_mob_list - viewers(get_turf(user)))
+ if(isnull(ghost.client) || isnewplayer(ghost))
+ continue
+ if(!ghost?.client.prefs?.read_player_preference(/datum/preference/toggle/chat_ghostsight))
+ continue
+ to_chat(ghost, "[FOLLOW_LINK(ghost, user)] [dchatmsg]")
return TRUE
/datum/emote/proc/get_sound(mob/living/user)
diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm
index ad9daa916ba87..b52d4b8ba99ec 100644
--- a/code/modules/asset_cache/asset_list.dm
+++ b/code/modules/asset_cache/asset_list.dm
@@ -21,6 +21,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
/// Whether or not this asset can be cached across rounds of the same commit under the `CACHE_ASSETS` config.
/// This is not a *guarantee* the asset will be cached. Not all asset subtypes respect this field, and the
/// config can, of course, be disabled.
+ /// Disable this if your asset can change between rounds on the same exact version of the code.
var/cross_round_cachable = FALSE
/// Whether or not this asset should be loaded in the "early assets" SS
@@ -146,6 +147,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/spritesheet
_abstract = /datum/asset/spritesheet
+ cross_round_cachable = TRUE
var/name
/// List of arguments to pass into queuedInsert
/// Exists so we can queue icon insertion, mostly for stuff like preferences
@@ -158,6 +160,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
/// If this asset should be fully loaded on new
/// Defaults to false so we can process this stuff nicely
var/load_immediately = FALSE
+ VAR_PRIVATE
+ // Kept in state so that the result is the same, even when the files are created, for this run
+ should_refresh = null
/datum/asset/spritesheet/proc/should_load_immediately()
#ifdef DO_NOT_DEFER_ASSETS
@@ -170,12 +175,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
if (..())
return TRUE
- // Static so that the result is the same, even when the files are created, for this run
- var/static/should_refresh = null
-
if (isnull(should_refresh))
// `fexists` seems to always fail on static-time
- should_refresh = !fexists("[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css")
+ should_refresh = !fexists(css_cache_filename()) || !fexists(data_cache_filename())
return should_refresh
@@ -329,8 +331,17 @@ GLOBAL_LIST_EMPTY(asset_datums)
return out.Join("\n")
+/datum/asset/spritesheet/proc/css_cache_filename()
+ return "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css"
+
+/datum/asset/spritesheet/proc/data_cache_filename()
+ return "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].json"
+
/datum/asset/spritesheet/proc/read_from_cache()
- var/replaced_css = rustg_file_read("[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css")
+ return read_css_from_cache() && read_data_from_cache()
+
+/datum/asset/spritesheet/proc/read_css_from_cache()
+ var/replaced_css = file2text(css_cache_filename())
var/regex/find_background_urls = regex(@"background:url\('%(.+?)%'\)", "g")
while (find_background_urls.Find(replaced_css))
@@ -352,6 +363,14 @@ GLOBAL_LIST_EMPTY(asset_datums)
return TRUE
+/datum/asset/spritesheet/proc/read_data_from_cache()
+ var/json = json_decode(file2text(data_cache_filename()))
+
+ if (islist(json["sprites"]))
+ sprites = json["sprites"]
+
+ return TRUE
+
/datum/asset/spritesheet/proc/send_from_cache(client/client)
if (isnull(cached_spritesheets_needed))
stack_trace("cached_spritesheets_needed was null when sending assets from [type] from cache")
@@ -367,6 +386,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
return SSassets.transport.get_asset_url(asset)
/datum/asset/spritesheet/proc/write_to_cache()
+ write_css_to_cache()
+ write_data_to_cache()
+
+/datum/asset/spritesheet/proc/write_css_to_cache()
for (var/size_id in sizes)
var/datum/asset_cache_item/temp = SSassets.cache["[name]_[size_id].png"]
fcopy(temp.resource, "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name]_[size_id].png")
@@ -375,7 +398,12 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/mock_css = generate_css()
generating_cache = FALSE
- rustg_file_write(mock_css, "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css")
+ rustg_file_write(mock_css, css_cache_filename())
+
+/datum/asset/spritesheet/proc/write_data_to_cache()
+ rustg_file_write(json_encode(list(
+ "sprites" = sprites,
+ )), data_cache_filename())
/datum/asset/spritesheet/proc/get_cached_url_mappings()
var/list/mappings = list()
diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm
index e151400e22558..3edf7d2a5a206 100644
--- a/code/modules/client/preferences/middleware/antags.dm
+++ b/code/modules/client/preferences/middleware/antags.dm
@@ -133,7 +133,6 @@
/datum/asset/spritesheet/antagonists
name = "antagonists"
early = TRUE
- cross_round_cachable = TRUE
/datum/asset/spritesheet/antagonists/create_spritesheets()
var/list/generated_icons = list()
diff --git a/code/modules/client/preferences/middleware/species.dm b/code/modules/client/preferences/middleware/species.dm
index 6d82d09917947..f960128e0d2f7 100644
--- a/code/modules/client/preferences/middleware/species.dm
+++ b/code/modules/client/preferences/middleware/species.dm
@@ -9,7 +9,6 @@
/datum/asset/spritesheet/species
name = "species"
early = TRUE
- cross_round_cachable = TRUE
/datum/asset/spritesheet/species/create_spritesheets()
var/list/to_insert = list()
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index e25003b96afd4..673634303abac 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -10,6 +10,7 @@
var/adjusted_flags = null
var/voice_change = FALSE //Used to mask/change the user's voice, only specific masks can set this to TRUE
var/obj/item/organ/tongue/chosen_tongue = null
+ var/unique_death /// The unique sound effect of dying while wearing this
/obj/item/clothing/mask/attack_self(mob/user)
if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE))
diff --git a/code/modules/clothing/masks/hailer.dm b/code/modules/clothing/masks/hailer.dm
index 7dd01b5036195..fdc91b95f9be1 100644
--- a/code/modules/clothing/masks/hailer.dm
+++ b/code/modules/clothing/masks/hailer.dm
@@ -14,6 +14,7 @@
visor_flags_inv = HIDEFACE | HIDESNOUT
flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES
visor_flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES
+ unique_death = 'sound/voice/sec_death.ogg'
var/aggressiveness = 2
var/cooldown_special
var/recent_uses = 0
diff --git a/code/modules/emote_panel/emote_panel.dm b/code/modules/emote_panel/emote_panel.dm
new file mode 100644
index 0000000000000..72caf05e92b27
--- /dev/null
+++ b/code/modules/emote_panel/emote_panel.dm
@@ -0,0 +1,64 @@
+/datum/emote_panel
+ var/list/blacklisted_emotes = list("me", "help")
+
+/datum/emote_panel/ui_static_data(mob/user)
+ var/list/data = list()
+
+ var/list/emotes = list()
+ var/list/keys = list()
+
+ for(var/key in GLOB.emote_list)
+ for(var/datum/emote/emote in GLOB.emote_list[key])
+ if(emote.key in keys)
+ continue
+ if(emote.key in blacklisted_emotes)
+ continue
+ if(emote.can_run_emote(user, status_check = FALSE, intentional = FALSE))
+ keys += emote.key
+ emotes += list(list(
+ "key" = emote.key,
+ "name" = emote.name,
+ "hands" = emote.hands_use_check,
+ "visible" = emote.emote_type & EMOTE_VISIBLE,
+ "audible" = emote.emote_type & EMOTE_AUDIBLE,
+ "sound" = !isnull(emote.get_sound(user)),
+ "use_params" = emote.message_param,
+ ))
+
+ data["emotes"] = emotes
+
+ return data
+
+/datum/emote_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("play_emote")
+ var/emote_key = params["emote_key"]
+ if(isnull(emote_key) || !GLOB.emote_list[emote_key])
+ return
+ var/use_params = params["use_params"]
+ var/datum/emote/emote = GLOB.emote_list[emote_key][1]
+ var/emote_param
+ if(emote.message_param && use_params)
+ emote_param = tgui_input_text(ui.user, "Add params to the emote...", emote.message_param)
+ ui.user.emote(emote_key, message = emote_param, intentional = TRUE)
+
+/datum/emote_panel/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "EmotePanel")
+ ui.open()
+
+/datum/emote_panel/ui_state(mob/user)
+ return GLOB.always_state
+
+/mob/living/verb/emote_panel()
+ set name = "Emote Panel"
+ set category = "IC"
+
+ var/static/datum/emote_panel/emote_panel
+ if(isnull(emote_panel))
+ emote_panel = new
+ emote_panel.ui_interact(src)
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index 2285b80496d9f..edff4142e2d86 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -26,6 +26,7 @@
hands_use_check = TRUE
mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/living/silicon/ai)
+ emote_type = EMOTE_VISIBLE
/datum/emote/flip/run_emote(mob/user, params , type_override, intentional)
. = ..()
@@ -41,6 +42,7 @@
hands_use_check = TRUE
mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
mob_type_ignore_stat_typecache = list(/mob/dead/observer)
+ emote_type = EMOTE_VISIBLE
/datum/emote/spin/run_emote(mob/user, params , type_override, intentional)
. = ..()
@@ -62,8 +64,10 @@
key = "inhale"
key_third_person = "inhales"
message = "breathes in"
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/exhale
key = "exhale"
key_third_person = "exhales"
message = "breathes out"
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm
index 2101a9406c67f..69f8e02e2320e 100644
--- a/code/modules/mob/living/basic/basic.dm
+++ b/code/modules/mob/living/basic/basic.dm
@@ -141,6 +141,12 @@
icon_state = icon_living
density = initial(density)
+/mob/living/basic/examine(mob/user)
+ . = ..()
+ if(stat != DEAD)
+ return
+ . += "Upon closer examination, [p_they()] appear[p_s()] to be [HAS_TRAIT(user.mind, TRAIT_NAIVE) ? "asleep" : "dead"]."
+
/mob/living/basic/proc/melee_attack(atom/target)
src.face_atom(target)
// if(SEND_SIGNAL(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, target) & COMPONENT_HOSTILE_NO_ATTACK)
diff --git a/code/modules/mob/living/brain/emote.dm b/code/modules/mob/living/brain/emote.dm
index 7c962a101f5b7..75599e892a25d 100644
--- a/code/modules/mob/living/brain/emote.dm
+++ b/code/modules/mob/living/brain/emote.dm
@@ -1,6 +1,7 @@
/datum/emote/brain
mob_type_allowed_typecache = list(/mob/living/brain)
mob_type_blacklist_typecache = list()
+ emote_type = EMOTE_AUDIBLE
/datum/emote/brain/can_run_emote(mob/user, status_check = TRUE, intentional)
. = ..()
@@ -11,24 +12,21 @@
/datum/emote/brain/alarm
key = "alarm"
message = "sounds an alarm."
- emote_type = EMOTE_AUDIBLE
/datum/emote/brain/alert
key = "alert"
message = "lets out a distressed noise."
- emote_type = EMOTE_AUDIBLE
/datum/emote/brain/flash
key = "flash"
message = "blinks their lights."
+ emote_type = EMOTE_VISIBLE
/datum/emote/brain/notice
key = "notice"
message = "plays a loud tone."
- emote_type = EMOTE_AUDIBLE
/datum/emote/brain/whistle
key = "whistle"
key_third_person = "whistles"
message = "whistles."
- emote_type = EMOTE_AUDIBLE
diff --git a/code/modules/mob/living/carbon/alien/emote.dm b/code/modules/mob/living/carbon/alien/emote.dm
index e1b6ab0cc9c52..18b2555e781fd 100644
--- a/code/modules/mob/living/carbon/alien/emote.dm
+++ b/code/modules/mob/living/carbon/alien/emote.dm
@@ -11,6 +11,7 @@
key_third_person = "hisses"
message_alien = "hisses."
message_larva = "hisses softly."
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/alien/hiss/get_sound(mob/living/user)
if(isalienadult(user))
@@ -21,7 +22,7 @@
key_third_person = "roars"
message_alien = "roars."
message_larva = "softly roars."
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
vary = TRUE
/datum/emote/living/alien/roar/get_sound(mob/living/user)
diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm
index 4b3beebc9e457..a8d7f19daa9a5 100644
--- a/code/modules/mob/living/carbon/emote.dm
+++ b/code/modules/mob/living/carbon/emote.dm
@@ -5,15 +5,19 @@
key = "airguitar"
message = "is strumming the air and headbanging like a safari chimp"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/blink
key = "blink"
key_third_person = "blinks"
message = "blinks"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/blink_r
key = "blink_r"
+ name = "blink (Rapid)"
message = "blinks rapidly"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/clap
key = "clap"
@@ -21,7 +25,7 @@
message = "claps"
muzzle_ignore = TRUE
hands_use_check = TRUE
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
vary = TRUE
/datum/emote/living/carbon/clap/can_run_emote(mob/user, status_check = TRUE, intentional)
@@ -44,6 +48,7 @@
key_third_person = "eyerolls"
message = "rolls their eyes"
vary = TRUE
+ emote_type = EMOTE_VISIBLE
mob_type_blacklist_typecache = list(/mob/living/carbon/alien)
/datum/emote/living/carbon/eyeroll/can_run_emote(mob/user, status_check = TRUE, intentional)
@@ -57,13 +62,14 @@
key_third_person = "gnarls"
message = "gnarls and shows its teeth.."
mob_type_allowed_typecache = list(/mob/living/carbon/monkey)
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/carbon/moan
key = "moan"
key_third_person = "moans"
message = "moans"
message_mime = "appears to moan"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/carbon/roll
key = "roll"
@@ -71,6 +77,7 @@
message = "rolls"
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/scratch
key = "scratch"
@@ -78,6 +85,7 @@
message = "scratches"
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/screech
key = "screech"
@@ -96,10 +104,10 @@
'sound/creatures/monkey/monkey_screech_7.ogg')
/datum/emote/living/carbon/snap
- emote_type = EMOTE_AUDIBLE
muzzle_ignore = TRUE
hands_use_check = TRUE
vary = TRUE
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/carbon/snap/can_run_emote(mob/user, status_check = TRUE, intentional)
if(!..())
@@ -133,6 +141,7 @@
key = "roar"
key_third_person = "roars"
message = "roars"
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/carbon/sign
key = "sign"
@@ -140,6 +149,7 @@
message_param = "signs the number %t"
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/sign/select_param(mob/user, params)
. = ..()
@@ -152,22 +162,25 @@
message_param = "raises %t fingers"
mob_type_allowed_typecache = list(/mob/living/carbon/human)
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/tail
key = "tail"
message = "waves their tail"
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/wink
key = "wink"
key_third_person = "winks"
message = "winks"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/sweatdrop
key = "sweatdrop"
key_third_person = "sweatdrops"
message = "sweats"
- emote_type = EMOTE_ANIMATED
+ emote_type = EMOTE_ANIMATED | EMOTE_VISIBLE
sound_volume = 25
vary = TRUE
overlay_icon_state = "sweatdrop"
@@ -176,9 +189,12 @@
emote_length = 3 SECONDS
sound = 'sound/emotes/sweatdrop.ogg'
+/datum/emote/living/carbon/sweatdrop/sweat //This is entirely the same as sweatdrop, however people might use either, so i'm adding this one instead of editing the other one.
+ key = "sweat"
+
/datum/emote/living/carbon/annoyed
key = "annoyed"
- emote_type = EMOTE_ANIMATED
+ emote_type = EMOTE_ANIMATED | EMOTE_VISIBLE
sound_volume = 25
vary = TRUE
overlay_icon_state = "annoyed"
@@ -190,7 +206,7 @@
/datum/emote/living/carbon/glasses
key = "glasses"
message = "pushes up their glasses"
- emote_type = EMOTE_ANIMATED
+ emote_type = EMOTE_ANIMATED | EMOTE_VISIBLE
overlay_icon_state = "glasses"
emote_length = 1 SECONDS
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 1f58b92024368..953f5ee00dbee 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -8,7 +8,7 @@
key = "cry"
key_third_person = "cries"
message = "cries"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE //Cry in silence as you should.
/datum/emote/living/carbon/human/cry/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -34,14 +34,16 @@
key = "dap"
key_third_person = "daps"
message = "sadly can't find anybody to give daps to, and daps themself. Shameful"
- message_param = "give daps to %t"
+ message_param = "gives daps to %t"
hands_use_check = TRUE
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/carbon/human/etwitch
key = "etwitch"
key_third_person = "twitches their ears"
message = "twitches their ears"
vary = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/etwitch/can_run_emote(mob/user, status_check = TRUE, intentional)
if(!..())
@@ -52,6 +54,7 @@
/datum/emote/living/carbon/human/eyebrow
key = "eyebrow"
message = "raises an eyebrow"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/grumble
key = "grumble"
@@ -64,7 +67,7 @@
message = "shakes their own hand"
message_param = "shakes hands with %t"
hands_use_check = TRUE
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/hug
key = "hug"
@@ -72,17 +75,18 @@
message = "hugs themself"
message_param = "hugs %t"
hands_use_check = TRUE
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/mumble
key = "mumble"
key_third_person = "mumbles"
message = "mumbles"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/carbon/human/offer
key = "offer"
message = "offers an item"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/moth
// allow mothroach as well as human base mob - species check is done in can_run_emote
@@ -101,7 +105,7 @@
message = "lets out a tiny squeak"
emote_type = EMOTE_AUDIBLE
vary = TRUE
- sound = 'sound/emotes/mothsqueak.ogg'
+ sound = 'sound/emotes/moth/mothsqueak.ogg'
/datum/emote/living/carbon/human/moth/chitter
key = "chitter"
@@ -109,13 +113,13 @@
message = "chitters"
emote_type = EMOTE_AUDIBLE
vary = TRUE
- sound = 'sound/emotes/mothchitter.ogg'
+ sound = 'sound/emotes/moth/mothchitter.ogg'
/datum/emote/living/carbon/human/scream
key = "scream"
key_third_person = "screams"
message = "screams"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
vary = TRUE
/datum/emote/living/carbon/human/scream/get_sound(mob/living/user)
@@ -127,11 +131,13 @@
/datum/emote/living/carbon/human/pale
key = "pale"
message = "goes pale for a second"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/raise
key = "raise"
key_third_person = "raises"
message = "raises a hand"
+ emote_type = EMOTE_VISIBLE
hands_use_check = TRUE
/datum/emote/living/carbon/human/salute
@@ -139,17 +145,26 @@
key_third_person = "salutes"
message = "salutes"
message_param = "salutes to %t"
+ emote_type = EMOTE_VISIBLE
hands_use_check = TRUE
/datum/emote/living/carbon/human/shrug
key = "shrug"
key_third_person = "shrugs"
message = "shrugs"
+ emote_type = EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/tilt
+ key = "tilt"
+ key_third_person = "tilts their head to the side"
+ message = "tilts their head to the side"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/wag
key = "wag"
key_third_person = "wags"
message = "wags their tail"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/carbon/human/wag/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -178,6 +193,7 @@
key = "wing"
key_third_person = "wings"
message = "their wings"
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
/datum/emote/living/carbon/human/wing/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -205,6 +221,7 @@
if(istype(wings))
if(wings.flight_level >= WINGS_FLYING)
return TRUE
+ return FALSE
/mob/living/carbon/human/proc/Togglewings()
if(!dna || !dna.species)
@@ -254,12 +271,14 @@
key_third_person = "beeps"
message = "beeps"
message_param = "beeps at %t"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/boop
key = "boop"
key_third_person = "boops"
message = "boops."
sound = 'sound/machines/boop.ogg'
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/beep/run_emote(mob/user, params)
if(..())
@@ -270,6 +289,7 @@
key_third_person = "buzzes"
message = "buzzes"
message_param = "buzzes at %t"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/buzz/run_emote(mob/user, params)
if(..())
@@ -278,6 +298,7 @@
/datum/emote/living/carbon/human/robot_tongue/buzz2
key = "buzz2"
message = "buzzes twice"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/buzz2/run_emote(mob/user, params)
if(..())
@@ -287,6 +308,7 @@
key = "chime"
key_third_person = "chimes"
message = "chimes"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/chime/run_emote(mob/user, params)
if(..())
@@ -297,6 +319,7 @@
key_third_person = "pings"
message = "pings"
message_param = "pings at %t"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/ping/run_emote(mob/user, params)
if(..())
@@ -324,6 +347,7 @@
key = "honk"
key_third_person = "honks"
message = "honks"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/clown/honk/run_emote(mob/user, params)
if(..())
@@ -333,6 +357,7 @@
key = "sad"
key_third_person = "plays a sad trombone"
message = "plays a sad trombone"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/robot_tongue/clown/sad/run_emote(mob/user, params)
if(..())
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index b8a3cc83af3a9..2c4e4ec209664 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -2435,6 +2435,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
/datum/species/proc/get_cough_sound(mob/living/carbon/user)
return
+/datum/species/proc/get_cry_sound(mob/living/carbon/user)
+ return
+
/datum/species/proc/get_gasp_sound(mob/living/carbon/user)
return
@@ -2447,6 +2450,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
/datum/species/proc/get_sniff_sound(mob/living/carbon/user)
return
+/datum/species/proc/get_giggle_sound(mob/living/carbon/user)
+ return
+
/datum/species/proc/get_clear_sound(mob/living/carbon/user)
return
diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index be59dc9e0cc70..2c2c6be29cb23 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -194,6 +194,9 @@
/datum/species/ethereal/get_sniff_sound(mob/living/carbon/user)
return SPECIES_DEFAULT_SNIFF_SOUND(user)
+/datum/species/ethereal/get_giggle_sound(mob/living/carbon/user)
+ return SPECIES_DEFAULT_GIGGLE_SOUND(user)
+
/datum/species/ethereal/get_features()
var/list/features = ..()
diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm
index 6c0ad26df0a4e..fe144a6ef7efc 100644
--- a/code/modules/mob/living/carbon/human/species_types/humans.dm
+++ b/code/modules/mob/living/carbon/human/species_types/humans.dm
@@ -43,6 +43,9 @@
/datum/species/human/get_sniff_sound(mob/living/carbon/user)
return SPECIES_DEFAULT_SNIFF_SOUND(user)
+/datum/species/human/get_giggle_sound(mob/living/carbon/user)
+ return SPECIES_DEFAULT_GIGGLE_SOUND(user)
+
/datum/species/human/prepare_human_for_preview(mob/living/carbon/human/human)
human.hair_style = "Business Hair"
human.hair_color = "b96" // brown
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 6be939510e757..7b32986dea02a 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -78,6 +78,9 @@
/datum/species/lizard/get_sniff_sound(mob/living/carbon/user)
return SPECIES_DEFAULT_SNIFF_SOUND(user)
+/datum/species/lizard/get_giggle_sound(mob/living/carbon/user)
+ return SPECIES_DEFAULT_GIGGLE_SOUND(user)
+
/datum/species/lizard/get_species_description()
return "Lizardpeople, unlike many 'Animalid' species, are not derived from humans, and are simply bipedal reptile-like people. \
Lizards often find great pride in their species."
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index 8dfbd2ac4a660..6b2683571c83c 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -60,7 +60,7 @@
return 0
/datum/species/moth/get_laugh_sound(mob/living/carbon/user)
- return 'sound/emotes/mothlaugh.ogg'
+ return 'sound/emotes/moth/mothlaugh.ogg'
/datum/species/moth/get_scream_sound(mob/living/carbon/user)
return 'sound/voice/moth/scream_moth.ogg'
diff --git a/code/modules/mob/living/carbon/human/species_types/oozelings.dm b/code/modules/mob/living/carbon/human/species_types/oozelings.dm
index 35dc8f5afb36d..a3aaaeefebfee 100644
--- a/code/modules/mob/living/carbon/human/species_types/oozelings.dm
+++ b/code/modules/mob/living/carbon/human/species_types/oozelings.dm
@@ -213,6 +213,9 @@
/datum/species/oozeling/get_sniff_sound(mob/living/carbon/user)
return SPECIES_DEFAULT_SNIFF_SOUND(user)
+/datum/species/oozeling/get_giggle_sound(mob/living/carbon/user)
+ return SPECIES_DEFAULT_GIGGLE_SOUND(user)
+
/datum/species/oozeling/get_species_description()
return "Literally made of jelly, Oozelings are squishy friends aboard Space Station 13."
diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
index 584ea5dddfa43..3f72ccca4d850 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -156,6 +156,9 @@
/datum/species/plasmaman/get_sniff_sound(mob/living/carbon/user)
return SPECIES_DEFAULT_SNIFF_SOUND(user)
+/datum/species/plasmaman/get_giggle_sound(mob/living/carbon/user)
+ return SPECIES_DEFAULT_GIGGLE_SOUND(user)
+
/datum/species/plasmaman/get_species_description()
return "Found on the Icemoon of Freyja, plasmamen consist of colonial \
fungal organisms which together form a sentient being. In human space, \
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 6908a0bbb88da..fd5bd82cd86a2 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -11,6 +11,7 @@
key = "blush"
key_third_person = "blushes"
message = "blushes"
+ emote_type = EMOTE_VISIBLE
/// Timer for the blush visual to wear off
/datum/emote/living/blush/run_emote(mob/user, params, type_override, intentional)
@@ -39,37 +40,39 @@
key_third_person = "bows"
message = "bows"
message_param = "bows to %t"
+ emote_type = EMOTE_VISIBLE
hands_use_check = TRUE
/datum/emote/living/burp
key = "burp"
key_third_person = "burps"
message = "burps"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/choke
key = "choke"
key_third_person = "chokes"
message = "chokes"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/cross
key = "cross"
key_third_person = "crosses"
message = "crosses their arms"
+ emote_type = EMOTE_VISIBLE
hands_use_check = TRUE
/datum/emote/living/chuckle
key = "chuckle"
key_third_person = "chuckles"
message = "chuckles"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/collapse
key = "collapse"
key_third_person = "collapses"
message = "collapses"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/collapse/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -82,6 +85,7 @@
key_third_person = "dances"
message = "dances around happily"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/deathgasp
key = "deathgasp"
@@ -94,6 +98,7 @@
message_monkey = "lets out a faint chimper as it collapses and stops moving"
message_ipc = "gives one shrill beep before falling limp, their monitor flashing blue before completely shutting off"
message_simple = "stops moving"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_IMPORTANT
stat_allowed = HARD_CRIT
/datum/emote/living/deathgasp/run_emote(mob/user, params, type_override, intentional)
@@ -102,22 +107,28 @@
message_simple = S.deathmessage
. = ..()
message_simple = initial(message_simple)
- if(. && user.deathsound)
- if(isliving(user))
- var/mob/living/L = user
- if(!L.can_speak_vocal() || L.oxyloss >= 50)
- return //stop the sound if oxyloss too high/cant speak
+ var/mob/living/living_user = user
+ if(!. && !living_user.can_speak_vocal() || living_user.getOxyLoss() >= 50)
+ return //stop the sound if oxyloss too high/cant speak
+ var/mob/living/carbon/carbon_user = user
+ // For masks that give unique death sounds
+ if(istype(carbon_user) && isclothing(carbon_user.wear_mask) && carbon_user.wear_mask.unique_death)
+ playsound(carbon_user, carbon_user.wear_mask.unique_death, 200, TRUE, TRUE)
+ return
+ if(user.deathsound)
playsound(user, user.deathsound, 200, TRUE, TRUE)
/datum/emote/living/drool
key = "drool"
key_third_person = "drools"
message = "drools"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/faint
key = "faint"
key_third_person = "faints"
message = "faints"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/faint/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -130,82 +141,110 @@
key_third_person = "flaps"
message = "flaps their wings"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
var/wing_time = 10
/datum/emote/living/flap/run_emote(mob/user, params, type_override, intentional)
. = ..()
if(. && ishuman(user))
var/mob/living/carbon/human/H = user
+ var/obj/item/organ/wings/wings = H.getorganslot(ORGAN_SLOT_WINGS)
if(H.Togglewings())
addtimer(CALLBACK(H,TYPE_PROC_REF(/mob/living/carbon/human, Togglewings)), wing_time)
+ // play moth flutter noise if moth wing
+ if(istype(wings, /obj/item/organ/wings/moth))
+ playsound(H, 'sound/emotes/moth/moth_flutter.ogg', 50, TRUE)
/datum/emote/living/flap/aflap
key = "aflap"
key_third_person = "aflaps"
+ name = "flap (Angry)"
message = "flaps their wings aggressively"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
wing_time = 5
/datum/emote/living/frown
key = "frown"
key_third_person = "frowns"
message = "frowns"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/gag
key = "gag"
key_third_person = "gags"
message = "gags"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/giggle
key = "giggle"
key_third_person = "giggles"
message = "giggles"
message_mime = "giggles silently"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+
+/datum/emote/living/giggle/get_sound(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+ return H?.dna?.species?.get_giggle_sound(H)
/datum/emote/living/glare
key = "glare"
key_third_person = "glares"
message = "glares"
message_param = "glares at %t"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/grin
key = "grin"
key_third_person = "grins"
message = "grins"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/groan
key = "groan"
key_third_person = "groans"
message = "groans"
message_mime = "appears to groan"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/grimace
key = "grimace"
key_third_person = "grimaces"
message = "grimaces"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/jump
key = "jump"
key_third_person = "jumps"
message = "jumps"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+
+/datum/emote/living/jump/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_y = user.pixel_y + 4, time = 0.1 SECONDS)
+ animate(pixel_y = user.pixel_y - 4, time = 0.1 SECONDS)
+
+/datum/emote/living/jump/get_sound(mob/living/user)
+ return 'sound/weapons/thudswoosh.ogg'
/datum/emote/living/kiss
key = "kiss"
key_third_person = "kisses"
message = "blows a kiss"
message_param = "blows a kiss to %t"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/laugh
key = "laugh"
key_third_person = "laughs"
message = "laughs"
message_mime = "laughs silently"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
vary = TRUE
/datum/emote/living/laugh/can_run_emote(mob/living/user, status_check = TRUE , intentional)
@@ -227,12 +266,14 @@
key_third_person = "looks"
message = "looks"
message_param = "looks at %t"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/nod
key = "nod"
key_third_person = "nods"
message = "nods"
message_param = "nods at %t"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/point
key = "point"
@@ -240,6 +281,7 @@
message = "points"
message_param = "points at %t"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/point/run_emote(mob/user, params, type_override, intentional)
message_param = initial(message_param) // reset
@@ -258,14 +300,14 @@
key = "pout"
key_third_person = "pouts"
message = "pouts"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/scream
key = "scream"
key_third_person = "screams"
message = "screams"
message_mime = "acts out a scream"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
mob_type_blacklist_typecache = list(/mob/living/carbon/human) //Humans get specialized scream.
/datum/emote/living/scream/select_message_type(mob/user, intentional)
@@ -277,41 +319,62 @@
key = "scowl"
key_third_person = "scowls"
message = "scowls"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/shake
key = "shake"
key_third_person = "shakes"
message = "shakes their head"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/shiver
key = "shiver"
key_third_person = "shiver"
message = "shivers"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE
+
+#define SHIVER_LOOP_DURATION (1 SECONDS)
+/datum/emote/living/shiver/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+ for(var/i in 1 to SHIVER_LOOP_DURATION / (0.2 SECONDS)) //desired total duration divided by the iteration duration to give the necessary iteration count
+ animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+#undef SHIVER_LOOP_DURATION
/datum/emote/living/sit
key = "sit"
key_third_person = "sits"
message = "sits down"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/smile
key = "smile"
key_third_person = "smiles"
message = "smiles"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/smug
key = "smug"
key_third_person = "smugs"
message = "grins smugly"
+ emote_type = EMOTE_VISIBLE
+
+/datum/emote/living/smirk
+ key = "smirk"
+ key_third_person = "smirks"
+ message = "smirks"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/snore
key = "snore"
key_third_person = "snores"
message = "snores"
message_mime = "sleeps soundly"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
stat_allowed = UNCONSCIOUS
/datum/emote/living/stare
@@ -319,22 +382,25 @@
key_third_person = "stares"
message = "stares"
message_param = "stares at %t"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/strech
key = "stretch"
key_third_person = "stretches"
message = "stretches their arms"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/sulk
key = "sulk"
key_third_person = "sulks"
message = "sulks down sadly"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/surrender
key = "surrender"
key_third_person = "surrenders"
message = "puts their hands on their head and falls to the ground, surrendering"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/surrender/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -346,48 +412,97 @@
key = "sway"
key_third_person = "sways"
message = "sways around dizzily"
+ emote_type = EMOTE_VISIBLE
+
+/datum/emote/living/sway/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x + 2, time = 0.5 SECONDS)
+ for(var/i in 1 to 2)
+ animate(pixel_x = user.pixel_x - 4, time = 1.0 SECONDS)
+ animate(pixel_x = user.pixel_x + 4, time = 1.0 SECONDS)
+ animate(pixel_x = user.pixel_x - 2, time = 0.5 SECONDS)
/datum/emote/living/tremble
key = "tremble"
key_third_person = "trembles"
- message = "trembles in fear"
+ message = "trembles"
+ emote_type = EMOTE_VISIBLE
+
+#define TREMBLE_LOOP_DURATION (4.4 SECONDS)
+/datum/emote/living/tremble/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x + 2, time = 0.2 SECONDS)
+ for(var/i in 1 to TREMBLE_LOOP_DURATION / (0.4 SECONDS)) //desired total duration divided by the iteration duration to give the necessary iteration count
+ animate(pixel_x = user.pixel_x - 2, time = 0.2 SECONDS)
+ animate(pixel_x = user.pixel_x + 2, time = 0.2 SECONDS)
+ animate(pixel_x = user.pixel_x - 2, time = 0.2 SECONDS)
+#undef TREMBLE_LOOP_DURATION
/datum/emote/living/twitch
key = "twitch"
key_third_person = "twitches"
message = "twitches violently"
+ emote_type = EMOTE_VISIBLE
+
+/datum/emote/living/twitch/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+ animate(time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
/datum/emote/living/twitch_s
key = "twitch_s"
+ name = "twitch (Slight)"
message = "twitches"
+ emote_type = EMOTE_VISIBLE
+
+/datum/emote/living/twitch_s/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
/datum/emote/living/wave
key = "wave"
key_third_person = "waves"
message = "waves"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/whimper
key = "whimper"
key_third_person = "whimpers"
message = "whimpers"
message_mime = "appears hurt"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/wsmile
key = "wsmile"
key_third_person = "wsmiles"
+ name = "smile (Weak)"
message = "smiles weakly"
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/yawn
key = "yawn"
key_third_person = "yawns"
message = "yawns"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/custom
key = "me"
key_third_person = "custom"
message = null
mob_type_blacklist_typecache = /mob/living/brain
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/custom/can_run_emote(mob/user, status_check, intentional)
. = ..() && intentional
@@ -411,11 +526,17 @@
to_chat(user, "You cannot send IC messages (muted).")
return FALSE
else if(!params)
- var/custom_emote = stripped_input(usr, "Choose an emote to display.")
+ var/custom_emote = stripped_input(user, "Choose an emote to display.")
if(custom_emote && !check_invalid(user, custom_emote))
- var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable")
- if(type == "Hearable")
- emote_type |= EMOTE_AUDIBLE
+ var/list/emote_list = list("Audible", "Visible", "Both")
+ var/type = tgui_input_list(user, "Is this a visible or audible emote?", "Emote Type", emote_list)
+ switch(type)
+ if("Audible")
+ emote_type |= EMOTE_AUDIBLE
+ if("Visible")
+ emote_type |= EMOTE_VISIBLE
+ if("Both")
+ emote_type |= EMOTE_VISIBLE | EMOTE_AUDIBLE
message = user.say_emphasis(custom_emote)
else
message = params
@@ -423,7 +544,7 @@
emote_type = type_override
. = ..()
message = null
- emote_type = 0
+ emote_type = null
/datum/emote/living/custom/replace_pronoun(mob/user, message)
return message
@@ -432,6 +553,7 @@
key = "help"
/datum/emote/living/help/run_emote(mob/user, params, type_override, intentional)
+ . = ..()
var/list/keys = list()
var/list/message = list("Available emotes, you can use them with say \"*emote\": ")
@@ -462,12 +584,14 @@
message = "beeps"
message_param = "beeps at %t"
sound = 'sound/machines/twobeep.ogg'
+ emote_type = EMOTE_AUDIBLE
mob_type_allowed_typecache = list(/mob/living/brain, /mob/living/silicon, /mob/living/simple_animal/hostile/mining_drone)
/datum/emote/living/circle
key = "circle"
key_third_person = "circles"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/circle/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -483,6 +607,7 @@
key = "slap"
key_third_person = "slaps"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/slap/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -500,6 +625,7 @@
key_third_person = "highfives"
message = "raises their hand"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/raisehand/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -516,6 +642,7 @@
key_third_person = "fingerguns"
message = "forms their fingers into the shape of a crude gun"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/fingergun/run_emote(mob/user, params, type_override, intentional)
. = ..()
@@ -533,6 +660,7 @@
message = "clicks their tongue"
message_ipc = "makes a click sound"
message_insect = "clicks their mandibles"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/click/get_sound(mob/living/user)
if(ismoth(user) || isapid(user) || isflyperson(user) || istype(user, /mob/living/basic/mothroach))
@@ -546,6 +674,7 @@
key = "zap"
key_third_person = "zaps"
message = "zaps"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/zap/can_run_emote(mob/user, status_check = TRUE , intentional)
. = ..()
@@ -562,11 +691,13 @@
key = "hum"
key_third_person = "hums"
message = "hums"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/hiss
key = "hiss"
key_third_person = "hisses"
message = "hisses"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/hiss/get_sound(mob/living/user)
if(islizard(user))
@@ -582,6 +713,7 @@
message_simple = "attempts a thumbs up"
message_param = "flashes a thumbs up at %t"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/thumbs_down
key = "thumbsdown"
@@ -593,6 +725,7 @@
message_simple = "attempts a thumbs down"
message_param = "flashes a thumbs down at %t"
hands_use_check = TRUE
+ emote_type = EMOTE_VISIBLE
/datum/emote/living/whistle
key="whistle"
@@ -601,14 +734,14 @@
message_robot = "whistles a few synthesized notes"
message_AI = "whistles a synthesized song"
message_ipc = "whistles a few synthesized notes"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/whistle/get_sound(mob/living/user)
- return 'sound/items/megaphone.ogg'
+ return 'sound/emotes/whistle1.ogg'
/// Breathing required + audible emotes
/datum/emote/living/must_breathe
- emote_type = EMOTE_AUDIBLE
vary = TRUE
/datum/emote/living/must_breathe/can_run_emote(mob/user, status_check = TRUE, intentional)
@@ -621,11 +754,13 @@
key = "clear"
key_third_person = "clears their throat"
message = "clears their throat"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/must_breathe/cough
key = "cough"
key_third_person = "coughs"
message = "coughs"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/must_breathe/cough/can_run_emote(mob/user, status_check = TRUE, intentional)
return ..() && !HAS_TRAIT(user, TRAIT_SOOTHED_THROAT)
@@ -640,6 +775,7 @@
key = "gasp"
key_third_person = "gasps"
message = "gasps"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE // You can see a person gasping.
/datum/emote/living/must_breathe/gasp/get_sound(mob/living/user)
if(!ishuman(user))
@@ -651,12 +787,13 @@
key = "huff"
key_third_person = "huffs"
message ="lets out a huff"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/must_breathe/sigh
key = "sigh"
key_third_person = "sighs"
message = "sighs"
- emote_type = EMOTE_AUDIBLE|EMOTE_ANIMATED
+ emote_type = EMOTE_ANIMATED | EMOTE_AUDIBLE | EMOTE_VISIBLE
emote_length = 3 SECONDS
overlay_y_offset = -1
overlay_icon_state = "sigh"
@@ -671,6 +808,7 @@
key = "sneeze"
key_third_person = "sneezes"
message = "sneezes"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/living/must_breathe/sneeze/get_sound(mob/living/user)
if(!ishuman(user))
@@ -682,6 +820,7 @@
key = "sniff"
key_third_person = "sniffs"
message = "sniffs"
+ emote_type = EMOTE_AUDIBLE
/datum/emote/living/must_breathe/sniff/get_sound(mob/living/user)
if(!ishuman(user))
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 22bbed72e029a..a31a5c1916f75 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -101,7 +101,8 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
if(!message)
return
- message = check_for_custom_say_emote(message, message_mods)
+ if(!forced && !saymode)
+ message = check_for_custom_say_emote(message, message_mods)
switch(stat)
if(SOFT_CRIT)
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
index a265a7582c7a4..d3ed774cb60c1 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
@@ -8,4 +8,5 @@
message = "oogas"
message_param = "oogas at %t"
sound = 'sound/creatures/gorilla.ogg'
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
diff --git a/code/modules/mob/living/simple_animal/slime/emote.dm b/code/modules/mob/living/simple_animal/slime/emote.dm
index c4526623762c6..da3a77b9ada00 100644
--- a/code/modules/mob/living/simple_animal/slime/emote.dm
+++ b/code/modules/mob/living/simple_animal/slime/emote.dm
@@ -6,27 +6,31 @@
key = "bounce"
key_third_person = "bounces"
message = "bounces in place."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/slime/jiggle
key = "jiggle"
key_third_person = "jiggles"
message = "jiggles!"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/slime/light
key = "light"
key_third_person = "lights"
message = "lights up for a bit, then stops."
+ emote_type = EMOTE_VISIBLE
/datum/emote/slime/vibrate
key = "vibrate"
key_third_person = "vibrates"
message = "vibrates!"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
/datum/emote/slime/squish
key = "squish"
key_third_person = "squishes"
message = "squishes"
- emote_type = EMOTE_AUDIBLE
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
sound_volume = 25
/datum/emote/slime/squish/get_sound(mob/living/user)
@@ -35,6 +39,7 @@
/datum/emote/slime/mood
key = "moodnone"
var/mood = null
+ emote_type = EMOTE_VISIBLE
/datum/emote/slime/mood/run_emote(mob/user, params, type_override, intentional)
. = ..()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index b81ca6908319b..e3e077c32a5bb 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -155,32 +155,37 @@
/mob/proc/show_message(msg, type, alt_msg, alt_type, avoid_highlighting = FALSE, dist)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
if(!client)
- return
+ return FALSE
msg = copytext_char(msg, 1, MAX_MESSAGE_LEN)
+ // Return TRUE if we sent the original msg, otherwise return FALSE
+ . = TRUE
if(type)
if(type & MSG_VISUAL && (is_blind() && dist > BLIND_TEXT_DIST))//Vision related
if(!alt_msg)
- return
+ return FALSE
else
msg = alt_msg
type = alt_type
+ . = FALSE
if(type & MSG_AUDIBLE && !can_hear())//Hearing related
if(!alt_msg)
- return
+ return FALSE
else
msg = alt_msg
type = alt_type
+ . = FALSE
if(type & MSG_VISUAL && is_blind())
- return
+ return FALSE
// voice muffling
if(stat == UNCONSCIOUS || stat == HARD_CRIT)
if(type & MSG_AUDIBLE) //audio
to_chat(src, "... You can almost hear something ...")
return
to_chat(src, msg, avoid_highlighting = avoid_highlighting)
+ return .
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, list/visible_message_flags, allow_inside_usr = FALSE, separation = " ")
@@ -233,8 +238,22 @@
/mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, list/visible_message_flags, separation = " ")
. = ..()
- if(self_message)
- show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE)
+ if(!self_message)
+ return
+ var/raw_self_message = self_message
+ var/self_runechat = FALSE
+ if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE))
+ self_message = "[src] [self_message]" // May make more sense as "You do x"
+
+ if(LAZYFIND(visible_message_flags, ALWAYS_SHOW_SELF_MESSAGE))
+ to_chat(src, self_message)
+ self_runechat = TRUE
+
+ else
+ self_runechat = show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE)
+
+ if(self_runechat && (LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE)) && runechat_prefs_check(src, visible_message_flags))
+ create_chat_message(src, null, list(src), raw_message = raw_self_message, message_mods = visible_message_flags)
/**
* Show a message to all mobs in earshot of this atom
@@ -280,8 +299,21 @@
*/
/mob/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, list/audible_message_flags, separation = " ")
. = ..()
- if(self_message)
- show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL)
+ if(!self_message)
+ return
+
+ var/raw_self_message = self_message
+ var/self_runechat = FALSE
+ if(LAZYFIND(audible_message_flags, CHATMESSAGE_EMOTE))
+ self_message = "[src] [self_message]"
+ if(LAZYFIND(audible_message_flags, ALWAYS_SHOW_SELF_MESSAGE))
+ to_chat(src, self_message)
+ self_runechat = TRUE
+ else
+ self_runechat = show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL)
+
+ if(self_runechat && (LAZYFIND(audible_message_flags, CHATMESSAGE_EMOTE)) && runechat_prefs_check(src, audible_message_flags))
+ create_chat_message(src, null, list(src), raw_message = raw_self_message, message_mods = audible_message_flags)
///Returns the client runechat visible messages preference according to the message type.
/atom/proc/runechat_prefs_check(mob/target, list/visible_message_flags)
@@ -289,14 +321,14 @@
return FALSE
if (!target.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat_non_mobs))
return FALSE
- if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_player_preference(/datum/preference/toggle/see_rc_emotes))
+ if((LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE)) && !target.client.prefs.read_player_preference(/datum/preference/toggle/see_rc_emotes))
return FALSE
return TRUE
/mob/runechat_prefs_check(mob/target, list/visible_message_flags)
if(!target.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat))
return FALSE
- if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_player_preference(/datum/preference/toggle/see_rc_emotes))
+ if((LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE)) && !target.client.prefs.read_player_preference(/datum/preference/toggle/see_rc_emotes))
return FALSE
return TRUE
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index f720d113c9d1c..6205706f1cefc 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -28,6 +28,7 @@
/mob/verb/me_verb(message as text)
set name = "Me"
set category = "IC"
+ set desc = "Perform a custom emote. Leave blank to pick between an audible or a visible emote (Defaults to visible)."
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "Speech is currently admin-disabled.")
@@ -35,7 +36,7 @@
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
- usr.emote("me",1,message,TRUE)
+ usr.emote("me",EMOTE_VISIBLE|EMOTE_AUDIBLE,message,TRUE)
///Speak as a dead person (ghost etc)
/mob/proc/say_dead(var/message)
@@ -120,7 +121,7 @@
return message
if(is_banned_from(ckey, "Emote"))
return copytext(message, customsaypos + 1)
- mods[MODE_CUSTOM_SAY_EMOTE] = trim_right(LOWER_TEXT(copytext_char(message, 1, customsaypos)))
+ mods[MODE_CUSTOM_SAY_EMOTE] = trim_right(copytext(message, 1, customsaypos))
message = trim_left(copytext(message, customsaypos + 1))
if(!message)
mods[MODE_CUSTOM_SAY_ERASE_INPUT] = TRUE
diff --git a/sound/attributions.txt b/sound/attributions.txt
index 3d594595af61a..9fa3c9dfce2e4 100644
--- a/sound/attributions.txt
+++ b/sound/attributions.txt
@@ -2,3 +2,12 @@ sound/items/handling/standard_stamp.ogg is adapted from tom_woysky's "Stamp.wav"
sound/ambience/antag/abductee.ogg is from "Warp SFX" https://freesound.org/people/Breviceps/sounds/453391 (CC0)
sound/ambience/antag/brainwash.ogg is from "nog.wav" https://freesound.org/people/_NOMINAL_/sounds/124602 (CC-BY 3.0)
sound/ambience/antag/hypnosis.ogg is from "Flashback.wav" https://freesound.org/people/Sclolex/sounds/342103 (CC0)
+
+
+modified by grungussuss:
+male_sneeze2.ogg: https://freesound.org/people/InspectorJ/sounds/352177/ , license: CC BY 4.0 DEED
+female_cough_4.ogg: https://freesound.org/people/OwlStorm/sounds/151213/ , license: CC0 1.0 DEED
+female_cough_5.ogg: https://freesound.org/people/thatkellytrna/sounds/425777/ , license: CC0 1.0 DEED
+female_cough_6.ogg: https://freesound.org/people/DarkNightPrincess/sounds/625322/ , license: CC0 1.0 DEED
+female_cough_7.ogg: https://freesound.org/people/drotzruhn/sounds/405206/ , license: CC BY 4.0 DEED
+whistle1.ogg: https://freesound.org/people/taure/sounds/411638/ , license: CC0 1.0 DEED
diff --git a/sound/emotes/female/female_cough_4.ogg b/sound/emotes/female/female_cough_4.ogg
new file mode 100644
index 0000000000000..53af74368c3bf
Binary files /dev/null and b/sound/emotes/female/female_cough_4.ogg differ
diff --git a/sound/emotes/female/female_cough_5.ogg b/sound/emotes/female/female_cough_5.ogg
new file mode 100644
index 0000000000000..eb3551a31fecb
Binary files /dev/null and b/sound/emotes/female/female_cough_5.ogg differ
diff --git a/sound/emotes/female/female_cough_6.ogg b/sound/emotes/female/female_cough_6.ogg
new file mode 100644
index 0000000000000..7562661bd4853
Binary files /dev/null and b/sound/emotes/female/female_cough_6.ogg differ
diff --git a/sound/emotes/female/female_cough_7.ogg b/sound/emotes/female/female_cough_7.ogg
new file mode 100644
index 0000000000000..62938b7b761af
Binary files /dev/null and b/sound/emotes/female/female_cough_7.ogg differ
diff --git a/sound/emotes/female/female_sneeze.ogg b/sound/emotes/female/female_sneeze1.ogg
similarity index 100%
rename from sound/emotes/female/female_sneeze.ogg
rename to sound/emotes/female/female_sneeze1.ogg
diff --git a/sound/emotes/female/female_sneeze2.ogg b/sound/emotes/female/female_sneeze2.ogg
new file mode 100644
index 0000000000000..8fe020a8c7e8a
Binary files /dev/null and b/sound/emotes/female/female_sneeze2.ogg differ
diff --git a/sound/emotes/male/male_giggle_1.ogg b/sound/emotes/male/male_giggle_1.ogg
new file mode 100644
index 0000000000000..7ba3a11664090
Binary files /dev/null and b/sound/emotes/male/male_giggle_1.ogg differ
diff --git a/sound/emotes/male/male_giggle_2.ogg b/sound/emotes/male/male_giggle_2.ogg
new file mode 100644
index 0000000000000..85eb97fd63ffd
Binary files /dev/null and b/sound/emotes/male/male_giggle_2.ogg differ
diff --git a/sound/emotes/male/male_giggle_3.ogg b/sound/emotes/male/male_giggle_3.ogg
new file mode 100644
index 0000000000000..d248f4d0d89d8
Binary files /dev/null and b/sound/emotes/male/male_giggle_3.ogg differ
diff --git a/sound/emotes/male/male_sneeze.ogg b/sound/emotes/male/male_sneeze1.ogg
similarity index 100%
rename from sound/emotes/male/male_sneeze.ogg
rename to sound/emotes/male/male_sneeze1.ogg
diff --git a/sound/emotes/male/male_sneeze2.ogg b/sound/emotes/male/male_sneeze2.ogg
new file mode 100644
index 0000000000000..1c7e8f42534d8
Binary files /dev/null and b/sound/emotes/male/male_sneeze2.ogg differ
diff --git a/sound/emotes/moth/moth_flutter.ogg b/sound/emotes/moth/moth_flutter.ogg
new file mode 100644
index 0000000000000..f5737d522ca20
Binary files /dev/null and b/sound/emotes/moth/moth_flutter.ogg differ
diff --git a/sound/emotes/mothchitter.ogg b/sound/emotes/moth/mothchitter.ogg
similarity index 100%
rename from sound/emotes/mothchitter.ogg
rename to sound/emotes/moth/mothchitter.ogg
diff --git a/sound/emotes/mothlaugh.ogg b/sound/emotes/moth/mothlaugh.ogg
similarity index 100%
rename from sound/emotes/mothlaugh.ogg
rename to sound/emotes/moth/mothlaugh.ogg
diff --git a/sound/emotes/mothsqueak.ogg b/sound/emotes/moth/mothsqueak.ogg
similarity index 100%
rename from sound/emotes/mothsqueak.ogg
rename to sound/emotes/moth/mothsqueak.ogg
diff --git a/sound/emotes/scream_moth.ogg b/sound/emotes/moth/scream_moth.ogg
similarity index 100%
rename from sound/emotes/scream_moth.ogg
rename to sound/emotes/moth/scream_moth.ogg
diff --git a/sound/emotes/whistle1.ogg b/sound/emotes/whistle1.ogg
new file mode 100644
index 0000000000000..4109260659723
Binary files /dev/null and b/sound/emotes/whistle1.ogg differ
diff --git a/sound/voice/moth/credit.txt b/sound/voice/moth/credit.txt
new file mode 100644
index 0000000000000..584d169bc09dc
--- /dev/null
+++ b/sound/voice/moth/credit.txt
@@ -0,0 +1,5 @@
+"moth_flutter" modified from
+https://freesound.org/people/Godowan/sounds/240476/
+(CC 0 license)
+
+who knows where the original moth scream noise was I sure as hell don't
diff --git a/sound/voice/sec_death.ogg b/sound/voice/sec_death.ogg
new file mode 100644
index 0000000000000..25f9b24c313f2
Binary files /dev/null and b/sound/voice/sec_death.ogg differ
diff --git a/tgui/packages/tgui/components/SearchBar.tsx b/tgui/packages/tgui/components/SearchBar.tsx
new file mode 100644
index 0000000000000..9f4e410f073f6
--- /dev/null
+++ b/tgui/packages/tgui/components/SearchBar.tsx
@@ -0,0 +1,38 @@
+import { Icon, Input, Stack } from '.';
+
+type RequiredProps = {
+ /** The state variable. */
+ query: string;
+ /** The function to call when the user searches. */
+ onSearch: (query: string) => void;
+};
+
+type OptionalProps = Partial<{
+ /** Whether the input should be focused on mount. */
+ autoFocus: boolean;
+ /** Whether to show the search icon. */
+ noIcon: boolean;
+ /** The placeholder text. */
+ placeholder: string;
+ /** Override styles of the search bar. */
+ style: Partial;
+}>;
+
+type Props = RequiredProps & OptionalProps;
+
+/**
+ * Simple component for searching.
+ * This component does not accept box props - just recreate it if needed
+ */
+export const SearchBar = (props: Props, content) => {
+ const { autoFocus, noIcon = false, onSearch, placeholder = 'Search...', query = '', style } = props;
+
+ return (
+
+ {!noIcon && }
+
+ onSearch(value)} placeholder={placeholder} value={query} />
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/EmotePanel.tsx b/tgui/packages/tgui/interfaces/EmotePanel.tsx
new file mode 100644
index 0000000000000..f894dd529727f
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/EmotePanel.tsx
@@ -0,0 +1,203 @@
+import { useBackend, useLocalState } from '../backend';
+import { Window } from '../layouts';
+import { Button, Section, Flex, Icon, Box } from '../components';
+import { BooleanLike } from '../../common/react';
+import { SearchBar } from '../components/SearchBar';
+import { capitalize } from '../../common/string';
+
+type Emote = {
+ key: string;
+ name: string;
+ hands: BooleanLike;
+ visible: BooleanLike;
+ audible: BooleanLike;
+ sound: BooleanLike;
+ use_params: BooleanLike;
+};
+
+type EmotePanelData = {
+ emotes: Emote[];
+};
+
+export const EmotePanelContent = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { emotes } = data;
+
+ const [filterVisible, toggleVisualFilter] = useLocalState(context, 'filterVisible', false);
+
+ const [filterAudible, toggleAudibleFilter] = useLocalState(context, 'filterAudible', false);
+
+ const [filterSound, toggleSoundFilter] = useLocalState(context, 'filterSound', false);
+
+ const [filterHands, toggleHandsFilter] = useLocalState(context, 'filterHands', false);
+
+ const [filterUseParams, toggleUseParamsFilter] = useLocalState(context, 'filterUseParams', false);
+
+ const [useParams, toggleUseParams] = useLocalState(context, 'useParams', false);
+
+ const [searchText, setSearchText] = useLocalState(context, 'search_text', '');
+
+ const [showNames, toggleShowNames] = useLocalState(context, 'showNames', true);
+
+ const [showIcons, toggleShowIcons] = useLocalState(context, 'showIcons', false);
+
+ return (
+
+
+
+ 0 ? `Search results of "${searchText}"` : `All Emotes`}
+ buttons={
+
+
+ toggleShowNames(!showNames)}>{showNames ? 'Show Names' : 'Show Keys'}
+ toggleShowIcons(!showIcons)}>
+ Show Icons
+
+
+
+ toggleUseParams(!useParams)}>
+ Use Params
+
+
+
+ }>
+
+
+ {emotes
+ .filter(
+ (emote) =>
+ emote.key &&
+ (searchText.length > 0
+ ? emote.key.toLowerCase().includes(searchText.toLowerCase()) ||
+ emote.name.toLowerCase().includes(searchText.toLowerCase())
+ : true) &&
+ (filterVisible ? emote.visible : true) &&
+ (filterAudible ? emote.audible : true) &&
+ (filterSound ? emote.sound : true) &&
+ (filterHands ? emote.hands : true) &&
+ (filterUseParams ? emote.use_params : true)
+ )
+ .sort((a, b) => (a.name > b.name ? 1 : -1))
+ .map((emote) => (
+
+ )
+ }
+ onClick={() =>
+ act('play_emote', {
+ emote_key: emote.key,
+ use_params: useParams,
+ })
+ }>
+
+ {showNames ? capitalize(emote.name.toLowerCase()) : emote.key}
+
+ {showIcons ? (
+
+ ) : (
+ ''
+ )}
+
+ ))}
+
+
+
+
+ );
+};
+
+const EmoteIcons = (props, context) => {
+ const { visible, audible, sound, hands, use_params, margin } = props;
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export const EmotePanel = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};