diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm
index 67d6b6e7d6f..f81e48b0446 100644
--- a/code/__defines/species_languages.dm
+++ b/code/__defines/species_languages.dm
@@ -111,6 +111,7 @@
#define NO_TALK_MSG 128 // Do not show the "\The [speaker] talks into \the [radio]" message
#define NO_STUTTER 256 // No stuttering, slurring, or other speech problems
#define ALT_TRANSMIT 512 // Language is not based on vision or sound (Todo: add this into the say code and use it for the rootspeak languages)
+#define INAUDIBLE 1024 // Language is not audible (similar to nonverbal) but is still using hearing-based recognition
#define SKIN_NORMAL 0
#define SKIN_THREAT 1
diff --git a/code/__defines/species_languages_vr.dm b/code/__defines/species_languages_vr.dm
index 32457d95679..1a5fbdab4c5 100644
--- a/code/__defines/species_languages_vr.dm
+++ b/code/__defines/species_languages_vr.dm
@@ -12,6 +12,7 @@
#define LANGUAGE_SPACER "Spacer"
#define LANGUAGE_CLOWNISH "Coulrian"
#define LANGUAGE_TAVAN "Tavan"
+#define LANGUAGE_ECHOSONG "Echo Song"
#define LANGUAGE_CHIMPANZEE "Chimpanzee"
#define LANGUAGE_NEAERA "Neaera"
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index 2c0de82adce..54a86d9b283 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -4,7 +4,7 @@
var/msg = "" // This is to make sure that the pieces have actually added something
var/raw_msg = ""
. = list("formatted" = "[verb], \"", "raw" = "")
-
+
for(var/datum/multilingual_say_piece/SP in message_pieces)
iteration_count++
var/piece = SP.message
@@ -15,11 +15,11 @@
if(radio)
.["formatted"] = SP.speaking.format_message_radio(piece)
.["raw"] = piece
- return
+ return
else
.["formatted"] = SP.speaking.format_message(piece)
.["raw"] = piece
- return
+ return
if(iteration_count == 1)
piece = capitalize(piece)
@@ -27,14 +27,17 @@
if(always_stars)
piece = stars(piece)
else if(!say_understands(speaker, SP.speaking))
- piece = saypiece_scramble(SP)
- if(isliving(speaker))
- var/mob/living/S = speaker
- if(istype(S.say_list) && length(S.say_list.speak))
- piece = pick(S.say_list.speak)
+ if(SP.speaking.flags & INAUDIBLE)
+ piece = ""
+ else
+ piece = saypiece_scramble(SP)
+ if(isliving(speaker))
+ var/mob/living/S = speaker
+ if(istype(S.say_list) && length(S.say_list.speak))
+ piece = pick(S.say_list.speak)
raw_msg += (piece + " ")
-
+
//HTML formatting
if(!SP.speaking) // Catch the most generic case first
piece = "[piece]"
@@ -44,7 +47,7 @@
piece = SP.speaking.format_message(piece)
msg += (piece + " ")
-
+
if(msg == "")
// There is literally no content left in this message, we need to shut this shit down
.["formatted"] = "" // hear_say will suppress it
@@ -90,7 +93,7 @@
var/message = combined["formatted"]
if(message == "")
return
-
+
if(sleeping || stat == UNCONSCIOUS)
hear_sleep(multilingual_to_message(message_pieces))
return FALSE
@@ -169,7 +172,7 @@
var/regex/R = new("\\[delimiter](.+?)\\[delimiter]","g")
var/tag = GLOB.speech_toppings[delimiter]
tagged_message = R.Replace(tagged_message,"<[tag]>$1[tag]>")
-
+
return tagged_message
/mob/proc/hear_radio(var/list/message_pieces, var/verb = "says", var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname = "")
@@ -226,13 +229,13 @@
final_message = "[time][final_message]"
to_chat(src, final_message)
-/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null)
+/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/verb_understood = "gestures", var/datum/language/language, var/mob/speaker = null, var/speech_type = 1)
if(!client)
return
if(say_understands(speaker, language))
- message = "[speaker] [verb], \"[message]\""
- else
+ message = "[speaker] [verb_understood], \"[message]\""
+ else if(!(language.ignore_adverb))
var/adverb
var/length = length(message) * pick(0.8, 0.9, 1.0, 1.1, 1.2) //Adds a little bit of fuzziness
switch(length)
@@ -242,8 +245,10 @@
if(48 to 90) adverb = " a lengthy message"
else adverb = " a very lengthy message"
message = "[speaker] [verb][adverb]."
+ else
+ message = "[speaker] [verb]."
- show_message(message, type = 1) // Type 1 is visual message
+ show_message(message, type = speech_type) // Type 1 is visual message
/mob/proc/hear_sleep(var/message)
var/heard = ""
@@ -293,4 +298,4 @@
name = speaker.voice_name
var/rendered = "[name] [message]"
- to_chat(src, rendered)
+ to_chat(src, rendered)
diff --git a/code/modules/mob/language/generic.dm b/code/modules/mob/language/generic.dm
index 03daea6197f..55ba735af4f 100644
--- a/code/modules/mob/language/generic.dm
+++ b/code/modules/mob/language/generic.dm
@@ -145,8 +145,9 @@
// which is exactly what sign language does.
/datum/language/sign/broadcast(var/mob/living/speaker, var/message, var/speaker_mask)
log_say("(SIGN) [message]", speaker)
- speaker.say_signlang(message, pick(signlang_verb), src)
-
+ var/verb_to_use = pick(signlang_verb)
+ speaker.say_signlang(message, verb_to_use, verb_to_use, src)
+
// Silly language for those times when you try to talk a languague you normally can't
/datum/language/gibberish
diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm
index cf372545e7b..4cb01b981fd 100644
--- a/code/modules/mob/language/language.dm
+++ b/code/modules/mob/language/language.dm
@@ -12,6 +12,7 @@
var/exclaim_verb = "exclaims" // Used when sentence ends in a !
var/whisper_verb // Optional. When not specified speech_verb + quietly/softly is used instead.
var/signlang_verb = list("signs", "gestures") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags
+ var/signlang_verb_understood = list("signs") // snowflake bs, used only for echo
var/colour = "body" // CSS style to use for strings in this language.
var/key = "x" // Character used to speak in language eg. :o for Unathi.
var/flags = 0 // Various language flags.
@@ -20,6 +21,7 @@
var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string
var/machine_understands = 1 // Whether machines can parse and understand this language
var/list/partial_understanding // List of languages that can /somehwat/ understand it, format is: name = chance of understanding a word
+ var/ignore_adverb = FALSE // For inaudible languages that we dont want adverb for
/datum/language/proc/get_random_name(var/gender, name_count=2, syllable_count=4, syllable_divisor=2)
if(!syllables || !syllables.len)
@@ -235,7 +237,7 @@
//TBD
/mob/proc/check_lang_data()
. = ""
-
+
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
. += "[L.name] ([get_language_prefix()][L.key])
[L.desc]
"
diff --git a/code/modules/mob/language/station.dm b/code/modules/mob/language/station.dm
index 85bd64c0fcc..b0de63108c3 100644
--- a/code/modules/mob/language/station.dm
+++ b/code/modules/mob/language/station.dm
@@ -96,7 +96,8 @@
/datum/language/tajsign/broadcast(var/mob/living/speaker, var/message, var/speaker_mask)
log_say("(SIGN) [message]", speaker)
- speaker.say_signlang(message, pick(signlang_verb), src)
+ var/verb_to_use = pick(signlang_verb)
+ speaker.say_signlang(message, verb_to_use, verb_to_use, src)
/datum/language/tajsign/can_speak_special(var/mob/speaker) // TODO: If ever we make external organs assist languages, convert this over to the new format
var/list/allowed_species = list(SPECIES_TAJ, SPECIES_TESHARI) // Need a tail and ears and such to use this.
diff --git a/code/modules/mob/language/station_vr.dm b/code/modules/mob/language/station_vr.dm
index 17b756f1f85..99917319502 100644
--- a/code/modules/mob/language/station_vr.dm
+++ b/code/modules/mob/language/station_vr.dm
@@ -158,7 +158,24 @@
exclaim_verb = "squeaks loudly"
syllables = list ("sque", "uik", "squeak", "squee", "eak", "eek", "uek", "squik",
"squeek", "sq", "squee", "ee", "ek", "ak", "ueak", "squea")
- colour = "tavan"
+ colour = "tavan"
+
+/datum/language/echosong
+ name = LANGUAGE_ECHOSONG
+ desc = "An ultrasound-based language, inaudible to those unable to understand it."
+ key = "U"
+ signlang_verb = list("opens their mouth soundlessly", "mouthes something silently")
+ signlang_verb_understood = list("squeaks")
+ colour = "echosong"
+ flags = INAUDIBLE
+ ignore_adverb = TRUE
+
+/datum/language/echosong/scramble(var/input, var/list/known_languages)
+ return stars(input)
+
+/datum/language/echosong/broadcast(var/mob/living/speaker, var/message, var/speaker_mask)
+ log_say("(INAUDIBLE) [message]", speaker)
+ speaker.say_signlang(format_message(message), pick(signlang_verb), pick(signlang_verb_understood), src, 2)
/datum/language/unathi
flags = 0
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index d10a1d3f0d7..e22ce60d342 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -295,7 +295,7 @@ var/list/channel_to_radio_key = new
//Handle nonverbal languages here
for(var/datum/multilingual_say_piece/S in message_pieces)
- if(S.speaking.flags & NONVERBAL)
+ if((S.speaking.flags & NONVERBAL) || (S.speaking.flags & INAUDIBLE))
custom_emote(1, "[pick(S.speaking.signlang_verb)].")
//These will contain the main receivers of the message
@@ -402,19 +402,19 @@ var/list/channel_to_radio_key = new
log_say(message, src)
return 1
-/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/datum/language/language)
+/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/verb_understood="gestures", var/datum/language/language, var/type = 1)
var/turf/T = get_turf(src)
//We're in something, gesture to people inside the same thing
if(loc != T)
for(var/mob/M in loc)
- M.hear_signlang(message, verb, language, src)
+ M.hear_signlang(message, verb, verb_understood, language, src, type)
//We're on a turf, gesture to visible as if we were a normal language
else
var/list/potentials = get_mobs_and_objs_in_view_fast(T, world.view)
var/list/mobs = potentials["mobs"]
for(var/mob/M as anything in mobs)
- M.hear_signlang(message, verb, language, src)
+ M.hear_signlang(message, verb, verb_understood, language, src, type)
var/list/objs = potentials["objs"]
for(var/obj/O as anything in objs)
O.hear_signlang(message, verb, language, src)
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index d51d048da18..646c82a168a 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -249,7 +249,7 @@
// There are a few things that will make us want to ignore all other languages in - namely, HIVEMIND languages.
var/datum/language/L = current[1]
- if(L && (L.flags & HIVEMIND || L.flags & SIGNLANG))
+ if(L && (L.flags & HIVEMIND || L.flags & SIGNLANG || L.flags & INAUDIBLE))
return new /datum/multilingual_say_piece(L, trim(sanitize(strip_prefixes(message))))
if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang
diff --git a/code/stylesheet.dm b/code/stylesheet.dm
index 2a12dfa81db..dde7f22ec30 100644
--- a/code/stylesheet.dm
+++ b/code/stylesheet.dm
@@ -105,6 +105,7 @@ h1.alert, h2.alert {color: #000000;}
.sergal {color: #0077FF;}
.birdsongc {color: #CC9900;}
.vulpkanin {color: #B97A57;}
+.echosong {color: #826D8C;}
.enochian {color: #848A33; letter-spacing:-1pt; word-spacing:4pt; font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;}
.daemon {color: #5E339E; letter-spacing:-1pt; word-spacing:0pt; font-family: "Courier New", Courier, monospace;}
.bug {color: #9e9e39;}