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"