diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index 2f2a441bafd9..bbf146bdcbd8 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/datums/trading/_trader.dm b/code/datums/trading/_trader.dm
index 27444f108d19..01e006e09947 100644
--- a/code/datums/trading/_trader.dm
+++ b/code/datums/trading/_trader.dm
@@ -39,9 +39,9 @@
if(!ispath(trader_currency, /decl/currency))
trader_currency = global.using_map.default_currency
if(ispath(name_language, /decl/language))
- var/decl/language/L = GET_DECL(name_language)
- if(istype(L))
- name = L.get_random_language_name(pick(MALE,FEMALE))
+ var/decl/language/language = GET_DECL(name_language)
+ if(istype(language))
+ name = language.get_random_language_name(pick(MALE,FEMALE))
if(!name)
name = capitalize(pick(global.using_map.first_names_female + global.using_map.first_names_male)) + " " + capitalize(pick(global.using_map.last_names))
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 0d0b617fc5a5..96b3f7ff4891 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,10 @@
// 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
+
+/atom/proc/see_signlang(message, verb = "gestures", decl/language/language, mob/speaker, prefix)
+ return
diff --git a/code/game/jobs/server_whitelist.dm b/code/game/jobs/server_whitelist.dm
index da26aaeea7cf..0579c000943f 100644
--- a/code/game/jobs/server_whitelist.dm
+++ b/code/game/jobs/server_whitelist.dm
@@ -74,20 +74,20 @@ var/global/list/alien_whitelist = list()
// Forbidden languages do not care about admin rights.
if(istype(species,/decl/language))
- var/decl/language/L = species
- if(L.flags & LANG_FLAG_FORBIDDEN)
+ var/decl/language/language = species
+ if(language.language_flags & LANG_FLAG_FORBIDDEN)
return FALSE
if(check_rights(R_ADMIN, FALSE, M))
return TRUE
if(istype(species,/decl/language))
- var/decl/language/L = species
- if(L.flags & LANG_FLAG_RESTRICTED)
+ var/decl/language/language = species
+ if(language.language_flags & LANG_FLAG_RESTRICTED)
return FALSE
- if(!get_config_value(/decl/config/toggle/use_alien_whitelist) || !(L.flags & LANG_FLAG_WHITELISTED))
+ if(!get_config_value(/decl/config/toggle/use_alien_whitelist) || !(language.language_flags & LANG_FLAG_WHITELISTED))
return TRUE
- return whitelist_lookup(L.name, M.ckey)
+ return whitelist_lookup(language.name, M.ckey)
if(istype(species,/decl/species))
var/decl/species/S = species
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 677184076293..ac8e7433fd59 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,49 +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/static/list/reachable_overmaps = list(OVERMAP_ID_SPACE)
-
var/static/list/used_holopad_ids = list()
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
@@ -85,14 +73,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)
@@ -100,14 +88,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
@@ -147,7 +135,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
@@ -169,18 +157,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)
@@ -188,7 +175,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()
@@ -196,10 +183,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.
@@ -217,79 +204,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!
@@ -318,7 +252,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
@@ -333,34 +267,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]
@@ -379,34 +312,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"
@@ -415,10 +329,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..0e60d52cd4a0
--- /dev/null
+++ b/code/game/machinery/holopad/holopad_listen.dm
@@ -0,0 +1,46 @@
+/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/see_signlang(message, verb = "gestures", decl/language/language, mob/speaker, prefix)
+ if(length(masters) || ((sourcepad || targetpad) && !incoming_connection))
+ prefix ||= "\icon[src] [capitalize(strip_improper(name))] relayed:"
+ 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.see_signlang(message, verb, language, speaker, prefix)
+
+/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 feebd9293847..1e9435fa6e47 100644
--- a/code/game/objects/__objs.dm
+++ b/code/game/objects/__objs.dm
@@ -118,19 +118,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 1bed5d4cf0f6..8f128cfa2a2f 100644
--- a/code/game/objects/items/__item.dm
+++ b/code/game/objects/items/__item.dm
@@ -624,7 +624,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..efcddc2a817e 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.language_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/admin/admin.dm b/code/modules/admin/admin.dm
index f1726ee6ac05..aff29ed9012f 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -209,16 +209,16 @@ var/global/BSACooldown = 0
var/f = 1
var/list/language_types = decls_repository.get_decls_of_subtype(/decl/language)
for(var/k in language_types)
- var/decl/language/L = language_types[k]
- if(!(L.flags & LANG_FLAG_INNATE))
+ var/decl/language/language = language_types[k]
+ if(!(language.language_flags & LANG_FLAG_INNATE))
if(!f)
body += " | "
else
f = 0
- if(L in M.languages)
- body += "[L.name]"
+ if(language in M.languages)
+ body += "[language.name]"
else
- body += "[L.name]"
+ body += "[language.name]"
body += {"