diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 2e5bd580bf..b452eff382 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -16,8 +16,9 @@
var/emote_type = EMOTE_VISIBLE //Whether the emote is visible or audible
var/restraint_check = FALSE //Checks if the mob is restrained before performing the emote
var/muzzle_ignore = FALSE //Will only work if the emote is EMOTE_AUDIBLE
- var/list/mob_type_allowed_typecache //Types that are allowed to use that emote
+ var/list/mob_type_allowed_typecache = list(/mob) //Types that are allowed to use that emote
var/list/mob_type_blacklist_typecache //Types that are NOT allowed to use that emote
+ var/list/mob_type_ignore_stat_typecache
var/stat_allowed = CONSCIOUS
var/static/list/emote_list = list()
@@ -26,6 +27,7 @@
emote_list[key_third_person] = src
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)
/datum/emote/proc/run_emote(mob/user, params, type_override)
. = TRUE
@@ -37,9 +39,10 @@
msg = replace_pronoun(user, msg)
- var/mob/living/L = user
- for(var/obj/item/implant/I in L.implants)
- I.trigger(key, L)
+ if(isliving(user))
+ var/mob/living/L = user
+ for(var/obj/item/implant/I in L.implants)
+ I.trigger(key, L)
if(!msg)
return
@@ -97,7 +100,7 @@
return FALSE
if(is_type_in_typecache(user, mob_type_blacklist_typecache))
return FALSE
- if(status_check)
+ if(status_check && !is_type_in_typecache(user, mob_type_ignore_stat_typecache))
if(user.stat > stat_allowed || (user.status_flags & FAKEDEATH))
to_chat(user, "You cannot [key] while unconscious.")
return FALSE
diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm
index ad64c53fc7..110d2b5cc4 100644
--- a/code/modules/mob/dead/observer/say.dm
+++ b/code/modules/mob/dead/observer/say.dm
@@ -5,6 +5,7 @@
return
log_talk(src,"Ghost/[src.key] : [message]", LOGSAY)
+<<<<<<< HEAD
. = src.say_dead(message)
@@ -23,3 +24,26 @@
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
to_chat(src, "[link] [message]")
+=======
+
+ if(check_emote(message))
+ return
+
+ . = say_dead(message)
+
+/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
+ var/atom/movable/to_follow = speaker
+ if(radio_freq)
+ var/atom/movable/virtualspeaker/V = speaker
+
+ if(isAI(V.source))
+ var/mob/living/silicon/ai/S = V.source
+ to_follow = S.eyeobj
+ else
+ to_follow = V.source
+ var/link = FOLLOW_LINK(src, to_follow)
+ // Recompose the message, because it's scrambled by default
+ message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
+ to_chat(src, "[link] [message]")
+
+>>>>>>> 887cc89... No flipping while unconscious (#33736)
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
new file mode 100644
index 0000000000..5487b5b284
--- /dev/null
+++ b/code/modules/mob/emote.dm
@@ -0,0 +1,48 @@
+//The code execution of the emote datum is located at code/datums/emotes.dm
+/mob/emote(act, m_type = null, message = null)
+ act = lowertext(act)
+ var/param = message
+ var/custom_param = findchar(act, " ")
+ if(custom_param)
+ param = copytext(act, custom_param + 1, length(act) + 1)
+ act = copytext(act, 1, custom_param)
+
+ var/datum/emote/E
+ E = E.emote_list[act]
+ if(!E)
+ to_chat(src, "Unusable emote '[act]'. Say *help for a list.")
+ return
+ E.run_emote(src, param, m_type)
+
+/datum/emote/flip
+ key = "flip"
+ key_third_person = "flips"
+ restraint_check = TRUE
+ mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
+ mob_type_ignore_stat_typecache = list(/mob/dead/observer)
+
+/datum/emote/flip/run_emote(mob/user, params)
+ . = ..()
+ if(.)
+ user.SpinAnimation(7,1)
+
+/datum/emote/spin
+ key = "spin"
+ key_third_person = "spins"
+ restraint_check = TRUE
+ mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
+ mob_type_ignore_stat_typecache = list(/mob/dead/observer)
+
+/datum/emote/spin/run_emote(mob/user)
+ . = ..()
+ if(.)
+ user.spin(20, 1)
+
+ if(iscyborg(user) && user.has_buckled_mobs())
+ var/mob/living/silicon/robot/R = user
+ GET_COMPONENT_FROM(riding_datum, /datum/component/riding, R)
+ if(riding_datum)
+ for(var/mob/M in R.buckled_mobs)
+ riding_datum.force_dismount(M)
+ else
+ R.unbuckle_all_mobs()
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 9f541aaf67..fd3c521cd1 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -1,18 +1,3 @@
-//The code execution of the emote datum is located at code/datums/emotes.dm
-/mob/living/emote(act, m_type = null, message = null)
- act = lowertext(act)
- var/param = message
- var/custom_param = findchar(act, " ")
- if(custom_param)
- param = copytext(act, custom_param + 1, length(act) + 1)
- act = copytext(act, 1, custom_param)
-
- var/datum/emote/E
- E = E.emote_list[act]
- if(!E)
- to_chat(src, "Unusable emote '[act]'. Say *help for a list.")
- return
- E.run_emote(src, param, m_type)
/* EMOTE DATUMS */
/datum/emote/living
@@ -143,16 +128,6 @@
restraint_check = TRUE
wing_time = 10
-/datum/emote/living/flip
- key = "flip"
- key_third_person = "flips"
- restraint_check = TRUE
-
-/datum/emote/living/flip/run_emote(mob/user, params)
- . = ..()
- if(.)
- user.SpinAnimation(7,1)
-
/datum/emote/living/frown
key = "frown"
key_third_person = "frowns"
@@ -482,25 +457,6 @@
message_param = "beeps at %t."
sound = 'sound/machines/twobeep.ogg'
-/datum/emote/living/spin
- key = "spin"
- key_third_person = "spins"
- restraint_check = TRUE
-
-/datum/emote/living/spin/run_emote(mob/user)
- . = ..()
- if(.)
- user.spin(20, 1)
- if(iscyborg(user) && user.has_buckled_mobs())
- var/mob/living/silicon/robot/R = user
- GET_COMPONENT_FROM(riding_datum, /datum/component/riding, R)
- if(riding_datum)
- for(var/mob/M in R.buckled_mobs)
- riding_datum.force_dismount(M)
- else
- R.unbuckle_all_mobs()
-
-
/datum/emote/living/circle
key = "circle"
key_third_person = "circles"
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index e21c21453a..4e731567f1 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -296,11 +296,6 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return 1
-/mob/living/proc/check_emote(message)
- if(copytext(message, 1, 2) == "*")
- emote(copytext(message, 2))
- return 1
-
/mob/living/proc/get_message_mode(message)
var/key = copytext(message, 1, 2)
if(key == "#")
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 1ee7f27452..32710c06f9 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
//Speech verbs.
/mob/verb/say_verb(message as text)
set name = "Say"
@@ -80,3 +81,93 @@
/mob/proc/lingcheck()
return LINGHIVE_NONE
+=======
+//Speech verbs.
+/mob/verb/say_verb(message as text)
+ set name = "Say"
+ set category = "IC"
+ if(GLOB.say_disabled) //This is here to try to identify lag problems
+ to_chat(usr, "Speech is currently admin-disabled.")
+ return
+ if(message)
+ say(message)
+
+
+/mob/verb/whisper_verb(message as text)
+ set name = "Whisper"
+ set category = "IC"
+ if(GLOB.say_disabled) //This is here to try to identify lag problems
+ to_chat(usr, "Speech is currently admin-disabled.")
+ return
+ whisper(message)
+
+/mob/proc/whisper(message, datum/language/language=null)
+ say(message, language) //only living mobs actually whisper, everything else just talks
+
+/mob/verb/me_verb(message as text)
+ set name = "Me"
+ set category = "IC"
+
+ if(GLOB.say_disabled) //This is here to try to identify lag problems
+ to_chat(usr, "Speech is currently admin-disabled.")
+ return
+
+ message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
+
+ usr.emote("me",1,message)
+
+/mob/proc/say_dead(var/message)
+ var/name = real_name
+ var/alt_name = ""
+
+ if(GLOB.say_disabled) //This is here to try to identify lag problems
+ to_chat(usr, "Speech is currently admin-disabled.")
+ return
+
+ if(jobban_isbanned(src, "OOC"))
+ to_chat(src, "You have been banned from deadchat.")
+ return
+
+ if (src.client)
+ if(src.client.prefs.muted & MUTE_DEADCHAT)
+ to_chat(src, "You cannot talk in deadchat (muted).")
+ return
+
+ if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
+ return
+
+ var/mob/dead/observer/O = src
+ if(isobserver(src) && O.deadchat_name)
+ name = "[O.deadchat_name]"
+ else
+ if(mind && mind.name)
+ name = "[mind.name]"
+ else
+ name = real_name
+ if(name != real_name)
+ alt_name = " (died as [real_name])"
+
+ var/K
+
+ if(key)
+ K = src.key
+
+ message = src.say_quote(message, get_spans())
+ var/rendered = "DEAD: [name][alt_name] [message]"
+
+ deadchat_broadcast(rendered, follow_target = src, speaker_key = K)
+
+/mob/proc/check_emote(message)
+ if(copytext(message, 1, 2) == "*")
+ emote(copytext(message, 2))
+ return 1
+
+/mob/proc/emote(var/act)
+ return
+
+/mob/proc/hivecheck()
+ return 0
+
+/mob/proc/lingcheck()
+ return LINGHIVE_NONE
+>>>>>>> 887cc89... No flipping while unconscious (#33736)
diff --git a/tgstation.dme b/tgstation.dme
index 4b6ba31e8b..45c9a8c75e 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1698,6 +1698,7 @@
#include "code\modules\mining\lavaland\necropolis_chests.dm"
#include "code\modules\mining\lavaland\ruins\gym.dm"
#include "code\modules\mob\death.dm"
+#include "code\modules\mob\emote.dm"
#include "code\modules\mob\inventory.dm"
#include "code\modules\mob\login.dm"
#include "code\modules\mob\logout.dm"