diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index f084ad999bbb..35a972320b36 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -204,19 +204,22 @@ var/list/hearturfs = list() FOR_DVIEW(var/turf/T, range, center, INVISIBILITY_MAXIMUM) hearturfs[T] = TRUE - for(var/mob/M in T) - mobs += M + if(islist(mobs)) + for(var/mob/M in T) + mobs += M END_FOR_DVIEW - for(var/mob/M in global.player_list) - if(check_ghosts && M.stat == DEAD && M.get_preference_value(check_ghosts) != PREF_NEARBY) - mobs |= M - else if(hearturfs[get_turf(M)]) - mobs |= M - - for(var/obj/O in global.listening_objects) - if(hearturfs[get_turf(O)]) - objs += O + if(islist(mobs)) + for(var/mob/M in global.player_list) + if(check_ghosts && M.stat == DEAD && M.get_preference_value(check_ghosts) != PREF_NEARBY) + mobs |= M + else if(hearturfs[get_turf(M)]) + mobs |= M + + if(islist(objs)) + for(var/obj/O in global.listening_objects) + if(hearturfs[get_turf(O)]) + objs += O diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index cef6a0ba6e65..d9072a5c9968 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -365,6 +365,15 @@ var/global/regex/starts_lowercase_regex = regex(@"^[a-z]") /proc/trim(text) return trim_left(trim_right(text)) +/// Adds punctuation to an emote or speech message automatically. +/proc/handle_autopunctuation(message) + if(!message) + return + var/end_char = copytext_char(trim_right(strip_html_properly(message)), -1) + if(!(end_char in list(".", "?", "!", "-", "~"))) + message += "." + return message + //Returns a string with the first element of the string capitalized. // NOTE: This will not work if there are any HTML tags. /proc/capitalize(text) diff --git a/code/controllers/subsystems/initialization/lore.dm b/code/controllers/subsystems/initialization/lore.dm index 96b7a9850e0a..ca7de06785f0 100644 --- a/code/controllers/subsystems/initialization/lore.dm +++ b/code/controllers/subsystems/initialization/lore.dm @@ -23,10 +23,6 @@ SUBSYSTEM_DEF(lore) var/list/credits_topics = list("SACRED GEOMETRY","ABSTRACT MATHEMATICS","LOVE","DRUGS","CRIME","PRODUCTIVITY","LAUNDRY") var/list/credits_nouns = list("DIGNITY", "SANITY") - // Probably not the best subsystem for these, but oh well. - var/list/languages_by_key - var/list/languages_by_name - /datum/controller/subsystem/lore/Initialize() var/list/all_backgrounds = decls_repository.get_decls_of_subtype(/decl/background_detail) @@ -71,23 +67,3 @@ SUBSYSTEM_DEF(lore) possible_titles |= credits_other global.end_credits_title = pick(possible_titles) . = global.end_credits_title - -/datum/controller/subsystem/lore/proc/get_language_by_name(var/language_name) - if(!languages_by_name) - languages_by_name = list() - var/list/language_types = decls_repository.get_decls_of_subtype(/decl/language) - for(var/thing in language_types) - var/decl/language/lang = language_types[thing] - if(lang.name) - languages_by_name[lang.name] = lang - . = languages_by_name[language_name] - -/datum/controller/subsystem/lore/proc/get_language_by_key(var/language_key) - if(!languages_by_key) - languages_by_key = list() - var/list/language_types = decls_repository.get_decls_of_subtype(/decl/language) - for(var/thing in language_types) - var/decl/language/lang = language_types[thing] - if(lang.key) - languages_by_key[lang.key] = lang - . = languages_by_key[language_key] diff --git a/code/game/atoms.dm b/code/game/atoms.dm index e698467579de..9c042d7e05c1 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -642,7 +642,7 @@ - `range?`: The number of tiles away the message will be visible from. Default: world.view - `check_ghosts?`: Set to `TRUE` if ghosts should see the message if their preferences allow */ -/atom/proc/visible_message(var/message, var/self_message, var/blind_message, var/range = world.view, var/check_ghosts = null) +/atom/proc/visible_message(var/message, var/self_message, var/blind_message, var/range = world.view, var/check_ghosts = null, atom/source) var/turf/T = get_turf(src) var/list/mobs = list() var/list/objs = list() @@ -650,14 +650,14 @@ for(var/o in objs) var/obj/O = o - O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE, source = source) for(var/m in mobs) var/mob/M = m if(M.see_invisible >= invisibility) - M.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + M.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE, source = source) else if(blind_message) - M.show_message(blind_message, AUDIBLE_MESSAGE) + M.show_message(blind_message, AUDIBLE_MESSAGE, source = source) /** Show a message to all mobs and objects in earshot of this atom @@ -670,7 +670,7 @@ - `check_ghosts?`: TRUE if ghosts should hear the message if their preferences allow - `radio_message?`: The string to send over radios */ -/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance = world.view, var/check_ghosts = null, var/radio_message) +/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance = world.view, var/check_ghosts = null, var/radio_message, atom/source) var/turf/T = get_turf(src) var/list/mobs = list() var/list/objs = list() @@ -678,10 +678,10 @@ for(var/m in mobs) var/mob/M = m - M.show_message(message,2,deaf_message,1) + M.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE, source = source) for(var/o in objs) var/obj/O = o - O.show_message(message,2,deaf_message,1) + O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE, source = source) /** Attempt to drop this atom onto the destination. @@ -1082,3 +1082,7 @@ // Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. /atom/proc/is_safe_to_step(mob/living/stepper) return TRUE + +//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) +/atom/proc/show_message(msg, type, alt, alt_type, atom/source) + return diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/holopad/_holopad.dm similarity index 60% rename from code/game/machinery/hologram.dm rename to code/game/machinery/holopad/_holopad.dm index 43f1ee5e59cd..dde889be5b6f 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/holopad/_holopad.dm @@ -1,12 +1,4 @@ -/* Holograms! - * Contains: - * Holopad - * Hologram - * Other stuff - */ - /* -Revised. Original based on space ninja hologram code. Which is also mine. /N How it works: AI clicks on holopad in camera view. View centers on holopad. AI clicks again on the holopad to display a hologram. Hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. @@ -19,48 +11,45 @@ Possible to do for anyone motivated enough: Itegrate EMP effect to disable the unit. */ - -/* - * Holopad - */ - -#define HOLOPAD_PASSIVE_POWER_USAGE 1 -#define HOLOGRAM_POWER_USAGE 2 -#define RANGE_BASED 4 -#define AREA_BASED 6 - -var/global/const/HOLOPAD_MODE = RANGE_BASED var/global/list/holopads = list() -/obj/machinery/hologram/holopad - name = "\improper holopad" - desc = "It's a floor-mounted device for projecting holographic images." - icon = 'icons/obj/machines/holopad.dmi' - icon_state = "holopad-B0" - layer = ABOVE_TILE_LAYER - idle_power_usage = 5 - - var/power_per_hologram = 500 //per usage per hologram - - var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad - var/last_request = 0 //to prevent request spam. ~Carn - var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. +/obj/machinery/holopad + name = "\improper holopad" + desc = "It's a floor-mounted device for projecting holographic images." + icon = 'icons/obj/machines/holopad.dmi' + icon_state = "holopad-B0" + layer = ABOVE_TILE_LAYER + anchored = TRUE + idle_power_usage = 5 + active_power_usage = 100 + /// per usage per hologram + var/power_per_hologram = 500 + /// List of AIs that use the holopad + var/list/mob/living/silicon/ai/masters = new() + /// Timer to prevent request spam. + var/last_request = 0 + /// Change to change how far the AI can move away from the holopad before deactivating. + var/holo_range = 5 var/incoming_connection = 0 var/mob/living/caller_id - var/obj/machinery/hologram/holopad/sourcepad - var/obj/machinery/hologram/holopad/targetpad + var/obj/machinery/holopad/sourcepad + var/obj/machinery/holopad/targetpad var/last_message - - var/holopadType = HOLOPAD_SHORT_RANGE //Whether the holopad is short-range or long-range. + /// Whether the holopad is short-range or long-range. + var/holopadType = HOLOPAD_SHORT_RANGE var/base_icon = "holopad-B" - var/allow_ai = TRUE + var/holopad_id var/static/list/reachable_overmaps = list(OVERMAP_ID_SPACE) - var/holopad_id + var/const/HOLOPAD_MODE = RANGE_BASED + var/const/HOLOPAD_PASSIVE_POWER_USAGE = 1 + var/const/HOLOGRAM_POWER_USAGE = 2 + var/const/RANGE_BASED = 4 + var/const/AREA_BASED = 6 -/obj/machinery/hologram/holopad/Initialize() +/obj/machinery/holopad/Initialize() . = ..() global.holopads += src @@ -78,14 +67,14 @@ var/global/list/holopads = list() // Update our desc. desc = "It's a floor-mounted device for projecting holographic images. Its ID is '[holopad_id]'" -/obj/machinery/hologram/holopad/Destroy() +/obj/machinery/holopad/Destroy() global.listening_objects -= src global.holopads -= src for (var/mob/living/master in masters) clear_holo(master) return ..() -/obj/machinery/hologram/holopad/interface_interact(var/mob/living/human/user) //Carn: Hologram requests. +/obj/machinery/holopad/interface_interact(var/mob/living/human/user) //Carn: Hologram requests. if(!CanInteract(user, DefaultTopicState())) return FALSE if(incoming_connection && caller_id) @@ -93,14 +82,14 @@ var/global/list/holopads = list() incoming_connection = 0 clear_holo() return TRUE - visible_message("The pad hums quietly as it establishes a connection.") + visible_message("The pad hums quietly as it establishes a connection.", source = src) if(caller_id.loc!=sourcepad.loc) - visible_message("The pad flashes an error message. The caller has left their holopad.") + visible_message("The pad flashes an error message. The caller has left their holopad.", source = src) return TRUE take_call(user) return TRUE else if(caller_id && !incoming_connection) - audible_message("Severing connection to distant holopad.") + audible_message("\The [src] announces, \"Severing connection to distant holopad.\"", source = src) end_call(user) return TRUE @@ -140,7 +129,7 @@ var/global/list/holopads = list() if(!isnull(O) && (O.overmap_id in reachable_overmaps) && LAZYLEN(O.map_z)) zlevels_long |= O.map_z - for(var/obj/machinery/hologram/holopad/H in SSmachines.machinery) + for(var/obj/machinery/holopad/H in SSmachines.machinery) if (H.operable()) if(H.z in zlevels) holopadlist["[H.holopad_id]"] = H //Define a list and fill it with the area of every holopad in the world @@ -162,18 +151,17 @@ var/global/list/holopads = list() to_chat(user, "A request for holographic communication was already sent recently.") -/obj/machinery/hologram/holopad/proc/make_call(var/obj/machinery/hologram/holopad/targetpad, var/mob/living/user) +/obj/machinery/holopad/proc/make_call(var/obj/machinery/holopad/targetpad, var/mob/living/user) targetpad.last_request = world.time targetpad.sourcepad = src //This marks the holopad you are making the call from targetpad.caller_id = user //This marks you as the caller targetpad.incoming_connection = 1 playsound(targetpad.loc, 'sound/machines/chime.ogg', 25, 5) targetpad.icon_state = "[targetpad.base_icon]1" - targetpad.audible_message("\The [src] announces, \"Incoming communications request from [holopad_id].\"") - to_chat(user, "Trying to establish a connection to the holopad in [targetpad.holopad_id]... Please await confirmation from recipient.") - + targetpad.audible_message("\The [src] announces, \"Incoming communications request from [holopad_id].\"", source = src) + to_chat(user, SPAN_NOTICE("Trying to establish a connection to the holopad in [targetpad.holopad_id]... Please await confirmation from recipient.")) -/obj/machinery/hologram/holopad/proc/take_call(mob/living/user) +/obj/machinery/holopad/proc/take_call(mob/living/user) incoming_connection = 0 caller_id.machine = sourcepad caller_id.reset_view(src) @@ -181,7 +169,7 @@ var/global/list/holopads = list() activate_holocall(caller_id) log_admin("[key_name(caller_id)] just established a holopad connection from [sourcepad.holopad_id] to [holopad_id]") -/obj/machinery/hologram/holopad/proc/end_call(mob/user) +/obj/machinery/holopad/proc/end_call(mob/user) if(!caller_id) return caller_id.unset_machine() @@ -189,10 +177,10 @@ var/global/list/holopads = list() clear_holo(0, caller_id) // destroy the hologram caller_id = null -/obj/machinery/hologram/holopad/check_eye(mob/user) +/obj/machinery/holopad/check_eye(mob/user) return 0 -/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) +/obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user) if(!istype(user)) return /*There are pretty much only three ways to interact here. @@ -210,79 +198,26 @@ var/global/list/holopads = list() clear_holo(user) return -/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/silicon/ai/user) +/obj/machinery/holopad/proc/activate_holo(mob/living/silicon/ai/user) if(!(stat & NOPOWER) && user.eyeobj && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it if (user.holo) to_chat(user, "ERROR: Image feed in progress.") return - src.visible_message("A holographic image of [user] flicks to life right before your eyes!") + audible_message("\The [src] announces, \"A holographic image of [user] flickers to life right before your eyes!\"", source = src) create_holo(user)//Create one. else to_chat(user, "ERROR: Unable to project hologram.") return -/obj/machinery/hologram/holopad/proc/activate_holocall(mob/living/caller_id) +/obj/machinery/holopad/proc/activate_holocall(mob/living/caller_id) if(caller_id) - src.visible_message("A holographic image of [caller_id] flicks to life right before your eyes!") - create_holo(0,caller_id)//Create one. + visible_message("A holographic image of [caller_id] flickers to life right before your eyes!", source = src) + create_holo(0, caller_id)//Create one. else to_chat(caller_id, "ERROR: Unable to project hologram.") return -/*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.*/ -// Note that speaking may be null here, presumably due to echo effects/non-mob transmission. -/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb, decl/language/speaking) - if(M) - for(var/mob/living/silicon/ai/master in masters) - var/ai_text = text - if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad. - if(speaking) - ai_text = speaking.scramble(M, text) - else - ai_text = stars(text) - if(isanimal(M) && !M.universal_speak) - ai_text = DEFAULTPICK(M.ai?.emote_speech, "...") - 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/short_links = master.get_preference_value(/datum/client_preference/ghost_follow_link_length) == PREF_SHORT - var/follow = short_links ? "\[F]" : "\[Follow]" - var/prefix = "[follow]" - master.show_message(get_hear_message(name_used, ai_text, verb, speaking, prefix), 2) - var/name_used = M.GetVoice() - var/message - if(isanimal(M) && !M.universal_speak) - message = get_hear_message(name_used, DEFAULTPICK(M.ai?.emote_speech, "..."), verb, speaking) - else - message = get_hear_message(name_used, text, verb, speaking) - if(targetpad && !targetpad.incoming_connection) //If this is the pad you're making the call from and the call is accepted - targetpad.audible_message(message) - targetpad.last_message = message - if(sourcepad && sourcepad.targetpad && !sourcepad.targetpad.incoming_connection) //If this is a pad receiving a call and the call is accepted - if(name_used==caller_id||text==last_message||findtext(text, "Holopad received")) //prevent echoes - return - sourcepad.audible_message(message) - -/obj/machinery/hologram/holopad/proc/get_hear_message(name_used, text, verb, decl/language/speaking, prefix = "") - if(speaking) - return "Holopad received, [name_used][prefix] [speaking.format_message(text, verb)]" - return "Holopad received, [name_used][prefix] [verb], \"[text]\"" - -/obj/machinery/hologram/holopad/show_message(msg, type, alt, alt_type) - for(var/mob/living/silicon/ai/master in masters) - var/rendered = "The holographic image of [msg]" - master.show_message(rendered, type) - if(findtext(msg, "Holopad received,")) - return - for(var/mob/living/master in masters) - var/rendered = "The holographic image of [msg]" - master.show_message(rendered, type) - if(targetpad) - for(var/mob/living/master in view(targetpad)) - var/rendered = "The holographic image of [msg]" - master.show_message(rendered, type) - -/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, mob/living/caller_id, turf/T = loc) +/obj/machinery/holopad/proc/create_holo(mob/living/silicon/ai/A, mob/living/caller_id, turf/T = loc) var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location. if(caller_id) hologram.overlays += getHologramIcon(getFlatIcon(caller_id), hologram_color = holopadType) // Add the callers image as an overlay to keep coloration! @@ -311,7 +246,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ icon_state = "[base_icon]1" return 1 -/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user, mob/living/caller_id) +/obj/machinery/holopad/proc/clear_holo(mob/living/silicon/ai/user, mob/living/caller_id) if(user) qdel(masters[user])//Get rid of user's hologram user.holo = null @@ -326,34 +261,33 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ sourcepad.targetpad = null sourcepad = null caller_id = null - return 1 + return TRUE +/obj/machinery/holopad/Process() -/obj/machinery/hologram/holopad/Process() - for (var/mob/living/silicon/ai/master in masters) + for(var/mob/living/silicon/ai/master in masters) var/active_ai = (master && !master.incapacitated() && master.client && master.eyeobj)//If there is an AI with an eye attached, it's not incapacitated, and it has a client if((stat & NOPOWER) || !active_ai) clear_holo(master) continue - if(!(masters[master] in view(src))) clear_holo(master) continue - use_power_oneoff(power_per_hologram) + if(last_request + 200 < world.time&&incoming_connection==1) if(sourcepad) - sourcepad.audible_message("The holopad connection timed out") + sourcepad.audible_message("\The [src] announces, \"The holopad connection timed out.\"", source = src) incoming_connection = 0 end_call() - if (caller_id&&sourcepad) + + if(caller_id&&sourcepad) if(caller_id.loc!=sourcepad.loc) to_chat(sourcepad.caller_id, "Severing connection to distant holopad.") end_call() - audible_message("The connection has been terminated by the caller.") - return 1 + audible_message("\The [src] announces, \"The connection has been terminated by the caller.\"", source = src) -/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user) +/obj/machinery/holopad/proc/move_hologram(mob/living/silicon/ai/user) if(masters[user]) step_to(masters[user], user.eyeobj) // So it turns. var/obj/effect/overlay/H = masters[user] @@ -372,34 +306,15 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ var/area/hologram_area = get_area(H) if(hologram_area != holo_area) clear_holo(user) - return 1 + return TRUE -/obj/machinery/hologram/holopad/proc/set_dir_hologram(new_dir, mob/living/silicon/ai/user) +/obj/machinery/holopad/proc/set_dir_hologram(new_dir, mob/living/silicon/ai/user) if(masters[user]) var/obj/effect/overlay/hologram = masters[user] hologram.set_dir(new_dir) - -/* - * Hologram - */ - -/obj/machinery/hologram - anchored = TRUE - idle_power_usage = 5 - active_power_usage = 100 - -/* - * Other Stuff: Is this even used? - */ -/obj/machinery/hologram/projector - name = "hologram projector" - desc = "It makes a hologram appear...with magnets or something..." - icon = 'icons/obj/machines/holopad.dmi' - icon_state = "hologram0" - -/obj/machinery/hologram/holopad/longrange +/obj/machinery/holopad/longrange name = "long range holopad" desc = "It's a floor-mounted device for projecting holographic images. This one utilizes micro-width wormholes to communicate with far away locations." icon_state = "holopad-Y0" @@ -408,10 +323,5 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ base_icon = "holopad-Y" // Used for overmap capable ships that should have communications, but not be AI accessible -/obj/machinery/hologram/holopad/longrange/remoteship +/obj/machinery/holopad/longrange/remoteship allow_ai = FALSE - -#undef RANGE_BASED -#undef AREA_BASED -#undef HOLOPAD_PASSIVE_POWER_USAGE -#undef HOLOGRAM_POWER_USAGE diff --git a/code/game/machinery/holopad/holopad_listen.dm b/code/game/machinery/holopad/holopad_listen.dm new file mode 100644 index 000000000000..3da35e0bb533 --- /dev/null +++ b/code/game/machinery/holopad/holopad_listen.dm @@ -0,0 +1,38 @@ +/obj/machinery/holopad/proc/get_audience() + . = list() + if(!incoming_connection) + for(var/mob/living/listener in range(7, targetpad)) + . += listener + for(var/mob/living/listener in range(7, sourcepad)) + . |= listener + for(var/mob/living/silicon/ai/master in masters) + . |= master + if(length(.)) + for(var/mob/observer/ghost/ghost in get_ghosts()) + if(ghost.client && ghost.get_preference_value(/datum/client_preference/ghost_ears) == PREF_ALL_SPEECH) + . |= ghost + for(var/obj/machinery/holopad/holopad in .) + . -= holopad + +/obj/machinery/holopad/show_message(msg, type, alt, alt_type, atom/source) + . = ..() + if(last_message == msg || istype(source, /obj/machinery/holopad)) + return + if(length(masters) || ((sourcepad || targetpad) && !incoming_connection)) + last_message = msg + var/list/local_audience = get_mobs_or_objects_in_view(7, get_turf(src), TRUE, FALSE) + for(var/mob/listener in get_audience()) + if(!(listener in local_audience)) + listener.show_message("\icon[src] [capitalize(strip_improper(name))] relayed: [msg]", type, source) + +/obj/machinery/holopad/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + ..() + var/phrase_msg = "\ref[speaker]: [istype(phrases) ? phrases.formatted_message : phrases]" + if(last_message == phrase_msg) + return + if(length(masters) || ((sourcepad || targetpad) && !incoming_connection)) + last_message = phrase_msg + var/list/local_audience = get_mobs_or_objects_in_view(7, get_turf(speaker), TRUE, FALSE) + for(var/mob/listener in get_audience()) + if(listener != speaker && !(listener in local_audience)) + listener.hear_say(phrases, verb, speaker = speaker, relayed_by = src) diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 123678e0cee3..3647018211f8 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -115,19 +115,9 @@ /obj/proc/hides_under_flooring() return level == LEVEL_BELOW_PLATING -/obj/proc/hear_talk(mob/M, text, verb, decl/language/speaking) +/obj/proc/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) if(talking_atom) - talking_atom.catchMessage(text, M) -/* - var/mob/mo = locate(/mob) in src - if(mo) - var/rendered = "[M.name]: [text]" - mo.show_message(rendered, 2) - */ - return - -/obj/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) - return + talking_atom.catchMessage(istype(phrases) ? phrases.unformatted_message : phrases, speaker) /obj/proc/damage_flags() . = 0 diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index d9164b23c5e2..ee945a09d67c 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -623,7 +623,7 @@ return ..() -/obj/item/proc/talk_into(mob/living/M, message, message_mode, var/verb = "says", var/decl/language/speaking = null) +/obj/item/proc/talk_into(mob/living/speaker, datum/speech/phrases, verb = "says") return // apparently called whenever an item is removed from a slot, container, or anything else. diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 9516a2a65a52..55f2eb6ec700 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -125,7 +125,7 @@ carded_ai = null update_icon() -/obj/item/aicard/show_message(msg, type, alt, alt_type) +/obj/item/aicard/show_message(msg, type, alt, alt_type, atom/source) if(carded_ai && carded_ai.client) var/rendered = "[msg]" carded_ai.show_message(rendered, type) diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 844fc1c2e5a4..5c872a451185 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -336,7 +336,7 @@ var/global/list/pai_cards = list() else qdel(src) -/obj/item/paicard/show_message(msg, type, alt, alt_type) +/obj/item/paicard/show_message(msg, type, alt, alt_type, atom/source) if(pai && pai.client) var/rendered = "[msg]" pai.show_message(rendered, type) diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index 1cda309212b5..9320e417034b 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -24,7 +24,7 @@ var/global/list/radio_beacons = list() /obj/item/radio/beacon/toggle_panel(var/mob/user) return FALSE -/obj/item/radio/beacon/hear_talk() +/obj/item/radio/beacon/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) return /obj/item/radio/beacon/emp_act(severity) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 48244d9fcdae..402ce818df15 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -321,28 +321,35 @@ return channel.frequency return frequency -/obj/item/radio/talk_into(mob/living/M, message, message_mode, var/verb = "says", var/decl/language/speaking = null) +/obj/item/radio/talk_into(mob/living/speaker, datum/speech/phrases, verb = "says") set waitfor = FALSE if(!on) return 0 // the device has to be on // Fix for permacell radios, but kinda eh about actually fixing them. - if(!istype(M) || !message) return 0 + if(!istype(speaker)) + return FALSE - if(speaking && (speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG))) return 0 + var/list/audible_phrases = list() + for(var/list/phrase in phrases.phrases) + var/decl/language/speaking = phrase[2] + if(!speaking || !(speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG))) + audible_phrases += list(phrase) + if(!length(audible_phrases)) + return FALSE - if (!broadcasting) + if(!broadcasting) // Sedation chemical effect should prevent radio use. - if((M.has_chemical_effect(CE_SEDATE, 1) || M.incapacitated(INCAPACITATION_DISRUPTED))) - to_chat(M, SPAN_WARNING("You're unable to reach \the [src].")) + if((speaker.has_chemical_effect(CE_SEDATE, 1) || speaker.incapacitated(INCAPACITATION_DISRUPTED))) + to_chat(speaker, SPAN_WARNING("You're unable to reach \the [src].")) return 0 - if(M.radio_interrupt_cooldown > world.time) - to_chat(M, SPAN_WARNING("You're disrupted as you reach for \the [src].")) + if(speaker.radio_interrupt_cooldown > world.time) + to_chat(speaker, SPAN_WARNING("You're disrupted as you reach for \the [src].")) return 0 - if(istype(M)) - M.trigger_aiming(TARGET_CAN_RADIO) + if(istype(speaker)) + speaker.trigger_aiming(TARGET_CAN_RADIO) - addtimer(CALLBACK(src, PROC_REF(transmit), M, message, message_mode, verb, speaking), 0) + addtimer(CALLBACK(src, PROC_REF(transmit), speaker, phrases, verb), 0) /obj/item/radio/proc/can_transmit_binary() for(var/obj/item/encryptionkey/key in encryption_keys) @@ -350,7 +357,7 @@ return TRUE return FALSE -/obj/item/radio/proc/transmit(var/mob/speaker, message, message_mode, var/verb = "says", var/decl/language/speaking = null) +/obj/item/radio/proc/transmit(var/mob/living/speaker, datum/speech/phrases, var/verb = "says") if(wires.IsIndexCut(WIRE_TRANSMIT)) return 0 @@ -368,9 +375,9 @@ if(loc && loc == speaker) playsound(loc, 'sound/effects/walkietalkie.ogg', 20, 0, -1) - if(message_mode == MESSAGE_MODE_SPECIAL && can_transmit_binary()) + if(phrases.message_mode == MESSAGE_MODE_SPECIAL && can_transmit_binary()) var/decl/language/binary/binary = GET_DECL(/decl/language/binary) - binary.broadcast(speaker, message) + binary.broadcast(speaker, phrases) return TRUE var/turf/position = get_turf(src) @@ -379,10 +386,10 @@ var/list/current_sector = SSmapping.get_connected_levels(position.z) var/use_frequency = frequency - if(message_mode && !analog) + if(phrases.message_mode && !analog) var/list/current_channels = get_available_channels() - message_mode = lowertext(message_mode) - if(message_mode == MESSAGE_MODE_DEFAULT) + phrases.message_mode = lowertext(phrases.message_mode) + if(phrases.message_mode == MESSAGE_MODE_DEFAULT) for(var/datum/radio_channel/channel in current_channels) if(!channel.secured) use_frequency = channel.frequency @@ -393,7 +400,7 @@ use_frequency = channel.frequency break - else if(message_mode == MESSAGE_MODE_DEPARTMENT) + else if(phrases.message_mode == MESSAGE_MODE_DEPARTMENT) for(var/datum/radio_channel/channel in current_channels) if(channel.secured && can_decrypt(channel.secured)) use_frequency = channel.frequency @@ -405,7 +412,7 @@ break else for(var/datum/radio_channel/channel in current_channels) - if(channel.key != message_mode || !(LAZYACCESS(channels, channel))) + if(channel.key != phrases.message_mode || !(LAZYACCESS(channels, channel))) continue if(can_decrypt(channel.secured)) use_frequency = channel.frequency @@ -416,7 +423,7 @@ if(last_frequency != use_frequency) set_frequency(use_frequency) - broadcast_analog_radio_message(analog_radio_connection, speaker, src, message, intercom, message_compression, current_sector, verb, speaking, analog_secured) + broadcast_analog_radio_message(analog_radio_connection, speaker, src, phrases, intercom, message_compression, current_sector, verb, analog_secured) if(frequency != last_frequency) set_frequency(last_frequency) else @@ -427,7 +434,7 @@ for(var/weakref/H as anything in network?.connected_hubs) var/obj/machinery/network/telecomms_hub/hub = H.resolve() if(istype(hub) && !QDELETED(hub) && hub.can_receive_message(network)) - hub.transmit_message(speaker, message, verb, speaking, use_frequency, message_compression, checked_hubs) + hub.transmit_message(speaker, phrases, verb, use_frequency, message_compression, checked_hubs) break // Only one hub per message, since it transmits over the whole network. /obj/item/radio/proc/can_receive_message(var/check_network_membership) @@ -436,9 +443,9 @@ var/datum/extension/network_device/network_device = get_extension(src, /datum/extension/network_device) return network_device?.get_network() == check_network_membership -/obj/item/radio/hear_talk(mob/M, msg, var/verb = "says", var/decl/language/speaking = null) - if(on && broadcasting && get_dist(src, M) <= canhear_range) - talk_into(M, msg, null, verb, speaking) +/obj/item/radio/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + if(on && broadcasting && get_dist(src, speaker) <= canhear_range) + talk_into(speaker, istype(phrases) ? phrases.unformatted_message : phrases, verb) /obj/item/radio/proc/get_accessible_channel_descriptions(var/mob/user) var/prefix = user?.get_department_radio_prefix() diff --git a/code/game/objects/items/devices/radio/radio_analog.dm b/code/game/objects/items/devices/radio/radio_analog.dm index c5f51e30385f..4f2f2f9e52bf 100644 --- a/code/game/objects/items/devices/radio/radio_analog.dm +++ b/code/game/objects/items/devices/radio/radio_analog.dm @@ -15,10 +15,7 @@ return FALSE return TRUE -/proc/broadcast_analog_radio_message(datum/radio_frequency/connection, mob/speaker, - obj/item/radio/radio, message, intercom_only = FALSE, - hard_to_hear, list/z_levels, verbage = "says", decl/language/speaking = null, list/secured - ) +/proc/broadcast_analog_radio_message(datum/radio_frequency/connection, mob/speaker, obj/item/radio/radio, datum/speech/phrases, intercom_only = FALSE, hard_to_hear, list/z_levels, verbage = "says", list/secured) var/list/radios = list(radio) var/list/radios_insecure = list() @@ -53,13 +50,11 @@ // Send to all recipients for (var/mob/receiver in receive) - receiver.hear_radio(message, verbage, speaking, formatted_msg, part_b, part_c, speaker, hard_to_hear, send_name) + receiver.hear_radio(phrases, verbage, formatted_msg, part_b, part_c, speaker, hard_to_hear, send_name) if(length(receive_insecure)) - var/decl/language/machine/noise_lang = GET_DECL(/decl/language/machine) - var/scrambled_message = noise_lang.scramble(null, message, null) for (var/mob/receiver in receive_insecure) - receiver.hear_radio(scrambled_message, verbage, speaking, formatted_msg, part_b, part_c, speaker, hard_to_hear, "unknown") + receiver.hear_radio(phrases, verbage, formatted_msg, part_b, part_c, speaker, hard_to_hear, "unknown", scramble = TRUE) return TRUE diff --git a/code/game/objects/items/devices/radio/radio_borg.dm b/code/game/objects/items/devices/radio/radio_borg.dm index 6ce348ceaaa3..97aa665fce33 100644 --- a/code/game/objects/items/devices/radio/radio_borg.dm +++ b/code/game/objects/items/devices/radio/radio_borg.dm @@ -26,7 +26,7 @@ . = INITIALIZE_HINT_QDEL CRASH("Invalid spawn location: [log_info_line(loc)]") -/obj/item/radio/borg/talk_into(mob/living/M, message, message_mode, var/verb = "says", var/decl/language/speaking = null) +/obj/item/radio/borg/talk_into(mob/living/speaker, datum/speech/phrases, verb = "says") . = ..() if(isrobot(loc)) var/mob/living/silicon/robot/robot = src.loc diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm index 518b43c3d377..b6649659f7cc 100644 --- a/code/game/objects/items/devices/spy_bug.dm +++ b/code/game/objects/items/devices/spy_bug.dm @@ -45,9 +45,8 @@ return TRUE return ..() -/obj/item/spy_bug/hear_talk(mob/M, var/msg, verb, decl/language/speaking) - radio.hear_talk(M, msg, speaking) - +/obj/item/spy_bug/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + radio.hear_talk(speaker, phrases, verb, stars, force_language) /obj/item/spy_monitor name = "\improper PDA" @@ -138,8 +137,8 @@ return -1 return 0 -/obj/item/spy_monitor/hear_talk(mob/M, var/msg, verb, decl/language/speaking) - return radio.hear_talk(M, msg, speaking) +/obj/item/spy_monitor/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + return radio.hear_talk(speaker, phrases, verb, stars, force_language) /obj/item/radio/spy listening = 0 diff --git a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm index 7677f30a1dc0..b660a31ae739 100644 --- a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm +++ b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm @@ -96,17 +96,12 @@ if(distance <= 1 && wires_accessible) . += SPAN_NOTICE("The wires are exposed.") -/obj/item/taperecorder/hear_talk(mob/living/M, msg, var/verb="says", decl/language/speaking=null) +/obj/item/taperecorder/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) if(mytape && recording) + var/list/messages = istype(phrases) ? phrases.compile_for_listener(src, machine_listener = TRUE) : phrases + mytape.record_speech("[speaker.name] [verb], \"[messages[1]]\"") - if(speaking) - if(!speaking.machine_understands) - msg = speaking.scramble(M, msg) - mytape.record_speech("[M.name] [speaking.format_message_plain(msg, verb)]") - else - mytape.record_speech("[M.name] [verb], \"[msg]\"") - -/obj/item/taperecorder/show_message(msg, type, alt, alt_type) +/obj/item/taperecorder/show_message(msg, type, alt, alt_type, atom/source) var/recordedtext if (msg && type == AUDIBLE_MESSAGE) //must be hearable recordedtext = msg diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index b0776e4e47ea..95d2b4279700 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -168,11 +168,9 @@ qdel(src) return -/obj/item/assembly_holder/hear_talk(mob/living/M, msg, verb, decl/language/speaking) - if(a_right) - a_right.hear_talk(M,msg,verb,speaking) - if(a_left) - a_left.hear_talk(M,msg,verb,speaking) +/obj/item/assembly_holder/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + a_left?.hear_talk( speaker, phrases, verb, stars, force_language) + a_right?.hear_talk(speaker, phrases, verb, stars, force_language) /obj/item/assembly_holder/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index 2dafb2ae5be8..92330930fcc6 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -15,14 +15,14 @@ . = ..() global.listening_objects += src -/obj/item/assembly/voice/hear_talk(mob/living/M, msg) +/obj/item/assembly/voice/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) if(listening) - recorded = msg + recorded = istype(phrases) ? phrases.unformatted_message : phrases listening = 0 var/turf/T = get_turf(src) //otherwise it won't work in hand T.visible_message("[html_icon(src)] beeps, \"Activation message is '[recorded]'.\"") else - if(findtext(msg, recorded)) + if(findtext(istype(phrases) ? phrases.unformatted_message : phrases, recorded)) pulse_device(0) /obj/item/assembly/voice/activate() diff --git a/code/modules/client/preference_setup/background/03_language.dm b/code/modules/client/preference_setup/background/03_language.dm index 486d6bffd109..757c58cd582d 100644 --- a/code/modules/client/preference_setup/background/03_language.dm +++ b/code/modules/client/preference_setup/background/03_language.dm @@ -13,11 +13,20 @@ for(var/lang in pref.alternate_languages) character.add_language(lang) +/datum/category_item/player_setup_item/background/languages/proc/get_language_by_name(var/language_name) + var/static/list/languages_by_name + if(!languages_by_name) + languages_by_name = list() + for(var/decl/language/lang in decls_repository.get_decls_of_subtype_unassociated(/decl/language)) + if(lang.name) + languages_by_name[lang.name] = lang + . = languages_by_name[language_name] + /datum/category_item/player_setup_item/background/languages/load_character(datum/pref_record_reader/R) pref.alternate_languages = list() var/list/language_names = R.read("language") for(var/lang in language_names) - var/decl/language/lang_decl = SSlore.get_language_by_name(lang) + var/decl/language/lang_decl = get_language_by_name(lang) if(istype(lang_decl)) pref.alternate_languages |= lang_decl.type diff --git a/code/modules/codex/categories/category_languages.dm b/code/modules/codex/categories/category_languages.dm index 5d24d398323a..b76ceeca1a58 100644 --- a/code/modules/codex/categories/category_languages.dm +++ b/code/modules/codex/categories/category_languages.dm @@ -11,7 +11,7 @@ continue var/list/lang_info = list() var/decl/prefix/P = /decl/prefix/language - lang_info += "Key to use it: '[initial(P.default_key)][L.key]'" + lang_info += "Key to use it: '[initial(P.default_key)][L.language_key]'" if(L.flags & LANG_FLAG_NONVERBAL) lang_info += "It has a significant non-verbal component. Speech is garbled without line-of-sight." if(L.flags & LANG_FLAG_SIGNLANG) diff --git a/code/modules/emotes/emote_mob.dm b/code/modules/emotes/emote_mob.dm index b91444de9608..9f6df9e487d8 100644 --- a/code/modules/emotes/emote_mob.dm +++ b/code/modules/emotes/emote_mob.dm @@ -182,7 +182,7 @@ // Specific mob type exceptions below. /mob/living/silicon/ai/emote(var/act, var/type, var/message) - var/obj/machinery/hologram/holopad/T = src.holo + var/obj/machinery/holopad/T = src.holo if(T && T.masters[src]) //Is the AI using a holopad? src.holopad_emote(message) return diff --git a/code/modules/fabrication/fabricator_food.dm b/code/modules/fabrication/fabricator_food.dm index cf6486e37019..b18efbd2f8e7 100644 --- a/code/modules/fabrication/fabricator_food.dm +++ b/code/modules/fabrication/fabricator_food.dm @@ -16,22 +16,27 @@ global.listening_objects -= src return ..() -/obj/machinery/fabricator/replicator/hear_talk(var/mob/M, var/text, var/verb, var/decl/language/speaking) - if(speaking && !speaking.machine_understands) +/obj/machinery/fabricator/replicator/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + if(!istype(phrases)) return ..() - var/true_text = lowertext(html_decode(text)) - if(findtext(true_text, "status")) - addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/fabricator/replicator, state_status)), 2 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) - else if(findtext(true_text, "menu")) - addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/fabricator/replicator, state_menu)), 2 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) - else + for(var/list/phrase in phrases.phrases) + var/decl/language/speaking = phrase[2] + if(speaking && !speaking.machine_understands) + continue + var/true_text = lowertext(html_decode(phrase[1])) + if(findtext(true_text, "status")) + addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/fabricator/replicator, state_status)), 2 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) + return + if(findtext(true_text, "menu")) + addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/fabricator/replicator, state_menu)), 2 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) + return for(var/datum/fabricator_recipe/recipe in design_cache) if(recipe.hidden && !(fab_status_flags & FAB_HACKED)) continue if(findtext(true_text, lowertext(recipe.name))) addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/fabricator, try_queue_build), recipe, 1), 2 SECONDS) - break - ..() + return + . = ..() /obj/machinery/fabricator/replicator/can_ingest(var/obj/item/thing) return istype(thing, /obj/item/food) || ..() diff --git a/code/modules/implants/implant_types/explosive.dm b/code/modules/implants/implant_types/explosive.dm index c4d2919bd763..4b12775e31f5 100644 --- a/code/modules/implants/implant_types/explosive.dm +++ b/code/modules/implants/implant_types/explosive.dm @@ -91,13 +91,13 @@ frequency = new_frequency radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) -/obj/item/implant/explosive/hear_talk(mob/M, msg) - hear(msg) +/obj/item/implant/explosive/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + hear(phrases) -/obj/item/implant/explosive/hear(var/msg) +/obj/item/implant/explosive/hear(datum/speech/phrases) if(!phrase) return - if(findtext(sanitize_phrase(msg),phrase)) + if(findtext(sanitize_phrase(istype(phrases) ? phrases.unformatted_message : phrases), phrase)) activate() qdel(src) diff --git a/code/modules/implants/implant_types/translator.dm b/code/modules/implants/implant_types/translator.dm index 0dbcc41231d8..ef03cba68efc 100644 --- a/code/modules/implants/implant_types/translator.dm +++ b/code/modules/implants/implant_types/translator.dm @@ -16,15 +16,19 @@ . = ..() global.listening_objects += src -/obj/item/implant/translator/hear_talk(mob/speaker, msg, verb, decl/language/speaking) - if(!imp_in) +/obj/item/implant/translator/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + if(!imp_in || !istype(phrases)) return if(length(languages) == max_languages) return - languages[speaking.name] += 1 - if(!imp_in.say_understands(speaker, speaking) && languages[speaking.name] > learning_threshold) - to_chat(imp_in, SPAN_NOTICE("You feel like you can understand [speaking.name] now...")) - imp_in.add_language(speaking.type) + for(var/list/phrase in phrases.phrases) + var/decl/language/speaking = phrase[2] + if(!speaking) + continue + languages[speaking.name] += 1 + if(!imp_in.say_understands(speaker, speaking) && languages[speaking.name] > learning_threshold) + to_chat(imp_in, SPAN_NOTICE("You feel like you can understand [speaking.name] now...")) + imp_in.add_language(speaking.type) /obj/item/implant/translator/implanted(mob/target) return TRUE diff --git a/code/modules/item_effects/_item_effect.dm b/code/modules/item_effects/_item_effect.dm index 54199b3584da..da9a692bb2ad 100644 --- a/code/modules/item_effects/_item_effect.dm +++ b/code/modules/item_effects/_item_effect.dm @@ -61,7 +61,7 @@ SHOULD_CALL_PARENT(FALSE) return FALSE -/decl/item_effect/proc/hear_speech(obj/item/item, mob/user, message, decl/language/speaking) +/decl/item_effect/proc/hear_speech(obj/item/item, mob/user, datum/speech/phrases) SHOULD_CALL_PARENT(FALSE) return FALSE diff --git a/code/modules/item_effects/item_effect_debug.dm b/code/modules/item_effects/item_effect_debug.dm index ae834c4bf2c5..815606b0727b 100644 --- a/code/modules/item_effects/item_effect_debug.dm +++ b/code/modules/item_effects/item_effect_debug.dm @@ -34,8 +34,8 @@ /decl/item_effect/debug/apply_onmob_appearance_to(obj/item/item, mob/user, bodytype, image/overlay, slot, bodypart) log_debug("[type]: [user] updated onmob appearance for [item] in [slot] for [bodytype]/[bodypart] ([json_encode(args)])") -/decl/item_effect/debug/hear_speech(obj/item/item, mob/user, message, decl/language/speaking) - log_debug("[type]: [item] heard [user] say [message] in [speaking] ([json_encode(args)])") +/decl/item_effect/debug/hear_speech(obj/item/item, mob/user, datum/speech/phrases) + log_debug("[type]: [item] heard [user] say [istype(phrases) ? phrases.unformatted_message : phrases] in [istype(phrases) ? phrases.language : "NULL"] ([json_encode(args)])") /decl/item_effect/debug/on_examined(obj/item/item, mob/user) log_debug("[type]: [user] examined [item] ([json_encode(args)])") diff --git a/code/modules/item_effects/item_effect_item.dm b/code/modules/item_effects/item_effect_item.dm index 197c22d8a01f..c641111f9250 100644 --- a/code/modules/item_effects/item_effect_item.dm +++ b/code/modules/item_effects/item_effect_item.dm @@ -167,13 +167,13 @@ wield_effect.do_wielded_effect(user, src, parameters) // LISTENING effects -/obj/item/hear_talk(mob/M, text, verb, decl/language/speaking) +/obj/item/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) . = ..() var/list/item_effects = get_item_effects(IE_CAT_LISTENER) if(!length(item_effects)) return for(var/decl/item_effect/listening_effect as anything in item_effects) - listening_effect.hear_speech(src, M, text, speaking) + listening_effect.hear_speech(src, speaker, phrases) // VISIBLE effects /obj/item/examined_by(mob/user, distance, infix, suffix) diff --git a/code/modules/mob/floating_message.dm b/code/modules/mob/floating_message.dm index a6cc4a3bb3f7..7672573e3a07 100644 --- a/code/modules/mob/floating_message.dm +++ b/code/modules/mob/floating_message.dm @@ -15,46 +15,36 @@ var/global/list/floating_chat_colors = list() /atom/movable var/list/stored_chat_text -/atom/movable/proc/animate_chat(message, decl/language/language, small, list/show_to, duration = CHAT_MESSAGE_LIFESPAN) +/atom/movable/proc/animate_chat(datum/speech/phrases, small, list/show_to, stars, duration = CHAT_MESSAGE_LIFESPAN) set waitfor = FALSE - /// Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag - var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g") - message = replacetext(message, url_scheme, "") - - var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g") - message = replacetext(message, html_metachars, "") - - //additional style params for the message + // Pre-calculate style params for the message. var/style var/fontsize = 7 var/limit = 120 - if(small) fontsize = 6 - - if(copytext_char(message, length_char(message) - 1) == "!!") + if(copytext_char(phrases.unformatted_message, length_char(phrases.unformatted_message) - 1) == "!!") fontsize = 8 limit = 60 style += "font-weight: bold;" - - if(length_char(message) > limit) - message = "[copytext_char(message, 1, limit)]..." - if(!global.floating_chat_colors[name]) global.floating_chat_colors[name] = get_random_colour(0, 160, 230) style += "color: [global.floating_chat_colors[name]];" - // create 2 messages, one that appears if you know the language, and one that appears when you don't know the language - var/image/understood = generate_floating_text(src, capitalize(message), style, fontsize, duration, show_to) - var/image/gibberish = language ? generate_floating_text(src, language.scramble(src, message), style, fontsize, duration, show_to) : understood - for(var/client/C in show_to) - if(!C.mob.is_deaf() && C.get_preference_value(/datum/client_preference/floating_messages) == PREF_SHOW) - if(C.mob.say_understands(src, language)) - C.images += understood - else - C.images += gibberish + if(C.mob.is_deaf() || C.get_preference_value(/datum/client_preference/floating_messages) != PREF_SHOW) + continue + var/list/messages = phrases.compile_for_listener(C.mob) + var/message = capitalize(messages[1]) + /// Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag + var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g") + message = replacetext(message, url_scheme, "") + var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g") + message = replacetext(message, html_metachars, "") + if(length_char(message) > limit) + message = "[copytext_char(message, 1, limit)]..." + C.images += generate_floating_text(src, message, style, fontsize, duration, show_to) /proc/generate_floating_text(atom/movable/holder, message, style, size, duration, show_to) var/image/I = image(null, get_atom_on_turf(holder)) diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 76104279447a..9d854858a063 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -1,6 +1,5 @@ // At minimum every mob has a hear_say proc. - -/mob/proc/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/proc/hear_say(datum/speech/phrases, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, stars = FALSE, atom/relayed_by) if(!client) return @@ -9,7 +8,7 @@ //Or someone snoring. So we make it where they won't hear it. return - if(language && (language.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG))) + if(phrases.language?.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG)) sound_vol = 0 speech_sound = null @@ -25,23 +24,12 @@ italics = 1 sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact + var/list/messages = phrases.compile_for_listener(src) if(HAS_STATUS(src, STAT_ASLEEP) || stat == UNCONSCIOUS) - hear_sleep(message) + hear_sleep(messages[1]) return - //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 & LANG_FLAG_NONVERBAL)) - if (!speaker || is_blind() || !(speaker in view(src))) - message = stars(message) - - var/understands_language = say_understands(speaker, language) - if(!(language && (language.flags & LANG_FLAG_INNATE))) // skip understanding checks for INNATE languages - if(!understands_language) - if(language) - message = language.scramble(speaker, message, languages) - else - message = stars(message) - + var/message = messages[2] var/speaker_name = speaker?.GetVoice() || "Unknown" if(italics) message = "[message]" @@ -54,35 +42,42 @@ if(get_preference_value(/datum/client_preference/ghost_ears) == PREF_ALL_SPEECH && (speaker in view(src))) message = "[message]" - if(is_deaf() || get_sound_volume_multiplier() < 0.2) - if(!language || !(language.flags & LANG_FLAG_INNATE)) // LANG_FLAG_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, SPAN_WARNING("You cannot hear yourself speak!")) - else if(!is_blind()) - var/decl/pronouns/pronouns = speaker.get_pronouns() - to_chat(src, "\The [speaker_name] talks but you cannot hear [pronouns.him].") + // LANG_FLAG_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((is_deaf() || get_sound_volume_multiplier() < 0.2) && (!phrases.language || !(phrases.language.flags & LANG_FLAG_INNATE))) + if(speaker == src) + to_chat(src, SPAN_WARNING("You cannot hear yourself speak!")) + else if(!is_blind()) + var/decl/pronouns/pronouns = speaker.get_pronouns() + to_chat(src, "\The [speaker_name] talks but you cannot hear [pronouns.him].") + return + + var/nverb = phrases.force_verb || verb + // This is kinda gross now we have mixed languages, but the alternative is just removing the language hint. + if (phrases.language) + if (say_understands(speaker, phrases.language)) + var/skip = FALSE + if (isliving(src)) + var/mob/living/L = src + skip = L.default_language == phrases.language + if (!skip) + switch(src.get_preference_value(/datum/client_preference/language_display)) + if (PREF_FULL) + nverb = "[verb] in [phrases.language.name]" + if(PREF_SHORTHAND) + nverb = "[verb] ([phrases.language.shorthand])" + if(PREF_OFF) + nverb = verb + + var/msg + if(istype(relayed_by)) + msg = "[track]\icon[relayed_by] [capitalize(strip_improper(relayed_by.name))] relayed: \The [speaker_name] [nverb], \"[message]\"" else - if (language) - var/nverb = verb - if (understands_language) - var/skip = FALSE - if (isliving(src)) - var/mob/living/L = src - skip = L.default_language == language - if (!skip) - switch(src.get_preference_value(/datum/client_preference/language_display)) - if(PREF_FULL) // Full language name - nverb = "[verb] in [language.name]" - if(PREF_SHORTHAND) //Shorthand codes - nverb = "[verb] ([language.shorthand])" - if(PREF_OFF)//Regular output - nverb = verb - on_hear_say("[track]\The [speaker_name] [language.format_message(message, nverb)]") - else - on_hear_say("[track]\The [speaker_name] [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) + msg = "[track]\The [speaker_name] [nverb], \"[message]\"" + + on_hear_say(msg) + 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/on_hear_say(var/message) to_chat(src, message) @@ -91,48 +86,25 @@ var/time = say_timestamp() to_chat(src, "[time] [message]") -/mob/proc/hear_radio(var/message, var/verb="says", var/decl/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/vsource) +/mob/proc/hear_radio(datum/speech/phrases, verb = "says", part_a, part_b, part_c, mob/speaker, hard_to_hear = FALSE, vname = "", vsource, scramble = FALSE) if(!client) return + if(phrases.language && (phrases.language.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG|LANG_FLAG_HIVEMIND))) + return // we should not have gotten this far + + var/list/messages = phrases.compile_for_listener(src, skip_non_verbal = TRUE, scramble = scramble, hard_to_hear = hard_to_hear) if(HAS_STATUS(src, STAT_ASLEEP) || stat == UNCONSCIOUS) //If unconscious or sleeping - hear_sleep(message) + hear_sleep(messages[1]) return var/track = null - - //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 & LANG_FLAG_NONVERBAL)) - if (!speaker || is_blind() || !(speaker in view(src))) - message = stars(message) - - if(!(language && (language.flags & LANG_FLAG_INNATE))) // skip understanding checks for LANG_FLAG_INNATE languages - if(!say_understands(speaker,language)) - if(isanimal(speaker)) - if(LAZYLEN(speaker.ai?.emote_speech)) - message = pick(speaker.ai.emote_speech) - else - return - else - if(language) - message = language.scramble(speaker, message, languages) - else - message = stars(message) - - if(hard_to_hear) - if(hard_to_hear <= 5) - message = stars(message) - else // Used for compression - message = RadioChat(null, message, 80, 1+(hard_to_hear/10)) - var/speaker_name = vname ? vname : speaker.name - if(ishuman(speaker)) var/mob/living/human/H = speaker if(H.voice && !vname) speaker_name = H.voice - if(hard_to_hear) speaker_name = "unknown" @@ -192,33 +164,32 @@ else track = "[speaker_name]" - var/formatted - if (language) - var/nverb = verb - if (say_understands(speaker, language)) + var/nverb = phrases.force_verb || verb + // This is kinda gross now we have mixed languages, but the alternative is just removing the language hint. + if (phrases.language) + if (say_understands(speaker, phrases.language)) var/skip = FALSE if (isliving(src)) var/mob/living/L = src - skip = L.default_language == language + skip = L.default_language == phrases.language if (!skip) switch(src.get_preference_value(/datum/client_preference/language_display)) if (PREF_FULL) - nverb = "[verb] in [language.name]" + nverb = "[verb] in [phrases.language.name]" if(PREF_SHORTHAND) - nverb = "[verb] ([language.shorthand])" + nverb = "[verb] ([phrases.language.shorthand])" if(PREF_OFF) nverb = verb - formatted = language.format_message_radio(message, nverb) - else - formatted = "[verb], \"[message]\"" + + var/message = "[nverb], \"[messages[2]]\"" if(has_genetic_condition(GENE_COND_DEAFENED) || GET_STATUS(src, STAT_DEAF)) var/mob/living/human/H = src if(istype(H) && H.has_headset_in_ears() && prob(20)) to_chat(src, SPAN_WARNING("You feel your headset vibrate but can hear nothing from it!")) else if(vsource) - on_hear_radio(part_a, speaker_name, track, part_b, part_c, formatted, " \[[vsource]\]") + on_hear_radio(part_a, speaker_name, track, part_b, part_c, message, " \[[vsource]\]") else - on_hear_radio(part_a, speaker_name, track, part_b, part_c, formatted, null) + on_hear_radio(part_a, speaker_name, track, part_b, part_c, message, null) /proc/say_timestamp() return "\[[stationtime2text()]\]" diff --git a/code/modules/mob/language/alien/antag.dm b/code/modules/mob/language/alien/antag.dm index 58a574c10acb..98056e21d945 100644 --- a/code/modules/mob/language/alien/antag.dm +++ b/code/modules/mob/language/alien/antag.dm @@ -5,7 +5,7 @@ ask_verb = "intones" exclaim_verb = "chants" colour = "cult" - key = "f" + language_key = "cult" flags = LANG_FLAG_RESTRICTED space_chance = 100 syllables = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri", \ @@ -26,7 +26,7 @@ ask_verb = "intones" exclaim_verb = "chants" colour = "cult" - key = "y" + language_key = "occult" flags = LANG_FLAG_RESTRICTED | LANG_FLAG_HIVEMIND shorthand = "N/A" hidden_from_codex = TRUE @@ -35,7 +35,7 @@ name = "Alium" colour = "cult" speech_verb = "hisses" - key = "c" + language_key = "alien" flags = LANG_FLAG_RESTRICTED syllables = list("qy","bok","mok","yok","dy","gly","ryl","byl","dok","forbici", "tarem", "n'ath", "reth", "sh'yro", "eth", "d'raggathnor","niii", "d'rekkathnor", "khari'd", "gual'te", "ki","ki","ki","ki","ya","ta","wej","nym","assah","qwssa","nieasl","qyno","shaffar", diff --git a/code/modules/mob/language/alien/monkey.dm b/code/modules/mob/language/alien/monkey.dm index 9c6d6727c444..8bbc8131315f 100644 --- a/code/modules/mob/language/alien/monkey.dm +++ b/code/modules/mob/language/alien/monkey.dm @@ -4,7 +4,7 @@ speech_verb = "chimpers" ask_verb = "chimpers" exclaim_verb = "screeches" - key = "" + language_key = "ook" flags = LANG_FLAG_RESTRICTED syllables = list("ook", "eek", "hiss", "gronk") shorthand = "Ook" diff --git a/code/modules/mob/language/animal.dm b/code/modules/mob/language/animal.dm index e1318a473fe3..1534f754e6a8 100644 --- a/code/modules/mob/language/animal.dm +++ b/code/modules/mob/language/animal.dm @@ -2,7 +2,7 @@ name = "Animal Noises" // translate them! desc = "Some varieties of animal can communicate amongst themselves, apparently." colour = "say_quote" - key = "a" + language_key = "awoo" shorthand = "A" hidden_from_codex = TRUE @@ -23,7 +23,7 @@ return TRUE return FALSE -/decl/language/animal/scramble(mob/living/speaker, input, list/known_languages) +/decl/language/animal/scramble(mob/living/speaker, input, list/known_languages, capitalize_string) if(istype(speaker.ai) && length(speaker.ai.emote_speech)) return DEFAULTPICK(speaker.ai.emote_speech, "...") return "..." diff --git a/code/modules/mob/language/generic.dm b/code/modules/mob/language/generic.dm index 1665c0b215c2..cc3a2fedd80e 100644 --- a/code/modules/mob/language/generic.dm +++ b/code/modules/mob/language/generic.dm @@ -2,19 +2,15 @@ /decl/language/noise name = "Noise" desc = "Noises" - key = "" flags = LANG_FLAG_RESTRICTED|LANG_FLAG_NONGLOBAL|LANG_FLAG_INNATE|LANG_FLAG_NO_TALK_MSG|LANG_FLAG_NO_STUTTER hidden_from_codex = TRUE -/decl/language/noise/format_message(message, verb) - return "[message]" - -/decl/language/noise/format_message_plain(message, verb) - return message - -/decl/language/noise/format_message_radio(message, verb) +/decl/language/noise/format_message_no_verb(message) return "[message]" +/decl/language/noise/format_message(message, verb) + return "[format_message_no_verb(message)]" + /decl/language/noise/get_talkinto_msg_range(message) // if you make a loud noise (screams etc), you'll be heard from 4 tiles over instead of two return (copytext(message, length(message)) == "!") ? 4 : 2 @@ -25,6 +21,6 @@ sign language is often an essential communication tool for large portions of the population." signlang_verb = list("gestures") colour = "say_quote" - key = "s" + language_key = "sign" flags = LANG_FLAG_SIGNLANG | LANG_FLAG_NO_STUTTER | LANG_FLAG_NONVERBAL shorthand = "HS" diff --git a/code/modules/mob/language/human/human.dm b/code/modules/mob/language/human/human.dm index 867b9adccbd4..327cfab9f141 100644 --- a/code/modules/mob/language/human/human.dm +++ b/code/modules/mob/language/human/human.dm @@ -38,7 +38,7 @@ speech_verb = "says" whisper_verb = "whispers" colour = "" - key = "1" + language_key = "common" flags = LANG_FLAG_WHITELISTED shorthand = "C" partial_understanding = list() diff --git a/code/modules/mob/language/human/misc/legalese.dm b/code/modules/mob/language/human/misc/legalese.dm index 729cee0401ae..77fa0e5fdf6a 100644 --- a/code/modules/mob/language/human/misc/legalese.dm +++ b/code/modules/mob/language/human/misc/legalese.dm @@ -5,7 +5,7 @@ exclaim_verb = "objects" ask_verb = "inquiries" space_chance = 100 - key = "u" + language_key = "legal" partial_understanding = list( /decl/language/human/common = 10 ) @@ -14,4 +14,3 @@ "exonerated", "effecuate", "accord", "caveat", "stipulation", "pledgee", "covenant", "rights", "lawful", "suit of law", "sequestrator", "et al", "et", "ex", "quid", "bono", "quo", "pro", "ad" ) - \ No newline at end of file diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index 1c7008ad9e63..6858efbc23d6 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -3,14 +3,6 @@ /* Datum based languages. Easily editable and modular. */ - -/* Current unused keys, please update when you use one. - * e - * n - * r - * t - * w -*/ /decl/language abstract_type = /decl/language // Used to point at root language types that shouldn't be visible @@ -25,7 +17,7 @@ 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/colour = "body" // CSS style to use for strings in this language. - var/key = "" // Character used to speak in language + var/language_key = "" // Character used to speak in language var/flags = 0 // Various language flags. /// Syllable list when scrambling text for display to a non-speaker. var/list/syllables @@ -46,6 +38,16 @@ /// Control for handling some of the random lang/name gen. var/allow_repeated_syllables = TRUE +/decl/language/Initialize() + . = ..() + if(language_key) + language_key = lowertext(language_key) // enforce lowertext + +/decl/language/validate() + . = ..() + if(isnum(language_key) || !isnull(text2num(language_key))) + . += "numerical/non-text language key 'language_key' - keys must not be text and must not be directly convertible to a number value." + /decl/language/proc/can_be_understood_by(var/mob/living/speaker, var/mob/living/listener) if(flags & LANG_FLAG_INNATE) return TRUE @@ -83,7 +85,7 @@ LAZYADD(., capitalize(lowertext(new_name))) . = "[trim(jointext(., " "))]" -/decl/language/proc/scramble(mob/living/speaker, input, list/known_languages) +/decl/language/proc/scramble(mob/living/speaker, input, list/known_languages, capitalize_string) var/understand_chance = 0 for(var/decl/language/L in known_languages) @@ -112,7 +114,8 @@ scrambled_text += nword . = jointext(scrambled_text, null) - . = capitalize_proper_html(.) + if(capitalize_string) + . = capitalize_proper_html(.) . = trim(.) /decl/language/proc/get_next_scramble_token() @@ -155,40 +158,37 @@ return scrambled_text -/decl/language/proc/format_message(message, verb) - return "[verb], \"[capitalize(message)]\"" +/decl/language/proc/format_message_no_verb(message) + return "[message]" -/decl/language/proc/format_message_plain(message, verb) - return "[verb], \"[capitalize(message)]\"" - -/decl/language/proc/format_message_radio(message, verb) - return "[verb], \"[capitalize(message)]\"" +/decl/language/proc/format_message(message, verb) + return "[verb], \"[capitalize(format_message_no_verb(message))]\"" /decl/language/proc/get_talkinto_msg_range(message) // if you yell, you'll be heard from two tiles over instead of one return (copytext(message, length(message)) == "!") ? 2 : 1 -/decl/language/proc/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) +/decl/language/proc/broadcast(mob/living/speaker, datum/speech/phrases, speaker_mask) + var/message = istype(phrases) ? phrases.unformatted_message : phrases log_say("[key_name(speaker)] : ([name]) [message]") - if(!speaker_mask) speaker_mask = speaker.name message = format_message(message, get_spoken_verb(speaker, message)) for(var/mob/player in global.player_list) - player.hear_broadcast(src, speaker, speaker_mask, message) + player.hear_broadcast(speaker, speaker_mask, phrases) -/mob/proc/hear_broadcast(var/decl/language/language, var/mob/speaker, var/speaker_name, var/message) - if((language in languages) && language.check_special_condition(src)) - var/msg = "[language.name], [speaker_name] [message]" +/mob/proc/hear_broadcast(mob/speaker, var/speaker_name, datum/speech/phrases) + if((phrases.language in languages) && phrases.language.check_special_condition(src)) + var/msg = "[phrases.language.name], [speaker_name] [phrases.formatted_message]" to_chat(src, msg) -/mob/new_player/hear_broadcast(var/decl/language/language, var/mob/speaker, var/speaker_name, var/message) +/mob/new_player/hear_broadcast(mob/speaker, var/speaker_name, datum/speech/phrases) return -/mob/observer/ghost/hear_broadcast(var/decl/language/language, var/mob/speaker, var/speaker_name, var/message) +/mob/observer/ghost/hear_broadcast(mob/speaker, var/speaker_name, datum/speech/phrases) if(speaker.name == speaker_name || antagHUD) - to_chat(src, "[language.name], [speaker_name] ([ghost_follow_link(speaker, src)]) [message]") + to_chat(src, "[phrases.language.name], [speaker_name] ([ghost_follow_link(speaker, src)]) [phrases.formatted_message]") else - to_chat(src, "[language.name], [speaker_name] [message]") + to_chat(src, "[phrases.language.name], [speaker_name] [phrases.formatted_message]") /decl/language/proc/check_special_condition(var/mob/other) return 1 @@ -242,9 +242,6 @@ /mob/proc/get_language_prefix() return get_prefix_key(/decl/prefix/language) -/mob/proc/is_language_prefix(var/prefix) - return prefix == get_prefix_key(/decl/prefix/language) - //TBD /mob/verb/check_languages() set name = "Check Known Languages" @@ -255,7 +252,7 @@ for(var/decl/language/L in languages) if(!(L.flags & LANG_FLAG_NONGLOBAL)) - dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.key])
[L.desc]

" + dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.language_key])
[L.desc]

" show_browser(src, dat, "window=checklanguage") return @@ -270,11 +267,11 @@ for(var/decl/language/L in languages) if(!(L.flags & LANG_FLAG_NONGLOBAL)) if(L == default_language) - dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.key]) - default - reset
[L.desc]

" + dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.language_key]) - default - reset
[L.desc]

" else if (can_speak(L)) - dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.key]) - set default
[L.desc]

" + dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.language_key]) - set default
[L.desc]

" else - dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.key]) - cannot speak!
[L.desc]

" + dat += "[L.name]([L.shorthand]) ([get_language_prefix()][L.language_key]) - cannot speak!
[L.desc]

" show_browser(src, dat, "window=checklanguage") diff --git a/code/modules/mob/language/synthetic.dm b/code/modules/mob/language/synthetic.dm index ffcc87c400cd..5ed95fc0d19d 100644 --- a/code/modules/mob/language/synthetic.dm +++ b/code/modules/mob/language/synthetic.dm @@ -6,7 +6,7 @@ ask_verb = "chirps" exclaim_verb = "whistles loudly" colour = "mutant" - key = "6" + language_key = "eal" flags = LANG_FLAG_NO_STUTTER syllables = list("beep","beep","beep","beep","beep","boop","boop","boop","bop","bop","dee","dee","doo","doo","hiss","hss","buzz","buzz","bzz","ksssh","keey","wurr","wahh","tzzz") space_chance = 10 @@ -28,16 +28,17 @@ speech_verb = "states" ask_verb = "queries" exclaim_verb = "declares" - key = "b" + language_key = "bot" flags = LANG_FLAG_RESTRICTED | LANG_FLAG_HIVEMIND shorthand = "N/A" var/drone_only -/decl/language/binary/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) +/decl/language/binary/broadcast(mob/living/speaker, datum/speech/phrases, speaker_mask) if(!speaker.binarycheck()) return + var/message = istype(phrases) ? phrases.unformatted_message : phrases if (!message) return @@ -82,7 +83,7 @@ ask_verb = "transmits" exclaim_verb = "transmits" colour = "say_quote" - key = "d" + language_key = "drone" flags = LANG_FLAG_RESTRICTED | LANG_FLAG_HIVEMIND drone_only = 1 shorthand = "N/A" diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index fca88bfd2e25..ad51af3dc566 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -198,12 +198,8 @@ /mob/living/bot/proc/CanAccessMaintenance(var/mob/user) return (open || issilicon(user)) -/mob/living/bot/say(var/message) - var/verb = "beeps" - - message = sanitize(message) - - ..(message, null, verb) +/mob/living/bot/say(datum/speech/phrases, verb = "beeps", whispering) + return ..() // Overrides default verb /mob/living/bot/Bump(var/atom/A) if(on && botcard && istype(A, /obj/machinery/door)) diff --git a/code/modules/mob/living/brain/say.dm b/code/modules/mob/living/brain/say.dm index c2542360968b..6d172a2d2a87 100644 --- a/code/modules/mob/living/brain/say.dm +++ b/code/modules/mob/living/brain/say.dm @@ -1,14 +1,18 @@ -/mob/living/brain/say(var/message, var/decl/language/speaking, var/verb = "says", whispering) +/mob/living/brain/say(datum/speech/phrases, verb = "says", whispering) if(GET_STATUS(src, STAT_SILENCE) || !is_in_interface()) return if(prob(emp_damage*4)) if(prob(10)) return - message = Gibberish(message, (emp_damage*6)) - . = ..(message, speaking, verb, whispering) + if(istext(phrases)) + phrases = Gibberish(phrases, (emp_damage*6)) + else if(islist(phrases)) + for(var/list/phrase in phrases.phrases) + phrase[1] = Gibberish(phrase[1], (emp_damage*6)) + . = ..() var/obj/item/radio/radio = get_radio() if(radio) - radio.hear_talk(src, sanitize(message), verb, speaking) + radio.hear_talk(src, phrases, verb) /mob/living/brain/get_radio() var/obj/item/organ/internal/brain_interface/container = get_container() diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index 3ea28d2aadad..24aab3812d31 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -232,7 +232,7 @@ /decl/human_examination/pose/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) if (!source.pose) return - var/treated_pose = trim(source.handle_autopunctuation(source.pose)) + var/treated_pose = trim(handle_autopunctuation(source.pose)) // if the pose starts with is, are, do, does, or doesn't, apply basic verb correction if(starts_with(treated_pose, "is ")) // if the pose starts with is treated_pose = "[pronouns.is] [copytext(treated_pose, 1, 4)]" @@ -246,7 +246,7 @@ treated_pose = "[pronouns.does]n't [copytext(treated_pose, 1, 9)]" else if(starts_with(treated_pose, "don't ")) treated_pose = "[pronouns.does]n't [copytext(treated_pose, 1, 7)]" - return "[pronouns.He] [source.handle_autopunctuation(source.pose)]" + return "[pronouns.He] [handle_autopunctuation(source.pose)]" /decl/human_examination/comments priority = /decl/human_examination/pose::priority + 99 // OOC info should show up pretty late. diff --git a/code/modules/mob/living/human/say.dm b/code/modules/mob/living/human/say.dm index 3b7ce80dab1f..d8f607369102 100644 --- a/code/modules/mob/living/human/say.dm +++ b/code/modules/mob/living/human/say.dm @@ -1,25 +1,8 @@ -/mob/living/human/say(var/message, var/decl/language/speaking, var/verb = "says", whispering) - if(!whispering) - var/obj/item/organ/internal/voicebox/voice = locate() in get_internal_organs() - // Check if the language they're speaking is vocal and not supplied by a machine, and if they are currently suffocating. - whispering = (whispering || has_chemical_effect(CE_VOICELOSS, 1)) - var/decl/bodytype/root_bodytype = get_bodytype() - if((!speaking || !(speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG))) && (!voice || !voice.is_usable() || !voice.assists_languages[speaking]) && !root_bodytype.is_robotic && need_breathe() && failed_last_breath) - var/obj/item/organ/internal/lungs/L = get_organ(root_bodytype.breathing_organ, /obj/item/organ/internal/lungs) - if(!L || L.breath_fail_ratio > 0.9) - if(L && world.time < L.last_successful_breath + 2 MINUTES) //if we're in grace suffocation period, give it up for last words - to_chat(src, SPAN_WARNING("You use your remaining air to say something!")) - L.last_successful_breath = world.time - 2 MINUTES - whispering = FALSE - else - to_chat(src, SPAN_WARNING("You don't have enough air[L ? " in [L]" : ""] to make a sound!")) - return - else if(L.breath_fail_ratio > 0.7 || (L.breath_fail_ratio > 0.4 && length(message) > 10) || (L.breath_fail_ratio > 0.2 && length(message) > 30)) - whispering = TRUE +/mob/living/human/say(datum/speech/phrases, verb = "says", whispering) if(name != GetVoice()) if(get_id_name("Unknown") == GetVoice()) SetName(get_id_name("Unknown")) - . = ..(message, speaking, verb, whispering) + . = ..() /mob/living/human/proc/forcesay(list/append) if(stat == CONSCIOUS) @@ -72,9 +55,9 @@ return verb -/mob/living/human/handle_message_mode(message_mode, message, verb, speaking, used_radios) - if(message_mode == MESSAGE_MODE_WHISPER) //It's going to get sanitized again immediately, so decode. - whisper_say(html_decode(message), speaking) +/mob/living/human/handle_message_mode(datum/speech/phrases, verb, used_radios) + if(phrases.message_mode == MESSAGE_MODE_WHISPER) //It's going to get sanitized again immediately, so decode. + say(phrases, "whisper", whispering = TRUE) return TRUE return ..() @@ -103,16 +86,3 @@ else if(speaking.type in species.unspeakable_langs) return FALSE . = ..() - -/mob/living/human/parse_language(var/message) - var/prefix = copytext(message,1,2) - if(length(message) >= 1 && prefix == get_prefix_key(/decl/prefix/audible_emote)) - return GET_DECL(/decl/language/noise) - - if(length(message) >= 2 && is_language_prefix(prefix)) - var/language_prefix = lowertext(copytext(message, 2 ,3)) - var/decl/language/L = SSlore.get_language_by_key(language_prefix) - if (can_speak(L)) - return L - - return null diff --git a/code/modules/mob/living/human/whisper.dm b/code/modules/mob/living/human/whisper.dm index 6d7ac0db0ab9..c696d9e50716 100644 --- a/code/modules/mob/living/human/whisper.dm +++ b/code/modules/mob/living/human/whisper.dm @@ -20,9 +20,4 @@ if(get_id_name("Unknown") == GetVoice()) SetName(get_id_name("Unknown")) - whisper_say(message) - - -//This is used by both the whisper verb and human/say() to handle whispering -/mob/living/human/proc/whisper_say(var/message, var/decl/language/speaking = null, var/verb="whispers") - say(message, speaking, verb, whispering = TRUE) \ No newline at end of file + say(list(message, get_default_language()), whispering = TRUE) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 49e187b25a7f..3fa4632a590d 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -5,13 +5,14 @@ return TRUE return FALSE -/mob/living/proc/get_default_language() +/mob/living/get_default_language(use_fallback = TRUE) var/decl/language/lang = GET_DECL(default_language) if(istype(lang) && can_speak(lang)) return lang + return use_fallback ? get_any_good_language(set_default=TRUE) : null -/mob/living/proc/get_any_good_language(set_default=FALSE) - . = get_default_language() +/mob/living/get_any_good_language(set_default=FALSE) + . = get_default_language(use_fallback = FALSE) if(!.) for(var/decl/language/L in languages) if(can_speak(L)) @@ -20,7 +21,6 @@ set_default_language(.) return - /mob/living/is_silenced() . = ..() || HAS_STATUS(src, STAT_SILENCE) @@ -90,17 +90,17 @@ // This proc takes in a string (message_mode) which maps to a radio key in global.department_radio_keys // It then processes the message_mode to implement an additional behavior needed for the message, such // as retrieving radios or looking for an intercom nearby. -/mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios) +/mob/living/proc/handle_message_mode(datum/speech/phrases, verb, used_radios) SHOULD_CALL_PARENT(TRUE) - if(!message_mode) + if(!phrases.message_mode) return - var/list/assess_items_as_radios = get_radios(message_mode) + var/list/assess_items_as_radios = get_radios(phrases.message_mode) if(!LAZYLEN(assess_items_as_radios)) return used_radios |= assess_items_as_radios for(var/obj/item/radio/radio as anything in used_radios) radio.add_fingerprint(src) - radio.talk_into(src, message, message_mode, verb, speaking) + radio.talk_into(src, phrases, verb) /mob/living/proc/handle_speech_sound() var/list/returns[2] @@ -108,102 +108,189 @@ returns[2] = null return returns -/mob/living/proc/handle_mob_specific_speech(message, message_mode, verb = "says", decl/language/speaking) +/mob/living/proc/handle_mob_specific_speech(datum/speech/phrases, verb = "says") SHOULD_CALL_PARENT(TRUE) return FALSE -/mob/living/say(var/message, var/decl/language/speaking, var/verb = "says", whispering) - set waitfor = FALSE - if(client) - if(client.prefs.muted & MUTE_IC) - to_chat(src, "You cannot speak in IC (Muted).") - return - - if(stat) - if(stat == DEAD) - return say_dead(message) - return - - if(findlasttextEx(message, get_prefix_key(/decl/prefix/custom_emote)) == 1) - return emote(copytext(message,2)) +// Parses a message into a list of list(text = spoken language) entries. +/mob/living/proc/parse_message_into_phrases(mob/living/_speaker, message) - if(findlasttextEx(message, get_prefix_key(/decl/prefix/visible_emote)) == 1) - return custom_emote(1, copytext(message,2)) + message = trim(message) + var/full_message = message //parse the radio code and consume it - var/message_mode = parse_message_mode(message, standard_mode = MESSAGE_MODE_DEFAULT) + var/message_mode = parse_message_mode(full_message, standard_mode = MESSAGE_MODE_DEFAULT) if(message_mode) if(message_mode == MESSAGE_MODE_DEFAULT) message = copytext_char(message, 2) else message = copytext_char(message, 3) - // trim pre-language-parsing so we can get language and radio keys - message = trim(message) + var/static/regex/split_msg_regex = regex("(.*)(,\\w.*)") + var/list/phrases = list() + while(split_msg_regex.Find(message)) + message = split_msg_regex.group[1] + phrases.Insert(1, split_msg_regex.group[2]) + if(message) + phrases.Insert(1, message) + + var/use_verb + var/list/parsed_phrases = list() + for(var/phrase in phrases) - //parse the language code and consume it - if(!speaking) - speaking = parse_language(message) + var/decl/language/speaking = parse_language(phrase) if(speaking) - message = copytext_char(message,2+length_char(speaking.key)) + if(speaking.type == /decl/language/noise) + phrase = copytext_char(phrase, 1) // trim the single-char noise prefix + else + phrase = copytext_char(phrase, findtext_char(phrase, " ")+1) // trim the entire key else speaking = get_any_good_language(set_default=TRUE) - if (!speaking) - to_chat(src, SPAN_WARNING("You don't know a language and cannot speak.")) - custom_emote(AUDIBLE_MESSAGE, "[pick("grunts", "babbles", "gibbers", "jabbers", "burbles")] aimlessly.") + + // Format and present nicely. + phrase = trim(html_encode(phrase)) + phrase = handle_autohiss(phrase, speaking) + + // The LANG_FLAG_NO_STUTTER check means nonvocal or unusually-produced + // languages (e.g. sign language, noise emotes, computer beeps) + // will not be affected by stuttering, slurring, silence, etc. effects. + if(speaking && !(speaking.flags & LANG_FLAG_NO_STUTTER)) + var/list/message_data = list(phrase, speaking.speech_verb, 0) + if(handle_speech_problems(message_data)) + phrase = message_data[1] + use_verb ||= message_data[2] + + if(speaking) + var/speech_ability_result = speaking.can_be_spoken_properly_by(src) + if(speech_ability_result == SPEECH_RESULT_MUDDLED) + phrase = speaking.muddle(phrase) + else if(speech_ability_result == SPEECH_RESULT_INCAPABLE) + // weird phrasing, but needs to cover speaking and signing + to_chat(src, SPAN_WARNING("You don't have the right equipment to communicate in that way!")) return - if(handle_mob_specific_speech(message, message_mode, verb, speaking)) + parsed_phrases += list(list(phrase, speaking)) + + if(length(parsed_phrases)) + return new /datum/speech(_speaker, full_message, message_mode, parsed_phrases, use_verb) + +/mob/living/say(datum/speech/phrases, verb = "says", whispering) + set waitfor = FALSE + + if(client?.prefs.muted & MUTE_IC) + to_chat(src, SPAN_WARNING("You cannot speak in-character as you are muted.")) + return FALSE + + if(istext(phrases)) + phrases = parse_message_into_phrases(src, phrases) + + if(phrases.incoherent_language_flagging) + to_chat(src, SPAN_WARNING("You cannot mix non-spoken and spoken language at the same time!")) return - // This is broadcast to all mobs with the language, - // irrespective of distance or anything else. - if(speaking && (speaking.flags & LANG_FLAG_HIVEMIND)) - speaking.broadcast(src,trim(message)) - return 1 + if(!length(phrases?.phrases)) + return // nothing to say for whatever reason - if(!speaking || !(speaking.flags & LANG_FLAG_SIGNLANG)) + if(stat) + if(stat == DEAD) + return say_dead(phrases.unformatted_message) + return + + if(findlasttextEx(phrases.raw_message, get_prefix_key(/decl/prefix/custom_emote)) == 1) + return emote(copytext(phrases.raw_message,2)) + + if(findlasttextEx(phrases.raw_message, get_prefix_key(/decl/prefix/visible_emote)) == 1) + return custom_emote(1, copytext(phrases.raw_message,2)) + + if(phrases.force_verb) + verb = phrases.force_verb + + // Do some organ checking for mobs that have lungs and need air to speak. + var/decl/bodytype/root_bodytype = get_bodytype() + if(root_bodytype && !root_bodytype.is_robotic && need_breathe() && failed_last_breath && root_bodytype.breathing_organ) + + // Grab lungs and breath data. + var/obj/item/organ/internal/lungs/lungs = get_organ(root_bodytype.breathing_organ, /obj/item/organ/internal/lungs) + var/is_short_of_breath = !lungs || (lungs.breath_fail_ratio > 0.7 || (lungs.breath_fail_ratio > 0.4 && length(phrases.unformatted_message) > 10) || (lungs.breath_fail_ratio > 0.2 && length(phrases.unformatted_message) > 30)) + var/is_suffocating = !lungs || lungs.breath_fail_ratio > 0.9 + + // We're short of breath, force whispering. + if(is_short_of_breath) + whispering = TRUE + + // If we don't have any lung issues we don't need to care about the rest of the logic. + if(is_suffocating) + // Track if we have some phrase that we can say aloud. + var/has_speech = FALSE + // Grab our voicebox (if we have one) in case of mechanically assisted languages. + var/obj/item/organ/internal/voicebox/voice = locate() in get_internal_organs() + var/has_voicebox = istype(voice) && voice.is_usable() + + // Check if any of our phrases need air. + for(var/list/phrase in phrases.phrases) + var/decl/language/speaking = phrase[2] + // This is not a vocal language, it doesn't need air. + if(speaking && (speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG))) + has_speech = TRUE + continue + // This is a mechanically assisted language, doesn't need air. + if(has_voicebox && voice.assists_languages[speaking]) + has_speech = TRUE + continue + + if(lungs && world.time < lungs.last_successful_breath + 2 MINUTES) //if we're in grace suffocation period, give it up for last words + to_chat(src, SPAN_WARNING("You use your remaining air to say something!")) + lungs.last_successful_breath = world.time - 2 MINUTES + whispering = FALSE + has_speech = TRUE + break + phrase[1] = "..." // Can't speak. + + if(!has_speech) + to_chat(src, SPAN_WARNING("You don't have enough air[lungs ? " in [lungs]" : ""] to make a sound!")) + return + + else + whispering ||= has_chemical_effect(CE_VOICELOSS, 1) + else + whispering ||= has_chemical_effect(CE_VOICELOSS, 1) + + + // Check if we're muzzled. + for(var/list/phrase in phrases.phrases) + var/decl/language/speaking = phrase[2] + if(speaking && (speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG|LANG_FLAG_HIVEMIND))) + continue var/obj/item/muzzle = get_item_blocking_speech() if(muzzle) - to_chat(src, SPAN_WARNING("You're can't speak, \the [muzzle] is in the way!")) + to_chat(src, SPAN_WARNING("You can't speak, \the [muzzle] is in the way!")) return - if (speaking) - if(whispering) - verb = speaking.whisper_verb ? speaking.whisper_verb : speaking.speech_verb - else - verb = say_quote(message, speaking) - - message = trim(html_encode(message)) // trim again post-language-parsing - message = handle_autohiss(message, speaking) - message = filter_modify_message(message) - message = handle_autopunctuation(message) - - if(speaking) - var/speech_ability_result = speaking.can_be_spoken_properly_by(src) - if(speech_ability_result == SPEECH_RESULT_MUDDLED) - message = speaking.muddle(message) - else if(speech_ability_result == SPEECH_RESULT_INCAPABLE) - to_chat(src, SPAN_WARNING("You don't have the right equipment to communicate in that way!")) // weird phrasing, but needs to cover speaking and signing - return + if(!phrases.language) + to_chat(src, SPAN_WARNING("You don't know a language and cannot speak.")) + custom_emote(AUDIBLE_MESSAGE, "[pick("grunts", "babbles", "gibbers", "jabbers", "burbles")] aimlessly.") + return + + if(handle_mob_specific_speech(phrases, verb)) + return - // The LANG_FLAG_NO_STUTTER check means nonvocal or unusually-produced - // languages (e.g. sign language, noise emotes, computer beeps) - // will not be affected by stuttering, slurring, silence, etc. effects. - if(!(speaking && (speaking.flags & LANG_FLAG_NO_STUTTER))) - var/list/message_data = list(message, verb, 0) - if(handle_speech_problems(message_data)) - message = message_data[1] - verb = message_data[2] + // This is broadcast to all mobs with the language, + // irrespective of distance or anything else. + if(phrases.language && (phrases.language.flags & LANG_FLAG_HIVEMIND)) + phrases.language.broadcast(src, phrases) + return 1 - if(!message || message == "") - return 0 + if(phrases.language) + if(whispering) + verb = phrases.language.whisper_verb ? phrases.language.whisper_verb : phrases.language.speech_verb + else + verb = say_quote(phrases.unformatted_message, phrases.language) var/list/obj/item/used_radios = list() - if(handle_message_mode(message_mode, message, verb, speaking, used_radios)) - return 1 + if(handle_message_mode(phrases, verb, used_radios)) + return TRUE - var/list/handle_v = (istype(speaking) && speaking.get_spoken_sound()) || handle_speech_sound() + var/list/handle_v = (istype(phrases.language) && phrases.language.get_spoken_sound()) || handle_speech_sound() var/sound/speech_sound = handle_v[1] var/sound_vol = handle_v[2] @@ -218,10 +305,10 @@ if(used_radios.len) italics = 1 message_range = 1 - if(speaking) - message_range = speaking.get_talkinto_msg_range(message) + if(phrases.language) + message_range = phrases.language.get_talkinto_msg_range(phrases.unformatted_message) var/msg - if(!speaking || !(speaking.flags & LANG_FLAG_NO_TALK_MSG)) + if(!phrases.language || !(phrases.language.flags & LANG_FLAG_NO_TALK_MSG)) msg = "\The [src] talks into \the [used_radios[1]]." for(var/mob/living/M in hearers(5, src)) if((M != src) && msg) @@ -229,23 +316,27 @@ if (speech_sound) sound_vol *= 0.5 - var/list/listening = list() - var/list/listening_obj = list() - var/turf/T = get_turf(src) - //handle nonverbal and sign languages here - if (speaking) - if (speaking.flags & LANG_FLAG_NONVERBAL) - if (prob(30)) - src.custom_emote(1, "[pick(speaking.signlang_verb)].") + var/is_nonverbal = FALSE + for(var/list/phrase in phrases.phrases) + var/decl/language/speaking = phrase[2] + if(!speaking) + continue + // Signlang is problematic for mixed-language speech... probably should check earlier and enforce not mixing signlang and vocal lang. + if((speaking.flags & LANG_FLAG_SIGNLANG)) + log_say("[name]/[key] : SIGN: [phrase[1]]") + return say_signlang(phrase[1], pick(speaking.signlang_verb), speaking) + if(!is_nonverbal && (speaking.flags & LANG_FLAG_NONVERBAL) && prob(30)) + is_nonverbal = pick(speaking.signlang_verb) + if(is_nonverbal) + custom_emote(1, "[is_nonverbal].") - if (speaking.flags & LANG_FLAG_SIGNLANG) - log_say("[name]/[key] : SIGN: [message]") - return say_signlang(message, pick(speaking.signlang_verb), speaking) - - if(T) + var/list/listening = list() + var/list/listening_obj = list() + var/turf/speaker_turf = get_turf(src) + if(speaker_turf) //make sure the air can transmit speech - speaker's side - var/datum/gas_mixture/environment = T.return_air() + var/datum/gas_mixture/environment = speaker_turf.return_air() var/pressure = (environment)? environment.return_pressure() : 0 if(pressure < SOUND_MINIMUM_PRESSURE) message_range = 1 @@ -254,9 +345,9 @@ italics = 1 sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact - get_listeners_in_range(T, message_range, listening, listening_obj, /datum/client_preference/ghost_ears) + get_listeners_in_range(speaker_turf, message_range, listening, listening_obj, /datum/client_preference/ghost_ears) - var/speech_bubble_state = check_speech_punctuation_state(message) + var/speech_bubble_state = check_speech_punctuation_state(phrases.unformatted_message) var/speech_state_modifier = get_speech_bubble_state_modifier() if(speech_bubble_state && speech_state_modifier) speech_bubble_state = "[speech_state_modifier]_[speech_bubble_state]" @@ -270,43 +361,43 @@ var/list/speech_bubble_recipients = list() for(var/mob/M in listening) if(M) - M.hear_say(message, verb, speaking, italics, src, speech_sound, sound_vol) + M.hear_say(phrases, verb, italics, src, speech_sound, sound_vol) if(M.client) speech_bubble_recipients += M.client 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, phrases, verb) var/list/eavesdroppers = list() if(whispering) var/eavesdroping_range = 5 var/list/eavesdroping = list() var/list/eavesdroping_obj = list() - get_listeners_in_range(T, eavesdroping_range, eavesdroping, eavesdroping_obj) + get_listeners_in_range(speaker_turf, eavesdroping_range, eavesdroping, eavesdroping_obj) eavesdroping -= listening eavesdroping_obj -= listening_obj for(var/mob/M in eavesdroping) if(M) - M.hear_say(stars(message), verb, speaking, italics, src, speech_sound, sound_vol) + M.hear_say(phrases, verb, italics, src, speech_sound, sound_vol, stars = TRUE) if(M.client) eavesdroppers |= M.client for(var/obj/O in eavesdroping) spawn(0) if(O) //It's possible that it could be deleted in the meantime. - O.hear_talk(src, stars(message), verb, speaking) + O.hear_talk(src, phrases, verb, stars = TRUE) INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(animate_speech_bubble), speech_bubble, speech_bubble_recipients | eavesdroppers, 30) - INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, animate_chat), message, speaking, italics, speech_bubble_recipients) + INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, animate_chat), phrases, italics, speech_bubble_recipients) if(length(eavesdroppers)) - INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, animate_chat), stars(message), speaking, italics, eavesdroppers) + INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, animate_chat), phrases, italics, eavesdroppers, TRUE) if(whispering) - log_whisper("[name]/[key] : [message]") + log_whisper("[name]/[key] : [phrases.unformatted_message]") else - log_say("[name]/[key] : [message]") + log_say("[name]/[key] : [phrases.unformatted_message]") return 1 /mob/living/proc/say_signlang(var/message, var/verb="gestures", var/decl/language/language) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index e72e3798f8cc..d2cf96b42fcf 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -359,7 +359,7 @@ var/global/list/custom_ai_icons_by_ckey_and_name = list() //Carn: holopad requests if (href_list["jumptoholopad"]) - var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"]) + var/obj/machinery/holopad/H = locate(href_list["jumptoholopad"]) if(stat == CONSCIOUS) if(H) H.attack_ai(src) //may as well recycle diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index 732261c052d2..6509fd541f9b 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,5 +1,5 @@ -/mob/living/silicon/pai/say(var/msg) +/mob/living/silicon/pai/say(datum/speech/phrases, verb = "says", whispering) if(HAS_STATUS(src, STAT_SILENCE)) to_chat(src, SPAN_WARNING("Communication circuits are disabled.")) return - return ..(msg) + return ..() 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 e4d40ac20dfe..358045c3beda 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -1,12 +1,11 @@ -/mob/living/silicon/robot/drone/say(var/message) +/mob/living/silicon/robot/drone/say(datum/speech/phrases, verb = "says", whispering) if(local_transmit) if (src.client) if(client.prefs.muted & MUTE_IC) to_chat(src, "You cannot send IC messages (muted).") return 0 - message = sanitize(message) - + var/message = sanitize(istype(phrases) ? phrases.unformatted_message : phrases) // we don't care about languages etc. for drones, so don't parse it into a datum. if (stat == DEAD) return say_dead(message) @@ -32,10 +31,10 @@ if(D.client && D.local_transmit) to_chat(D, "[src] transmits, \"[message]\"") - for (var/mob/M in global.player_list) - if (isnewplayer(M)) + for (var/mob/listener in global.player_list) + if (isnewplayer(listener)) continue - else if(M.stat == DEAD && M.get_preference_value(/datum/client_preference/ghost_ears) == PREF_ALL_SPEECH) - if(M.client) to_chat(M, "[src] transmits, \"[message]\"") + if(listener.stat == DEAD && listener.get_preference_value(/datum/client_preference/ghost_ears) == PREF_ALL_SPEECH && listener.client) + to_chat(listener, "[src] transmits, \"[message]\"") return 1 - return ..(message, 0) + return ..() diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index c8c23e0d3c12..2d069982b4ad 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -9,9 +9,9 @@ to_chat(src, SPAN_WARNING("Your radio isn't functional at this time.")) LAZYREMOVE(., silicon_radio) -/mob/living/silicon/ai/handle_mob_specific_speech(message, message_mode, verb = "says", decl/language/speaking) - if(message_mode == MESSAGE_MODE_DEPARTMENT) - holopad_talk(message, verb, speaking) +/mob/living/silicon/ai/handle_mob_specific_speech(datum/speech/phrases, verb = "says") + if(phrases.message_mode == MESSAGE_MODE_DEPARTMENT) + holopad_talk(phrases, verb) return TRUE return ..() @@ -39,23 +39,16 @@ return (!speaking && (ishuman(speaker) || issilicon(speaker) || isbrain(speaker))) || ..() //For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(var/message, verb, decl/language/speaking) +/mob/living/silicon/ai/proc/holopad_talk(datum/speech/phrases, verb) - log_say("[key_name(src)] : [message]") + log_say("[key_name(src)]: [phrases.unformatted_message]") - message = trim(message) - - if (!message) - return - - var/obj/machinery/hologram/holopad/H = src.holo + var/obj/machinery/holopad/H = src.holo if(H && H.masters[src])//If there is a hologram and its master is the user. + var/list/messages = phrases.compile_for_listener(src, machine_listener = TRUE) // AI can hear their own message, this formats it for them. - if(speaking) - to_chat(src, "Holopad transmitted, [real_name] [speaking.format_message(message, verb)]") - else - to_chat(src, "Holopad transmitted, [real_name] [verb], \"[message]\"") + to_chat(src, "Holopad transmitted, [real_name] [phrases.language.format_message(messages[1], verb)]") //This is so pAI's and people inside lockers/boxes,etc can hear the AI Holopad, the alternative being recursion through contents. //This is much faster. @@ -79,19 +72,16 @@ hearturfs += O.locs[1] listening_obj |= O - for(var/mob/M in global.player_list) if(M.stat == DEAD && M.get_preference_value(/datum/client_preference/ghost_ears) == PREF_ALL_SPEECH) - M.hear_say(message,verb,speaking, null, src) + M.hear_say(phrases, verb, null, src) continue if(M.loc && (M.locs[1] in hearturfs)) - M.hear_say(message,verb,speaking, null, src) - - + M.hear_say(phrases, verb, null, src) else to_chat(src, "No holopad connected.") - return 0 - return 1 + return FALSE + return TRUE /mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. @@ -102,7 +92,7 @@ if (!message) return - var/obj/machinery/hologram/holopad/T = src.holo + var/obj/machinery/holopad/T = src.holo if(T && T.masters[src]) var/turf/our_turf = get_turf(T) var/rendered = "[name] [message]" diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index b5508d6d2217..66b052dff515 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -216,7 +216,7 @@ default_str = " - set default" var/synth = (L in speech_synthesizer_langs) - dat += "[L.name] ([get_language_prefix()][L.key])[synth ? default_str : null]
Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
[L.desc]

" + dat += "[L.name] ([get_language_prefix()][L.language_key])[synth ? default_str : null]
Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
[L.desc]

" show_browser(src, dat, "window=checklanguage") return diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index 88fceca95f7e..9b6f2e7f4c53 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -402,14 +402,10 @@ var/global/list/simplemob_icon_bitflag_cache = list() damage = 30 apply_damage(damage, BRUTE, damage_flags = DAM_EXPLODE) -/mob/living/simple_animal/say(var/message) - var/verb = "says" +/mob/living/simple_animal/say(datum/speech/phrases, verb = "says", whispering) if(speak_emote.len) verb = pick(speak_emote) - - message = sanitize(message) - - ..(message, null, verb) + ..() /mob/living/simple_animal/is_burnable() return heat_damage_per_tick diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm index 9b77858189e3..70ce30bfaecd 100644 --- a/code/modules/mob/living/simple_animal/friendly/possum.dm +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -109,13 +109,14 @@ /mob/living/simple_animal/opossum/poppy/is_tagging_suitable() return FALSE -/mob/living/simple_animal/opossum/poppy/hear_broadcast(decl/language/language, mob/speaker, speaker_name, message) +// Poppy is omnilingual apparently. +/mob/living/simple_animal/opossum/poppy/hear_broadcast(mob/speaker, speaker_name, datum/speech/phrases) . = ..() - addtimer(CALLBACK(src, PROC_REF(check_keywords), message), rand(1 SECOND, 3 SECONDS)) + addtimer(CALLBACK(src, PROC_REF(check_keywords), phrases.unformatted_message), rand(1 SECOND, 3 SECONDS)) -/mob/living/simple_animal/opossum/poppy/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/living/simple_animal/opossum/poppy/hear_say(datum/speech/phrases, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, stars = FALSE, atom/relayed_by) . = ..() - addtimer(CALLBACK(src, PROC_REF(check_keywords), message), rand(1 SECOND, 3 SECONDS)) + addtimer(CALLBACK(src, PROC_REF(check_keywords), phrases.unformatted_message), rand(1 SECOND, 3 SECONDS)) /mob/living/simple_animal/opossum/poppy/proc/check_keywords(var/message) if(!client && istype(ai) && stat == CONSCIOUS) diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/_commanded.dm b/code/modules/mob/living/simple_animal/hostile/commanded/_commanded.dm index 99f449419a73..7c5f622ab5e3 100644 --- a/code/modules/mob/living/simple_animal/hostile/commanded/_commanded.dm +++ b/code/modules/mob/living/simple_animal/hostile/commanded/_commanded.dm @@ -4,10 +4,10 @@ density = FALSE ai = /datum/mob_controller/aggressive/commanded -/mob/living/simple_animal/hostile/commanded/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) - ai?.memorise(speaker, message) +/mob/living/simple_animal/hostile/commanded/hear_say(datum/speech/phrases, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, stars = FALSE, atom/relayed_by) + ai?.memorise(speaker, phrases.unformatted_message) return ..() -/mob/living/simple_animal/hostile/commanded/hear_radio(var/message, var/verb="says", var/decl/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/vsource) - ai?.memorise(speaker, message) +/mob/living/simple_animal/hostile/commanded/hear_radio(datum/speech/phrases, verb = "says", part_a, part_b, part_c, mob/speaker, hard_to_hear = FALSE, vname = "", vsource, scramble = FALSE) + ai?.memorise(speaker, phrases.unformatted_message) return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm index 3e5bb0112dbf..3fd37e9a273e 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm @@ -65,9 +65,9 @@ new /obj/item/ectoplasm(get_turf(src)) qdel(src) -/mob/living/simple_animal/faithful_hound/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/living/simple_animal/faithful_hound/hear_say(datum/speech/phrases, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, stars = FALSE, atom/relayed_by) set waitfor = FALSE - if(!ai?.check_memory(speaker, message)) + if(!ai?.check_memory(speaker, phrases.unformatted_message)) return ai.add_friend(speaker) sleep(1 SECOND) diff --git a/code/modules/mob/living/speech.dm b/code/modules/mob/living/speech.dm new file mode 100644 index 000000000000..1b1b7e56fc1a --- /dev/null +++ b/code/modules/mob/living/speech.dm @@ -0,0 +1,122 @@ +// Structure used for passing around spoken text. +/datum/speech + var/weakref/speaker_ref + var/raw_message + var/message_mode + var/list/phrases + var/force_verb + var/formatted_message + var/unformatted_message + var/decl/language/language + var/incoherent_language_flagging = FALSE + +/datum/speech/New(mob/living/_speaker, _msg, _mode, _phrases, _verb) + speaker_ref = weakref(_speaker) + raw_message = _msg + message_mode = _mode + force_verb = _verb + phrases = _phrases + + var/saw_signlang = null + var/saw_broadcast = null + var/list/all_phrases = list() + var/first_string = TRUE + for(var/list/phrase in phrases) + + var/decl/language/speaking = phrase[2] + + if(speaking) + + // Keep track of our first vocal language. + language ||= speaking + + // Check if our combined language flagging is incoherent. + // Can't mix sign lang, vocal lang and broadcast in the same message. + if(!incoherent_language_flagging) + if(speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG)) + if(isnull(saw_signlang)) + saw_signlang = TRUE + else if(!saw_signlang) + incoherent_language_flagging = TRUE + else + if(isnull(saw_signlang)) + saw_broadcast = FALSE + else if(saw_signlang) + incoherent_language_flagging = TRUE + + if(speaking.flags & LANG_FLAG_HIVEMIND) + if(isnull(saw_broadcast)) + saw_broadcast = TRUE + else if(!saw_broadcast) + incoherent_language_flagging = TRUE + else + if(isnull(saw_broadcast)) + saw_broadcast = FALSE + else if(saw_broadcast) + incoherent_language_flagging = TRUE + + phrase[1] = trim(phrase[1]) + if(first_string) + phrase[1] = capitalize(phrase[1]) + first_string = FALSE + // Pre-generate scrambled versions for people who don't understand the language. + phrase += speaking ? speaking.scramble(_speaker, phrase[1], _speaker.languages) : phrase[1] + // Pre-generate obfuscated starred version for people who are hard of hearing, eavesdropping, etc. + phrase += stars(phrase[1]) + all_phrases += phrase[1] + // Store fully translated, stripped versions for stuff like broadcasts/ghosts/logs. + unformatted_message = jointext(all_phrases, " ") + formatted_message = language?.format_message_no_verb(unformatted_message) || unformatted_message + +// Returns a list of formatted and unformatted message strings for display to a target listener. +// Future TODO: cache as much of this as possible. +/datum/speech/proc/compile_for_listener(atom/listener, skip_verbal = FALSE, skip_non_verbal = FALSE, scramble = FALSE, stars = FALSE, hard_to_hear = FALSE, machine_listener = FALSE) + + var/mob/speaker = speaker_ref.resolve() + var/mob/listener_mob = ismob(listener) ? listener : null + var/list/final_strings = list() + var/list/formatted_strings = list() + + for(var/list/phrase in phrases) + var/decl/language/speaking = phrase[2] + + if(speaking) + if(skip_verbal && !(speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG|LANG_FLAG_HIVEMIND))) + continue + if(skip_non_verbal && (speaking.flags & (LANG_FLAG_NONVERBAL|LANG_FLAG_SIGNLANG|LANG_FLAG_HIVEMIND))) + continue + + var/message = phrase[1] + if(scramble || (machine_listener && !speaking?.machine_understands)) + var/decl/language/machine/noise_lang = GET_DECL(/decl/language/machine) + message = noise_lang.scramble(null, message, null) + //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet. + else if(stars || ((speaking?.flags & LANG_FLAG_NONVERBAL) && (!speaker || listener_mob?.is_blind() || !(speaker in view(listener))))) + message = phrase[4] + + // skip understanding checks for LANG_FLAG_INNATE languages + if(!(speaking?.flags & LANG_FLAG_INNATE)) + if(!listener_mob?.say_understands(speaker, speaking)) + if(isanimal(speaker)) + if(LAZYLEN(speaker.ai?.emote_speech)) + message = pick(speaker.ai.emote_speech) + else + if(speaking) + message = phrase[3] + else + message = phrase[4] + + if(hard_to_hear) + if(hard_to_hear <= 5) + message = phrase[4] + else // Used for compression + message = RadioChat(null, message, 80, 1+(hard_to_hear/10)) + + message = trim(message) + final_strings += message + formatted_strings += speaking ? speaking.format_message_no_verb(message) : message + + return list( + handle_autopunctuation(filter_modify_message(jointext(final_strings, " "))), + handle_autopunctuation(filter_modify_message(jointext(formatted_strings, " "))) + ) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index e0952f1222c5..73b87c6862fd 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -61,7 +61,8 @@ if(ispath(ai_type)) return ai_type -/mob/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) +//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) +/mob/show_message(msg, type, alt, alt_type, atom/source) if(!client) return //spaghetti code @@ -89,7 +90,7 @@ // message is the message output to anyone who can see e.g. "[src] does something!" // self_message (optional) is what the src mob sees e.g. "You do something!" // blind_message (optional) is what blind people will hear e.g. "You hear something!" -/mob/visible_message(var/message, var/self_message, var/blind_message, var/range = world.view, var/check_ghosts = null, var/narrate = FALSE) +/mob/visible_message(var/message, var/self_message, var/blind_message, var/range = world.view, var/check_ghosts = null, var/narrate = FALSE, atom/source) var/turf/T = get_turf(src) var/list/mobs = list() var/list/objs = list() @@ -97,7 +98,7 @@ for(var/o in objs) var/obj/O = o - O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE, source = source) for(var/m in mobs) var/mob/M = m @@ -109,19 +110,19 @@ mob_message = add_ghost_track(mob_message, M) if(self_message && M == src) - M.show_message(self_message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + M.show_message(self_message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE, source = source) continue if(!M.is_blind() || narrate) - M.show_message(mob_message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + M.show_message(mob_message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE, source = source) continue if(blind_message) - M.show_message(blind_message, AUDIBLE_MESSAGE) + M.show_message(blind_message, AUDIBLE_MESSAGE, source = source) continue //Multiz, have shadow do same if(bound_overlay) - bound_overlay.visible_message(message, self_message, blind_message) + bound_overlay.visible_message(message, self_message, blind_message, source = source) /mob/proc/get_action_string(is_self, var/using_verb, var/object_phrase, var/infix, var/postfix) var/decl/pronouns/using_pronouns = is_self ? get_self_pronouns() : get_visible_pronouns() @@ -245,7 +246,7 @@ var/global/const/ACTION_DANGER_ALL = 2 // self_message (optional) is what the src mob hears. // deaf_message (optional) is what deaf people will see. // hearing_distance (optional) is the range, how many tiles away the message can be heard. -/mob/audible_message(var/message, var/self_message, var/deaf_message, var/hearing_distance = world.view, var/check_ghosts = null, var/narrate = FALSE, var/radio_message) +/mob/audible_message(var/message, var/self_message, var/deaf_message, var/hearing_distance = world.view, var/check_ghosts = null, var/narrate = FALSE, var/radio_message, atom/source) var/turf/T = get_turf(src) var/list/mobs = list() var/list/objs = list() @@ -261,18 +262,18 @@ var/global/const/ACTION_DANGER_ALL = 2 mob_message = add_ghost_track(mob_message, M) if(self_message && M == src) - M.show_message(self_message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + M.show_message(self_message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE, source = source) else if(is_invisible_to(M) || narrate) // Cannot view the invisible - M.show_message(mob_message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + M.show_message(mob_message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE, source = source) else - M.show_message(mob_message, AUDIBLE_MESSAGE) + M.show_message(mob_message, AUDIBLE_MESSAGE, source = source) for(var/o in objs) var/obj/O = o if(radio_message) - O.hear_talk(src, radio_message, null, GET_DECL(/decl/language/noise)) + O.hear_talk(src, radio_message, null, null, GET_DECL(/decl/language/noise)) else - O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE, source = source) /mob/proc/add_ghost_track(var/message, var/mob/observer/ghost/M) ASSERT(istype(M)) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 77ef427f3e49..88177feffe4c 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -441,13 +441,13 @@ INITIALIZE_IMMEDIATE(/mob/new_player) return global.using_map.default_species return chosen_species.name -/mob/new_player/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/italics = 0, var/mob/speaker = null) +/mob/new_player/hear_say(datum/speech/phrases, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, stars = FALSE, atom/relayed_by) return -/mob/new_player/hear_radio(var/message, var/verb="says", var/decl/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/vsource) +/mob/new_player/hear_radio(datum/speech/phrases, verb = "says", part_a, part_b, part_c, mob/speaker, hard_to_hear = FALSE, vname = "", vsource, scramble = FALSE) return -/mob/new_player/show_message(msg, type, alt, alt_type) +/mob/new_player/show_message(msg, type, alt, alt_type, atom/source) return /mob/new_player/MayRespawn() @@ -456,8 +456,8 @@ INITIALIZE_IMMEDIATE(/mob/new_player) /mob/new_player/touch_map_edge(var/overmap_id = OVERMAP_ID_SPACE) return -/mob/new_player/say(var/message) - sanitize_and_communicate(/decl/communication_channel/ooc, client, message) +/mob/new_player/say(datum/speech/phrases, verb = "says", whispering) + sanitize_and_communicate(/decl/communication_channel/ooc, client, istype(phrases) ? phrases.unformatted_message : phrases) /mob/new_player/verb/next_lobby_track() set name = "Play Different Lobby Track" diff --git a/code/modules/mob/observer/eye/freelook/ai/eye.dm b/code/modules/mob/observer/eye/freelook/ai/eye.dm index d7bee9821a03..3d736b0c3a37 100644 --- a/code/modules/mob/observer/eye/freelook/ai/eye.dm +++ b/code/modules/mob/observer/eye/freelook/ai/eye.dm @@ -49,7 +49,7 @@ // The AI's "eye". Described on the top of the page. /mob/living/silicon/ai - var/obj/machinery/hologram/holopad/holo = null + var/obj/machinery/holopad/holo = null /mob/living/silicon/ai/proc/destroy_eyeobj(var/atom/new_eye) if(!eyeobj) return diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm index aa1268cbaa5d..c95bf7526b20 100644 --- a/code/modules/mob/observer/ghost/ghost.dm +++ b/code/modules/mob/observer/ghost/ghost.dm @@ -639,5 +639,5 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ..() addtimer(CALLBACK(src, PROC_REF(check_existence_failure)), 0) -/mob/observer/ghost/say(var/message) - sanitize_and_communicate(/decl/communication_channel/dsay, client, message) +/mob/observer/ghost/say(datum/speech/phrases, verb = "says", whispering) + sanitize_and_communicate(/decl/communication_channel/dsay, client, phrases.unformatted_message) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index db8faa6840aa..974c84c8228c 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -12,7 +12,7 @@ var/global/list/special_channel_keys = list( "ц" = MESSAGE_MODE_WHISPER ) -/mob/proc/say() +/mob/proc/say(datum/speech/phrases, verb = "says", whispering) return /mob/verb/whisper() @@ -86,19 +86,53 @@ var/global/list/special_channel_keys = list( var/channel_prefix = copytext(message, 2, 3) . = global.special_channel_keys[channel_prefix] || channel_prefix -//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) +// returns a language based on the supplied key +/mob/proc/get_language_by_key(language_key) + + // This is a numerical key to our default language. + var/num_key = isnum(language_key) ? language_key : text2num(language_key) + if(num_key == 0) + return get_default_language() + + // This is a positional numerical key to our language list. + if(num_key > 0 && num_key <= length(languages)) + return languages[num_key] + + // This is a text key - it's either a full key reference, or a partial reference to a language we know. + // Try the full language list first. + var/static/list/languages_by_key + if(!languages_by_key) + languages_by_key = list() + for(var/decl/language/lang in decls_repository.get_decls_of_subtype_unassociated(/decl/language)) + if(lang.language_key) + languages_by_key[lang.language_key] = lang + language_key = lowertext(language_key) + . = languages_by_key[language_key] + if(.) + return + // Find the first language that has a key starting with the key we asked for. + for(var/decl/language/lang as anything in languages) + lang = RESOLVE_TO_DECL(lang) + if(!lang?.language_key) // no key, why is this non-abstract? + continue + if(length(lang.language_key) <= length(language_key)) // if we had identical keys, we'd have returned already + continue + if(copytext(lang.language_key, 1, length(language_key)+1) == language_key) + return lang + +//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(message) var/prefix = copytext_char(message,1,2) if(length(message) >= 1 && prefix == get_prefix_key(/decl/prefix/audible_emote)) return GET_DECL(/decl/language/noise) - - if(length(message) >= 2 && is_language_prefix(prefix)) - var/language_prefix = lowertext(copytext_char(message, 2 ,3)) - var/decl/language/L = SSlore.get_language_by_key(language_prefix) - if (can_speak(L)) - return L + if(length(message) >= 2 && prefix == get_prefix_key(/decl/prefix/language)) + var/key_end = findtext_char(message, " ") + if(key_end > 0) + var/decl/language/speaking = get_language_by_key(lowertext(copytext_char(message, 2, key_end))) + if(speaking && can_speak(speaking)) + return speaking /mob/proc/is_silenced() . = !!get_item_blocking_speech() @@ -115,11 +149,8 @@ var/global/list/special_channel_keys = list( if(mask?.blocks_speech_in_mouth(src)) return mask -/// Adds punctuation to an emote or speech message automatically. -/mob/proc/handle_autopunctuation(message) - if(!message) - return - var/end_char = copytext_char(trim_right(strip_html_properly(message)), -1) - if(!(end_char in list(".", "?", "!", "-", "~"))) - message += "." - return message \ No newline at end of file +/mob/proc/get_default_language() + return + +/mob/proc/get_any_good_language(set_default=FALSE) + return diff --git a/code/modules/mob_modifiers/definitions/modifiers_stasis.dm b/code/modules/mob_modifiers/definitions/modifiers_stasis.dm index e855920a734c..8f6aeec4721a 100644 --- a/code/modules/mob_modifiers/definitions/modifiers_stasis.dm +++ b/code/modules/mob_modifiers/definitions/modifiers_stasis.dm @@ -18,7 +18,6 @@ var/add_stasis = source_atom.get_cryogenic_power() if(add_stasis) stasis_power += add_stasis - to_chat(_owner, "[source_atom] gave [add_stasis]") if(stasis_power > 1 && GET_STATUS(_owner, STAT_DROWSY) < stasis_power * 4) ADJ_STATUS(_owner, STAT_DROWSY, min(stasis_power, 3)) diff --git a/code/modules/modular_computers/networking/machinery/telecomms.dm b/code/modules/modular_computers/networking/machinery/telecomms.dm index 7afe7441668f..02a06723d7ed 100644 --- a/code/modules/modular_computers/networking/machinery/telecomms.dm +++ b/code/modules/modular_computers/networking/machinery/telecomms.dm @@ -138,7 +138,7 @@ var/global/list/telecomms_hubs = list() var/datum/extension/network_device/network_device = get_extension(src, /datum/extension/network_device) return network_device?.get_network() == check_network_membership -/obj/machinery/network/telecomms_hub/proc/transmit_message(mob/speaker, message, message_verb, decl/language/speaking, frequency, message_compression, list/checked_hubs, list/encryption = list(), obj/effect/overmap/send_overmap_object, chain_transmit = TRUE) +/obj/machinery/network/telecomms_hub/proc/transmit_message(mob/speaker, datum/speech/phrases, message_verb, frequency, message_compression, list/checked_hubs, list/encryption = list(), obj/effect/overmap/send_overmap_object, chain_transmit = TRUE) if(src in checked_hubs) return checked_hubs += src @@ -190,7 +190,7 @@ var/global/list/telecomms_hubs = list() listeners[ghost_listener] = TRUE for(var/mob/listener in listeners) - listener.hear_radio(message, message_verb, speaking, formatted_msg, " ", "", speaker, message_compression, vname = send_name, vsource = (listeners[listener] ? send_overmap_object.name : null)) + listener.hear_radio(phrases, message_verb, formatted_msg, " ", "", speaker, message_compression, vname = send_name, vsource = (listeners[listener] ? send_overmap_object.name : null)) if(!chain_transmit) return @@ -233,7 +233,7 @@ var/global/list/telecomms_hubs = list() if(QDELETED(other_hub) || !other_hub.can_receive_message(check_network_membership)) continue // Don't allow further chaining of the message. - other_hub.transmit_message(speaker, message, message_verb, speaking, frequency, message_compression, checked_hubs, encryption.Copy(), send_overmap_object, FALSE) + other_hub.transmit_message(speaker, phrases, message_verb, frequency, message_compression, checked_hubs, encryption.Copy(), send_overmap_object, FALSE) /obj/machinery/network/telecomms_hub/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) diff --git a/code/modules/xenoarcheaology/finds/find_types/mask.dm b/code/modules/xenoarcheaology/finds/find_types/mask.dm index 37627ffa114d..1251b220fd5b 100644 --- a/code/modules/xenoarcheaology/finds/find_types/mask.dm +++ b/code/modules/xenoarcheaology/finds/find_types/mask.dm @@ -29,10 +29,10 @@ var/mob/living/M = src.loc M.say(pick(heard_talk)) -/obj/item/clothing/mask/gas/poltergeist/hear_talk(mob/M, text) +/obj/item/clothing/mask/gas/poltergeist/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) ..() if(heard_talk.len > max_stored_messages) heard_talk.Remove(pick(heard_talk)) - heard_talk.Add(text) + heard_talk.Add(istype(phrases) ? phrases.unformatted_message : phrases) if(isliving(src.loc) && world.time - last_twitch > 50) last_twitch = world.time \ No newline at end of file diff --git a/code/modules/xenoarcheaology/finds/find_types/statuette.dm b/code/modules/xenoarcheaology/finds/find_types/statuette.dm index e5d985de8dc9..caa8f8eb51a7 100644 --- a/code/modules/xenoarcheaology/finds/find_types/statuette.dm +++ b/code/modules/xenoarcheaology/finds/find_types/statuette.dm @@ -94,10 +94,10 @@ else if(get_dist(wight, src) > 10) shadow_wights.Remove(wight_check_index) -/obj/item/vampiric/hear_talk(mob/M, text) +/obj/item/vampiric/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) ..() - if(world.time - last_bloodcall >= bloodcall_interval && (M in view(7, src))) - bloodcall(M) + if(world.time - last_bloodcall >= bloodcall_interval && (speaker in view(7, src))) + bloodcall(speaker) /obj/item/vampiric/proc/bloodcall(var/mob/living/human/M) last_bloodcall = world.time diff --git a/code/unit_tests/unique_tests.dm b/code/unit_tests/unique_tests.dm index cef4aba20aab..4d0c6be655f7 100644 --- a/code/unit_tests/unique_tests.dm +++ b/code/unit_tests/unique_tests.dm @@ -100,7 +100,7 @@ for(var/lt in decls_repository.get_decl_paths_of_subtype(/decl/language)) var/decl/language/l = lt - var/language_key = initial(l.key) + var/language_key = lowertext(initial(l.language_key)) if(!language_key) continue @@ -239,7 +239,7 @@ var/list/failures = list() var/list/seen_holopad_ids = list() - for(var/obj/machinery/hologram/holopad/holopad in global.holopads) + for(var/obj/machinery/holopad/holopad in global.holopads) var/area/area = get_area(holopad) var/holopad_loc = "x[holopad.x],y[holopad.y],z[holopad.z] - [area?.proper_name || "Unknown"]" if(istext(holopad.holopad_id)) diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index a96df6a57b6b..7b9d13be5f66 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -140,7 +140,7 @@ "av" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/hologram/holopad/longrange/remoteship, +/obj/machinery/holopad/longrange/remoteship, /turf/floor/tiled/dark/usedup, /area/ship/scrap/command/bridge) "aw" = ( diff --git a/maps/away/liberia/liberia.dmm b/maps/away/liberia/liberia.dmm index 8d9c0495340a..c1af31df67a8 100644 --- a/maps/away/liberia/liberia.dmm +++ b/maps/away/liberia/liberia.dmm @@ -1126,7 +1126,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 6 }, -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/carpet/blue2, /area/liberia/mule) "cf" = ( @@ -1898,7 +1898,7 @@ /turf/floor/tiled/dark, /area/liberia/bridge) "dv" = ( -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /obj/effect/floor_decal/corner/blue/mono, /obj/effect/overmap/visitable/ship/liberia, /turf/floor/tiled/dark/monotile, diff --git a/maps/away/unishi/unishi-3.dmm b/maps/away/unishi/unishi-3.dmm index ccb4c5e12c0d..2bc46bef4711 100644 --- a/maps/away/unishi/unishi-3.dmm +++ b/maps/away/unishi/unishi-3.dmm @@ -1954,7 +1954,7 @@ /turf/floor/laminate, /area/unishi/living) "ur" = ( -/obj/machinery/hologram/holopad/longrange/remoteship, +/obj/machinery/holopad/longrange/remoteship, /turf/floor/tiled, /area/unishi/bridge) "vn" = ( diff --git a/maps/exodus/exodus-1.dmm b/maps/exodus/exodus-1.dmm index e9125095e1b6..2e86a4f75f15 100644 --- a/maps/exodus/exodus-1.dmm +++ b/maps/exodus/exodus-1.dmm @@ -4281,7 +4281,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/atmos) "lm" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/engineering/atmos) "ln" = ( @@ -6091,7 +6091,7 @@ "Si" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/lino, /area/exodus/maintenance/telecomms) "Sp" = ( diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 2ec898d7fddb..71bfd4665150 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -1952,7 +1952,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/main) "aen" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Security South" }, /turf/floor/tiled/steel_grid, @@ -2098,7 +2098,7 @@ /turf/wall/prepainted, /area/exodus/security/meeting) "aeD" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/security/meeting) "aeE" = ( @@ -3776,7 +3776,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/brig/interrogation) "aip" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "4-8" }, @@ -3960,7 +3960,7 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/dark, /area/exodus/security/warden) "aiM" = ( @@ -4745,7 +4745,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/security/brig/processing) "aki" = ( @@ -7124,7 +7124,7 @@ /turf/floor/lino, /area/exodus/security/detectives_office) "aoY" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/dark, /area/exodus/lawoffice) "aoZ" = ( @@ -7433,7 +7433,7 @@ /turf/floor/laminate, /area/exodus/maintenance/dormitory) "apH" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/security/lobby) "apI" = ( @@ -7823,7 +7823,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/security/detectives_office) "aqr" = ( @@ -8609,7 +8609,7 @@ /obj/structure/cable/green{ icon_state = "2-8" }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/crew_quarters/kitchen) "arW" = ( @@ -9066,7 +9066,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/security/brig/solitaryA) "asP" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/security/prison) "asQ" = ( @@ -9775,7 +9775,7 @@ /turf/floor/tiled/freezer, /area/exodus/crew_quarters/sleep/cryo) "auG" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/freezer, /area/exodus/crew_quarters/sleep/cryo) "auH" = ( @@ -10328,7 +10328,7 @@ /obj/structure/cable/green{ icon_state = "1-2" }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/abstract/landmark/paperwork_finish_exodus, /turf/floor/tiled/steel_grid, /area/exodus/bridge) @@ -11370,7 +11370,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/crew_quarters/fitness) "ayc" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/laminate/walnut, /area/exodus/crew_quarters/sleep/bedrooms) "ayd" = ( @@ -11767,7 +11767,7 @@ /obj/structure/cable/green{ icon_state = "4-8" }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -11965,7 +11965,7 @@ /obj/effect/floor_decal/corner/blue{ dir = 6 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/fitness) "azg" = ( @@ -14130,7 +14130,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/ai_monitored/storage/eva) "aEn" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-2" }, @@ -15230,7 +15230,7 @@ dir = 8 }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/laminate/walnut, /area/exodus/crew_quarters/bar/cabin) "aGp" = ( @@ -18207,7 +18207,7 @@ dir = 8; pixel_x = 24 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/lino, /area/exodus/chapel/office) "aMP" = ( @@ -19191,7 +19191,7 @@ /turf/floor/tiled/dark, /area/exodus/chapel/main) "aOK" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/effect/floor_decal/corner/lime{ @@ -19760,7 +19760,7 @@ /area/exodus/hydroponics) "aPT" = ( /obj/structure/disposalpipe/segment, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/effect/floor_decal/corner/lime{ dir = 6 }, @@ -21800,7 +21800,7 @@ /turf/floor/laminate/walnut, /area/exodus/crew_quarters/bar) "aUH" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-2" }, @@ -23215,7 +23215,7 @@ /turf/floor/tiled/dark, /area/exodus/chapel/main) "aXX" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable{ icon_state = "4-8" }, @@ -25456,7 +25456,7 @@ /obj/effect/floor_decal/corner/lime{ dir = 1 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/hydroponics/garden) "bcK" = ( @@ -25709,7 +25709,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/hallway/secondary/exit) "bdi" = ( @@ -27313,7 +27313,7 @@ /turf/floor/carpet, /area/exodus/crew_quarters/captain) "bgR" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/carpet, /area/exodus/bridge/meeting_room) "bgS" = ( @@ -29223,7 +29223,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/office) "bkC" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Supply Foyer" }, /turf/floor/tiled/steel_grid, @@ -29751,7 +29751,7 @@ /turf/floor/laminate/walnut, /area/exodus/crew_quarters/captain) "blJ" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-2" }, @@ -30896,7 +30896,7 @@ /turf/floor/tiled/white, /area/exodus/medical/reception) "bnV" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/medical/reception) "bnW" = ( @@ -31093,7 +31093,7 @@ /turf/floor/bluegrid, /area/exodus/turret_protected/ai) "box" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 6 }, @@ -32014,7 +32014,7 @@ /turf/floor/tiled/dark, /area/exodus/medical/morgue) "bqn" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/dark, /area/exodus/medical/morgue) "bqo" = ( @@ -33156,7 +33156,7 @@ /turf/floor/tiled/dark, /area/exodus/research/lab) "bsD" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/research/lab) "bsE" = ( @@ -33793,7 +33793,7 @@ /turf/floor/tiled/white, /area/exodus/medical/exam_room) "btV" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 10 }, @@ -33893,7 +33893,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/research/robotics) "buh" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-8" }, @@ -33907,7 +33907,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/research/chargebay) "buj" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-2" }, @@ -34458,7 +34458,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/heads/hop) "bve" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/cyan{ icon_state = "1-2" }, @@ -35768,7 +35768,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) "bxU" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) "bxV" = ( @@ -36104,7 +36104,7 @@ /turf/floor/tiled/dark, /area/exodus/research/storage) "byH" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/carpet, /area/exodus/crew_quarters/heads/hop) "byI" = ( @@ -36164,7 +36164,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -36519,7 +36519,7 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/office) "bzy" = ( @@ -36594,7 +36594,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/crew_quarters/heads/hor) "bzH" = ( @@ -36944,7 +36944,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/medical/medbay) "bAp" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Medbay Storage" }, /obj/structure/cable/green{ @@ -38881,7 +38881,7 @@ /turf/floor/plating, /area/exodus/storage/tech) "bEc" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-2" }, @@ -40190,7 +40190,7 @@ /obj/structure/noticeboard{ default_pixel_y = 28 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/effect/floor_decal/corner/paleblue{ dir = 5 }, @@ -40355,7 +40355,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "4-8" }, @@ -41661,7 +41661,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/central_three) "bJt" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable{ icon_state = "4-8" }, @@ -42362,7 +42362,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/qm) "bKN" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/qm) "bKO" = ( @@ -42610,7 +42610,7 @@ /turf/wall/prepainted, /area/exodus/medical/genetics/cloning) "bLp" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -43271,7 +43271,7 @@ /turf/floor/tiled/white, /area/exodus/crew_quarters/heads/cmo) "bMD" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/floor/tiled/white, /area/exodus/crew_quarters/heads/cmo) @@ -45431,7 +45431,7 @@ /turf/floor/tiled/white, /area/exodus/medical/sleeper) "bQZ" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Emergency Recovery" }, /turf/floor/tiled/white, @@ -45563,7 +45563,7 @@ /obj/effect/floor_decal/corner/pink{ dir = 5 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/medical/patient_a) "bRj" = ( @@ -45614,7 +45614,7 @@ /obj/effect/floor_decal/corner/pink{ dir = 5 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/medical/patient_b) "bRn" = ( @@ -45799,7 +45799,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/research/mixing) "bRF" = ( @@ -46208,7 +46208,7 @@ /turf/floor/tiled/white, /area/exodus/crew_quarters/medbreak) "bSw" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/effect/floor_decal/corner/grey/diagonal, /obj/item/radio/intercom{ dir = 1; @@ -47510,7 +47510,7 @@ /obj/structure/cable/green{ icon_state = "4-8" }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/effect/floor_decal/corner/purple{ dir = 5 }, @@ -47699,7 +47699,7 @@ /turf/floor/laminate/walnut, /area/exodus/engineering/break_room) "bVF" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/research/xenobiology/xenoflora) "bVG" = ( @@ -47907,7 +47907,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 }, -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Emergency Scanning" }, /turf/floor/tiled/steel_grid, @@ -47983,7 +47983,7 @@ /turf/floor/tiled/white, /area/exodus/medical/medbay4) "bWl" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -48799,7 +48799,7 @@ /turf/floor/tiled/white, /area/exodus/medical/patient_wing) "bXS" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "4-8" }, @@ -50639,7 +50639,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/engine_eva) "cbE" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-2" }, @@ -50704,7 +50704,7 @@ /turf/floor/plating, /area/exodus/maintenance/research_starboard) "cbM" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 }, @@ -51548,7 +51548,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 10 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/medical/ward) "cdq" = ( @@ -51685,7 +51685,7 @@ /obj/effect/floor_decal/corner/pink{ dir = 10 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/medical/patient_c) "cdC" = ( @@ -52203,7 +52203,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/break_room) "ceG" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/effect/floor_decal/corner/white/diagonal, /turf/floor/tiled/steel_grid, /area/exodus/engineering/break_room) @@ -54038,7 +54038,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/engine_eva) "civ" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/engineering/engine_eva) "ciw" = ( @@ -54132,7 +54132,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/foyer) "ciK" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable/green{ icon_state = "1-2" }, @@ -54854,7 +54854,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/medical/surgeryprep) "cko" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/floor/tiled/steel_grid, @@ -59510,7 +59510,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/workshop) "cvH" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/steel_grid, /area/exodus/engineering/workshop) "cvJ" = ( @@ -59547,7 +59547,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/medical/virology) "cvM" = ( @@ -60017,7 +60017,7 @@ /turf/floor/reinforced, /area/exodus/research/xenobiology) "cxW" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/exodus/research/xenobiology) "cxX" = ( @@ -60825,7 +60825,7 @@ /turf/floor/tiled/white, /area/exodus/medical/virology) "cCW" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Virology Cafeteria" }, /turf/floor/tiled/white, @@ -63196,7 +63196,7 @@ /turf/floor/plating, /area/ship/exodus_pod_research) "elq" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Security North" }, /turf/floor/tiled/steel_grid, @@ -63391,7 +63391,7 @@ /turf/floor, /area/exodus/quartermaster/miningdock) "gpG" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/monotile, /area/exodus/teleporter) "gsd" = ( @@ -63577,7 +63577,7 @@ /turf/floor/tiled/steel_grid, /area/ship/exodus_pod_mining) "igB" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/abstract/landmark/latejoin/observer, /turf/floor/tiled/dark/monotile, /area/shuttle/arrival/station) @@ -64107,7 +64107,7 @@ /turf/space, /area/space) "qlX" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/laminate/walnut, /area/exodus/library) "qmC" = ( @@ -64662,7 +64662,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/hallway/primary/port) "woj" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Library Rec Area" }, /turf/floor/laminate/walnut, @@ -64784,7 +64784,7 @@ /turf/floor/tiled/white, /area/exodus/research) "xTe" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Sorting Office" }, /turf/floor/tiled/steel_grid, diff --git a/maps/exodus/exodus-admin.dmm b/maps/exodus/exodus-admin.dmm index 493d426ffd7b..e4c021b2a2be 100644 --- a/maps/exodus/exodus-admin.dmm +++ b/maps/exodus/exodus-admin.dmm @@ -654,7 +654,7 @@ /turf/floor/tiled/dark, /area/shuttle/escape_shuttle) "aHt" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Emergency Shuttle Bridge" }, /obj/effect/floor_decal/corner/blue{ @@ -991,7 +991,7 @@ /turf/floor/tiled/dark/monotile, /area/shuttle/escape_shuttle) "aLd" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Emergency Shuttle Brig" }, /turf/floor/tiled/dark/monotile, @@ -1406,7 +1406,7 @@ /turf/unsimulated/floor/laminate, /area/centcom/holding) "aMT" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/effect/shuttle_landmark/escape_shuttle/start, /obj/effect/floor_decal/industrial/warning{ dir = 4 @@ -1666,7 +1666,7 @@ /turf/floor/tiled, /area/shuttle/escape_shuttle) "aPa" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/unsimulated/floor/laminate, /area/centcom/holding) "aPH" = ( @@ -1677,7 +1677,7 @@ /turf/unsimulated/floor/lino, /area/centcom/holding) "aPJ" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Holding Facility Bar" }, /turf/unsimulated/floor/lino, @@ -1775,7 +1775,7 @@ /turf/unsimulated/floor/steel, /area/centcom/holding) "bkb" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Holding Facility Foyer" }, /turf/unsimulated/floor/steel, @@ -2035,7 +2035,7 @@ /turf/unsimulated/floor/vault, /area/tdome) "fpV" = ( -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Emergency Shuttle Medbay" }, /turf/floor/tiled/white, diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 51a460a05f08..690149b5934a 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -1320,7 +1320,7 @@ /area/ministation/engine) "ft" = ( /obj/effect/decal/cleanable/dirt/visible, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/item/radio/intercom/locked{ dir = 4; pixel_x = -22 @@ -9321,7 +9321,7 @@ /turf/floor/bluegrid, /area/ministation/ai_upload) "JM" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/structure/cable{ icon_state = "1-2" }, @@ -11560,7 +11560,7 @@ /obj/structure/cable/yellow{ icon_state = "4-8" }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled, /area/ministation/smcontrol) "QG" = ( diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 5cb8bef7f558..607dffa005cb 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -829,7 +829,7 @@ /turf/floor/plating, /area/ministation/maint/secmaint) "eg" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /obj/machinery/light_switch{ dir = 4; pixel_x = -23 @@ -2576,7 +2576,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/hologram/holopad{ +/obj/machinery/holopad{ holopad_id = "Security Meeting Room" }, /turf/floor/carpet/red, @@ -9625,7 +9625,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/dark, /area/ministation/security) "To" = ( @@ -10646,7 +10646,7 @@ /turf/floor/tiled/dark, /area/ministation/cafe) "Zz" = ( -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/ministation/medical) "ZE" = ( diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index c503f8dbd579..d7f91895f1bf 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -530,7 +530,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/tiled, /area/ministation/bridge) "bO" = ( @@ -1904,7 +1904,7 @@ /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 }, -/obj/machinery/hologram/holopad, +/obj/machinery/holopad, /turf/floor/tiled/white, /area/ministation/science) "im" = ( @@ -2014,7 +2014,7 @@ /area/ministation/science) "iM" = ( /obj/machinery/atmospherics/pipe/simple/hidden, -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/tiled, /area/ministation/shuttle/outgoing) "iN" = ( @@ -4913,7 +4913,7 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/lino, /area/ministation/telecomms) "CC" = ( diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index 8f79725b6aac..62e4fc20d5f2 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -6468,7 +6468,7 @@ /turf/floor/plating, /area/map_template/colony/airlock) "mx" = ( -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/tiled/techfloor, /area/map_template/colony/command) "my" = ( diff --git a/maps/tradeship/tradeship-0.dmm b/maps/tradeship/tradeship-0.dmm index 64ebd190f204..92269a5e2670 100644 --- a/maps/tradeship/tradeship-0.dmm +++ b/maps/tradeship/tradeship-0.dmm @@ -2122,7 +2122,7 @@ /turf/floor, /area/ship/trade/loading_bay) "VT" = ( -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/laminate/walnut, /area/ship/trade/disused) "Xe" = ( diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index f724fda47819..8251dbaa4b9f 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -5807,7 +5807,7 @@ /turf/floor/plating, /area/ship/trade/crew/hallway/port) "Ba" = ( -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/tiled, /area/ship/trade/shuttle/outgoing/general) "Bb" = ( @@ -8019,7 +8019,7 @@ /turf/space, /area/space) "YE" = ( -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /obj/structure/cable{ icon_state = "1-2" }, diff --git a/mods/content/bigpharma/language.dm b/mods/content/bigpharma/language.dm index 51d9335b5eee..0a6d2e9108f8 100644 --- a/mods/content/bigpharma/language.dm +++ b/mods/content/bigpharma/language.dm @@ -7,7 +7,7 @@ exclaim_verb = "invokes" ask_verb = "wails" space_chance = 0 - key = "💊" + language_key = "💊" allow_repeated_syllables = FALSE flags = LANG_FLAG_RESTRICTED | LANG_FLAG_FORBIDDEN syllables = list( diff --git a/mods/content/fantasy/datum/hnoll/language.dm b/mods/content/fantasy/datum/hnoll/language.dm index 2cefb4941743..256930957661 100644 --- a/mods/content/fantasy/datum/hnoll/language.dm +++ b/mods/content/fantasy/datum/hnoll/language.dm @@ -9,7 +9,7 @@ ask_verb = "chuffs" exclaim_verb = "howls" colour = "serpentid_lang" - key = "j" + language_key = "hnoll" flags = LANG_FLAG_WHITELISTED space_chance = 40 diff --git a/mods/content/fantasy/datum/kobaloi/language.dm b/mods/content/fantasy/datum/kobaloi/language.dm index bf02148ce55c..31c551f93a94 100644 --- a/mods/content/fantasy/datum/kobaloi/language.dm +++ b/mods/content/fantasy/datum/kobaloi/language.dm @@ -6,7 +6,7 @@ ask_verb = "asks" exclaim_verb = "exclaims" colour = "indian" - key = "m" + language_key = "kobaloi" flags = LANG_FLAG_WHITELISTED space_chance = 100 // We generate entire words rather than syllables, so we always need a space. diff --git a/mods/content/integrated_electronics/components/input.dm b/mods/content/integrated_electronics/components/input.dm index f39d70cfb867..75f7d02326be 100644 --- a/mods/content/integrated_electronics/components/input.dm +++ b/mods/content/integrated_electronics/components/input.dm @@ -828,18 +828,19 @@ . = ..() global.listening_objects += src -/obj/item/integrated_circuit/input/microphone/hear_talk(var/mob/living/M, text, verb, decl/language/speaking) - var/translated = TRUE - if(M && text) - if(speaking && !speaking.machine_understands) - text = speaking.scramble(M, text) - translated = FALSE - set_pin_data(IC_OUTPUT, 1, M.GetVoice()) - set_pin_data(IC_OUTPUT, 2, text) +/obj/item/integrated_circuit/input/microphone/hear_talk(mob/living/speaker, datum/speech/phrases, verb, stars = FALSE, decl/language/force_language) + + if(speaker) + set_pin_data(IC_OUTPUT, 1, speaker.GetVoice()) + if(istype(phrases)) + var/list/messages = phrases.compile_for_listener(src, machine_listener = TRUE) + set_pin_data(IC_OUTPUT, 2, messages[1]) + else + set_pin_data(IC_OUTPUT, 2, phrases) push_data() activate_pin(1) - if(translated && !(speaking.type == /decl/language/human/common)) + if(istype(phrases) && phrases.language.type != /decl/language/human/common) activate_pin(2) /obj/item/integrated_circuit/input/sensor diff --git a/mods/content/response_team/maps/ert_base.dmm b/mods/content/response_team/maps/ert_base.dmm index dbb532ffeb55..c835d53648ed 100644 --- a/mods/content/response_team/maps/ert_base.dmm +++ b/mods/content/response_team/maps/ert_base.dmm @@ -880,7 +880,7 @@ /turf/unsimulated/floor/vault, /area/map_template/rescue_base/base) "cv" = ( -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/unsimulated/floor/dark, /area/map_template/rescue_base/base) "cw" = ( @@ -1960,7 +1960,7 @@ /turf/floor/tiled/dark, /area/map_template/rescue_base/start) "fo" = ( -/obj/machinery/hologram/holopad/longrange, +/obj/machinery/holopad/longrange, /turf/floor/tiled/dark, /area/map_template/rescue_base/start) "fp" = ( diff --git a/mods/content/xenobiology/slime/say.dm b/mods/content/xenobiology/slime/say.dm index 625d7b434501..971dd7aa03bc 100644 --- a/mods/content/xenobiology/slime/say.dm +++ b/mods/content/xenobiology/slime/say.dm @@ -9,14 +9,14 @@ /mob/living/slime/say_understands(mob/speaker, decl/language/speaking) . = isslime(speaker) || ..() -/mob/living/slime/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/living/slime/hear_say(datum/speech/phrases, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, stars = FALSE, atom/relayed_by) var/datum/mob_controller/slime/slime_ai = ai if(istype(slime_ai) && (weakref(speaker) in slime_ai.observed_friends)) - LAZYSET(slime_ai.speech_buffer, speaker, lowertext(html_decode(message))) + LAZYSET(slime_ai.speech_buffer, speaker, lowertext(html_decode(phrases.unformatted_message))) return ..() -/mob/living/slime/hear_radio(var/message, var/verb="says", var/decl/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/vsource) +/mob/living/slime/hear_radio(datum/speech/phrases, verb = "says", part_a, part_b, part_c, mob/speaker, hard_to_hear = FALSE, vname = "", vsource, scramble = FALSE) var/datum/mob_controller/slime/slime_ai = ai if(istype(slime_ai) && (weakref(speaker) in slime_ai.observed_friends)) - LAZYSET(slime_ai.speech_buffer, speaker, lowertext(html_decode(message))) + LAZYSET(slime_ai.speech_buffer, speaker, lowertext(html_decode(phrases.unformatted_message))) return ..() diff --git a/mods/gamemodes/mercenary/maps/mercenary_base.dmm b/mods/gamemodes/mercenary/maps/mercenary_base.dmm index 7386a3cd58a0..8278cb3c38b7 100644 --- a/mods/gamemodes/mercenary/maps/mercenary_base.dmm +++ b/mods/gamemodes/mercenary/maps/mercenary_base.dmm @@ -344,7 +344,7 @@ /obj/structure/cable{ icon_state = "1-2" }, -/obj/machinery/hologram/holopad/longrange/remoteship, +/obj/machinery/holopad/longrange/remoteship, /turf/floor/shuttle/darkred, /area/map_template/merc_shuttle) "aK" = ( diff --git a/mods/mobs/borers/datum/language.dm b/mods/mobs/borers/datum/language.dm index fda4c7711510..f84e0ef5bf56 100644 --- a/mods/mobs/borers/datum/language.dm +++ b/mods/mobs/borers/datum/language.dm @@ -5,13 +5,14 @@ ask_verb = "sings" exclaim_verb = "sings" colour = "alien" - key = "z" + language_key = "borer" flags = LANG_FLAG_RESTRICTED | LANG_FLAG_HIVEMIND shorthand = "N/A" hidden_from_codex = TRUE #define isborer(X) istype(X, /mob/living/simple_animal/borer) -/decl/language/corticalborer/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) +/decl/language/corticalborer/broadcast(mob/living/speaker, datum/speech/phrases, speaker_mask) + var/message = istype(phrases) ? phrases.unformatted_message : phrases var/mob/living/simple_animal/borer/B = isborer(speaker) ? speaker : speaker.has_brain_worms() if(B) if(B.host) diff --git a/mods/mobs/borers/mob/borer/borer_captive.dm b/mods/mobs/borers/mob/borer/borer_captive.dm index e790354fc8c9..94b5252edd0c 100644 --- a/mods/mobs/borers/mob/borer/borer_captive.dm +++ b/mods/mobs/borers/mob/borer/borer_captive.dm @@ -4,7 +4,7 @@ universal_understand = TRUE butchery_data = null -/mob/living/captive_brain/say(var/message) +/mob/living/captive_brain/say(datum/speech/phrases, verb = "says", whispering) if (src.client) if(client.prefs.muted & MUTE_IC) @@ -13,7 +13,7 @@ if(isborer(src.loc)) - message = sanitize(message) + var/message = sanitize(phrases.unformatted_message) if (!message) return log_say("[key_name(src)] : [message]") diff --git a/mods/mobs/borers/mob/borer/say.dm b/mods/mobs/borers/mob/borer/say.dm index d60017170f91..12181c41566d 100644 --- a/mods/mobs/borers/mob/borer/say.dm +++ b/mods/mobs/borers/mob/borer/say.dm @@ -1,5 +1,6 @@ -/mob/living/simple_animal/borer/say(var/message) +/mob/living/simple_animal/borer/say(datum/speech/phrases, verb = "says", whispering) + var/message = istype(phrases) ? phrases.unformatted_message : phrases message = sanitize(message) message = capitalize(message) @@ -25,7 +26,7 @@ var/decl/language/L = parse_language(message) if(L && L.flags & LANG_FLAG_HIVEMIND) - L.broadcast(src,trim(copytext(message,3)),src.truename) + L.broadcast(src, trim(copytext(message,3)), truename) return if(!host) diff --git a/mods/mobs/dionaea/datums/language.dm b/mods/mobs/dionaea/datums/language.dm index 4ef0755db61b..67ad97c6d283 100644 --- a/mods/mobs/dionaea/datums/language.dm +++ b/mods/mobs/dionaea/datums/language.dm @@ -5,7 +5,7 @@ ask_verb = "creaks" exclaim_verb = "rustles" colour = "soghun" - key = "q" + language_key = "chirp" flags = LANG_FLAG_RESTRICTED syllables = list("hs","zt","kr","st","sh") shorthand = "RT" diff --git a/mods/species/adherent/datum/language.dm b/mods/species/adherent/datum/language.dm index 8b8e24935314..d9eeb44fbde7 100644 --- a/mods/species/adherent/datum/language.dm +++ b/mods/species/adherent/datum/language.dm @@ -6,7 +6,7 @@ ask_verb = "rings" exclaim_verb = "peals" colour = "adherent" - key = "p" + language_key = "protocol" flags = LANG_FLAG_WHITELISTED syllables = list("\[Ab\]", "\[Bb\]", "\[Cb\]", "\[Db\]", "\[Eb\]", "\[Fb\]", "\[Gb\]", "\[A#\]", "\[B#\]", "\[C#\]", "\[D#\]", "\[E#\]", "\[F#\]", diff --git a/mods/species/ascent/datum/languages.dm b/mods/species/ascent/datum/languages.dm index 6ea22b2a117d..2a90025fc492 100644 --- a/mods/species/ascent/datum/languages.dm +++ b/mods/species/ascent/datum/languages.dm @@ -7,7 +7,7 @@ colour = "alien" syllables = list("-","=","+","_","|","/") space_chance = 0 - key = "|" + language_key = "avocal" flags = LANG_FLAG_RESTRICTED shorthand = "KV" machine_understands = FALSE @@ -41,11 +41,12 @@ message = replacetext(message, "'", "'") return message -/decl/language/mantid/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) - . = ..(speaker, message, speaker.real_name) +/decl/language/mantid/broadcast(mob/living/speaker, datum/speech/phrases, speaker_mask) + speaker_mask = speaker.real_name + . = ..() /decl/language/mantid/nonvocal - key = "]" + language_key = "ascent" name = "Ascent-Glow" desc = "A complex visual language of bright bioluminescent flashes, 'spoken' natively by the Kharmaani of the Ascent." colour = "alien" @@ -56,7 +57,7 @@ shorthand = "KNV" #define MANTID_SCRAMBLE_CACHE_LEN 20 -/decl/language/mantid/nonvocal/scramble(mob/living/speaker, input, list/known_languages) +/decl/language/mantid/nonvocal/scramble(mob/living/speaker, input, list/known_languages, capitalize_string) if(input in scramble_cache) var/n = scramble_cache[input] scramble_cache -= input @@ -78,7 +79,7 @@ return FALSE /decl/language/mantid/worldnet - key = "\[" + language_key = "worldnet" name = "Worldnet" desc = "The mantid aliens of the Ascent maintain an extensive self-supporting broadcast network for use in team communications." colour = "alien" diff --git a/mods/species/ascent/machines/ship_machines.dm b/mods/species/ascent/machines/ship_machines.dm index b761299d5138..005c531d28da 100644 --- a/mods/species/ascent/machines/ship_machines.dm +++ b/mods/species/ascent/machines/ship_machines.dm @@ -1,7 +1,7 @@ MANTIDIFY(/obj/machinery/apc/hyper, "mantid power node", "power controller") MANTIDIFY(/obj/machinery/atmospherics/unary/vent_pump/on, "mantid atmosphere outlet", "vent") MANTIDIFY(/obj/machinery/atmospherics/unary/vent_scrubber/on, "mantid atmosphere intake", "scrubber") -MANTIDIFY(/obj/machinery/hologram/holopad/longrange, "mantid holopad", "holopad") +MANTIDIFY(/obj/machinery/holopad/longrange, "mantid holopad", "holopad") MANTIDIFY(/obj/machinery/optable, "mantid operating table", "operating table") MANTIDIFY(/obj/machinery/door/airlock/external/bolted, "mantid airlock", "door") @@ -80,7 +80,7 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") base_type = /obj/machinery/fabricator construct_state = /decl/machine_construction/default/no_deconstruct -/obj/machinery/hologram/holopad/longrange/ascent +/obj/machinery/holopad/longrange/ascent req_access = list(access_ascent) /obj/effect/catwalk_plated/ascent diff --git a/mods/species/drakes/language.dm b/mods/species/drakes/language.dm index f674ebb84261..8377a06f1fc6 100644 --- a/mods/species/drakes/language.dm +++ b/mods/species/drakes/language.dm @@ -27,7 +27,7 @@ ask_verb = "chirps" exclaim_verb = "rumbles" colour = "alien" - key = "l" // l is for lizard, probably + language_key = "drake" flags = LANG_FLAG_RESTRICTED machine_understands = 0 space_chance = 30 diff --git a/mods/species/neoavians/datum/language.dm b/mods/species/neoavians/datum/language.dm index b3d72b2de5d2..a808a68961c6 100644 --- a/mods/species/neoavians/datum/language.dm +++ b/mods/species/neoavians/datum/language.dm @@ -6,7 +6,7 @@ ask_verb = "rattles" exclaim_verb = "calls" colour = "alien" - key = "v" + language_key = "crow" flags = LANG_FLAG_WHITELISTED space_chance = 50 syllables = list( @@ -33,7 +33,7 @@ ask_verb = "chirrups" exclaim_verb = "trills" colour = "alien" - key = "i" + language_key = "avian" flags = LANG_FLAG_WHITELISTED space_chance = 50 syllables = list( diff --git a/mods/species/skrell/datum/language.dm b/mods/species/skrell/datum/language.dm index 252a48595e0b..06067e16c7ea 100644 --- a/mods/species/skrell/datum/language.dm +++ b/mods/species/skrell/datum/language.dm @@ -8,7 +8,7 @@ var/global/list/last_name_skrell = file2list("mods/species/skrell/names/last_n ask_verb = "trills" exclaim_verb = "croaks" colour = "selenian" - key = "k" + language_key = "skrell" flags = LANG_FLAG_WHITELISTED syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","'","!") shorthand = "SK" @@ -23,7 +23,7 @@ var/global/list/last_name_skrell = file2list("mods/species/skrell/names/last_n ask_verb = "trills" exclaim_verb = "chirps" colour = "selenian" - key = "h" + language_key = "uekatish" flags = LANG_FLAG_WHITELISTED syllables = list("qra","xa","vilz*","s!zaao","qu!*m","girx","vol","xrim","za-r*oo","qu-xx","qix","qa'taz","zix","tuep","!","*") shorthand = "UK" diff --git a/mods/species/tajaran/datum/language.dm b/mods/species/tajaran/datum/language.dm index aa7782a9560f..724e129e4bd0 100644 --- a/mods/species/tajaran/datum/language.dm +++ b/mods/species/tajaran/datum/language.dm @@ -5,7 +5,7 @@ ask_verb = "purrs" exclaim_verb = "howls" whisper_verb = "purrs softly" - key = "2" + language_key = "siik" flags = LANG_FLAG_WHITELISTED shorthand = "T" syllables = list("mrr","rr","tajr","kir","raj","kii","mir","kra","ahk","nal","vah","khaz","jri","ran","darr", @@ -22,9 +22,5 @@ return new_name //#803b56 is color - -/decl/language/tajaran/format_message(message, verb) - return "[verb], \"[capitalize(message)]\"" - -/decl/language/tajaran/format_message_radio(message, verb) - return "[verb], \"[capitalize(message)]\"" +/decl/language/tajaran/format_message_no_verb(message) + return "[message]" diff --git a/mods/species/unathi/datum/language.dm b/mods/species/unathi/datum/language.dm index e302d2208217..1124a6bdc3c0 100644 --- a/mods/species/unathi/datum/language.dm +++ b/mods/species/unathi/datum/language.dm @@ -5,7 +5,7 @@ ask_verb = "hisses" exclaim_verb = "roars" colour = "soghun" - key = "o" + language_key = "sinta" flags = LANG_FLAG_WHITELISTED space_chance = 40 syllables = list( diff --git a/mods/species/vox/datum/language.dm b/mods/species/vox/datum/language.dm index a5729749f617..beabae6f315e 100644 --- a/mods/species/vox/datum/language.dm +++ b/mods/species/vox/datum/language.dm @@ -5,7 +5,7 @@ ask_verb = "creels" exclaim_verb = "SHRIEKS" colour = "vox" - key = "x" + language_key = "vox" flags = LANG_FLAG_WHITELISTED syllables = list("ti","ti","ti","hi","hi","ki","ki","ki","ki","ya","ta","ha","ka","ya","chi","cha","kah", \ "SKRE","AHK","EHK","RAWK","KRA","AAA","EEE","KI","II","KRI","KA") diff --git a/nebula.dme b/nebula.dme index ce318ce3d015..3d4f0c8e9693 100644 --- a/nebula.dme +++ b/nebula.dme @@ -832,7 +832,6 @@ #include "code\game\machinery\floodlight.dm" #include "code\game\machinery\floor_light.dm" #include "code\game\machinery\floorlayer.dm" -#include "code\game\machinery\hologram.dm" #include "code\game\machinery\holosign.dm" #include "code\game\machinery\igniter.dm" #include "code\game\machinery\jukebox.dm" @@ -960,6 +959,8 @@ #include "code\game\machinery\embedded_controller\embedded_program_base.dm" #include "code\game\machinery\embedded_controller\simple_docking_controller.dm" #include "code\game\machinery\embedded_controller\tin_can.dm" +#include "code\game\machinery\holopad\_holopad.dm" +#include "code\game\machinery\holopad\holopad_listen.dm" #include "code\game\machinery\kitchen\gibber.dm" #include "code\game\machinery\kitchen\icecream.dm" #include "code\game\machinery\kitchen\microwave.dm" @@ -2875,6 +2876,7 @@ #include "code\modules\mob\living\login.dm" #include "code\modules\mob\living\logout.dm" #include "code\modules\mob\living\say.dm" +#include "code\modules\mob\living\speech.dm" #include "code\modules\mob\living\stasis.dm" #include "code\modules\mob\living\stress.dm" #include "code\modules\mob\living\bot\bot.dm"