diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 8bde73c238..313b5b0743 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -106,9 +106,10 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al
for (var/language_name in all_languages)
var/datum/language/L = all_languages[language_name]
- language_keys[":[lowertext(L.key)]"] = L
- language_keys[".[lowertext(L.key)]"] = L
- language_keys["#[lowertext(L.key)]"] = L
+ if(!(L.flags & NONGLOBAL))
+ language_keys[":[lowertext(L.key)]"] = L
+ language_keys[".[lowertext(L.key)]"] = L
+ language_keys["#[lowertext(L.key)]"] = L
var/rkey = 0
paths = typesof(/datum/species)-/datum/species
diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm
index 14e1b5a30e..4f08fac88d 100644
--- a/code/game/gamemodes/heist/heist.dm
+++ b/code/game/gamemodes/heist/heist.dm
@@ -109,6 +109,7 @@ var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind'
vox.add_language("Vox-pidgin")
vox.add_language("Galactic Common")
vox.add_language("Tradeband")
+ vox.add_language("Noise")
vox.h_style = "Short Vox Quills"
vox.f_style = "Shaved"
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 66708db7f7..dc98e0ce7e 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -81,13 +81,17 @@ var/const/HOLOPAD_MODE = 0
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
-/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb)
+/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb, datum/language/speaking)
if(M&&hologram&&master)//Master is mostly a safety in case lag hits or something.
- if(!master.say_understands(M))//The AI will be able to understand most mobs talking through the holopad.
+ if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad.
text = stars(text)
var/name_used = M.GetVoice()
//This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
- var/rendered = "Holopad received, [name_used] [verb], \"[text]\""
+ var/rendered
+ if(speaking)
+ rendered = "Holopad received, [name_used] [speaking.format_message(text, verb)]"
+ else
+ rendered = "Holopad received, [name_used] [verb], \"[text]\""
master.show_message(rendered, 2)
return
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 291418f510..12078e0add 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -452,11 +452,11 @@
del(src)
//BubbleWrap END
-/obj/item/weapon/storage/hear_talk(mob/M as mob, text)
+/obj/item/weapon/storage/hear_talk(mob/M as mob, text, verb, datum/language/speaking)
for (var/atom/A in src)
if(istype(A,/obj/))
var/obj/O = A
- O.hear_talk(M, text)
+ O.hear_talk(M, text, verb, speaking)
//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area).
//Returns -1 if the atom was not found on container.
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 44fc441926..ba696b6850 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -126,7 +126,7 @@
return
-/obj/proc/hear_talk(mob/M as mob, text)
+/obj/proc/hear_talk(mob/M as mob, text, verb, datum/language/speaking)
if(talking_atom)
talking_atom.catchMessage(text, M)
/*
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 44c2c3b900..8ff522a858 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -293,11 +293,11 @@
else
icon_state = icon_opened
-/obj/structure/closet/hear_talk(mob/M as mob, text)
+/obj/structure/closet/hear_talk(mob/M as mob, text, verb, datum/language/speaking)
for (var/atom/A in src)
if(istype(A,/obj/))
var/obj/O = A
- O.hear_talk(M, text)
+ O.hear_talk(M, text, verb, speaking)
/obj/structure/closet/attack_generic(var/mob/user, var/damage, var/attack_message = "destroys", var/wallbreaker)
if(!damage || !wallbreaker)
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index a9a4f2cc25..82f7450981 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -211,11 +211,11 @@
return 1
-/obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg)
+/obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg, verb, datum/language/speaking)
if(a_right)
- a_right.hear_talk(M,msg)
+ a_right.hear_talk(M,msg,verb,speaking)
if(a_left)
- a_left.hear_talk(M,msg)
+ a_left.hear_talk(M,msg,verb,speaking)
diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm
index 5e37fd240f..f34c9f00c3 100644
--- a/code/modules/clothing/suits/storage.dm
+++ b/code/modules/clothing/suits/storage.dm
@@ -24,8 +24,8 @@
pockets.emp_act(severity)
..()
-/obj/item/clothing/suit/storage/hear_talk(mob/M, var/msg)
- pockets.hear_talk(M, msg)
+/obj/item/clothing/suit/storage/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
+ pockets.hear_talk(M, msg, verb, speaking)
..()
//Jackets with buttons, used for labcoats, IA jackets, First Responder jackets, and brown jackets.
diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm
index 51290cf777..b70df2c84b 100644
--- a/code/modules/clothing/under/ties.dm
+++ b/code/modules/clothing/under/ties.dm
@@ -347,8 +347,8 @@
hold.emp_act(severity)
..()
-/obj/item/clothing/tie/storage/hear_talk(mob/M, var/msg)
- hold.hear_talk(M, msg)
+/obj/item/clothing/tie/storage/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
+ hold.hear_talk(M, msg, verb, speaking)
..()
/obj/item/clothing/tie/storage/attack_self(mob/user as mob)
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index 04fa4273a0..7765cda928 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -24,23 +24,19 @@
if(sleeping || stat == 1)
hear_sleep(message)
return
-
- var/style = "body"
//non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet.
if (language && (language.flags & NONVERBAL))
if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src)))
message = stars(message)
- if(!say_understands(speaker,language))
- if(istype(speaker,/mob/living/simple_animal))
- var/mob/living/simple_animal/S = speaker
- message = pick(S.speak)
- else
- message = stars(message)
-
- if(language)
- style = language.colour
+ if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages
+ if(!say_understands(speaker,language))
+ if(istype(speaker,/mob/living/simple_animal))
+ var/mob/living/simple_animal/S = speaker
+ message = pick(S.speak)
+ else
+ message = stars(message)
var/speaker_name = speaker.name
if(istype(speaker, /mob/living/carbon/human))
@@ -61,12 +57,16 @@
message = "[message]"
if(sdisabilities & DEAF || ear_deaf)
- if(speaker == src)
- src << "You cannot hear yourself speak!"
- else
- src << "[speaker_name][alt_name] talks but you cannot hear \him."
+ if(!language || !(language.flags & INNATE)) // INNATE is the flag for audible-emote-language, so we don't want to show an "x talks but you cannot hear them" message if it's set
+ if(speaker == src)
+ src << "You cannot hear yourself speak!"
+ else
+ src << "[speaker_name][alt_name] talks but you cannot hear \him."
else
- src << "[speaker_name][alt_name] [track][verb], \"[message]\""
+ if(language)
+ src << "[speaker_name][alt_name] [track][language.format_message(message, verb)]"
+ else
+ src << "[speaker_name][alt_name] [track][verb], \"[message]\""
if (speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z))
var/turf/source = speaker? get_turf(speaker) : get_turf(src)
src.playsound_local(source, speech_sound, sound_vol, 1)
@@ -83,26 +83,22 @@
var/track = null
- var/style = "body"
-
//non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet.
if (language && (language.flags & NONVERBAL))
if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src)))
message = stars(message)
- if(!say_understands(speaker,language))
- if(istype(speaker,/mob/living/simple_animal))
- var/mob/living/simple_animal/S = speaker
- message = pick(S.speak)
- else
+ if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages
+ if(!say_understands(speaker,language))
+ if(istype(speaker,/mob/living/simple_animal))
+ var/mob/living/simple_animal/S = speaker
+ message = pick(S.speak)
+ else
+ message = stars(message)
+
+ if(hard_to_hear)
message = stars(message)
- if(language)
- style = language.colour
-
- if(hard_to_hear)
- message = stars(message)
-
var/speaker_name = speaker.name
if(vname)
@@ -162,13 +158,18 @@
speaker_name = "[speaker.real_name] ([speaker_name])"
track = "[speaker_name] (follow)"
+ var/formatted
+ if(language)
+ formatted = language.format_message_radio(message, verb)
+ else
+ formatted = "[verb], \"[message]\""
if(sdisabilities & DEAF || ear_deaf)
if(prob(20))
src << "You feel your headset vibrate but can hear nothing from it!"
else if(track)
- src << "[part_a][track][part_b][verb], \"[message]\""
+ src << "[part_a][track][part_b][formatted]"
else
- src << "[part_a][speaker_name][part_b][verb], \"[message]\""
+ src << "[part_a][speaker_name][part_b][formatted]"
/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null)
if(!client)
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index 2e627330a0..e619734700 100755
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -15,23 +15,21 @@
var/flags = 0 // Various language flags.
var/native // If set, non-native speakers will have trouble speaking.
-/datum/language/proc/broadcast(var/mob/living/speaker,var/message,var/speaker_mask)
+/datum/language/proc/format_message(message, verb)
+ return "[verb], \"[capitalize(message)]\""
+/datum/language/proc/format_message_radio(message, verb)
+ return "[verb], \"[capitalize(message)]\""
+
+/datum/language/proc/broadcast(var/mob/living/speaker,var/message,var/speaker_mask)
log_say("[key_name(speaker)] : ([name]) [message]")
+ if(!speaker_mask) speaker_mask = speaker.name
+ var/msg = "[name], [speaker_mask] [format_message(message, get_spoken_verb(message))]"
+
for(var/mob/player in player_list)
-
- var/understood = 0
-
- if(istype(player,/mob/dead))
- understood = 1
- else if((src in player.languages) && check_special_condition(player))
- understood = 1
-
- if(understood)
- if(!speaker_mask) speaker_mask = speaker.name
- var/msg = "[name], [speaker_mask] [speech_verb], \"[message]\""
- player << "[msg]"
+ if(istype(player,/mob/dead) || ((src in player.languages) && check_special_condition(player)))
+ player << msg
/datum/language/proc/check_special_condition(var/mob/other)
return 1
@@ -44,6 +42,19 @@
return ask_verb
return speech_verb
+// Noise "language", for audible emotes.
+/datum/language/noise
+ name = "Noise"
+ desc = "Noises"
+ key = ""
+ flags = RESTRICTED|NONGLOBAL|INNATE
+
+/datum/language/noise/format_message(message, verb)
+ return "[message]"
+
+/datum/language/noise/format_message_radio(message, verb)
+ return "[message]"
+
/datum/language/unathi
name = "Sinta'unathi"
desc = "The common language of Moghes, composed of sibilant hisses and rattles. Spoken natively by Unathi."
@@ -305,7 +316,8 @@
var/dat = "Known Languages
"
for(var/datum/language/L in languages)
- dat += "[L.name] (:[L.key])
[L.desc]
"
+ if(!(L.flags & NONGLOBAL))
+ dat += "[L.name] (:[L.key])
[L.desc]
"
src << browse(dat, "window=checklanguage")
return
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/alien/say.dm b/code/modules/mob/living/carbon/alien/say.dm
index 8b13a07fdc..038c77fcde 100644
--- a/code/modules/mob/living/carbon/alien/say.dm
+++ b/code/modules/mob/living/carbon/alien/say.dm
@@ -15,21 +15,12 @@
if(copytext(message,1,2) == "*")
return emote(copytext(message,2))
- var/datum/language/speaking = null
-
- if(length(message) >= 2)
- var/channel_prefix = copytext(message, 1 ,3)
- if(languages.len)
- for(var/datum/language/L in languages)
- if(lowertext(channel_prefix) == ":[L.key]")
- verb = L.speech_verb
- speaking = L
- break
+ var/datum/language/speaking = parse_language(message)
if(speaking)
- message = trim(copytext(message,3))
+ message = copytext(message, 2+length(speaking.key))
- message = capitalize(trim_left(message))
+ message = trim(message)
if(!message || stat)
return
diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm
index 6c2394eb61..ecfb825684 100644
--- a/code/modules/mob/living/carbon/brain/say.dm
+++ b/code/modules/mob/living/carbon/brain/say.dm
@@ -2,10 +2,23 @@
/mob/living/carbon/brain/say(var/message)
if (silent)
return
-
- if(!(container && istype(container, /obj/item/device/mmi)))
+
+ if(!(container && istype(container, /obj/item/device/mmi)))
return //No MMI, can't speak, bucko./N
else
+ var/datum/language/speaking = parse_language(message)
+ if(speaking)
+ message = copytext(message, 2+length(speaking.key))
+ var/verb = "says"
+ var/ending = copytext(message, length(message))
+ if (speaking)
+ verb = speaking.get_spoken_verb(ending)
+ else
+ if(ending=="!")
+ verb=pick("exclaims","shouts","yells")
+ if(ending=="?")
+ verb="asks"
+
if(prob(emp_damage*4))
if(prob(10))//10% chane to drop the message entirely
return
@@ -14,5 +27,5 @@
if(istype(container, /obj/item/device/mmi/radio_enabled))
var/obj/item/device/mmi/radio_enabled/R = container
if(R.radio)
- spawn(0) R.radio.hear_talk(src, sanitize(message))
+ spawn(0) R.radio.hear_talk(src, trim(sanitize(message)), verb, speaking)
..()
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index 6b13e0b3a1..d787c3f733 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -33,7 +33,7 @@
//parse the language code and consume it
var/datum/language/speaking = parse_language(message)
if(speaking)
- message = copytext(message,3)
+ message = copytext(message,2+length(speaking.key))
else if(species.default_language)
speaking = all_languages[species.default_language]
@@ -55,7 +55,7 @@
if (istype(wear_mask, /obj/item/clothing/mask/muzzle))
return
- message = capitalize(trim(message))
+ message = trim(message)
if(speech_problem_flag)
var/list/handle_r = handle_speech_problems(message)
diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm
index dc81d3d1b5..0c6b9e147f 100644
--- a/code/modules/mob/living/carbon/human/whisper.dm
+++ b/code/modules/mob/living/carbon/human/whisper.dm
@@ -29,7 +29,7 @@
//parse the language code and consume it
var/datum/language/speaking = parse_language(message)
if (speaking)
- message = copytext(message,3)
+ message = copytext(message,2+length(speaking.key))
whisper_say(message, speaking, alt_name)
@@ -49,7 +49,7 @@
else
var/adverb = pick("quietly", "softly")
verb = "[speaking.speech_verb] [adverb]"
- not_heard = "[verb] something [adverb]"
+ not_heard = "[speaking.speech_verb] something [adverb]"
else
not_heard = "[verb] something" //TODO get rid of the null language and just prevent speech if language is null
@@ -126,7 +126,7 @@
for (var/obj/O in view(message_range, src))
spawn (0)
if (O)
- O.hear_talk(src, message) //O.hear_talk(src, message, verb, speaking)
+ O.hear_talk(src, message, verb, speaking)
var/list/eavesdropping = hearers(eavesdropping_range, src)
eavesdropping -= src
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index f883ce46ff..a712d26139 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1,3 +1,7 @@
+/mob/living/New()
+ ..()
+ add_language("Noise")
+
//mob verbs are faster than object verbs. See mob/verb/examine.
/mob/living/verb/pulled(atom/movable/AM as mob|obj in oview(1))
set name = "Pull"
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index 22db25d678..caf62e484f 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -72,7 +72,7 @@
var/datum/language/speaking = parse_language(message)
if (speaking)
verb = speaking.speech_verb
- message = copytext(message,3)
+ message = trim(copytext(message,2+length(speaking.key)))
if(speaking.flags & HIVEMIND)
speaking.broadcast(src,trim(message))
@@ -102,7 +102,7 @@
if("department")
switch(bot_type)
if(IS_AI)
- return AI.holopad_talk(message)
+ return AI.holopad_talk(message, verb, speaking)
if(IS_ROBOT)
log_say("[key_name(src)] : [message]")
return R.radio.talk_into(src,message,message_mode,verb,speaking)
@@ -149,7 +149,7 @@
return ..(message,speaking,verb)
//For holopads only. Usable by AI.
-/mob/living/silicon/ai/proc/holopad_talk(var/message)
+/mob/living/silicon/ai/proc/holopad_talk(var/message, verb, datum/language/speaking)
log_say("[key_name(src)] : [message]")
@@ -160,16 +160,22 @@
var/obj/machinery/hologram/holopad/T = src.holo
if(T && T.hologram && T.master == src)//If there is a hologram and its master is the user.
- var/verb = say_quote(message)
//Human-like, sorta, heard by those who understand humans.
- var/rendered_a = "[name] [verb], \"[message]\""
-
+ var/rendered_a
//Speach distorted, heard by those who do not understand AIs.
var/message_stars = stars(message)
- var/rendered_b = "[voice_name] [verb], \"[message_stars]\""
+ var/rendered_b
+
+ if(speaking)
+ rendered_a = "[name] [speaking.format_message(message, verb)]"
+ rendered_b = "[voice_name] [speaking.format_message(message_stars, verb)]"
+ src << "Holopad transmitted, [real_name] [speaking.format_message(message, verb)]"//The AI can "hear" its own message.
+ else
+ rendered_a = "[name] [verb], \"[message]\""
+ rendered_b = "[voice_name] [verb], \"[message_stars]\""
+ src << "Holopad transmitted, [real_name] [verb], \"[message]\""//The AI can "hear" its own message.
- src << "Holopad transmitted, [real_name] [verb], [message]"//The AI can "hear" its own message.
for(var/mob/M in hearers(T.loc))//The location is the object, default distance.
if(M.say_understands(src))//If they understand AI speak. Humans and the like will be able to.
M.show_message(rendered_a, 2)
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 93d4438d7b..892f129f11 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -197,7 +197,8 @@
var/dat = "Known Languages
"
for(var/datum/language/L in languages)
- dat += "[L.name] (:[L.key])
Speech Synthesizer: [(L in speech_synthesizer_langs)? "YES":"NOT SUPPORTED"]
[L.desc]
"
+ if(!(L.flags & NONGLOBAL))
+ dat += "[L.name] (:[L.key])
Speech Synthesizer: [(L in speech_synthesizer_langs)? "YES":"NOT SUPPORTED"]
[L.desc]
"
src << browse(dat, "window=checklanguage")
return
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 10e6c4266c..da75999c41 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -142,6 +142,9 @@
//parses the language code (e.g. :j) from text, such as that supplied to say.
//returns the language object only if the code corresponds to a language that src can speak, otherwise null.
/mob/proc/parse_language(var/message)
+ if(length(message) >= 1 && copytext(message,1,2) == "!")
+ return all_languages["Noise"]
+
if(length(message) >= 2)
var/language_prefix = lowertext(copytext(message, 1 ,3))
var/datum/language/L = language_keys[language_prefix]
diff --git a/code/setup.dm b/code/setup.dm
index 7de8cfc987..9e204bf120 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -764,6 +764,8 @@ var/list/RESTRICTED_CAMERA_NETWORKS = list( //Those networks can only be accesse
#define NONVERBAL 4 // Language has a significant non-verbal component. Speech is garbled without line-of-sight
#define SIGNLANG 8 // Language is completely non-verbal. Speech is displayed through emotes for those who can understand.
#define HIVEMIND 16 // Broadcast to all mobs with this language.
+#define NONGLOBAL 32 // Do not add to general languages list
+#define INNATE 64 // All mobs can be assumed to speak and understand this language (audible emotes)
//Flags for zone sleeping
#define ZONE_ACTIVE 1