diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 1e5e994636..77425e14dd 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -189,3 +189,5 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
/obj/item/stack/sheet/plasmarglass)))
#define is_glass_sheet(O) (is_type_in_typecache(O, GLOB.glass_sheet_types))
+
+#define isblobmonster(O) (istype(O, /mob/living/simple_animal/hostile/blob))
\ No newline at end of file
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index d30eb36132..74a1669686 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -49,4 +49,10 @@
#define LOGCHAT "chat"
#define LOGASAY "adminsay"
#define LOGCOMMENT "comment"
-#define LOGOOC "ooc"
\ No newline at end of file
+#define LOGOOC "ooc"
+
+
+#define LINGHIVE_NONE 0
+#define LINGHIVE_OUTSIDER 1
+#define LINGHIVE_LING 2
+#define LINGHIVE_LINK 3
\ No newline at end of file
diff --git a/code/controllers/subsystem/radio.dm b/code/controllers/subsystem/radio.dm
index de605cb554..43803aa647 100644
--- a/code/controllers/subsystem/radio.dm
+++ b/code/controllers/subsystem/radio.dm
@@ -3,6 +3,13 @@ SUBSYSTEM_DEF(radio)
flags = SS_NO_FIRE|SS_NO_INIT
var/list/datum/radio_frequency/frequencies = list()
+ var/list/saymodes = list()
+
+/datum/controller/subsystem/radio/PreInit(timeofday)
+ for(var/_SM in subtypesof(/datum/saymode))
+ var/datum/saymode/SM = new _SM()
+ saymodes[SM.key] = SM
+ return ..()
/datum/controller/subsystem/radio/proc/add_object(obj/device, new_frequency as num, filter = null as text|null)
var/f_text = num2text(new_frequency)
diff --git a/code/datums/saymode.dm b/code/datums/saymode.dm
new file mode 100644
index 0000000000..6aa1c48d8a
--- /dev/null
+++ b/code/datums/saymode.dm
@@ -0,0 +1,112 @@
+/datum/saymode
+ var/key
+ var/mode
+
+//Return FALSE if you have handled the message. Otherwise, return TRUE and saycode will continue doing saycode things.
+//user = whoever said the message
+//message = the message
+//language = the language.
+/datum/saymode/proc/handle_message(mob/living/user, message, datum/language/language)
+ return TRUE
+
+
+/datum/saymode/changeling
+ key = "g"
+ mode = MODE_CHANGELING
+
+/datum/saymode/changeling/handle_message(mob/living/user, message, datum/language/language)
+ switch(user.lingcheck())
+ if(LINGHIVE_LINK)
+ var/msg = "[user.mind]: [message]"
+ for(var/_M in GLOB.mob_list)
+ var/mob/M = _M
+ if(M in GLOB.dead_mob_list)
+ var/link = FOLLOW_LINK(M, user)
+ to_chat(M, "[link] [msg]")
+ else
+ switch(M.lingcheck())
+ if(LINGHIVE_LINK, LINGHIVE_LING)
+ to_chat(M, msg)
+ if(LINGHIVE_OUTSIDER)
+ if(prob(40))
+ to_chat(M, "We can faintly sense an outsider trying to communicate through the hivemind...")
+ if(LINGHIVE_LING)
+ var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
+ var/msg = "[changeling.changelingID]: [message]"
+ log_talk(src,"[changeling.changelingID]/[user.key] : [message]",LOGSAY)
+ for(var/_M in GLOB.mob_list)
+ var/mob/M = _M
+ if(M in GLOB.dead_mob_list)
+ var/link = FOLLOW_LINK(M, user)
+ to_chat(M, "[link] [msg]")
+ else
+ switch(M.lingcheck())
+ if(LINGHIVE_LINK)
+ to_chat(M, msg)
+ if(LINGHIVE_LING)
+ to_chat(M, msg)
+ if(LINGHIVE_OUTSIDER)
+ if(prob(40))
+ to_chat(M, "We can faintly sense another of our kind trying to communicate through the hivemind...")
+ if(LINGHIVE_OUTSIDER)
+ to_chat(user, "Our senses have not evolved enough to be able to communicate this way...")
+ return FALSE
+
+
+/datum/saymode/xeno
+ key = "a"
+ mode = MODE_ALIEN
+
+/datum/saymode/xeno/handle_message(mob/living/user, message, datum/language/language)
+ if(user.hivecheck())
+ user.alien_talk(message)
+ return FALSE
+
+
+/datum/saymode/vocalcords
+ key = "x"
+ mode = MODE_VOCALCORDS
+
+/datum/saymode/vocalcords/handle_message(mob/living/user, message, datum/language/language)
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ var/obj/item/organ/vocal_cords/V = C.getorganslot(ORGAN_SLOT_VOICE)
+ if(V && V.can_speak_with())
+ V.handle_speech(message) //message
+ V.speak_with(message) //action
+ return FALSE
+
+
+/datum/saymode/binary //everything that uses .b (silicons, drones, blobbernauts/spores, swarmers)
+ key = "b"
+ mode = MODE_BINARY
+
+/datum/saymode/binary/handle_message(mob/living/user, message, datum/language/language)
+ if(isswarmer(user))
+ var/mob/living/simple_animal/hostile/swarmer/S = user
+ S.swarmer_chat(message)
+ return FALSE
+ if(isblobmonster(user))
+ var/mob/living/simple_animal/hostile/blob/B = user
+ B.blob_chat(message)
+ return FALSE
+ if(isdrone(user))
+ var/mob/living/simple_animal/drone/D = user
+ D.drone_chat(message)
+ return FALSE
+ if(user.binarycheck())
+ user.robot_talk(message)
+ return FALSE
+ return FALSE
+
+
+/datum/saymode/holopad
+ key = "h"
+ mode = MODE_HOLOPAD
+
+/datum/saymode/holopad/handle_message(mob/living/user, message, datum/language/language)
+ if(isAI(user))
+ var/mob/living/silicon/ai/AI = user
+ AI.holopad_talk(message, language)
+ return FALSE
+ return FALSE
\ No newline at end of file
diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm
index 5572bbd9d8..c5c813ecfc 100644
--- a/code/game/gamemodes/blob/blobs/blob_mobs.dm
+++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm
@@ -56,13 +56,6 @@
return 1
return ..()
-/mob/living/simple_animal/hostile/blob/handle_inherent_channels(message, message_mode)
- if(message_mode == MODE_BINARY)
- blob_chat(message)
- return 1
- else
- ..()
-
/mob/living/simple_animal/hostile/blob/proc/blob_chat(msg)
var/spanned_message = say_quote(msg, get_spans())
var/rendered = "\[Blob Telepathy\] [real_name] [spanned_message]"
diff --git a/code/game/gamemodes/changeling/powers/linglink.dm b/code/game/gamemodes/changeling/powers/linglink.dm
index d6ceb4edd0..cd7c944137 100644
--- a/code/game/gamemodes/changeling/powers/linglink.dm
+++ b/code/game/gamemodes/changeling/powers/linglink.dm
@@ -50,7 +50,7 @@
to_chat(target, "A migraine throbs behind your eyes, you hear yourself screaming - but your mouth has not opened!")
for(var/mi in GLOB.mob_list)
var/mob/M = mi
- if(M.lingcheck() == 2)
+ if(M.lingcheck() == LINGHIVE_LING)
to_chat(M, "We can sense a foreign presence in the hivemind...")
target.mind.linglink = 1
target.say(":g AAAAARRRRGGGGGHHHHH!!")
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index 7005b26b12..e59fc2c9b7 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
@@ -121,13 +121,6 @@
if(statpanel("Status"))
stat("Resources:",resources)
-/mob/living/simple_animal/hostile/swarmer/handle_inherent_channels(message, message_mode)
- if(message_mode == MODE_BINARY)
- swarmer_chat(message)
- return ITALICS | REDUCE_RANGE
- else
- . = ..()
-
/mob/living/simple_animal/hostile/swarmer/get_spans()
return ..() | SPAN_ROBOT
diff --git a/code/modules/mob/living/brain/say.dm b/code/modules/mob/living/brain/say.dm
index 0cfbf5d170..0f8e9e60bd 100644
--- a/code/modules/mob/living/brain/say.dm
+++ b/code/modules/mob/living/brain/say.dm
@@ -21,7 +21,7 @@
return ITALICS | REDUCE_RANGE
/mob/living/brain/lingcheck()
- return 0
+ return LINGHIVE_NONE
/mob/living/brain/treat_message(message)
message = capitalize(message)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 5a58c67c6b..8849d52f22 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1022,4 +1022,15 @@
/mob/living/proc/add_abilities_to_panel()
for(var/obj/effect/proc_holder/A in abilities)
- statpanel("[A.panel]",A.get_panel_text(),A)
\ No newline at end of file
+ statpanel("[A.panel]",A.get_panel_text(),A)
+
+/mob/living/lingcheck()
+ if(mind)
+ var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
+ if(changeling)
+ if(changeling.changeling_speak)
+ return LINGHIVE_LING
+ return LINGHIVE_OUTSIDER
+ if(mind && mind.linglink)
+ return LINGHIVE_LINK
+ return LINGHIVE_NONE
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index a2fdb9d2b0..739b4e3584 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -72,6 +72,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE)
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
+ var/key = get_key(message)
var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE)
@@ -134,8 +135,11 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
// AIs use inherent channels for the holopad. Most inherent channels
// ignore the language argument however.
- if(handle_inherent_channels(message, message_mode, language)) //Hiveminds, binary chat & holopad.
- return
+ var/datum/saymode/SM = SSradio.saymodes[key]
+ if(key && SM)
+ if(!SM.handle_message(src, message, language))
+ return
+
if(!can_speak_vocal(message))
to_chat(src, "You find yourself unable to speak!")
@@ -264,7 +268,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30)
/mob/proc/binarycheck()
- return 0
+ return FALSE
/mob/living/can_speak(message) //For use outside of Say()
if(can_speak_basic(message) && can_speak_vocal(message))
@@ -307,6 +311,11 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
var/key_symbol = lowertext(copytext(message, 2, 3))
return GLOB.department_radio_keys[key_symbol]
+/mob/living/proc/get_key(message)
+ var/key = copytext(message, 1, 2)
+ if(key in GLOB.department_radio_prefixes)
+ return lowertext(copytext(message, 2, 3))
+
/mob/living/proc/get_message_language(message)
if(copytext(message, 1, 2) == ",")
var/key = copytext(message, 2, 3)
@@ -316,60 +325,6 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return LD
return null
-/mob/living/proc/handle_inherent_channels(message, message_mode)
- if(message_mode == MODE_CHANGELING)
- switch(lingcheck())
- if(3)
- var/msg = "[src.mind]: [message]"
- for(var/_M in GLOB.mob_list)
- var/mob/M = _M
- if(M in GLOB.dead_mob_list)
- var/link = FOLLOW_LINK(M, src)
- to_chat(M, "[link] [msg]")
- else
- switch(M.lingcheck())
- if(3)
- to_chat(M, msg)
- if(2)
- to_chat(M, msg)
- if(1)
- if(prob(40))
- to_chat(M, "We can faintly sense an outsider trying to communicate through the hivemind...")
- if(2)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- var/msg = "[changeling.changelingID]: [message]"
- log_talk(src,"[changeling.changelingID]/[key] : [message]",LOGSAY)
- for(var/_M in GLOB.mob_list)
- var/mob/M = _M
- if(M in GLOB.dead_mob_list)
- var/link = FOLLOW_LINK(M, src)
- to_chat(M, "[link] [msg]")
- else
- switch(M.lingcheck())
- if(3)
- to_chat(M, msg)
- if(2)
- to_chat(M, msg)
- if(1)
- if(prob(40))
- to_chat(M, "We can faintly sense another of our kind trying to communicate through the hivemind...")
- if(1)
- to_chat(src, "Our senses have not evolved enough to be able to communicate this way...")
- return TRUE
- if(message_mode == MODE_ALIEN)
- if(hivecheck())
- alien_talk(message)
- return TRUE
- if(message_mode == MODE_VOCALCORDS)
- if(iscarbon(src))
- var/mob/living/carbon/C = src
- var/obj/item/organ/vocal_cords/V = C.getorganslot(ORGAN_SLOT_VOICE)
- if(V && V.can_speak_with())
- V.handle_speech(message) //message
- V.speak_with(message) //action
- return TRUE
- return FALSE
-
/mob/living/proc/treat_message(message)
if(getBrainLoss() >= 60)
message = derpspeech(message, stuttering)
@@ -408,22 +363,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return ITALICS | REDUCE_RANGE
if(MODE_BINARY)
- if(binarycheck())
- robot_talk(message)
return ITALICS | REDUCE_RANGE //Does not return 0 since this is only reached by humans, not borgs or AIs.
return 0
-/mob/living/lingcheck() //1 is ling w/ no hivemind. 2 is ling w/hivemind. 3 is ling victim being linked into hivemind.
- if(mind)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- if(changeling.changeling_speak)
- return 2
- return 1
- if(mind && mind.linglink)
- return 3
- return 0
-
/mob/living/say_mod(input, message_mode)
if(message_mode == MODE_WHISPER)
. = verb_whisper
diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm
index 13f0609c0d..b55fa2a663 100644
--- a/code/modules/mob/living/silicon/ai/say.dm
+++ b/code/modules/mob/living/silicon/ai/say.dm
@@ -31,15 +31,6 @@
else
return ..()
-/mob/living/silicon/ai/handle_inherent_channels(message, message_mode, language)
- . = ..()
- if(.)
- return .
-
- if(message_mode == MODE_HOLOPAD)
- holopad_talk(message, language)
- return 1
-
//For holopads only. Usable by AI.
/mob/living/silicon/ai/proc/holopad_talk(message, language)
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index f3e7baa0d9..f10ac43858 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -57,14 +57,3 @@
return MODE_ROBOT
else
return .
-
-/mob/living/silicon/handle_inherent_channels(message, message_mode)
- . = ..()
- if(.)
- return .
-
- if(message_mode == MODE_BINARY)
- if(binarycheck())
- robot_talk(message)
- return 1
- return 0
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/say.dm b/code/modules/mob/living/simple_animal/friendly/drone/say.dm
index fb0c0c0614..16bf370f02 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/say.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/say.dm
@@ -3,14 +3,6 @@
/////////////
//Drone speach
-/mob/living/simple_animal/drone/handle_inherent_channels(message, message_mode)
- if(message_mode == MODE_BINARY)
- drone_chat(message)
- return 1
- else
- ..()
-
-
/mob/living/simple_animal/drone/get_spans()
return ..() | SPAN_ROBOT
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index a396072b4f..7ffa7f8ed0 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -1,83 +1,82 @@
-//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
- usr.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 message)
- 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
-
- var/list/replace_chars = list("\n"=" ","\t"=" ")
- message = copytext(sanitize(message, replace_chars), 1, (MAX_MESSAGE_LEN*2))
-
- 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/emote(var/act)
- return
-
-/mob/proc/hivecheck()
- return 0
-
-/mob/proc/lingcheck()
- return 0
+//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
+ usr.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/emote(var/act)
+ return
+
+/mob/proc/hivecheck()
+ return 0
+
+/mob/proc/lingcheck()
+ return LINGHIVE_NONE
diff --git a/tgstation.dme b/tgstation.dme
index 34227a9a18..132b4689dc 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -308,6 +308,7 @@
#include "code\datums\recipe.dm"
#include "code\datums\riding.dm"
#include "code\datums\ruins.dm"
+#include "code\datums\saymode.dm"
#include "code\datums\shuttles.dm"
#include "code\datums\soullink.dm"
#include "code\datums\spawners_menu.dm"