diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 8bde73c238..313b5b0743 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -106,9 +106,10 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al for (var/language_name in all_languages) var/datum/language/L = all_languages[language_name] - language_keys[":[lowertext(L.key)]"] = L - language_keys[".[lowertext(L.key)]"] = L - language_keys["#[lowertext(L.key)]"] = L + if(!(L.flags & NONGLOBAL)) + language_keys[":[lowertext(L.key)]"] = L + language_keys[".[lowertext(L.key)]"] = L + language_keys["#[lowertext(L.key)]"] = L var/rkey = 0 paths = typesof(/datum/species)-/datum/species diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm index 14e1b5a30e..4f08fac88d 100644 --- a/code/game/gamemodes/heist/heist.dm +++ b/code/game/gamemodes/heist/heist.dm @@ -109,6 +109,7 @@ var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' vox.add_language("Vox-pidgin") vox.add_language("Galactic Common") vox.add_language("Tradeband") + vox.add_language("Noise") vox.h_style = "Short Vox Quills" vox.f_style = "Shaved" diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 66708db7f7..dc98e0ce7e 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -81,13 +81,17 @@ var/const/HOLOPAD_MODE = 0 /*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(mob/living/M, text, verb) +/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb, datum/language/speaking) if(M&&hologram&&master)//Master is mostly a safety in case lag hits or something. - if(!master.say_understands(M))//The AI will be able to understand most mobs talking through the holopad. + if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad. text = stars(text) 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 = "Holopad received, [name_used] [verb], \"[text]\"" + var/rendered + if(speaking) + rendered = "Holopad received, [name_used] [speaking.format_message(text, verb)]" + else + rendered = "Holopad received, [name_used] [verb], \"[text]\"" master.show_message(rendered, 2) return diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 291418f510..12078e0add 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -452,11 +452,11 @@ del(src) //BubbleWrap END -/obj/item/weapon/storage/hear_talk(mob/M as mob, text) +/obj/item/weapon/storage/hear_talk(mob/M as mob, text, verb, datum/language/speaking) for (var/atom/A in src) if(istype(A,/obj/)) var/obj/O = A - O.hear_talk(M, text) + O.hear_talk(M, text, verb, speaking) //Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area). //Returns -1 if the atom was not found on container. diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 44fc441926..ba696b6850 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -126,7 +126,7 @@ return -/obj/proc/hear_talk(mob/M as mob, text) +/obj/proc/hear_talk(mob/M as mob, text, verb, datum/language/speaking) if(talking_atom) talking_atom.catchMessage(text, M) /* diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 44c2c3b900..8ff522a858 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -293,11 +293,11 @@ else icon_state = icon_opened -/obj/structure/closet/hear_talk(mob/M as mob, text) +/obj/structure/closet/hear_talk(mob/M as mob, text, verb, datum/language/speaking) for (var/atom/A in src) if(istype(A,/obj/)) var/obj/O = A - O.hear_talk(M, text) + O.hear_talk(M, text, verb, speaking) /obj/structure/closet/attack_generic(var/mob/user, var/damage, var/attack_message = "destroys", var/wallbreaker) if(!damage || !wallbreaker) diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index a9a4f2cc25..82f7450981 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -211,11 +211,11 @@ return 1 -/obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg) +/obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg, verb, datum/language/speaking) if(a_right) - a_right.hear_talk(M,msg) + a_right.hear_talk(M,msg,verb,speaking) if(a_left) - a_left.hear_talk(M,msg) + a_left.hear_talk(M,msg,verb,speaking) diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm index 5e37fd240f..f34c9f00c3 100644 --- a/code/modules/clothing/suits/storage.dm +++ b/code/modules/clothing/suits/storage.dm @@ -24,8 +24,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, var/msg, verb, datum/language/speaking) + pockets.hear_talk(M, msg, verb, speaking) ..() //Jackets with buttons, used for labcoats, IA jackets, First Responder jackets, and brown jackets. diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm index 51290cf777..b70df2c84b 100644 --- a/code/modules/clothing/under/ties.dm +++ b/code/modules/clothing/under/ties.dm @@ -347,8 +347,8 @@ hold.emp_act(severity) ..() -/obj/item/clothing/tie/storage/hear_talk(mob/M, var/msg) - hold.hear_talk(M, msg) +/obj/item/clothing/tie/storage/hear_talk(mob/M, var/msg, verb, datum/language/speaking) + hold.hear_talk(M, msg, verb, speaking) ..() /obj/item/clothing/tie/storage/attack_self(mob/user as mob) diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 04fa4273a0..7765cda928 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -24,23 +24,19 @@ if(sleeping || stat == 1) hear_sleep(message) return - - var/style = "body" //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet. if (language && (language.flags & NONVERBAL)) if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src))) message = stars(message) - if(!say_understands(speaker,language)) - if(istype(speaker,/mob/living/simple_animal)) - var/mob/living/simple_animal/S = speaker - message = pick(S.speak) - else - message = stars(message) - - if(language) - style = language.colour + if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages + if(!say_understands(speaker,language)) + if(istype(speaker,/mob/living/simple_animal)) + var/mob/living/simple_animal/S = speaker + message = pick(S.speak) + else + message = stars(message) var/speaker_name = speaker.name if(istype(speaker, /mob/living/carbon/human)) @@ -61,12 +57,16 @@ message = "[message]" if(sdisabilities & DEAF || ear_deaf) - if(speaker == src) - src << "You cannot hear yourself speak!" - else - src << "[speaker_name][alt_name] talks but you cannot hear \him." + if(!language || !(language.flags & INNATE)) // INNATE is the flag for audible-emote-language, so we don't want to show an "x talks but you cannot hear them" message if it's set + if(speaker == src) + src << "You cannot hear yourself speak!" + else + src << "[speaker_name][alt_name] talks but you cannot hear \him." else - src << "[speaker_name][alt_name] [track][verb], \"[message]\"" + if(language) + src << "[speaker_name][alt_name] [track][language.format_message(message, verb)]" + else + src << "[speaker_name][alt_name] [track][verb], \"[message]\"" if (speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z)) var/turf/source = speaker? get_turf(speaker) : get_turf(src) src.playsound_local(source, speech_sound, sound_vol, 1) @@ -83,26 +83,22 @@ var/track = null - var/style = "body" - //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet. if (language && (language.flags & NONVERBAL)) if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src))) message = stars(message) - if(!say_understands(speaker,language)) - if(istype(speaker,/mob/living/simple_animal)) - var/mob/living/simple_animal/S = speaker - message = pick(S.speak) - else + if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages + if(!say_understands(speaker,language)) + if(istype(speaker,/mob/living/simple_animal)) + var/mob/living/simple_animal/S = speaker + message = pick(S.speak) + else + message = stars(message) + + if(hard_to_hear) message = stars(message) - if(language) - style = language.colour - - if(hard_to_hear) - message = stars(message) - var/speaker_name = speaker.name if(vname) @@ -162,13 +158,18 @@ speaker_name = "[speaker.real_name] ([speaker_name])" track = "[speaker_name] (follow)" + var/formatted + if(language) + formatted = language.format_message_radio(message, verb) + else + formatted = "[verb], \"[message]\"" if(sdisabilities & DEAF || ear_deaf) if(prob(20)) src << "You feel your headset vibrate but can hear nothing from it!" else if(track) - src << "[part_a][track][part_b][verb], \"[message]\"" + src << "[part_a][track][part_b][formatted]" else - src << "[part_a][speaker_name][part_b][verb], \"[message]\"" + src << "[part_a][speaker_name][part_b][formatted]" /mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null) if(!client) diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm index 2e627330a0..e619734700 100755 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -15,23 +15,21 @@ var/flags = 0 // Various language flags. var/native // If set, non-native speakers will have trouble speaking. -/datum/language/proc/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) +/datum/language/proc/format_message(message, verb) + return "[verb], \"[capitalize(message)]\"" +/datum/language/proc/format_message_radio(message, verb) + return "[verb], \"[capitalize(message)]\"" + +/datum/language/proc/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) log_say("[key_name(speaker)] : ([name]) [message]") + if(!speaker_mask) speaker_mask = speaker.name + var/msg = "[name], [speaker_mask] [format_message(message, get_spoken_verb(message))]" + for(var/mob/player in player_list) - - var/understood = 0 - - if(istype(player,/mob/dead)) - understood = 1 - else if((src in player.languages) && check_special_condition(player)) - understood = 1 - - if(understood) - if(!speaker_mask) speaker_mask = speaker.name - var/msg = "[name], [speaker_mask] [speech_verb], \"[message]\"" - player << "[msg]" + if(istype(player,/mob/dead) || ((src in player.languages) && check_special_condition(player))) + player << msg /datum/language/proc/check_special_condition(var/mob/other) return 1 @@ -44,6 +42,19 @@ return ask_verb return speech_verb +// Noise "language", for audible emotes. +/datum/language/noise + name = "Noise" + desc = "Noises" + key = "" + flags = RESTRICTED|NONGLOBAL|INNATE + +/datum/language/noise/format_message(message, verb) + return "[message]" + +/datum/language/noise/format_message_radio(message, verb) + return "[message]" + /datum/language/unathi name = "Sinta'unathi" desc = "The common language of Moghes, composed of sibilant hisses and rattles. Spoken natively by Unathi." @@ -305,7 +316,8 @@ var/dat = "Known Languages

" for(var/datum/language/L in languages) - dat += "[L.name] (:[L.key])
[L.desc]

" + if(!(L.flags & NONGLOBAL)) + dat += "[L.name] (:[L.key])
[L.desc]

" src << browse(dat, "window=checklanguage") return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/alien/say.dm b/code/modules/mob/living/carbon/alien/say.dm index 8b13a07fdc..038c77fcde 100644 --- a/code/modules/mob/living/carbon/alien/say.dm +++ b/code/modules/mob/living/carbon/alien/say.dm @@ -15,21 +15,12 @@ if(copytext(message,1,2) == "*") return emote(copytext(message,2)) - var/datum/language/speaking = null - - if(length(message) >= 2) - var/channel_prefix = copytext(message, 1 ,3) - if(languages.len) - for(var/datum/language/L in languages) - if(lowertext(channel_prefix) == ":[L.key]") - verb = L.speech_verb - speaking = L - break + var/datum/language/speaking = parse_language(message) if(speaking) - message = trim(copytext(message,3)) + message = copytext(message, 2+length(speaking.key)) - message = capitalize(trim_left(message)) + message = trim(message) if(!message || stat) return diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index 6c2394eb61..ecfb825684 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -2,10 +2,23 @@ /mob/living/carbon/brain/say(var/message) if (silent) return - - if(!(container && istype(container, /obj/item/device/mmi))) + + if(!(container && istype(container, /obj/item/device/mmi))) return //No MMI, can't speak, bucko./N else + var/datum/language/speaking = parse_language(message) + if(speaking) + message = copytext(message, 2+length(speaking.key)) + var/verb = "says" + var/ending = copytext(message, length(message)) + if (speaking) + verb = speaking.get_spoken_verb(ending) + else + if(ending=="!") + verb=pick("exclaims","shouts","yells") + if(ending=="?") + verb="asks" + if(prob(emp_damage*4)) if(prob(10))//10% chane to drop the message entirely return @@ -14,5 +27,5 @@ if(istype(container, /obj/item/device/mmi/radio_enabled)) var/obj/item/device/mmi/radio_enabled/R = container if(R.radio) - spawn(0) R.radio.hear_talk(src, sanitize(message)) + spawn(0) R.radio.hear_talk(src, trim(sanitize(message)), verb, speaking) ..() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 6b13e0b3a1..d787c3f733 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -33,7 +33,7 @@ //parse the language code and consume it var/datum/language/speaking = parse_language(message) if(speaking) - message = copytext(message,3) + message = copytext(message,2+length(speaking.key)) else if(species.default_language) speaking = all_languages[species.default_language] @@ -55,7 +55,7 @@ if (istype(wear_mask, /obj/item/clothing/mask/muzzle)) return - message = capitalize(trim(message)) + message = trim(message) if(speech_problem_flag) var/list/handle_r = handle_speech_problems(message) diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index dc81d3d1b5..0c6b9e147f 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -29,7 +29,7 @@ //parse the language code and consume it var/datum/language/speaking = parse_language(message) if (speaking) - message = copytext(message,3) + message = copytext(message,2+length(speaking.key)) whisper_say(message, speaking, alt_name) @@ -49,7 +49,7 @@ else var/adverb = pick("quietly", "softly") verb = "[speaking.speech_verb] [adverb]" - not_heard = "[verb] something [adverb]" + not_heard = "[speaking.speech_verb] something [adverb]" else not_heard = "[verb] something" //TODO get rid of the null language and just prevent speech if language is null @@ -126,7 +126,7 @@ for (var/obj/O in view(message_range, src)) spawn (0) if (O) - O.hear_talk(src, message) //O.hear_talk(src, message, verb, speaking) + O.hear_talk(src, message, verb, speaking) var/list/eavesdropping = hearers(eavesdropping_range, src) eavesdropping -= src diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f883ce46ff..a712d26139 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1,3 +1,7 @@ +/mob/living/New() + ..() + add_language("Noise") + //mob verbs are faster than object verbs. See mob/verb/examine. /mob/living/verb/pulled(atom/movable/AM as mob|obj in oview(1)) set name = "Pull" diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 22db25d678..caf62e484f 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -72,7 +72,7 @@ var/datum/language/speaking = parse_language(message) if (speaking) verb = speaking.speech_verb - message = copytext(message,3) + message = trim(copytext(message,2+length(speaking.key))) if(speaking.flags & HIVEMIND) speaking.broadcast(src,trim(message)) @@ -102,7 +102,7 @@ if("department") switch(bot_type) if(IS_AI) - return AI.holopad_talk(message) + return AI.holopad_talk(message, verb, speaking) if(IS_ROBOT) log_say("[key_name(src)] : [message]") return R.radio.talk_into(src,message,message_mode,verb,speaking) @@ -149,7 +149,7 @@ return ..(message,speaking,verb) //For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(var/message) +/mob/living/silicon/ai/proc/holopad_talk(var/message, verb, datum/language/speaking) log_say("[key_name(src)] : [message]") @@ -160,16 +160,22 @@ var/obj/machinery/hologram/holopad/T = src.holo if(T && T.hologram && T.master == src)//If there is a hologram and its master is the user. - var/verb = say_quote(message) //Human-like, sorta, heard by those who understand humans. - var/rendered_a = "[name] [verb], \"[message]\"" - + var/rendered_a //Speach distorted, heard by those who do not understand AIs. var/message_stars = stars(message) - var/rendered_b = "[voice_name] [verb], \"[message_stars]\"" + var/rendered_b + + if(speaking) + rendered_a = "[name] [speaking.format_message(message, verb)]" + rendered_b = "[voice_name] [speaking.format_message(message_stars, verb)]" + 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]\"" + src << "Holopad transmitted, [real_name] [verb], \"[message]\""//The AI can "hear" its own message. - 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) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 93d4438d7b..892f129f11 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -197,7 +197,8 @@ var/dat = "Known Languages

" for(var/datum/language/L in languages) - dat += "[L.name] (:[L.key])
Speech Synthesizer: [(L in speech_synthesizer_langs)? "YES":"NOT SUPPORTED"]
[L.desc]

" + if(!(L.flags & NONGLOBAL)) + dat += "[L.name] (:[L.key])
Speech Synthesizer: [(L in speech_synthesizer_langs)? "YES":"NOT SUPPORTED"]
[L.desc]

" src << browse(dat, "window=checklanguage") return diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 10e6c4266c..da75999c41 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -142,6 +142,9 @@ //parses the language code (e.g. :j) from text, such as that supplied to say. //returns the language object only if the code corresponds to a language that src can speak, otherwise null. /mob/proc/parse_language(var/message) + if(length(message) >= 1 && copytext(message,1,2) == "!") + return all_languages["Noise"] + if(length(message) >= 2) var/language_prefix = lowertext(copytext(message, 1 ,3)) var/datum/language/L = language_keys[language_prefix] diff --git a/code/setup.dm b/code/setup.dm index 7de8cfc987..9e204bf120 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -764,6 +764,8 @@ var/list/RESTRICTED_CAMERA_NETWORKS = list( //Those networks can only be accesse #define NONVERBAL 4 // Language has a significant non-verbal component. Speech is garbled without line-of-sight #define SIGNLANG 8 // Language is completely non-verbal. Speech is displayed through emotes for those who can understand. #define HIVEMIND 16 // Broadcast to all mobs with this language. +#define NONGLOBAL 32 // Do not add to general languages list +#define INNATE 64 // All mobs can be assumed to speak and understand this language (audible emotes) //Flags for zone sleeping #define ZONE_ACTIVE 1