diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm index 2af0fd6ade8..cfc568afb66 100644 --- a/code/__DEFINES/language.dm +++ b/code/__DEFINES/language.dm @@ -11,8 +11,6 @@ //Language flags. #define WHITELISTED 1 // Language is available if the speaker is whitelisted. #define RESTRICTED 2 // Language can only be accquired by spawning or an admin. -#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) diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index 6cdfdc736d9..5ba8d1360b0 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -3,7 +3,6 @@ real_name = "host brain" /mob/living/captive_brain/say(message) - if(client) if(client.prefs.muted & MUTE_IC) to_chat(src, "You cannot speak in IC (muted).") @@ -152,13 +151,13 @@ stat("Chemicals", chemicals) /mob/living/simple_animal/borer/say(var/message) - var/datum/language/dialect = parse_language(message) - if(!dialect) - dialect = get_default_language() - if(!istype(dialect, /datum/language/corticalborer) && loc == host && !talk_inside_host) - to_chat(src, "You've disabled audible speech while inside a host! Re-enable it under the borer tab, or stick to borer communications.") - return - ..() + var/list/message_pieces = parse_languages(message) + for(var/datum/multilingual_say_piece/S in message_pieces) + if(!istype(S.speaking, /datum/language/corticalborer) && loc == host && !talk_inside_host) + to_chat(src, "You've disabled audible speech while inside a host! Re-enable it under the borer tab, or stick to borer communications.") + return + + . = ..() /mob/living/simple_animal/borer/verb/Communicate() set category = "Borer" diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 20132f614af..2a4173b377e 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -373,19 +373,19 @@ var/list/holopads = list() /*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(atom/movable/speaker, message, verb, datum/language/message_language) +/obj/machinery/hologram/holopad/hear_talk(atom/movable/speaker, list/message_pieces, verb) if(speaker && masters.len)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. for(var/mob/living/silicon/ai/master in masters) if(masters[master] && speaker != master) - master.relay_speech(speaker, message, verb, message_language) + master.relay_speech(speaker, message_pieces, verb) for(var/I in holo_calls) var/datum/holocall/HC = I if(HC.connected_holopad == src && speaker != HC.hologram) - HC.user.hear_say(message, verb, message_language, speaker = speaker) + HC.user.hear_say(message_pieces, verb, speaker = speaker) if(outgoing_call && speaker == outgoing_call.user) - outgoing_call.hologram.atom_say(message) + outgoing_call.hologram.atom_say(multilingual_to_message(message_pieces)) diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 10451c758be..8e34e2e19bc 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -57,7 +57,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept signal.data["name"], signal.data["job"], signal.data["realname"], signal.data["vname"],, signal.data["compression"], signal.data["level"], signal.frequency, - signal.data["verb"], signal.data["language"] ) + signal.data["verb"] ) /** #### - Simple Broadcast - #### **/ @@ -83,7 +83,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency, - signal.data["verb"], signal.data["language"]) + signal.data["verb"]) if(!message_delay) message_delay = 1 @@ -146,7 +146,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency, - signal.data["verb"], signal.data["language"]) + signal.data["verb"]) else if(intercept) Broadcast_Message(signal.data["connection"], signal.data["mob"], @@ -154,7 +154,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency, - signal.data["verb"], signal.data["language"]) + signal.data["verb"]) #define CREW_RADIO_TYPE 0 #define CENTCOMM_RADIO_TYPE 1 @@ -234,9 +234,9 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept **/ /proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M, - var/vmask, var/vmessage, var/obj/item/radio/radio, - var/message, var/name, var/job, var/realname, var/vname, - var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/datum/language/speaking = null, + var/vmask, list/vmessage_pieces, var/obj/item/radio/radio, + list/message_pieces, var/name, var/job, var/realname, var/vname, + var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/atom/follow_target = null) @@ -339,7 +339,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept else // - The speaker has a prespecified "voice message" to display if not understood - - if(vmessage) + if(vmessage_pieces) heard_voice += R // - Just display a garbled message - @@ -364,12 +364,11 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept // --- Filter the message; place it in quotes apply a verb --- - var/quotedmsg = null if(M) - quotedmsg = M.say_quote(message) + quotedmsg = "[M.say_quote(multilingual_to_message(message_pieces))], \"[multilingual_to_message(message_pieces)]\"" else - quotedmsg = "says, \"[message]\"" + quotedmsg = "says, \"[multilingual_to_message(message_pieces)]\"" // --- This following recording is intended for research and feedback in the use of department radio channels --- @@ -414,38 +413,38 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept if(length(heard_masked)) for(var/mob/R in heard_masked) - R.hear_radio(message,verbage, speaking, part_a, part_b, M, 0, name, follow_target=follow_target) + R.hear_radio(message_pieces, verbage, part_a, part_b, M, 0, name, follow_target=follow_target) /* --- Process all the mobs that heard the voice normally (understood) --- */ if(length(heard_normal)) for(var/mob/R in heard_normal) - R.hear_radio(message, verbage, speaking, part_a, part_b, M, 0, realname, follow_target=follow_target) + R.hear_radio(message_pieces, verbage, part_a, part_b, M, 0, realname, follow_target=follow_target) /* --- Process all the mobs that heard the voice normally (did not understand) --- */ if(length(heard_voice)) for(var/mob/R in heard_voice) - R.hear_radio(message,verbage, speaking, part_a, part_b, M,0, vname, follow_target=follow_target) + R.hear_radio(message_pieces, verbage, part_a, part_b, M,0, vname, follow_target=follow_target) /* --- Process all the mobs that heard a garbled voice (did not understand) --- */ // Displays garbled message (ie "f*c* **u, **i*er!") if(length(heard_garbled)) for(var/mob/R in heard_garbled) - R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1, vname, follow_target=follow_target) + R.hear_radio(message_pieces, verbage, part_a, part_b, M, 1, vname, follow_target=follow_target) /* --- Complete gibberish. Usually happens when there's a compressed message --- */ if(length(heard_gibberish)) for(var/mob/R in heard_gibberish) - R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1, follow_target=follow_target) + R.hear_radio(message_pieces, verbage, part_a, part_b, M, 1, follow_target=follow_target) return 1 -/proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level) - +/proc/Broadcast_SimpleMessage(var/source, var/frequency, list/message_pieces, var/data, var/mob/M, var/compression, var/level) + var/text = multilingual_to_message(message_pieces) /* ###### Prepare the radio connection ###### */ var/mob/living/carbon/human/H diff --git a/code/game/machinery/telecomms/ntsl2.dm b/code/game/machinery/telecomms/ntsl2.dm index fa76f98ec39..d24b51389ec 100644 --- a/code/game/machinery/telecomms/ntsl2.dm +++ b/code/game/machinery/telecomms/ntsl2.dm @@ -316,16 +316,20 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new()) signal.data["name"] = new_name signal.data["realname"] = new_name // this is required because the broadcaster uses this directly if the speaker doesn't have a voice changer on + // This is hacky stuff for multilingual messages... + var/list/message_pieces = signal.data["message"] + // Makes heads of staff bold if(toggle_command_bold) var/job = signal.data["job"] if((job in ert_jobs) || (job in heads)) - signal.data["message"] = "" + signal.data["message"] + "" + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = "[S.message]" // Hacks! // Censor dat shit like nobody's business if(toggle_gibberish) - signal.data["message"] = Gibberish(signal.data["message"], 80) + Gibberish_all(message_pieces, 80) // Replace everything with HONK! if(toggle_honk) @@ -334,24 +338,25 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new()) var/new_message = "" for(var/i in 1 to honklength) new_message += pick("HoNK!", "HONK", "HOOOoONK", "HONKHONK!", "HoNnnkKK!!!", "HOOOOOOOOOOONK!!!!11!", "henk!") + " " - signal.data["message"] = new_message + signal.data["message"] = message_to_multilingual(new_message) // Language Conversion if(setting_language && valid_languages[setting_language]) if(setting_language == "--DISABLE--") setting_language = null else - signal.data["language"] = GLOB.all_languages[setting_language] + for(var/datum/multilingual_say_piece/S in message_pieces) + S.speaking = GLOB.all_languages[setting_language] // Regex replacements if(islist(regex) && regex.len > 0) - var/original = signal.data["message"] - var/new_message = original - for(var/reg in regex) - var/replacePattern = pencode_to_html(regex[reg]) - var/regex/start = regex("[reg]", "gi") - new_message = start.Replace(new_message, replacePattern) - signal.data["message"] = new_message + for(var/datum/multilingual_say_piece/S in message_pieces) + var/new_message = S.message + for(var/reg in regex) + var/replacePattern = pencode_to_html(regex[reg]) + var/regex/start = regex("[reg]", "gi") + new_message = start.Replace(new_message, replacePattern) + S.message = new_message // Make sure the message is valid after we tinkered with it, otherwise reject it if(signal.data["message"] == "" || !signal.data["message"]) diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index b38eca98817..06eacdd8da1 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -438,7 +438,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() var/encryption = "null" // encryption key: ie "password" var/salt = "null" // encryption salt: ie "123comsat" // would add up to md5("password123comsat") - var/language = "human" var/obj/item/radio/headset/server_radio = null /obj/machinery/telecomms/server/Initialize() @@ -463,11 +462,10 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() log.parameters["mobtype"] = signal.data["mobtype"] log.parameters["race"] = signal.data["race"] log.parameters["job"] = signal.data["job"] - log.parameters["language"] = signal.data["language"] log.parameters["key"] = signal.data["key"] - log.parameters["vmessage"] = signal.data["message"] + log.parameters["vmessage"] = multilingual_to_message(signal.data["message"]) log.parameters["vname"] = signal.data["vname"] - log.parameters["message"] = signal.data["message"] + log.parameters["message"] = multilingual_to_message(signal.data["message"]) log.parameters["name"] = signal.data["name"] log.parameters["realname"] = signal.data["realname"] @@ -478,7 +476,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() // If the signal is still compressed, make the log entry gibberish if(signal.data["compression"] > 0) - log.parameters["message"] = Gibberish(signal.data["message"], signal.data["compression"] + 50) + log.parameters["message"] = Gibberish(multilingual_to_message(signal.data["message"]), signal.data["compression"] + 50) log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 2205a9f6a72..998228beee5 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -171,9 +171,9 @@ for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) to_chat(user, "[bicon(ME)] [ME]") -/obj/mecha/hear_talk(mob/M, text) +/obj/mecha/hear_talk(mob/M, list/message_pieces) if(M == occupant && radio.broadcasting) - radio.talk_into(M, text) + radio.talk_into(M, message_pieces) /obj/mecha/proc/click_action(atom/target, mob/user, params) if(!occupant || occupant != user ) diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm index 43515ed5241..59a7a650383 100644 --- a/code/game/objects/items/devices/megaphone.dm +++ b/code/game/objects/items/devices/megaphone.dm @@ -47,8 +47,10 @@ message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN)) if(!message) return - message = user.handle_speech_problems(message)[1] message = capitalize(message) + var/list/message_pieces = message_to_multilingual(message) + user.handle_speech_problems(message_pieces) + message = multilingual_to_message(message_pieces) if((loc == user && !user.incapacitated())) if(emagged) if(insults) @@ -69,7 +71,7 @@ audible_message("[user.GetVoice()] [user.GetAltName()] broadcasts, \"[message]\"", hearing_distance = 14) log_say(message, user) for(var/obj/O in oview(14, get_turf(src))) - O.hear_talk(user, "[message]") + O.hear_talk(user, message_to_multilingual("[message]")) /obj/item/megaphone/emag_act(user as mob) if(!emagged) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 4bd3896c7a5..1e3acfc7812 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -50,15 +50,15 @@ to_chat(user, "The following channels are available:") to_chat(user, radio_desc) -/obj/item/radio/headset/handle_message_mode(mob/living/M as mob, message, channel) +/obj/item/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel) if(channel == "special") if(translate_binary) var/datum/language/binary = GLOB.all_languages["Robot Talk"] - binary.broadcast(M, message) + binary.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces))) return RADIO_CONNECTION_NON_SUBSPACE if(translate_hive) var/datum/language/hivemind = GLOB.all_languages["Hivemind"] - hivemind.broadcast(M, message) + hivemind.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces))) return RADIO_CONNECTION_NON_SUBSPACE return RADIO_CONNECTION_FAIL diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 1409c463000..68d31f075b6 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -285,7 +285,7 @@ var/global/list/default_medbay_channels = list( qdel(src) // Interprets the message mode when talking into a radio, possibly returning a connection datum -/obj/item/radio/proc/handle_message_mode(mob/living/M as mob, message, message_mode) +/obj/item/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode) // If a channel isn't specified, send to common. if(!message_mode || message_mode == "headset") return radio_connection @@ -301,11 +301,11 @@ var/global/list/default_medbay_channels = list( // If we were to send to a channel we don't have, drop it. return RADIO_CONNECTION_FAIL -/obj/item/radio/talk_into(mob/living/M as mob, message, channel, var/verb = "says", var/datum/language/speaking = null) +/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") if(!on) return 0 // the device has to be on // Fix for permacell radios, but kinda eh about actually fixing them. - if(!M || !message) + if(!M || !message_pieces) return 0 // Uncommenting this. To the above comment: @@ -328,7 +328,7 @@ var/global/list/default_medbay_channels = list( */ //#### Grab the connection datum ####// - var/message_mode = handle_message_mode(M, message, channel) + var/message_mode = handle_message_mode(M, message_pieces, channel) switch(message_mode) //special cases if(RADIO_CONNECTION_FAIL) return 0 @@ -361,7 +361,7 @@ var/global/list/default_medbay_channels = list( var/jobname // the mob's "job" if(jammed) - message = Gibberish(message, 100) + Gibberish_all(message_pieces, 100) // --- Human: use their actual job --- if(ishuman(M)) @@ -432,7 +432,7 @@ var/global/list/default_medbay_channels = list( // Other tags: "compression" = rand(45,50), // compressed radio signal - "message" = message, // the actual sent message + "message" = message_pieces, // the actual sent message "connection" = connection, // the radio connection to use "radio" = src, // stores the radio used for transmission "slow" = 0, // how much to sleep() before broadcasting - simulates net lag @@ -441,7 +441,6 @@ var/global/list/default_medbay_channels = list( "server" = null, // the last server to log this signal "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery "level" = position.z, // The source's z level - "language" = speaking, "verb" = verb ) signal.frequency = connection.frequency // Quick frequency set @@ -490,7 +489,7 @@ var/global/list/default_medbay_channels = list( "vmask" = voicemask, // 1 if the mob is using a voice gas mas "compression" = 0, // uncompressed radio signal - "message" = message, // the actual sent message + "message" = message_pieces, // the actual sent message "connection" = connection, // the radio connection to use "radio" = src, // stores the radio used for transmission "slow" = 0, @@ -499,7 +498,6 @@ var/global/list/default_medbay_channels = list( "server" = null, "reject" = 0, "level" = position.z, - "language" = speaking, "verb" = verb ) signal.frequency = connection.frequency // Quick frequency set @@ -522,15 +520,14 @@ var/global/list/default_medbay_channels = list( if(!connection) return 0 //~Carn return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], list(position.z), connection.frequency,verb,speaking) + src, message_pieces, displayname, jobname, real_name, M.voice_name, + filter_type, signal.data["compression"], list(position.z), connection.frequency,verb) -/obj/item/radio/hear_talk(mob/M as mob, msg, var/verb = "says", var/datum/language/speaking = null) - +/obj/item/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says") if(broadcasting) if(get_dist(src, M) <= canhear_range) - talk_into(M, msg,null,verb,speaking) + talk_into(M, message_pieces, null, verb) /* diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 9b36b70fdf2..4eaf0e79dce 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -85,7 +85,8 @@ icon_state = "taperecorder_idle" -/obj/item/taperecorder/hear_talk(mob/living/M as mob, msg) +/obj/item/taperecorder/hear_talk(mob/living/M as mob, list/message_pieces) + var/msg = multilingual_to_message(message_pieces) if(mytape && recording) var/ending = copytext(msg, length(msg)) mytape.timestamp += mytape.used_capacity diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index dbd7701ce93..371c6248a6e 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -75,10 +75,10 @@ return attached_device.HasProximity(AM) -/obj/item/transfer_valve/hear_talk(mob/living/M, msg) +/obj/item/transfer_valve/hear_talk(mob/living/M, list/message_pieces) ..() for(var/obj/O in contents) - O.hear_talk(M, msg) + O.hear_talk(M, message_pieces) /obj/item/transfer_valve/hear_message(mob/living/M, msg) ..() diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 8419f0b7537..2f5b48ff329 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -247,9 +247,9 @@ if(nadeassembly) nadeassembly.on_found(finder) -/obj/item/grenade/chem_grenade/hear_talk(mob/living/M, msg) +/obj/item/grenade/chem_grenade/hear_talk(mob/living/M, list/message_pieces) if(nadeassembly) - nadeassembly.hear_talk(M, msg) + nadeassembly.hear_talk(M, message_pieces) /obj/item/grenade/chem_grenade/hear_message(mob/living/M, msg) if(nadeassembly) diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm index d461700695d..c080715a035 100644 --- a/code/game/objects/items/weapons/storage/lockbox.dm +++ b/code/game/objects/items/weapons/storage/lockbox.dm @@ -67,7 +67,7 @@ origin_tech = null //wipe out any origin tech if it's unlocked in any way so you can't double-dip tech levels at R&D. return -/obj/item/storage/lockbox/hear_talk(mob/living/M as mob, msg) +/obj/item/storage/lockbox/hear_talk(mob/living/M as mob, list/message_pieces) /obj/item/storage/lockbox/hear_message(mob/living/M as mob, msg) diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index dd5091375d8..aeaa7991420 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -158,7 +158,7 @@ to_chat(usr, "[src] is locked!") return 0 -/obj/item/storage/secure/hear_talk(mob/living/M as mob, msg) +/obj/item/storage/secure/hear_talk(mob/living/M as mob, list/message_pieces) return /obj/item/storage/secure/hear_message(mob/living/M as mob, msg) diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 2d7c6842547..99590fe1d13 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -473,10 +473,10 @@ O.emp_act(severity) ..() -/obj/item/storage/hear_talk(mob/living/M as mob, msg) +/obj/item/storage/hear_talk(mob/living/M as mob, list/message_pieces) ..() for(var/obj/O in contents) - O.hear_talk(M, msg) + O.hear_talk(M, message_pieces) /obj/item/storage/hear_message(mob/living/M as mob, msg) ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index cc2fa8d1d69..f13806b90f1 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -173,7 +173,7 @@ return -/obj/proc/hear_talk(mob/M as mob, text) +/obj/proc/hear_talk(mob/M, list/message_pieces) return /obj/proc/hear_message(mob/M as mob, text) diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index 25f9f6fe285..2c5a2d28883 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -85,9 +85,9 @@ if(bombassembly) bombassembly.on_found(finder) -/obj/item/onetankbomb/hear_talk(mob/living/M, msg) +/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces) if(bombassembly) - bombassembly.hear_talk(M, msg) + bombassembly.hear_talk(M, message_pieces) /obj/item/onetankbomb/hear_message(mob/living/M, msg) if(bombassembly) diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 19683743b3e..bc36a0543e8 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -98,11 +98,11 @@ a_right.on_found(finder) -/obj/item/assembly_holder/hear_talk(mob/living/M, msg) +/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces) if(a_left) - a_left.hear_talk(M, msg) + a_left.hear_talk(M, message_pieces) if(a_right) - a_right.hear_talk(M, msg) + a_right.hear_talk(M, message_pieces) /obj/item/assembly_holder/hear_message(mob/living/M, msg) if(a_left) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index e8462884cd4..3c7fdfa8fef 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -16,11 +16,11 @@ else return "[src] is deactivated." -/obj/item/assembly/voice/hear_talk(mob/living/M as mob, msg) - hear_input(M, msg, 0) +/obj/item/assembly/voice/hear_talk(mob/living/M as mob, list/message_pieces) + hear_input(M, multilingual_to_message(message_pieces), 0) /obj/item/assembly/voice/hear_message(mob/living/M as mob, msg) - hear_input(M, msg, 1) + hear_input(M, msg, 1) /obj/item/assembly/voice/proc/hear_input(mob/living/M as mob, msg, type) if(!istype(M,/mob/living)) @@ -68,7 +68,7 @@ /obj/item/assembly/voice/noise/describe() return "[src] does not appear to have any controls." -/obj/item/assembly/voice/noise/hear_talk(mob/living/M as mob, msg) +/obj/item/assembly/voice/noise/hear_talk(mob/living/M as mob, list/message_pieces) return /obj/item/assembly/voice/noise/hear_message(mob/living/M as mob, msg) diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index c10ccf32f89..cfb5a39ef4c 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -182,9 +182,9 @@ trigger.HasProximity(AM) -/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, msg) +/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces) if(trigger) - trigger.hear_talk(M, msg) + trigger.hear_talk(M, message_pieces) /obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) if(trigger) diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm index f64ae7ca6c0..0c77183b578 100644 --- a/code/modules/clothing/suits/storage.dm +++ b/code/modules/clothing/suits/storage.dm @@ -28,8 +28,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, list/message_pieces) + pockets.hear_talk(M, message_pieces) ..() /obj/item/clothing/suit/storage/hear_message(mob/M, var/msg) diff --git a/code/modules/clothing/under/accessories/storage.dm b/code/modules/clothing/under/accessories/storage.dm index ae771a0dffd..3b2e9c75a20 100644 --- a/code/modules/clothing/under/accessories/storage.dm +++ b/code/modules/clothing/under/accessories/storage.dm @@ -40,8 +40,8 @@ hold.emp_act(severity) ..() -/obj/item/clothing/accessory/storage/hear_talk(mob/M, var/msg, verb, datum/language/speaking) - hold.hear_talk(M, msg, verb, speaking) +/obj/item/clothing/accessory/storage/hear_talk(mob/M, list/message_pieces, verb) + hold.hear_talk(M, message_pieces, verb) ..() /obj/item/clothing/accessory/storage/hear_message(mob/M, var/msg, verb, datum/language/speaking) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index f9f147e9bf8..5273bcb768b 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -728,7 +728,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_ people += H if(person) //Basic talk var/image/speech_overlay = image('icons/mob/talk.dmi', person, "h0", layer = ABOVE_MOB_LAYER) - target.hear_say(pick(speak_messages),language = pick(person.languages),speaker = person) + target.hear_say(message_to_multilingual(pick(speak_messages), pick(person.languages)), speaker = person) if(target.client) target.client.images |= speech_overlay sleep(30) @@ -738,7 +738,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_ for(var/mob/living/carbon/human/H in GLOB.living_mob_list) humans += H person = pick(humans) - target.hear_radio(pick(radio_messages),language = pick(person.languages),speaker = person, part_a = "\[[get_frequency_name(PUB_FREQ)]\] ", part_b = " ") + target.hear_radio(message_to_multilingual(pick(radio_messages), pick(person.languages)), speaker = person, part_a = "\[[get_frequency_name(PUB_FREQ)]\] ", part_b = " ") qdel(src) /obj/effect/hallucination/message @@ -754,7 +754,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_ "You feel a tiny prick!", "[target] sneezes.", "You feel faint.", - "You hear a strange, alien voice in your head...[pick("Hiss","Ssss")]", + "You hear a strange, alien voice in your head... [pick("Hiss","Ssss")]", "You can see...everything!") to_chat(target, chosen) qdel(src) diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm index 4c748067a5b..78dcbca2cf4 100644 --- a/code/modules/mining/lavaland/loot/colossus_loot.dm +++ b/code/modules/mining/lavaland/loot/colossus_loot.dm @@ -112,9 +112,9 @@ activation_method = pick("touch","laser","bullet","energy","bomb","mob_bump","weapon","speech") // "heat" removed due to lack of is_hot() ..() -/obj/machinery/anomalous_crystal/hear_talk(mob/speaker, message) +/obj/machinery/anomalous_crystal/hear_talk(mob/speaker, list/message_pieces) ..() - if(isliving(speaker) && message) + if(isliving(speaker) && LAZYLEN(message_pieces)) ActivationReaction(speaker, "speech") /obj/machinery/anomalous_crystal/attack_hand(mob/user) diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index b780ac0d381..ab47a84ee76 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -38,7 +38,7 @@ . = src.emote_dead(message) -/mob/dead/observer/handle_track(var/message, var/verb = "says", var/datum/language/language, var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) +/mob/dead/observer/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) return "[speaker_name] ([ghost_follow_link(follow_target, ghost=src)])" /mob/dead/observer/handle_speaker_name(var/mob/speaker = null, var/vname, var/hard_to_hear) diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 2593d2eebaf..382eb94b4db 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -1,6 +1,53 @@ // At minimum every mob has a hear_say proc. -/mob/proc/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/proc/combine_message(var/list/message_pieces, var/verb, var/mob/speaker, always_stars = FALSE) + var/iteration_count = 0 + var/msg = "" // This is to make sure that the pieces have actually added something + . = "[verb], \"" + for(var/datum/multilingual_say_piece/SP in message_pieces) + iteration_count++ + var/piece = SP.message + if(piece == "") + continue + + if(SP.speaking && SP.speaking.flags & INNATE) // Fucking snowflake noise lang + return SP.speaking.format_message(piece) + + if(iteration_count == 1) + piece = capitalize(piece) + + if(SP.speaking) + if(!say_understands(speaker, SP.speaking)) + if(isanimal(speaker)) + var/mob/living/simple_animal/S = speaker + if(LAZYLEN(S.speak)) + piece = pick(S.speak) + else + piece = stars(piece) + else + piece = SP.speaking.scramble(piece) + if(always_stars) + piece = stars(piece) + piece = SP.speaking.format_message(piece) + else + if(!say_understands(speaker, null)) + piece = stars(piece) + if(isanimal(speaker)) + var/mob/living/simple_animal/S = speaker + if(LAZYLEN(S.speak)) + piece = pick(S.speak) + if(always_stars) + piece = stars(piece) + piece = "[piece]" + msg += (piece + " ") + if(msg == "") + // There is literally no content left in this message, we need to shut this shit down + . = "" // hear_say will suppress it + else + . = trim(. + trim(msg)) + . += "\"" + +/mob/proc/hear_say(var/list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) if(!client) return 0 @@ -23,35 +70,18 @@ sound_vol *= 0.5 if(sleeping || stat == UNCONSCIOUS) - hear_sleep(message) + hear_sleep(message_pieces) return 0 - //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(!has_vision(information_only = TRUE)) //blind people can't see dumbass - message = stars(message) - - if(!speaker || !(speaker in view(src))) - message = stars(message) - - if(!say_understands(speaker, language)) - if(isanimal(speaker)) - var/mob/living/simple_animal/S = speaker - if(S.speak.len) - message = pick(S.speak) - else - message = stars(message) - else - if(language) - message = language.scramble(message) - else - message = stars(message) - var/speaker_name = speaker.name if(ishuman(speaker)) var/mob/living/carbon/human/H = speaker speaker_name = H.GetVoice() + var/message = combine_message(message_pieces, verb, speaker) + if(message == "") + return + if(italics) message = "[message]" @@ -66,74 +96,47 @@ message = "[message]" if(!can_hear()) - 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) - to_chat(src, "You cannot hear yourself speak!") - else - to_chat(src, "[speaker_name][speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].") - else - if(language) - to_chat(src, "[speaker_name][speaker.GetAltName()] [track][language.format_message(message, verb)]") + // 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(!language || !(language.flags & INNATE)) + if(speaker == src) + to_chat(src, "You cannot hear yourself speak!") else - to_chat(src, "[speaker_name][speaker.GetAltName()] [track][verb], \"[message]\"") + to_chat(src, "[speaker_name][speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].") + else + to_chat(src, "[speaker_name][speaker.GetAltName()] [track][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) -/mob/proc/hear_radio(var/message, var/verb = "says", var/datum/language/language = null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname = "", var/atom/follow_target) +/mob/proc/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target) if(!client) return if(sleeping || stat == UNCONSCIOUS) //If unconscious or sleeping - hear_sleep(message) + hear_sleep(multilingual_to_message(message_pieces)) + return + + var/message = combine_message(message_pieces, verb, speaker, always_stars = hard_to_hear) + if(message == "") return var/track = null if(!follow_target) follow_target = 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(!has_vision(information_only=TRUE)) //blind people can't see dumbass - message = stars(message) - - if(!speaker || !(speaker in view(src))) - message = stars(message) - - if(!say_understands(speaker, language)) - if(isanimal(speaker)) - 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) - var/speaker_name = handle_speaker_name(speaker, vname, hard_to_hear) - track = handle_track(message, verb, language, speaker, speaker_name, follow_target, hard_to_hear) + track = handle_track(message, verb, speaker, speaker_name, follow_target, hard_to_hear) - var/formatted - if(language) - formatted = language.format_message_radio(message, verb) - else - formatted = "[verb], \"[message]\"" if(!can_hear()) if(prob(20)) to_chat(src, "You feel your headset vibrate but can hear nothing from it!") else if(track) - to_chat(src, "[part_a][track][part_b][formatted]") + to_chat(src, "[part_a][track][part_b][message]") else - to_chat(src, "[part_a][speaker_name][part_b][formatted]") + to_chat(src, "[part_a][speaker_name][part_b][message]") -/mob/proc/handle_speaker_name(var/mob/speaker = null, var/vname, var/hard_to_hear) +/mob/proc/handle_speaker_name(mob/speaker = null, vname, hard_to_hear) var/speaker_name = "unknown" if(speaker) speaker_name = speaker.name @@ -146,26 +149,10 @@ return speaker_name -/mob/proc/handle_track(var/message, var/verb = "says", var/datum/language/language, var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) +/mob/proc/handle_track(message, verb = "says", mob/speaker = null, speaker_name, atom/follow_target, hard_to_hear) return -/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null) - if(!client) - return - - if(say_understands(speaker, language)) - message = "[src] [verb], \"[message]\"" - else - message = "[src] [verb]." - - if(src.status_flags & PASSEMOTES) - for(var/obj/item/holder/H in src.contents) - H.show_message(message) - for(var/mob/living/M in src.contents) - M.show_message(message) - src.show_message(message) - -/mob/proc/hear_sleep(var/message) +/mob/proc/hear_sleep(message) var/heard = "" if(prob(15)) message = strip_html_properly(message) @@ -177,9 +164,19 @@ heardword = copytext(heardword,2) if(copytext(heardword,-1) in punctuation) heardword = copytext(heardword,1,lentext(heardword)) - heard = "...You hear something about...[heardword]" + heard = "...You hear something about... '[heardword]'..." else - heard = "...You almost hear someone talking..." + heard = "...You almost hear someone talking..." to_chat(src, heard) + +/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null) + var/message = combine_message(message_pieces, verb, speaker) + + var/name = speaker.name + if(!say_understands(speaker)) + name = speaker.voice_name + + var/rendered = "[name] [message]" + to_chat(src, rendered) \ No newline at end of file diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm index 0a1f2d322ce..2d4b555448b 100644 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -11,7 +11,6 @@ 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/key = "x" // Character used to speak in language eg. :o for Unathi. var/flags = 0 // Various language flags. @@ -85,11 +84,11 @@ return scrambled_text -/datum/language/proc/format_message(message, verb) - return "[verb], \"[capitalize(message)]\"" +/datum/language/proc/format_message(message) + return "[message]" -/datum/language/proc/format_message_radio(message, verb) - return "[verb], \"[capitalize(message)]\"" +/datum/language/proc/format_message_radio(message) + return "[message]" /datum/language/proc/get_talkinto_msg_range(message) // if you yell, you'll be heard from two tiles over instead of one @@ -103,11 +102,11 @@ if(!speaker_mask) speaker_mask = speaker.name - var/msg = "[name], [speaker_mask] [format_message(message, get_spoken_verb(message))]" + var/msg = "[name], [speaker_mask] [get_spoken_verb(message)], [format_message(message)]" for(var/mob/player in GLOB.player_list) if(istype(player,/mob/dead) && follow) - var/msg_dead = "[name], [speaker_mask] ([ghost_follow_link(speaker, ghost=player)]) [format_message(message, get_spoken_verb(message))]" + var/msg_dead = "[name], [speaker_mask] ([ghost_follow_link(speaker, ghost=player)]) [get_spoken_verb(message)], [format_message(message)]" to_chat(player, msg_dead) continue @@ -135,10 +134,10 @@ key = "" flags = RESTRICTED|NONGLOBAL|INNATE|NO_TALK_MSG|NO_STUTTER -/datum/language/noise/format_message(message, verb) +/datum/language/noise/format_message(message) return "[message]" -/datum/language/noise/format_message_radio(message, verb) +/datum/language/noise/format_message_radio(message) return "[message]" /datum/language/noise/get_talkinto_msg_range(message) @@ -635,10 +634,9 @@ // Language handling. /mob/proc/add_language(language) - var/datum/language/new_language = GLOB.all_languages[language] - if(!istype(new_language) || new_language in languages) + if(!istype(new_language) || (new_language in languages)) return FALSE languages |= new_language @@ -657,8 +655,7 @@ // Can we speak this language, as opposed to just understanding it? /mob/proc/can_speak_language(datum/language/speaking) - - return (universal_speak || (speaking && speaking.flags & INNATE) || speaking in languages) + return universal_speak || (speaking && speaking.flags & INNATE) || (speaking in languages) //TBD /mob/proc/check_lang_data() diff --git a/code/modules/mob/living/autohiss.dm b/code/modules/mob/living/autohiss.dm index df1bde34306..286901baf5c 100644 --- a/code/modules/mob/living/autohiss.dm +++ b/code/modules/mob/living/autohiss.dm @@ -1,4 +1,4 @@ -/mob/living/proc/handle_autohiss(message, datum/language/L) +/mob/proc/handle_autohiss(message, datum/language/L) return message // no autohiss at this level /mob/living/carbon/human/handle_autohiss(message, datum/language/L) diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index f2a15119156..eb4750c9780 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -29,7 +29,7 @@ to_chat(usr, "You cannot speak, as your internal speaker is turned off.") . = FALSE -/mob/living/carbon/brain/handle_message_mode(var/message_mode, var/message, var/verb, var/speaking, var/used_radios) +/mob/living/carbon/brain/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) switch(message_mode) if("headset") var/radio_worked = 0 // If any of the radios our brainmob could use functioned, this is set true so that we don't use any others @@ -39,12 +39,12 @@ if(!radio_worked && c.mecha) var/obj/mecha/metalgear = c.mecha if(metalgear.radio) - radio_worked = metalgear.radio.talk_into(src, message, message_mode, verb, speaking) + radio_worked = metalgear.radio.talk_into(src, message_pieces, message_mode, verb) else if(!radio_worked && c.radio) - radio_worked = c.radio.talk_into(src, message, message_mode, verb, speaking) + radio_worked = c.radio.talk_into(src, message_pieces, message_mode, verb) return radio_worked if("whisper") - whisper_say(message, speaking) + whisper_say(message_pieces) return 1 else return 0 diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index d4c14bda32a..b858cca130a 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -233,7 +233,6 @@ var/obj/item/organ/external/picked = pick(parts) if(picked.receive_damage(brute, burn, sharp, updating_health)) UpdateDamageIcon() - speech_problem_flag = 1 //Heal MANY external organs, in random order @@ -256,7 +255,6 @@ if(updating_health) updatehealth("heal overall damage") - speech_problem_flag = 1 if(update) UpdateDamageIcon() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 96693a0d086..22a9d40f6fd 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -42,8 +42,6 @@ var/global/default_martial_art = new/datum/martial_art var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() - var/speech_problem_flag = 0 - var/datum/personal_crafting/handcrafting var/datum/martial_art/martial_art = null diff --git a/code/modules/mob/living/carbon/human/interactive/interactive.dm b/code/modules/mob/living/carbon/human/interactive/interactive.dm index 3eb2f4e65fd..bb9ae87bdc7 100644 --- a/code/modules/mob/living/carbon/human/interactive/interactive.dm +++ b/code/modules/mob/living/carbon/human/interactive/interactive.dm @@ -580,14 +580,14 @@ return FALSE saveVoice() -/mob/living/carbon/human/interactive/hear_say(message, verb = "says", datum/language/language = null, italics = 0, mob/speaker = null, sound/speech_sound, sound_vol) +/mob/living/carbon/human/interactive/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol) if(!istype(speaker, /mob/living/carbon/human/interactive)) - knownStrings |= html_decode(message) + knownStrings |= html_decode(multilingual_to_message(message_pieces)) ..() -/mob/living/carbon/human/interactive/hear_radio(message, verb = "says", datum/language/language=null, part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target) +/mob/living/carbon/human/interactive/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target) if(!istype(speaker, /mob/living/carbon/human/interactive)) - knownStrings |= html_decode(message) + knownStrings |= html_decode(multilingual_to_message(message_pieces)) ..() /mob/living/carbon/human/interactive/proc/doProcess() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index ce1d9a7f56e..8d0261528ed 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -71,7 +71,6 @@ drop_item() emote("cough") if(disabilities & TOURETTES) - speech_problem_flag = 1 if((prob(10) && paralysis <= 1)) Stun(10) switch(rand(1, 3)) @@ -86,12 +85,10 @@ animate(pixel_x = initial(pixel_x) , pixel_y = initial(pixel_y), time = 1) if(disabilities & NERVOUS) - speech_problem_flag = 1 if(prob(10)) Stuttering(10) if(getBrainLoss() >= 60 && stat != DEAD) - speech_problem_flag = 1 if(prob(3)) var/list/s1 = list("IM A [pick("PONY","LIZARD","taJaran","kitty","Vulpakin","drASK","BIRDIE","voxxie","race car","combat meCH","SPESSSHIP")] [pick("NEEEEEEIIIIIIIIIGH","sKREEEEEE","MEOW","NYA~","rawr","Barkbark","Hissssss","vROOOOOM","pewpew","choo Choo")]!", "without oxigen blob don't evoluate?", @@ -158,7 +155,6 @@ if(!gene.block) continue if(gene.is_active(src)) - speech_problem_flag = 1 gene.OnMobLife(src) if(!ignore_gene_stability && gene_stability < GENETIC_DAMAGE_STAGE_1) var/instability = DEFAULT_GENE_STABILITY - gene_stability @@ -741,7 +737,6 @@ stat = UNCONSCIOUS else if(sleeping) - speech_problem_flag = 1 stat = UNCONSCIOUS @@ -1003,29 +998,6 @@ This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag. */ - -/mob/living/carbon/human/handle_silent() - if(..()) - speech_problem_flag = 1 - return silent - -/mob/living/carbon/human/handle_slurring() - if(..()) - speech_problem_flag = 1 - return slurring - -/mob/living/carbon/human/handle_stunned() - if(..()) - speech_problem_flag = 1 - return stunned - -/mob/living/carbon/human/handle_stuttering() - if(..()) - speech_problem_flag = 1 - return stuttering - - - /mob/living/carbon/human/proc/can_heartattack() if(NO_BLOOD in dna.species.species_traits) return FALSE diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 70cab4751e2..8d9355c1f26 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -107,65 +107,58 @@ /mob/living/carbon/human/proc/GetSpecialVoice() return special_voice -/mob/living/carbon/human/handle_speech_problems(var/message, var/verb) - var/list/returns[3] - var/speech_problem_flag = 0 +/mob/living/carbon/human/handle_speech_problems(list/message_pieces, var/verb) var/span = "" if(mind) span = mind.speech_span - if(silent || (disabilities & MUTE)) - message = "" - speech_problem_flag = 1 + if((COMIC in mutations) \ + || (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \ + || istype(get_item_by_slot(slot_wear_mask), /obj/item/clothing/mask/gas/voice/clown)) + span = "sans" + + var/list/parent = ..() + verb = parent["verb"] - if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) - var/obj/item/clothing/mask/horsehead/hoers = wear_mask - if(hoers.voicechange) - message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") - verb = pick("whinnies","neighs", "says") - speech_problem_flag = 1 + for(var/datum/multilingual_say_piece/S in message_pieces) + if(S.speaking && S.speaking.flags & NO_STUTTER) + continue - if(dna) - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - message = gene.OnSay(src,message) - speech_problem_flag = 1 + if(silent || (disabilities & MUTE)) + S.message = "" - if(message != "") - var/list/parent = ..() - message = parent[1] - verb = parent[2] - if(parent[3]) - speech_problem_flag = 1 + if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) + var/obj/item/clothing/mask/horsehead/hoers = wear_mask + if(hoers.voicechange) + S.message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") + verb = pick("whinnies", "neighs", "says") + + if(dna) + for(var/datum/dna/gene/gene in dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + S.message = gene.OnSay(src, S.message) var/braindam = getBrainLoss() if(braindam >= 60) - speech_problem_flag = 1 - if(prob(braindam/4)) - message = stutter(message) + if(prob(braindam / 4)) + S.message = stutter(S.message) verb = "gibbers" if(prob(braindam)) - message = uppertext(message) + S.message = uppertext(S.message) verb = "yells loudly" - if((COMIC in mutations) || (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) || istype(get_item_by_slot(slot_wear_mask), /obj/item/clothing/mask/gas/voice/clown)) - span = "sans" + if(span) + S.message = "[S.message]" + return list("verb" = verb) - if(span) - message = "[message]" - returns[1] = message - returns[2] = verb - returns[3] = speech_problem_flag - return returns - -/mob/living/carbon/human/handle_message_mode(var/message_mode, var/message, var/verb, var/speaking, var/used_radios) +/mob/living/carbon/human/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) switch(message_mode) if("intercom") for(var/obj/item/radio/intercom/I in view(1, src)) spawn(0) - I.talk_into(src, message, null, verb, speaking) + I.talk_into(src, message_pieces, null, verb) used_radios += I if("headset") @@ -173,13 +166,13 @@ if(isradio(l_ear)) R = l_ear used_radios += R - if(R.talk_into(src, message, null, verb, speaking)) + if(R.talk_into(src, message_pieces, null, verb)) return if(isradio(r_ear)) R = r_ear used_radios += R - if(R.talk_into(src, message, null, verb, speaking)) + if(R.talk_into(src, message_pieces, null, verb)) return if("right ear") @@ -190,7 +183,7 @@ R = r_hand if(R) used_radios += R - R.talk_into(src, message, null, verb, speaking) + R.talk_into(src, message_pieces, null, verb) if("left ear") var/obj/item/radio/R @@ -200,21 +193,21 @@ R = l_hand if(R) used_radios += R - R.talk_into(src, message, null, verb, speaking) + R.talk_into(src, message_pieces, null, verb) if("whisper") - whisper_say(message, speaking) + whisper_say(message_pieces) return 1 else if(message_mode) if(isradio(l_ear)) used_radios += l_ear - if(l_ear.talk_into(src, message, message_mode, verb, speaking)) + if(l_ear.talk_into(src, message_pieces, message_mode, verb)) return if(isradio(r_ear)) used_radios += r_ear - if(r_ear.talk_into(src, message, message_mode, verb, speaking)) + if(r_ear.talk_into(src, message_pieces, message_mode, verb)) return /mob/living/carbon/human/handle_speech_sound() diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index 4e74688e10f..4f7270bfcba 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -3,19 +3,11 @@ message = trim_strip_html_properly(message) //bit of duplicate code, acceptable because the workaround would be annoying //parse the language code and consume it - var/datum/language/speaking = parse_language(message) - if(speaking) - message = copytext(message, 2 + length(speaking.key)) - else - speaking = get_default_language() - - // This is broadcast to all mobs with the language, - // irrespective of distance or anything else. - if(speaking && (speaking.flags & HIVEMIND)) - speaking.broadcast(src,trim(message)) + var/list/message_pieces = parse_languages(message) + if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages + var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting + S.speaking.broadcast(src, S.message) return 1 message = trim_left(message) - message = handle_autohiss(message, speaking) - - whisper_say(message, speaking) \ No newline at end of file + whisper_say(message, message_pieces) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/slime/say.dm b/code/modules/mob/living/carbon/slime/say.dm index 04416e945ee..2096da01360 100644 --- a/code/modules/mob/living/carbon/slime/say.dm +++ b/code/modules/mob/living/carbon/slime/say.dm @@ -14,16 +14,16 @@ return 1 return ..() -/mob/living/carbon/slime/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/living/carbon/slime/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) if(speaker in Friends) speech_buffer = list() speech_buffer.Add(speaker) - speech_buffer.Add(lowertext(html_decode(message))) + speech_buffer.Add(lowertext(html_decode(multilingual_to_message(message_pieces)))) ..() -/mob/living/carbon/slime/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/atom/follow_target) +/mob/living/carbon/slime/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/atom/follow_target) if(speaker in Friends) speech_buffer = list() speech_buffer.Add(speaker) - speech_buffer.Add(lowertext(html_decode(message))) + speech_buffer.Add(lowertext(html_decode(multilingual_to_message(message_pieces)))) ..() diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index cb08ebff021..b32925badb4 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -54,51 +54,48 @@ proc/get_radio_key_from_channel(var/channel) /mob/living/proc/binarycheck() return FALSE -/mob/living/proc/get_default_language() +/mob/proc/get_default_language() + return null + +/mob/living/get_default_language() return default_language -/mob/living/proc/handle_speech_problems(var/message, var/verb) - var/list/returns[3] - var/speech_problem_flag = 0 +/mob/living/proc/handle_speech_problems(list/message_pieces, var/verb) var/robot = isSynthetic() + for(var/datum/multilingual_say_piece/S in message_pieces) + if(S.speaking && S.speaking.flags & NO_STUTTER) + continue + if((HULK in mutations) && health >= 25) + S.message = "[uppertext(S.message)]!!!" + verb = pick("yells", "roars", "hollers") - if((HULK in mutations) && health >= 25 && length(message)) - message = "[uppertext(message)]!!!" - verb = pick("yells","roars","hollers") - speech_problem_flag = 1 + if(slurring) + if(robot) + S.message = slur(S.message, list("@", "!", "#", "$", "%", "&", "?")) + else + S.message = slur(S.message) + verb = "slurs" - if(slurring) - if(robot) - message = slur(message, list("@", "!", "#", "$", "%", "&", "?")) - else - message = slur(message) - verb = "slurs" - speech_problem_flag = 1 - if(stuttering) - if(robot) - message = robostutter(message) - else - message = stutter(message) - verb = "stammers" - speech_problem_flag = 1 - if(cultslurring) - message = cultslur(message) - verb = "slurs" - speech_problem_flag = 1 - if(!IsVocal()) - message = "" - speech_problem_flag = 1 + if(stuttering) + if(robot) + S.message = robostutter(S.message) + else + S.message = stutter(S.message) + verb = "stammers" - returns[1] = message - returns[2] = verb - returns[3] = speech_problem_flag - return returns + if(cultslurring) + S.message = cultslur(S.message) + verb = "slurs" -/mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios) + if(!IsVocal()) + S.message = "" + return list("verb" = verb) + +/mob/living/proc/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) switch(message_mode) if("whisper") //all mobs can whisper by default - whisper_say(message, speaking) + whisper_say(message_pieces) return 1 return 0 @@ -109,7 +106,7 @@ proc/get_radio_key_from_channel(var/channel) return returns -/mob/living/say(var/message, var/datum/language/speaking = null, var/verb = "says", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) +/mob/living/say(var/message, var/verb = "says", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) if(client) if(client.prefs.muted & MUTE_IC) to_chat(src, "You cannot speak in IC (Muted).") @@ -138,19 +135,17 @@ proc/get_radio_key_from_channel(var/channel) message = trim_left(message) //parse the language code and consume it - if(!speaking) - speaking = parse_language(message) - if(speaking) - message = copytext(message, 2 + length(speaking.key)) - else - speaking = get_default_language() - - // This is broadcast to all mobs with the language, - // irrespective of distance or anything else. - if(speaking && (speaking.flags & HIVEMIND)) - speaking.broadcast(src,trim(message)) + var/list/message_pieces = parse_languages(message) + if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages + var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting + S.speaking.broadcast(src, S.message) return 1 + + if(!LAZYLEN(message_pieces)) + log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]")) + return 0 + if(message_mode == "cords") if(iscarbon(src)) var/mob/living/carbon/C = src @@ -160,7 +155,8 @@ proc/get_radio_key_from_channel(var/channel) V.speak_with(message) //words come before actions return 1 - verb = say_quote(message, speaking) + var/datum/multilingual_say_piece/first_piece = message_pieces[1] + verb = say_quote(message, first_piece.speaking) if(is_muzzled()) var/obj/item/clothing/mask/muzzle/G = wear_mask @@ -168,25 +164,19 @@ proc/get_radio_key_from_channel(var/channel) to_chat(src, "You're muzzled and cannot speak!") return else if(G.mute == MUZZLE_MUTE_MUFFLE) - message = muffledspeech(message) + muffledspeech_all(message_pieces) verb = "mumbles" - message = trim_left(message) + if(!ignore_speech_problems) + var/list/hsp = handle_speech_problems(message_pieces, verb) + verb = hsp["verb"] - message = handle_autohiss(message, speaking) - - if(!ignore_speech_problems && (speaking && !(speaking.flags & NO_STUTTER))) - var/list/handle_s = handle_speech_problems(message, verb) - message = handle_s[1] - verb = handle_s[2] - - if(!message || message == "") - return 0 var/list/used_radios = list() - if(handle_message_mode(message_mode, message, verb, speaking, used_radios)) + if(handle_message_mode(message_mode, message_pieces, verb, used_radios)) return 1 + var/list/handle_v = handle_speech_sound() var/sound/speech_sound = handle_v[1] var/sound_vol = handle_v[2] @@ -198,11 +188,12 @@ proc/get_radio_key_from_channel(var/channel) if(used_radios.len) italics = 1 message_range = 1 - if(speaking) - message_range = speaking.get_talkinto_msg_range(message) + if(first_piece.speaking) + message_range = first_piece.speaking.get_talkinto_msg_range(message) + var/msg - if(!speaking || !(speaking.flags & NO_TALK_MSG)) - msg = "\The [src] talks into \the [used_radios[1]]" + if(!first_piece.speaking || !(first_piece.speaking.flags & NO_TALK_MSG)) + msg = "[src] talks into [used_radios[1]]" if(msg) for(var/mob/living/M in hearers(5, src) - src) @@ -213,16 +204,6 @@ proc/get_radio_key_from_channel(var/channel) var/turf/T = get_turf(src) - - //handle nonverbal and sign languages here - if(speaking) - if(speaking.flags & NONVERBAL) - if(prob(30)) - custom_emote(1, "[pick(speaking.signlang_verb)].") - - if(speaking.flags & SIGNLANG) - return say_signlang(message, pick(speaking.signlang_verb), speaking) - var/list/listening = list() var/list/listening_obj = list() @@ -268,7 +249,7 @@ proc/get_radio_key_from_channel(var/channel) var/speech_bubble_test = say_test(message) for(var/mob/M in listening) - M.hear_say(message, verb, speaking, italics, src, speech_sound, sound_vol) + M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol) if(M.client) speech_bubble_recipients.Add(M.client) spawn(0) @@ -281,19 +262,13 @@ proc/get_radio_key_from_channel(var/channel) for(var/obj/O in listening_obj) spawn(0) if(O) //It's possible that it could be deleted in the meantime. - O.hear_talk(src, message, verb, speaking) + O.hear_talk(src, message_pieces, verb) //Log of what we've said, plain message, no spans or junk say_log += message - log_say(message, src) return 1 -/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/datum/language/language) - for(var/mob/O in viewers(src, null)) - O.hear_signlang(message, verb, language, src) - return 1 - /obj/effect/speech_bubble var/mob/parent @@ -338,28 +313,19 @@ proc/get_radio_key_from_channel(var/channel) message = trim_strip_html_properly(message) //parse the language code and consume it - var/datum/language/speaking = parse_language(message) - if(speaking) - message = copytext(message, 2 + length(speaking.key)) - else - speaking = get_default_language() - - // This is broadcast to all mobs with the language, - // irrespective of distance or anything else. - if(speaking && (speaking.flags & HIVEMIND)) - speaking.broadcast(src,trim(message)) + var/list/message_pieces = parse_languages(message) + if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages + var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting + S.speaking.broadcast(src, S.message) return 1 - message = trim_left(message) - message = handle_autohiss(message, speaking) - - whisper_say(message, speaking) + whisper_say(message_pieces) // for weird circumstances where you're inside an atom that is also you, like pai's /mob/living/proc/get_whisper_loc() return src -/mob/living/proc/whisper_say(var/message, var/datum/language/speaking = null, var/verb="whispers") +/mob/living/proc/whisper_say(list/message_pieces, verb = "whispers") if(client) if(client.prefs.muted & MUTE_IC) to_chat(src, "You cannot speak in IC (Muted).") @@ -367,7 +333,7 @@ proc/get_radio_key_from_channel(var/channel) if(stat) if(stat == DEAD) - return say_dead(message) + return say_dead(message_pieces) return if(is_muzzled()) @@ -382,37 +348,29 @@ proc/get_radio_key_from_channel(var/channel) var/watching_range = 5 var/italics = 1 var/adverb_added = FALSE - var/not_heard //the message displayed to people who could not hear the whispering - if(speaking) - if(speaking.whisper_verb) - verb = speaking.whisper_verb + + var/datum/multilingual_say_piece/first_piece = message_pieces[1] + if(first_piece.speaking) + if(first_piece.speaking.whisper_verb) + verb = first_piece.speaking.whisper_verb not_heard = "[verb] something" else var/adverb = pick("quietly", "softly") adverb_added = TRUE - verb = "[speaking.speech_verb] [adverb]" - not_heard = "[speaking.speech_verb] something [adverb]" + verb = "[first_piece.speaking.speech_verb] [adverb]" + not_heard = "[first_piece.speaking.speech_verb] something [adverb]" else not_heard = "[verb] something" - message = trim(message) - - var/speech_problem_flag = 0 - var/list/handle_s = handle_speech_problems(message, verb) - message = handle_s[1] - verb = handle_s[2] - speech_problem_flag = handle_s[3] + var/list/hsp = handle_speech_problems(message_pieces, verb) + verb = hsp["verb"] if(verb == "yells loudly") verb = "slurs emphatically" - - else if(speech_problem_flag && !adverb_added) + else if(!adverb_added) var/adverb = pick("quietly", "softly") verb = "[verb] [adverb]" - if(!message) - return - var/atom/whisper_loc = get_whisper_loc() var/list/listening = hear(message_range, whisper_loc) listening |= src @@ -450,7 +408,7 @@ proc/get_radio_key_from_channel(var/channel) for(var/obj/O in view(message_range, whisper_loc)) spawn(0) if(O) - O.hear_talk(src, message, verb, speaking) + O.hear_talk(src, message_pieces, verb) var/list/eavesdropping = hearers(eavesdropping_range, whisper_loc) eavesdropping -= src @@ -463,17 +421,17 @@ proc/get_radio_key_from_channel(var/channel) //now mobs var/list/speech_bubble_recipients = list() - var/speech_bubble_test = say_test(message) + var/speech_bubble_test = say_test(multilingual_to_message(message_pieces)) for(var/mob/M in listening) - M.hear_say(message, verb, speaking, italics, src) + M.hear_say(message_pieces, verb, italics, src) if(M.client) speech_bubble_recipients.Add(M.client) if(eavesdropping.len) - var/new_message = stars(message) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. + stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. for(var/mob/M in eavesdropping) - M.hear_say(new_message, verb, speaking, italics, src) + M.hear_say(message_pieces, verb, italics, src) if(M.client) speech_bubble_recipients.Add(M.client) @@ -487,7 +445,7 @@ proc/get_radio_key_from_channel(var/channel) for(var/mob/M in watching) M.show_message(rendered, 2) - log_whisper(message, src) + log_whisper(multilingual_to_message(message_pieces), src) return 1 /mob/living/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 67bbc6c5388..a6400142086 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -1184,19 +1184,11 @@ var/list/ai_verbs_default = list( var/list/viewscale = getviewsize(client.view) return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5) -/mob/living/silicon/ai/proc/relay_speech(mob/living/M, text, verb, datum/language/speaking) - if(!say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad. - if(speaking) - text = speaking.scramble(text) - else - text = stars(text) +/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb) + var/message = combine_message(message_pieces, verb, M) 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 - if(speaking) - rendered = "Relayed Speech: [name_used] [speaking.format_message(text, verb)]" - else - rendered = "Relayed Speech: [name_used] [verb], \"[text]\"" + var/rendered = "Relayed Speech: [name_used] [message]" show_message(rendered, 2) /mob/living/silicon/ai/proc/malfhacked(obj/machinery/power/apc/apc) diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index 1037ec7fe61..a53d346fab1 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -141,6 +141,6 @@ acceleration = !acceleration to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") -/mob/camera/aiEye/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/camera/aiEye/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) if(relay_speech) - ai.relay_speech(speaker, message, verb, language) + ai.relay_speech(speaker, message_pieces, verb) diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 431cf44a331..6b1306f94d1 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -3,7 +3,7 @@ */ -/mob/living/silicon/ai/handle_track(var/message, var/verb = "says", var/datum/language/language, var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) +/mob/living/silicon/ai/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) if(hard_to_hear) return diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 76ac7ab9886..2ec1505741b 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -17,7 +17,6 @@ ventcrawler = 2 magpulse = 1 mob_size = MOB_SIZE_SMALL - default_language = "Drone" // We need to keep track of a few module items so we don't need to do list operations // every time we need them. These get set in New() after the module is chosen. @@ -38,7 +37,6 @@ /mob/living/silicon/robot/drone/New() - ..() remove_language("Robot Talk") diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm index 741207d6f2d..7b51952a685 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -1,15 +1,14 @@ -/mob/living/silicon/robot/drone/say(var/message, var/datum/language/speaking = null) +/mob/living/silicon/robot/drone/say(var/message) if(copytext(message, 1, 2) == "*") return emote(copytext(message, 2)) - if(!speaking) - speaking = parse_language(message) - if(!speaking) - speaking = istype(get_default_language(), /datum/language) ? get_default_language() : GLOB.all_languages[get_default_language()] - message = speaking.key + " " + message; // Prepend key to prevent the message from getting trimmed - if(speaking) - return ..() + return ..() -/mob/living/silicon/robot/drone/whisper_say(var/message, var/datum/language/speaking = null, var/verb="whispers") - say(message) //drones do not get to whisper, only speak normally - return 1 \ No newline at end of file +/mob/living/silicon/robot/drone/whisper_say(list/message_pieces) + say(multilingual_to_message(message_pieces)) //drones do not get to whisper, only speak normally + return 1 + +/mob/living/silicon/robot/drone/get_default_language() + if(default_language) + return default_language + return GLOB.all_languages["Drone"] \ No newline at end of file diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 48297dbb401..17a7e67adf7 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -1,9 +1,9 @@ -/mob/living/silicon/handle_message_mode(message_mode, message, verb, speaking, used_radios) - log_say(message, src) +/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + log_say(multilingual_to_message(message_pieces), src) if(..()) return 1 -/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios) +/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) if(..()) return 1 if(message_mode) @@ -13,14 +13,14 @@ return 0 if(message_mode == "general") message_mode = null - return radio.talk_into(src,message,message_mode,verb,speaking) + return radio.talk_into(src,message_pieces,message_mode,verb) -/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios) +/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) if(..()) return 1 if(message_mode == "department") used_radios += aiRadio - return holopad_talk(message, verb, speaking) + return holopad_talk(message_pieces, verb) else if(message_mode) used_radios += aiRadio if(aiRadio.disabledAi || aiRestorePowerRoutine || stat) @@ -28,19 +28,19 @@ return 0 if(message_mode == "general") message_mode = null - return aiRadio.talk_into(src,message,message_mode,verb,speaking) + return aiRadio.talk_into(src, message_pieces, message_mode, verb) -/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios) +/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) if(..()) return 1 else if(message_mode == "whisper") - whisper_say(message, speaking) + whisper_say(message_pieces) return 1 else if(message_mode) if(message_mode == "general") message_mode = null used_radios += radio - return radio.talk_into(src,message,message_mode,verb,speaking) + return radio.talk_into(src, message_pieces, message_mode, verb) /mob/living/silicon/say_quote(var/text) var/ending = copytext(text, length(text)) @@ -70,41 +70,14 @@ return ..() //For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(var/message, verb, datum/language/speaking) - log_say("(HPAD) [message]", src) - - message = trim(message) - - if(!message) - return +/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb) + log_say("(HPAD) [multilingual_to_message(message_pieces)]", src) var/obj/machinery/hologram/holopad/T = current if(istype(T) && T.masters[src]) - - //Human-like, sorta, heard by those who understand humans. - var/rendered_a - //Speach distorted, heard by those who do not understand AIs. - var/message_stars = stars(message) - var/rendered_b - - if(speaking) - rendered_a = "[name] [speaking.format_message(message, verb)]" - rendered_b = "[voice_name] [speaking.format_message(message_stars, verb)]" - to_chat(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]\"" - to_chat(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) - else//If they do not. - M.show_message(rendered_b, 2) - /*Radios "filter out" this conversation channel so we don't need to account for them. - This is another way of saying that we won't bother dealing with them.*/ + M.hear_holopad_talk(message_pieces, verb, src) + to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]") else to_chat(src, "No holopad connected.") return diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index a60ae1d323e..c8e43a936d2 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -704,21 +704,21 @@ available_channels = list(":e") ..() -/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, var/message, var/verb, var/speaking, var/used_radios) +/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) if(message_mode && istype(ears)) - ears.talk_into(src, message, message_mode, verb, speaking) + ears.talk_into(src, message_pieces, message_mode, verb) used_radios += ears -/mob/living/simple_animal/parrot/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null) +/mob/living/simple_animal/parrot/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null) if(speaker != src && prob(50)) - parrot_hear(html_decode(message)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) ..() -/mob/living/simple_animal/parrot/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) +/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) if(speaker != src && prob(50)) - parrot_hear(html_decode(message)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) ..() diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm index 94769baf94b..daf058bb41b 100644 --- a/code/modules/mob/living/update_status.dm +++ b/code/modules/mob/living/update_status.dm @@ -44,7 +44,7 @@ . = !(disabilities & DEAF) // Whether the mob is able to see -// `information_only` is for stuff that's purely informational - like understanding nonverbal languages, or blindness overlays +// `information_only` is for stuff that's purely informational - like blindness overlays // This flag exists because certain things like angel statues expect this to be false for dead people /mob/living/has_vision(information_only = FALSE) return (information_only && stat == DEAD) || !(eye_blind || (disabilities & BLIND) || stat) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 659a50880a0..46b7d1141b8 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -177,6 +177,10 @@ p++ return t +/proc/stars_all(list/message_pieces, pr) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = stars(S.message, pr) + /proc/slur(phrase, var/list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters phrase = html_decode(phrase) var/leng=lentext(phrase) @@ -266,6 +270,11 @@ return returntext +/proc/Gibberish_all(list/message_pieces, p) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = Gibberish(S.message, p) + + proc/muffledspeech(phrase) phrase = html_decode(phrase) var/leng=lentext(phrase) @@ -284,6 +293,10 @@ proc/muffledspeech(phrase) counter-=1 return newphrase +/proc/muffledspeech_all(list/message_pieces) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = muffledspeech(S.message) + /proc/shake_camera(mob/M, duration, strength=1) if(!M || !M.client || M.shakecamera) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index a1236e9c719..aeb6d8f14bf 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -132,17 +132,77 @@ return null -//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) - var/prefix = copytext(message, 1, 2) - if(length(message) >= 1 && prefix == "!") - return GLOB.all_languages["Noise"] +/datum/multilingual_say_piece + var/datum/language/speaking = null + var/message = "" - if(length(message) >= 2) - var/language_prefix = trim_right(lowertext(copytext(message, 1 ,4))) - var/datum/language/L = GLOB.language_keys[language_prefix] - if(can_speak_language(L)) - return L +/datum/multilingual_say_piece/New(datum/language/new_speaking, new_message) + . = ..() + speaking = new_speaking + if(new_message) + message = new_message - return null +/mob/proc/find_valid_prefixes(message) + var/list/prefixes = list() // [["Common", start, end], ["Gutter", start, end]] + for(var/i in 1 to length(message)) + var/selection = trim_right(lowertext(copytext(message, i, i + 3))) + var/datum/language/L = GLOB.language_keys[selection] + if(L != null && can_speak_language(L)) // What the fuck... remove the L != null check if you ever find out what the fuck is adding `null` to the languages list on absolutely random mobs... seriously what the hell... + prefixes[++prefixes.len] = list(L, i, i + length(selection)) + else if(!L && i == 1) + prefixes[++prefixes.len] = list(get_default_language(), i, i) + else + return prefixes + +/proc/strip_prefixes(message) + . = "" + var/last_index = 1 + for(var/i in 1 to length(message)) + var/selection = trim_right(lowertext(copytext(message, i, i + 3))) + var/datum/language/L = GLOB.language_keys[selection] + if(L) + . += copytext(message, last_index, i) + last_index = i + 3 + if(i + 1 > length(message)) + . += copytext(message, last_index) + +// this returns a structured message with language sections +// list(/datum/multilingual_say_piece(common, "hi"), /datum/multilingual_say_piece(farwa, "squik"), /datum/multilingual_say_piece(common, "meow!")) +/mob/proc/parse_languages(message) + . = list() + + // Noise language is a snowflake + if(copytext(message, 1, 2) == "!" && length(message) > 1) + return list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], trim(strip_prefixes(copytext(message, 2))))) + + // Scan the message for prefixes + var/list/prefix_locations = find_valid_prefixes(message) + if(!LAZYLEN(prefix_locations)) // There are no prefixes... or at least, no _valid_ prefixes. + . += new /datum/multilingual_say_piece(get_default_language(), trim(strip_prefixes(message))) // So we'll just strip those pesky things and still make the message. + + for(var/i in 1 to length(prefix_locations)) + var/current = prefix_locations[i] // ["Common", keypos] + + // 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) + . = new /datum/multilingual_say_piece(L, trim(strip_prefixes(message))) + break + + if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang + var/spoke_message = handle_autohiss(trim(copytext(message, current[3])), L) + . += new /datum/multilingual_say_piece(current[1], spoke_message) + else + var/next = prefix_locations[i + 1] // We look ahead at the next message to see where we need to stop. + var/spoke_message = handle_autohiss(trim(copytext(message, current[3], next[2])), L) + . += new /datum/multilingual_say_piece(current[1], spoke_message) + +/* These are here purely because it would be hell to try to convert everything over to using the multi-lingual system at once */ +/proc/message_to_multilingual(message, datum/language/speaking = null) + . = list(new /datum/multilingual_say_piece(speaking, message)) + +/proc/multilingual_to_message(list/message_pieces) + . = "" + for(var/datum/multilingual_say_piece/S in message_pieces) + . += S.message + " " + . = trim_right(.) \ No newline at end of file diff --git a/code/modules/paperwork/frames.dm b/code/modules/paperwork/frames.dm index 4203b5abfff..cbffb1cd117 100644 --- a/code/modules/paperwork/frames.dm +++ b/code/modules/paperwork/frames.dm @@ -297,10 +297,10 @@ toggle_tilt(usr) -/obj/structure/sign/picture_frame/hear_talk(mob/living/M as mob, msg) +/obj/structure/sign/picture_frame/hear_talk(mob/living/M as mob, list/message_pieces) ..() for(var/obj/O in contents) - O.hear_talk(M, msg) + O.hear_talk(M, message_pieces) /obj/structure/sign/picture_frame/hear_message(mob/living/M as mob, msg) ..() diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 3b37e551e0f..688bffa8ebc 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -541,7 +541,8 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s if(..(user, 1)) to_chat(user, "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active.") -/obj/item/videocam/hear_talk(mob/M as mob, msg) +/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces) + var/msg = multilingual_to_message(message_pieces) if(camera && on) if(get_dist(src, M) <= canhear_range) talk_into(M, msg) diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index e82dbda249d..db70bf255e6 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -245,9 +245,9 @@ if(assembly) assembly.on_found(finder) -/obj/item/reagent_containers/glass/beaker/hear_talk(mob/living/M, msg) +/obj/item/reagent_containers/glass/beaker/hear_talk(mob/living/M, list/message_pieces) if(assembly) - assembly.hear_talk(M, msg) + assembly.hear_talk(M, message_pieces) /obj/item/reagent_containers/glass/beaker/hear_message(mob/living/M, msg) if(assembly) diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index edbc32f36a1..4f4277980e0 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -181,9 +181,9 @@ if(rig) rig.Crossed(AM) -/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, msg) +/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, list/message_pieces) if(rig) - rig.hear_talk(M, msg) + rig.hear_talk(M, message_pieces) /obj/structure/reagent_dispensers/fueltank/hear_message(mob/living/M, msg) if(rig) diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index bb9c2d41fa1..715adb668ab 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -522,8 +522,8 @@ obj/spacepod/proc/add_equipment(mob/user, var/obj/item/spacepod_equipment/SPE, v to_chat(user, "You need an open hand to do that.") -/obj/spacepod/hear_talk/hear_talk(mob/M, var/msg) - cargo_hold.hear_talk(M, msg) +/obj/spacepod/hear_talk/hear_talk(mob/M, list/message_pieces) + cargo_hold.hear_talk(M, message_pieces) ..() /obj/spacepod/hear_message(mob/M, var/msg) diff --git a/code/modules/surgery/organs/subtypes/skrell.dm b/code/modules/surgery/organs/subtypes/skrell.dm index fd8a02f18d3..20eae88139c 100644 --- a/code/modules/surgery/organs/subtypes/skrell.dm +++ b/code/modules/surgery/organs/subtypes/skrell.dm @@ -52,8 +52,8 @@ pocket.emp_act(severity) ..() -/obj/item/organ/internal/headpocket/hear_talk(mob/living/M as mob, msg) - pocket.hear_talk(M, msg) +/obj/item/organ/internal/headpocket/hear_talk(mob/living/M as mob, list/message_pieces) + pocket.hear_talk(M, message_pieces) ..() /obj/item/organ/internal/headpocket/hear_message(mob/living/M as mob, msg)