diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index e2c1b007..3bbd38bf 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -316,3 +316,34 @@ proc/TextPreview(var/string,var/len=40)
return string
else
return "[copytext(string, 1, 37)]..."
+
+
+//This proc strips html properly, but it's not lazy like the other procs.
+//This means that it doesn't just remove < and > and call it a day.
+//Also limit the size of the input, if specified.
+/proc/strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN)
+ if(!input)
+ return
+ var/opentag = 1 //These store the position of < and > respectively.
+ var/closetag = 1
+ while(1)
+ opentag = findtext(input, "<")
+ closetag = findtext(input, ">")
+ if(closetag && opentag)
+ if(closetag < opentag)
+ input = copytext(input, (closetag + 1))
+ else
+ input = copytext(input, 1, opentag) + copytext(input, (closetag + 1))
+ else if(closetag || opentag)
+ if(opentag)
+ input = copytext(input, 1, opentag)
+ else
+ input = copytext(input, (closetag + 1))
+ else
+ break
+ if(max_length)
+ input = copytext(input,1,max_length)
+ return sanitize(input)
+
+/proc/trim_strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN)
+ return trim(strip_html_properly(input, max_length))
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index 47778073..d74fab95 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -9,26 +9,37 @@
//Or someone snoring. So we make it where they won't hear it.
return
+ //make sure the air can transmit speech - hearer's side
+ var/turf/T = get_turf(src)
+ if ((T) && (!(istype(src, /mob/dead/observer)))) //Ghosts can hear even in vacuum.
+ var/datum/gas_mixture/environment = T.return_air()
+ var/pressure = (environment)? environment.return_pressure() : 0
+ if(pressure < SOUND_MINIMUM_PRESSURE && get_dist(speaker, src) > 1)
+ return
+
+ if (pressure < ONE_ATMOSPHERE*0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet
+ italics = 1
+ sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
+
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
+ if(language)
+ message = language.scramble(message)
+ else
+ message = stars(message)
var/speaker_name = speaker.name
if(istype(speaker, /mob/living/carbon/human))
@@ -41,8 +52,7 @@
var/track = null
if(istype(src, /mob/dead/observer))
if(italics && client.prefs.toggles & CHAT_GHOSTRADIO)
- if(!verb == ("whispers" || " quietly" || " softly"))
- return
+ return
if(speaker_name != speaker.real_name && speaker.real_name)
speaker_name = "[speaker.real_name] ([speaker_name])"
track = "(follow) "
@@ -50,12 +60,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)
@@ -71,40 +85,35 @@
return
var/track = null
- var/speaker_name
- var/style = "body"
-
- if(!language)
- language = new /datum/language/common
-// if(!speaker)
//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
+ if(S.speak && S.speak.len)
+ message = pick(S.speak)
+ else
+ return
+ else
+ if(language)
+ message = language.scramble(message)
+ else
+ message = stars(message)
+
+ if(hard_to_hear)
message = stars(message)
- if(language)
- verb = language.speech_verb
- style = language.colour
-
-
-
- if(hard_to_hear)
- message = stars(message)
- if(speaker)
- speaker_name = speaker.name
+ var/speaker_name = speaker.name
if(vname)
speaker_name = vname
- if(speaker && (istype(speaker, /mob/living/carbon/human)))
+ if(istype(speaker, /mob/living/carbon/human))
var/mob/living/carbon/human/H = speaker
if(H.voice)
speaker_name = H.voice
@@ -118,7 +127,7 @@
var/jobname // the mob's "job"
var/mob/living/carbon/human/impersonating //The crewmember being impersonated, if any.
- if (speaker && (ishuman(speaker)))
+ if (ishuman(speaker))
var/mob/living/carbon/human/H = speaker
if((H.wear_id && istype(H.wear_id,/obj/item/weapon/card/id/syndicate)) && (H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice)))
@@ -158,13 +167,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)
@@ -205,8 +219,6 @@
M.show_message(message)
src << message
-////////////////////////////////////////////////////////////////////////
-
/mob/proc/hear_sleep(var/message)
var/heard = ""
if(prob(15))
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index 253a54a4..c8594609 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -1,3 +1,5 @@
+#define SCRAMBLE_CACHE_LEN 20
+
/*
Datum based languages. Easily editable and modular.
*/
@@ -8,29 +10,103 @@
var/speech_verb = "says" // 'says', 'hisses', 'farts'.
var/ask_verb = "asks" // Used when sentence ends in a ?
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() // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags
- var/colour = "body" // CSS style to use for strings in this language.
+ 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.
var/native // If set, non-native speakers will have trouble speaking.
+ var/list/syllables // Used when scrambling text for a non-speaker.
+ var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string.
+
+/datum/language/proc/get_random_name(var/gender, name_count=2, syllable_count=4)
+ if(!syllables || !syllables.len)
+ if(gender==FEMALE)
+ return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names))
+ else
+ return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names))
+
+ var/full_name = ""
+ var/new_name = ""
+
+ for(var/i = 0;i0;x--)
+ new_name += pick(syllables)
+ full_name += " [capitalize(lowertext(new_name))]"
+
+ return "[trim(full_name)]"
+
+/datum/language
+ var/list/scramble_cache = list()
+
+/datum/language/proc/scramble(var/input)
+
+ if(!syllables || !syllables.len)
+ return stars(input)
+
+ // If the input is cached already, move it to the end of the cache and return it
+ if(input in scramble_cache)
+ var/n = scramble_cache[input]
+ scramble_cache -= input
+ scramble_cache[input] = n
+ return n
+
+ var/input_size = length(input)
+ var/scrambled_text = ""
+ var/capitalize = 1
+
+ while(length(scrambled_text) < input_size)
+ var/next = pick(syllables)
+ if(capitalize)
+ next = capitalize(next)
+ capitalize = 0
+ scrambled_text += next
+ var/chance = rand(100)
+ if(chance <= 5)
+ scrambled_text += ". "
+ capitalize = 1
+ else if(chance > 5 && chance <= space_chance)
+ scrambled_text += " "
+
+ scrambled_text = trim(scrambled_text)
+ var/ending = copytext(scrambled_text, length(scrambled_text))
+ if(ending == ".")
+ scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1)
+ var/input_ending = copytext(input, input_size)
+ if(input_ending in list("!","?","."))
+ scrambled_text += input_ending
+
+ // Add it to cache, cutting old entries if the list is too long
+ scramble_cache[input] = scrambled_text
+ if(scramble_cache.len > SCRAMBLE_CACHE_LEN)
+ scramble_cache.Cut(1, scramble_cache.len-SCRAMBLE_CACHE_LEN-1)
+
+
+ return scrambled_text
+
+/datum/language/proc/format_message(message, verb)
+ return "[verb], \"[capitalize(message)]\""
+
+/datum/language/proc/format_message_plain(message, verb)
+ return "[verb], \"[capitalize(message)]\""
+
+/datum/language/proc/format_message_radio(message, verb)
+ return "[verb], \"[capitalize(message)]\""
+
+/datum/language/proc/get_talkinto_msg_range(message)
+ // if you yell, you'll be heard from two tiles over instead of one
+ return (copytext(message, length(message)) == "!") ? 2 : 1
/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
@@ -43,6 +119,26 @@
return ask_verb
return speech_verb
+// Noise "language", for audible emotes.
+/datum/language/noise
+ name = "Noise"
+ desc = "Noises"
+ key = ""
+ flags = RESTRICTED|NONGLOBAL|INNATE|NO_TALK_MSG|NO_STUTTER
+
+/datum/language/noise/format_message(message, verb)
+ return "[message]"
+
+/datum/language/noise/format_message_plain(message, verb)
+ return message
+
+/datum/language/noise/format_message_radio(message, verb)
+ return "[message]"
+
+/datum/language/noise/get_talkinto_msg_range(message)
+ // if you make a loud noise (screams etc), you'll be heard from 4 tiles over instead of two
+ return (copytext(message, length(message)) == "!") ? 4 : 2
+
/datum/language/unathi
name = "Sinta'unathi"
desc = "The common language of Moghes, composed of sibilant hisses and rattles. Spoken natively by Unathi."
@@ -52,16 +148,37 @@
colour = "soghun"
key = "o"
flags = WHITELISTED
+ syllables = list("ss","ss","ss","ss","skak","seeki","resh","las","esi","kor","sh")
+
+/datum/language/unathi/get_random_name()
+
+ var/new_name = ..()
+ while(findtextEx(new_name,"sss",1,null))
+ new_name = replacetext(new_name, "sss", "ss")
+ return capitalize(new_name)
/datum/language/tajaran
name = "Siik'Maas"
- desc = "An expressive language that combines yowls and chirps with posture, tail and ears. Native to the Tajaran."
+ desc = "The traditionally employed tongue of Ahdomai, composed of expressive yowls and chirps. Native to the Tajaran."
speech_verb = "mrowls"
ask_verb = "mrowls"
exclaim_verb = "yowls"
colour = "tajaran"
key = "j"
flags = WHITELISTED
+ syllables = list("rr","rr","tajr","kir","raj","kii","mir","kra","ahk","nal","vah","khaz","jri","ran","darr", \
+ "mi","jri","dynh","manq","rhe","zar","rrhaz","kal","chur","eech","thaa","dra","jurl","mah","sanu","dra","ii'r", \
+ "ka","aasi","far","wa","baq","ara","qara","zir","sam","mak","hrar","nja","rir","khan","jun","dar","rik","kah", \
+ "hal","ket","jurl","mah","tul","cresh","azu","ragh")
+
+/datum/language/tajaran/get_random_name(var/gender)
+
+ var/new_name = ..(gender,1)
+ if(prob(80))
+ new_name += " [pick(list("Hadii","Kaytam","Zhan-Khazan","Hharar","Njarir'Akhan"))]"
+ else
+ new_name += ..(gender,1)
+ return new_name
/datum/language/skrell
name = "Skrellian"
@@ -72,6 +189,35 @@
colour = "skrell"
key = "k"
flags = WHITELISTED
+ syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","*","!")
+
+/datum/language/sign
+ name = "Sign language"
+ desc = "A mixture of manual communication and body-languaged, used to communication with those who have impaired hearing."
+ speech_verb = "signs"
+ signlang_verb = list("signs")
+ key = "4"
+ flags = SIGNLANG
+
+/datum/language/sini
+ name = "Sini"
+ desc = "Kocasslani traditional language"
+ speech_verb = "sings"
+ colour = "skrell"
+ key = "z"
+ flags = WHITELISTED
+ syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","*","!")
+
+
+/datum/language/vaurcese
+ name = "Vaurcese"
+ desc = "Vaurca native language made of clicks and sputters, \"It's a bugs life.\""
+ speech_verb = "clicks"
+ colour = "vaurca"
+ key = "p"
+ flags = WHITELISTED
+ syllables = list("kic","klic","\'tic","kit","lit","xic","vil","xrit","tshh","qix","qlit","zix","*","!")
+
/datum/language/vox
name = "Vox-pidgin"
@@ -81,7 +227,12 @@
exclaim_verb = "SHRIEKS"
colour = "vox"
key = "v"
- flags = RESTRICTED
+ flags = WHITELISTED
+ syllables = list("ti","ti","ti","hi","hi","ki","ki","ki","ki","ya","ta","ha","ka","ya","chi","cha","kah", \
+ "SKRE","AHK","EHK","RAWK","KRA","AAA","EEE","KI","II","KRI","KA")
+
+/datum/language/vox/get_random_name()
+ return ..(FEMALE,1,6)
/datum/language/diona
name = "Rootspeak"
@@ -92,14 +243,23 @@
colour = "soghun"
key = "q"
flags = RESTRICTED
+ syllables = list("hs","zt","kr","st","sh")
+
+/datum/language/diona/get_random_name()
+ var/new_name = "[pick(list("To Sleep Beneath","Wind Over","Embrace of","Dreams of","Witnessing","To Walk Beneath","Approaching the"))]"
+ new_name += " [pick(list("the Void","the Sky","Encroaching Night","Planetsong","Starsong","the Wandering Star","the Empty Day","Daybreak","Nightfall","the Rain"))]"
+ return new_name
/datum/language/common
- name = "Ceti Basic"
+ name = "Galactic Common"
desc = "The common galactic tongue."
speech_verb = "says"
+ whisper_verb = "whispers"
key = "0"
flags = RESTRICTED
+ syllables = list("blah","blah","blah","bleh","meh","neh","nah","wah")
+//TODO flag certain languages to use the mob-type specific say_quote and then get rid of these.
/datum/language/common/get_spoken_verb(var/msg_end)
switch(msg_end)
if("!")
@@ -111,9 +271,20 @@
/datum/language/human
name = "Sol Common"
desc = "A bastardized hybrid of informal English and elements of Mandarin Chinese; the common language of the Sol system."
- colour = "rough"
+ speech_verb = "says"
+ whisper_verb = "whispers"
+ colour = "solcom"
key = "1"
flags = RESTRICTED
+ syllables = list("tao","shi","tzu","yi","com","be","is","i","op","vi","ed","lec","mo","cle","te","dis","e")
+
+/datum/language/human/get_spoken_verb(var/msg_end)
+ switch(msg_end)
+ if("!")
+ return pick("exclaims","shouts","yells") //TODO: make the basic proc handle lists of verbs.
+ if("?")
+ return ask_verb
+ return speech_verb
// Galactic common languages (systemwide accepted standards).
/datum/language/trader
@@ -122,41 +293,24 @@
speech_verb = "enunciates"
colour = "say_quote"
key = "2"
+ space_chance = 100
+ syllables = list("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit",
+ "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore",
+ "magna", "aliqua", "ut", "enim", "ad", "minim", "veniam", "quis", "nostrud",
+ "exercitation", "ullamco", "laboris", "nisi", "ut", "aliquip", "ex", "ea", "commodo",
+ "consequat", "duis", "aute", "irure", "dolor", "in", "reprehenderit", "in",
+ "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla",
+ "pariatur", "excepteur", "sint", "occaecat", "cupidatat", "non", "proident", "sunt",
+ "in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum")
/datum/language/gutter
name = "Gutter"
- desc = "Much like Ceti Basic, this crude pidgin tongue descended from numerous languages and serves as Tradeband for criminal elements."
+ desc = "Much like Standard, this crude pidgin tongue descended from numerous languages and serves as Tradeband for criminal elements."
speech_verb = "growls"
colour = "rough"
key = "3"
+ syllables = list ("gra","ba","ba","breh","bra","rah","dur","ra","ro","gro","go","ber","bar","geh","heh", "gra")
-/datum/language/sini
- name = "Sini"
- desc = "Kocasslani traditional language"
- speech_verb = "sings"
- colour = "skrell"
- key = "z"
- flags = WHITELISTED
-
-/datum/language/sign
- name = "Sign language"
- desc = "A mixture of manual communication and body-languaged, used to communication with those who have impaired hearing."
- speech_verb = "signs"
- signlang_verb = list("signs")
- key = "4"
- flags = SIGNLANG
-
-/datum/language/vaurcese
- name = "Vaurcese"
- desc = "Vaurca native language made of clicks and sputters, \"It's a bugs life.\""
- speech_verb = "clicks"
- colour = "vaurca"
- key = "p"
- flags = WHITELISTED
-
-////////////////////////////////////
-//Not sure if stuff but yeah
-////////////////////////////////////
/datum/language/xenocommon
name = "Xenomorph"
colour = "alien"
@@ -166,6 +320,7 @@
exclaim_verb = "hisses"
key = "5"
flags = RESTRICTED
+ syllables = list("sss","sSs","SSS")
/datum/language/xenos
name = "Hivemind"
@@ -257,7 +412,7 @@
if(drone_only && !istype(S,/mob/living/silicon/robot/drone))
continue
else if(istype(S , /mob/living/silicon/ai))
- message_start = "[name], [speaker.name]"
+ message_start = "[name], [speaker.name]"
else if (!S.binarycheck())
continue
@@ -270,13 +425,13 @@
if(istype(M, /mob/living/silicon) || M.binarycheck())
continue
M.show_message("synthesised voice beeps, \"beep beep beep\"",2)
-/*
+
//robot binary xmitter component power usage
- if (isrobot(speaker))
- var/mob/living/silicon/robot/R = speaker
- var/datum/robot_component/C = R.components["comms"]
- R.cell_use_power(C.active_usage)
-*/
+// if (isrobot(speaker))
+// var/mob/living/silicon/robot/R = speaker
+// var/datum/robot_component/C = R.components["comms"]
+// R.cell_use_power(C.active_usage)
+
/datum/language/binary/drone
name = "Drone Talk"
desc = "A heavily encoded damage control coordination stream."
@@ -287,9 +442,6 @@
key = "d"
flags = RESTRICTED | HIVEMIND
drone_only = 1
-///////////////////////////////////////
-//Yeah but stuff if sure not
-///////////////////////////////////////
// Language handling.
/mob/proc/add_language(var/language)
@@ -303,15 +455,14 @@
return 1
/mob/proc/remove_language(var/rem_language)
-
- languages.Remove(all_languages[rem_language])
-
- return 0
+ var/datum/language/L = all_languages[rem_language]
+ . = (L in languages)
+ languages.Remove(L)
// Can we speak this language, as opposed to just understanding it?
/mob/proc/can_speak(datum/language/speaking)
- return (universal_speak || speaking in src.languages)
+ return (universal_speak || (speaking && speaking.flags & INNATE) || speaking in src.languages)
//TBD
/mob/verb/check_languages()
@@ -322,7 +473,10 @@
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
+ return
+
+#undef SCRAMBLE_CACHE_LEN
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index b679a874..648bcefa 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -1,4 +1,6 @@
-/mob/living/carbon/human/say(var/message, var/verb = "says")
+/mob/living/carbon/human/say(var/message)
+
+ var/verb = "says"
var/alt_name = ""
var/message_range = world.view
var/italics = 0
@@ -8,16 +10,19 @@
src << "\red You cannot speak in IC (Muted)."
return
- message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
+ message = trim_strip_html_properly(message)
- if(stat == 2)
- return say_dead(message)
+ if(stat)
+ if(stat == 2)
+ return say_dead(message)
+ return
+
+ if (istype(src.wear_mask, /obj/item/clothing/mask/muzzle))
+ src << "You're muzzled and cannot speak!"
+ return
var/message_mode = parse_message_mode(message, "headset")
-// if (istype(wear_mask, /obj/item/clothing/mask/muzzle) && message_mode != "changeling") //Todo: Add this to speech_problem_flag checks.
-// return
-//
if(copytext(message,1,2) == "*")
return emote(copytext(message,2))
@@ -31,6 +36,8 @@
else
message = copytext(message,3)
+ message = trim_left(message)
+
//parse the language code and consume it
var/datum/language/speaking = parse_language(message)
if(speaking)
@@ -66,7 +73,6 @@
if(!message || stat)
return
-
if(speaking && speaking.flags & SIGNLANG)
message_mode = null
..(message, speaking, verb, alt_name, italics, message_range)
@@ -123,27 +129,14 @@
if("whisper")
whisper_say(message, speaking, alt_name)
return
-/* if("binary")
- if(robot_talk_understand || binarycheck())
- robot_talk(message)
- return
- if("changeling")
- if(mind && mind.changeling)
- for(var/mob/Changeling in mob_list)
- if((Changeling.mind && Changeling.mind.changeling) || istype(Changeling, /mob/dead/observer))
- Changeling << "[mind.changeling.changelingID]: [message]"
- return
-*/
else
- if(!(stunned >= 4))
- if(message_mode)
- if(message_mode in (radiochannels | "department" | "special"))
- if(l_ear && istype(l_ear,/obj/item/device/radio))
- l_ear.talk_into(src,message, message_mode, verb, speaking)
- used_radios += l_ear
- else if(r_ear && istype(r_ear,/obj/item/device/radio))
- r_ear.talk_into(src,message, message_mode, verb, speaking)
- used_radios += r_ear
+ if(message_mode)
+ if(l_ear && istype(l_ear,/obj/item/device/radio))
+ l_ear.talk_into(src,message, message_mode, verb, speaking)
+ used_radios += l_ear
+ else if(r_ear && istype(r_ear,/obj/item/device/radio))
+ r_ear.talk_into(src,message, message_mode, verb, speaking)
+ used_radios += r_ear
var/sound/speech_sound
var/sound_vol
@@ -155,10 +148,14 @@
if(used_radios.len)
italics = 1
message_range = 1
-
+ if(speaking)
+ message_range = speaking.get_talkinto_msg_range(message)
+ var/msg
+ if(!speaking || !(speaking.flags & NO_TALK_MSG))
+ msg = "\The [src] talks into \the [used_radios[1]]"
for(var/mob/living/M in hearers(5, src))
- if(M != src)
- M.show_message("[src] talks into [used_radios.len ? used_radios[1] : "the radio."]")
+ if((M != src) && msg)
+ M.show_message(msg)
if (speech_sound)
sound_vol *= 0.5
@@ -237,6 +234,7 @@
return GetSpecialVoice()
return real_name
+
/mob/living/carbon/human/proc/SetSpecialVoice(var/new_voice)
if(new_voice)
special_voice = new_voice
@@ -274,45 +272,46 @@
return verb
/mob/living/carbon/human/proc/handle_speech_problems(var/message)
+
var/list/returns[3]
var/verb = "says"
var/handled = 0
- if(silent)
+ if(silent || (sdisabilities & MUTE))
message = ""
handled = 1
- if(sdisabilities & MUTE)
- message = ""
- handled = 1
- if(wear_mask)
- if(istype(wear_mask, /obj/item/clothing/mask/horsehead))
- var/obj/item/clothing/mask/horsehead/hoers = wear_mask
- if(hoers.voicechange)
- if(mind && mind.changeling && department_radio_keys[copytext(message, 1, 3)] != "changeling")
- message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!")
- verb = pick("whinnies","neighs", "says")
- handled = 1
+ if(istype(wear_mask, /obj/item/clothing/mask/horsehead))
+ var/obj/item/clothing/mask/horsehead/hoers = wear_mask
+ if(hoers.voicechange)
+ if(mind && mind.changeling && department_radio_keys[copytext(message, 1, 3)] != "changeling")
+ message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!")
+ verb = pick("whinnies","neighs", "says")
+ handled = 1
- if((HULK in mutations) && health >= 25 && length(message))
-// message = "[uppertext(message)]!!!" //No!
- verb = pick("yells","roars","hollers")
- handled = 1
- if(slurring)
- message = slur(message)
- verb = pick("stammers","stutters")
- handled = 1
-
- var/braindam = getBrainLoss()
- if(braindam >= 60)
- handled = 1
- if(prob(braindam/4))
+ if(message != "")
+ if((HULK in mutations) && health >= 25 && length(message))
+ message = "[uppertext(message)]!!!"
+ verb = pick("yells","roars","hollers")
+ handled = 1
+ if(slurring)
+ message = slur(message)
+ verb = pick("slobbers","slurs")
+ handled = 1
+ if(stuttering)
message = stutter(message)
- verb = pick("stammers", "stutters")
- if(prob(braindam))
- message = uppertext(message)
- verb = pick("yells like an idiot","says rather loudly")
+ verb = pick("stammers","stutters")
+ handled = 1
+
+ var/braindam = getBrainLoss()
+ if(braindam >= 60)
+ handled = 1
+ if(prob(braindam/4))
+ message = stutter(message)
+ verb = pick("stammers", "stutters")
+ if(prob(braindam))
+ message = uppertext(message)
+ verb = "yells loudly"
returns[1] = message
returns[2] = verb
returns[3] = handled
-
return returns
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index ad2e5977..454c962f 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -12,6 +12,21 @@
if(say_disabled) //This is here to try to identify lag problems
usr << "\red Speech is currently admin-disabled."
return
+ //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words
+ /*
+ var/first_letter = copytext(message,1,2)
+ if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say "))
+ var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No")
+ if(response == "Edit message")
+ message = input(usr, "Please edit your message carefully:", "Edit message", message)
+ if(!message)
+ return
+ else if(response == "No")
+ return
+ */
+
+//We do not have typing indicator code yet - Waiting for the okay from Skull before considering adding - Jamini
+// set_typing_indicator(0)
usr.say(message)
/mob/verb/me_verb(message as text)
@@ -22,8 +37,9 @@
usr << "\red Speech is currently admin-disabled."
return
- message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
-
+ message = strip_html_properly(message)
+//We do not have typing indicator code yet - Waiting for the okay from Skull before considering adding - Jamini
+// set_typing_indicator(0)
if(use_me)
usr.emote("me",usr.emote_type,message)
else
@@ -68,7 +84,6 @@
return
-
/mob/proc/say_understands(var/mob/other,var/datum/language/speaking = null)
if (src.stat == 2) //Dead
@@ -90,6 +105,9 @@
return 1
return 0
+ if(speaking.flags & INNATE)
+ return 1
+
//Language check.
for(var/datum/language/L in src.languages)
if(speaking.name == L.name)
@@ -153,6 +171,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 9f687a5b..37517f27 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -710,6 +710,9 @@ var/list/be_special_flags = list(
#define LEFT 1
#define RIGHT 2
+
+
+
// for secHUDs and medHUDs and variants. The number is the location of the image on the list hud_list of humans.
#define HEALTH_HUD 1 // a simple line rounding the mob's number health
#define STATUS_HUD 2 // alive, dead, diseased, etc.
@@ -773,6 +776,10 @@ 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)
+#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
//Flags for zone sleeping
#define ZONE_ACTIVE 1