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