From e56750481c528e4eac6a4d60df2bda7ddf293108 Mon Sep 17 00:00:00 2001 From: tigercat2000 Date: Sat, 24 Nov 2018 00:22:05 -0800 Subject: [PATCH] Multilingualism This commit adjusts the speech parsing system to allow for an infinite number of languages to be present in the same message. You can transition freely between any language you are able to speak simply by putting it's language key in the middle of the sentence. Honestly, this was a massive pain in the ass, and there's probably still broken stuff, even though I've spent around 8 hours testing and refining this. --- code/__DEFINES/language.dm | 2 - code/game/gamemodes/miniantags/borer/borer.dm | 15 +- code/game/machinery/hologram.dm | 8 +- code/game/machinery/telecomms/broadcaster.dm | 35 ++- code/game/machinery/telecomms/ntsl2.dm | 27 ++- .../machinery/telecomms/telecomunications.dm | 8 +- code/game/mecha/mecha.dm | 4 +- code/game/objects/items/devices/megaphone.dm | 6 +- .../objects/items/devices/radio/headset.dm | 6 +- .../game/objects/items/devices/radio/radio.dm | 25 +-- .../objects/items/devices/taperecorder.dm | 3 +- .../objects/items/devices/transfer_valve.dm | 4 +- .../items/weapons/grenades/chem_grenade.dm | 4 +- .../objects/items/weapons/storage/lockbox.dm | 2 +- .../objects/items/weapons/storage/secure.dm | 2 +- .../objects/items/weapons/storage/storage.dm | 4 +- code/game/objects/objs.dm | 2 +- code/modules/assembly/bomb.dm | 4 +- code/modules/assembly/holder.dm | 6 +- code/modules/assembly/voice.dm | 8 +- code/modules/clothing/masks/miscellaneous.dm | 4 +- code/modules/clothing/suits/storage.dm | 4 +- .../clothing/under/accessories/storage.dm | 4 +- code/modules/flufftext/Hallucination.dm | 6 +- .../mining/lavaland/loot/colossus_loot.dm | 4 +- code/modules/mob/dead/observer/say.dm | 2 +- code/modules/mob/hear_say.dm | 171 ++++++++------- code/modules/mob/language.dm | 23 +- code/modules/mob/living/autohiss.dm | 2 +- code/modules/mob/living/carbon/brain/say.dm | 8 +- .../mob/living/carbon/human/human_damage.dm | 2 - .../mob/living/carbon/human/human_defines.dm | 2 - .../carbon/human/interactive/interactive.dm | 8 +- code/modules/mob/living/carbon/human/life.dm | 28 --- code/modules/mob/living/carbon/human/say.dm | 87 ++++---- .../mob/living/carbon/human/whisper.dm | 18 +- code/modules/mob/living/carbon/slime/say.dm | 8 +- code/modules/mob/living/say.dm | 202 +++++++----------- code/modules/mob/living/silicon/ai/ai.dm | 14 +- .../mob/living/silicon/ai/freelook/eye.dm | 4 +- code/modules/mob/living/silicon/ai/say.dm | 2 +- .../mob/living/silicon/robot/drone/drone.dm | 2 - .../living/silicon/robot/drone/drone_say.dm | 21 +- code/modules/mob/living/silicon/say.dm | 55 ++--- .../mob/living/simple_animal/parrot.dm | 12 +- code/modules/mob/living/update_status.dm | 2 +- code/modules/mob/mob_helpers.dm | 13 ++ code/modules/mob/say.dm | 84 ++++++-- code/modules/paperwork/frames.dm | 4 +- code/modules/paperwork/photography.dm | 3 +- .../reagent_containers/glass_containers.dm | 4 +- code/modules/reagents/reagent_dispenser.dm | 4 +- code/modules/spacepods/spacepod.dm | 4 +- .../modules/surgery/organs/subtypes/skrell.dm | 4 +- 54 files changed, 465 insertions(+), 525 deletions(-) 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)