diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index 56289d16843..d5f1496079f 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -82,7 +82,11 @@
#define REDUCE_RANGE (1<<1)
#define NOPASS (1<<2)
-//Eavesdropping
+/// Range to hear normal messages
+#define MESSAGE_RANGE 7
+/// Range to hear whispers normally
+#define WHISPER_RANGE 1
+/// Additional range to partially hear whispers
#define EAVESDROP_EXTRA_RANGE 1 //how much past the specified message_range does the message get starred, whispering only
/// How close intercoms can be for radio code use
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index e782d44818c..bebe182107f 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -359,6 +359,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define IGNORING_GRAVITY_NEGATION "ignoring_gravity_negation"
/// We have some form of forced gravity acting on us
#define TRAIT_FORCED_GRAVITY "forced_gravity"
+/// Makes whispers clearly heard from seven tiles away, the full hearing range
+#define TRAIT_GOOD_HEARING "good_hearing"
+/// Allows you to hear speech through walls
+#define TRAIT_XRAY_HEARING "xray_hearing"
/// Lets us scan reagents
#define TRAIT_REAGENT_SCANNER "reagent_scanner"
diff --git a/code/__DEFINES/~skyrat_defines/chat.dm b/code/__DEFINES/~skyrat_defines/chat.dm
index cc41ace6f28..fc1c09c63d1 100644
--- a/code/__DEFINES/~skyrat_defines/chat.dm
+++ b/code/__DEFINES/~skyrat_defines/chat.dm
@@ -3,3 +3,4 @@
#define EXAMINE_SECTION_BREAK "
"
+#define LOOC_RANGE 7
diff --git a/code/__HELPERS/spatial_info.dm b/code/__HELPERS/spatial_info.dm
index 78bc79f1ef8..033d5a60b79 100644
--- a/code/__HELPERS/spatial_info.dm
+++ b/code/__HELPERS/spatial_info.dm
@@ -118,6 +118,40 @@
return .
+/**
+ * The exact same as get_hearers_in_view, but not limited by visibility. Does no filtering for traits, line of sight, or any other such criteria.
+ * Filtering is intended to be done by whatever calls this function.
+ *
+ * This function exists to allow for mobs to hear speech without line of sight, if such a thing is needed.
+ *
+ * * radius - what radius search circle we are using, worse performance as this increases
+ * * source - object at the center of our search area. everything in get_turf(source) is guaranteed to be part of the search area
+ */
+/proc/get_hearers_in_range(range, atom/source)
+ var/turf/center_turf = get_turf(source)
+ if(!center_turf)
+ return
+
+ . = list()
+
+ if(range <= 0)//special case for if only source cares
+ for(var/atom/movable/target as anything in center_turf)
+ var/list/recursive_contents = target.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE]
+ if(recursive_contents)
+ . += recursive_contents
+ return .
+
+ var/list/hearables_from_grid = SSspatial_grid.orthogonal_range_search(source, RECURSIVE_CONTENTS_HEARING_SENSITIVE, range)
+
+ if(!length(hearables_from_grid))//we know that something is returned by the grid, but we dont know if we need to actually filter down the output
+ return .
+
+ for(var/atom/movable/hearable as anything in hearables_from_grid)
+ if (get_dist(center_turf, hearable) <= range)
+ . += hearable
+
+ return .
+
/**
* Returns a list of movable atoms that are hearing sensitive in view_radius and line of sight to source
* the majority of the work is passed off to the spatial grid if view_radius > 0
diff --git a/code/__HELPERS/~skyrat_helpers/unsorted.dm b/code/__HELPERS/~skyrat_helpers/unsorted.dm
index 0d9d7c2c013..f6ae26cc82b 100644
--- a/code/__HELPERS/~skyrat_helpers/unsorted.dm
+++ b/code/__HELPERS/~skyrat_helpers/unsorted.dm
@@ -17,7 +17,7 @@
/** Get all hearers in range, ignores walls and such. Code stolen from `/proc/get_hearers_in_view()`
* Much faster and less expensive than range()
*/
-/proc/get_hearers_in_range(range_radius, atom/source)
+/proc/get_hearers_in_looc_range(atom/source, range_radius = LOOC_RANGE)
var/turf/center_turf = get_turf(source)
if(!center_turf)
return
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index fc924d2d7f0..bc34feb5709 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -207,6 +207,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_MORBID" = TRAIT_MORBID,
"TRAIT_UNDENSE" = TRAIT_UNDENSE,
"TRAIT_EXPANDED_FOV" = TRAIT_EXPANDED_FOV,
+ "TRAIT_GOOD_HEARING" = TRAIT_GOOD_HEARING,
+ "TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING,
),
/obj/item/bodypart = list(
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS,
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index d507b4f9139..81d250ef79f 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -241,7 +241,7 @@
Hear(rendered, src, language, message, null, spans, message_mods) // We always hear what we say
var/group = owner.imaginary_group - src // The people in our group don't, so we have to exclude ourselves not to hear twice
for(var/mob/person in group)
- if(eavesdrop_range && get_dist(src, person) > 1 + eavesdrop_range)
+ if(eavesdrop_range && get_dist(src, person) > WHISPER_RANGE + eavesdrop_range && !HAS_TRAIT(person, TRAIT_GOOD_HEARING))
var/new_rendered = "[span_name("[name]")] [say_quote(say_emphasis(eavesdropped_message), spans, message_mods)]"
person.Hear(new_rendered, src, language, eavesdropped_message, null, spans, message_mods)
else
diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm
index 72f6d4297ee..0b5a2df246c 100644
--- a/code/datums/station_traits/positive_traits.dm
+++ b/code/datums/station_traits/positive_traits.dm
@@ -304,7 +304,7 @@
/datum/job/mime = /obj/item/organ/internal/tongue/robot, //...
/datum/job/paramedic = /obj/item/organ/internal/cyberimp/eyes/hud/medical,
/datum/job/prisoner = /obj/item/organ/internal/eyes/robotic/shield,
- /datum/job/psychologist = /obj/item/organ/internal/ears/cybernetic/upgraded,
+ /datum/job/psychologist = /obj/item/organ/internal/ears/cybernetic/whisper,
/datum/job/quartermaster = /obj/item/organ/internal/stomach/cybernetic/tier3,
/datum/job/research_director = /obj/item/organ/internal/cyberimp/bci,
/datum/job/roboticist = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic,
@@ -317,7 +317,7 @@
// SKYRAT EDIT ADDITION START - MODULAR JOBS
/datum/job/blueshield = /obj/item/organ/internal/cyberimp/brain/anti_stun,
/datum/job/nanotrasen_consultant = /obj/item/organ/internal/heart/cybernetic/tier3,
- /datum/job/barber = /obj/item/organ/internal/ears/cybernetic/upgraded,
+ /datum/job/barber = /obj/item/organ/internal/ears/cybernetic/whisper,
/datum/job/corrections_officer = /obj/item/organ/internal/cyberimp/arm/flash,
/datum/job/orderly = /obj/item/organ/internal/cyberimp/brain/anti_drop,
/datum/job/science_guard = /obj/item/organ/internal/cyberimp/arm/flash,
diff --git a/code/modules/cargo/bounties/medical.dm b/code/modules/cargo/bounties/medical.dm
index 4d6fd051bdb..f9b367b12f0 100644
--- a/code/modules/cargo/bounties/medical.dm
+++ b/code/modules/cargo/bounties/medical.dm
@@ -36,6 +36,8 @@
/obj/item/organ/internal/ears = TRUE,
/obj/item/organ/internal/ears/cybernetic = FALSE,
/obj/item/organ/internal/ears/cybernetic/upgraded = TRUE,
+ /obj/item/organ/internal/ears/cybernetic/whisper = TRUE,
+ /obj/item/organ/internal/ears/cybernetic/xray = TRUE,
)
/datum/bounty/item/medical/liver
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index f92e380a44a..8803d5df374 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -279,10 +279,13 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
understood = FALSE
// if someone is whispering we make an extra type of message that is obfuscated for people out of range
- var/is_speaker_whispering = message_mods[WHISPER_MODE]
- var/can_hear_whisper = get_dist(speaker, src) <= message_range
- if(is_speaker_whispering && !can_hear_whisper && !isobserver(src)) // ghosts can hear all messages clearly
+ // Less than or equal to 0 means normal hearing. More than 0 and less than or equal to EAVESDROP_EXTRA_RANGE means
+ // partial hearing. More than EAVESDROP_EXTRA_RANGE means no hearing. Exception for GOOD_HEARING trait
+ var/dist = get_dist(speaker, src) - message_range
+ if(dist > 0 && dist <= EAVESDROP_EXTRA_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING) && !isobserver(src)) // ghosts can hear all messages clearly
raw_message = stars(raw_message)
+ if (dist > EAVESDROP_EXTRA_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING) && !isobserver(src))
+ return FALSE // Too far away and don't have good hearing, you can't hear anything
// we need to send this signal before compose_message() is used since other signals need to modify
// the raw_message first. After the raw_message is passed through the various signals, it's ready to be formatted
@@ -337,10 +340,18 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
var/whisper_range = 0
var/is_speaker_whispering = FALSE
if(message_mods[WHISPER_MODE]) //If we're whispering
- whisper_range = EAVESDROP_EXTRA_RANGE
+ // Needed for good hearing trait. The actual filtering for whispers happens at the /mob/living/Hear proc
+ whisper_range = MESSAGE_RANGE - WHISPER_RANGE
is_speaker_whispering = TRUE
- var/list/listening = get_hearers_in_view(message_range + whisper_range, source)
+ var/list/in_view = get_hearers_in_view(message_range + whisper_range, source)
+ var/list/listening = get_hearers_in_range(message_range + whisper_range, source)
+
+ // Pre-process listeners to account for line-of-sight
+ for(var/atom/movable/listening_movable as anything in listening)
+ if(!(listening_movable in in_view) && !HAS_TRAIT(listening_movable, TRAIT_XRAY_HEARING))
+ listening.Remove(listening_movable)
+
if(client) //client is so that ghosts don't have to listen to mice
for(var/mob/player_mob as anything in GLOB.player_list)
if(QDELETED(player_mob)) //Some times nulls and deleteds stay in this list. This is a workaround to prevent ic chat breaking for everyone when they do.
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 79d905b81b0..0dd42d53be6 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -348,32 +348,6 @@
//////////Cybernetic Implants////////////
/////////////////////////////////////////
-/datum/design/cyberimp_welding
- name = "Welding Shield Eyes"
- desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision."
- id = "ci-welding"
- build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
- construction_time = 40
- materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*6, /datum/material/glass = SMALL_MATERIAL_AMOUNT*4)
- build_path = /obj/item/organ/internal/eyes/robotic/shield
- category = list(
- RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_MISC
- )
- departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
-
-/datum/design/cyberimp_gloweyes
- name = "Luminescent Eyes"
- desc = "A pair of cybernetic eyes that can emit multicolored light"
- id = "ci-gloweyes"
- build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
- construction_time = 40
- materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*6, /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT)
- build_path = /obj/item/organ/internal/eyes/robotic/glow
- category = list(
- RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_MISC
- )
- departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
-
/datum/design/cyberimp_breather
name = "Breathing Tube Implant"
desc = "This simple implant adds an internals connector to your back, allowing you to use internals without a mask and protecting you from being choked."
@@ -845,13 +819,51 @@
id = "cybernetic_ears_u"
build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
construction_time = 40
- materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT*5, /datum/material/glass =SMALL_MATERIAL_AMOUNT*5, /datum/material/silver =SMALL_MATERIAL_AMOUNT*5)
+ materials = list(
+ /datum/material/iron = SMALL_MATERIAL_AMOUNT*5,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT*5,
+ /datum/material/silver = SMALL_MATERIAL_AMOUNT*5,
+ )
build_path = /obj/item/organ/internal/ears/cybernetic/upgraded
category = list(
RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_2
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
+/datum/design/cybernetic_ears_whisper
+ name = "Whisper-sensitive Cybernetic Ears"
+ desc = "A pair of whisper-sensitive cybernetic ears."
+ id = "cybernetic_ears_whisper"
+ build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
+ construction_time = 40
+ materials = list(
+ /datum/material/iron = SMALL_MATERIAL_AMOUNT*5,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT*5,
+ /datum/material/silver = SMALL_MATERIAL_AMOUNT*5,
+ )
+ build_path = /obj/item/organ/internal/ears/cybernetic/whisper
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_3
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
+
+/datum/design/cybernetic_ears_xray
+ name = "Wall-penetrating Cybernetic Ears"
+ desc = "A pair of wall-penetrating cybernetic ears."
+ id = "cybernetic_ears_xray"
+ build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
+ construction_time = 40
+ materials = list(
+ /datum/material/iron = SMALL_MATERIAL_AMOUNT*5,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT*5,
+ /datum/material/silver = SMALL_MATERIAL_AMOUNT*5,
+ )
+ build_path = /obj/item/organ/internal/ears/cybernetic/xray
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_3
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
+
/datum/design/cybernetic_eyes
name = "Basic Cybernetic Eyes"
desc = "A basic pair of cybernetic eyes."
@@ -875,6 +887,32 @@
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
+/datum/design/cyberimp_welding
+ name = "Welding Shield Eyes"
+ desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision."
+ id = "ci-welding"
+ build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
+ construction_time = 40
+ materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*6, /datum/material/glass = SMALL_MATERIAL_AMOUNT*4)
+ build_path = /obj/item/organ/internal/eyes/robotic/shield
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_3
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
+
+/datum/design/cyberimp_gloweyes
+ name = "Luminescent Eyes"
+ desc = "A pair of cybernetic eyes that can emit multicolored light"
+ id = "ci-gloweyes"
+ build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
+ construction_time = 40
+ materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*6, /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT)
+ build_path = /obj/item/organ/internal/eyes/robotic/glow
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_3
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
+
/////////////////////
///Surgery Designs///
/////////////////////
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index ed0ea51aba6..6622e3fd5d6 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -277,6 +277,7 @@
"bonesetter",
"cautery",
"circular_saw",
+ "cybernetic_ears",
"cybernetic_eyes",
"cybernetic_heart",
"cybernetic_liver",
@@ -1381,7 +1382,7 @@
description = "We have the technology to rebuild him."
prereq_ids = list("biotech")
design_ids = list(
- "cybernetic_ears",
+ "cybernetic_ears_u",
"cybernetic_eyes_improved",
"cybernetic_heart_tier2",
"cybernetic_liver_tier2",
@@ -1402,7 +1403,10 @@
description = "We have the technology to upgrade him."
prereq_ids = list("adv_biotech", "cyber_organs")
design_ids = list(
- "cybernetic_ears_u",
+ "cybernetic_ears_whisper",
+ "cybernetic_ears_xray",
+ "ci-gloweyes",
+ "ci-welding",
"cybernetic_heart_tier3",
"cybernetic_liver_tier3",
"cybernetic_lungs_tier3",
@@ -1423,11 +1427,9 @@
design_ids = list(
"ci-breather",
"ci-diaghud",
- "ci-gloweyes",
"ci-medhud",
"ci-nutriment",
"ci-sechud",
- "ci-welding",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm
index 67cd69ebcc3..54d35628f85 100644
--- a/code/modules/surgery/organs/internal/ears/_ears.dm
+++ b/code/modules/surgery/organs/internal/ears/_ears.dm
@@ -104,18 +104,51 @@
ear_owner.RemoveElement(/datum/element/waddling)
/obj/item/organ/internal/ears/cybernetic
- name = "cybernetic ears"
+ name = "basic cybernetic ears"
icon_state = "ears-c"
desc = "A basic cybernetic organ designed to mimic the operation of ears."
damage_multiplier = 0.9
organ_flags = ORGAN_ROBOTIC
/obj/item/organ/internal/ears/cybernetic/upgraded
- name = "upgraded cybernetic ears"
+ name = "cybernetic ears"
icon_state = "ears-c-u"
- desc = "An advanced cybernetic ear, surpassing the performance of organic ears."
+ desc = "An advanced cybernetic ear, surpassing the performance of organic ears."
damage_multiplier = 0.5
+/obj/item/organ/internal/ears/cybernetic/whisper
+ name = "whisper-sensitive cybernetic ears"
+ icon_state = "ears-c-u"
+ desc = "Allows the user to more easily hear whispers. The user becomes extra vulnerable to loud noises, however"
+ // Same sensitivity as felinid ears
+ damage_multiplier = 2
+
+// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
+// This seems like the least invasive solution
+/obj/item/organ/internal/ears/cybernetic/whisper/on_insert(mob/living/carbon/ear_owner)
+ . = ..()
+ ADD_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
+
+/obj/item/organ/internal/ears/cybernetic/whisper/on_remove(mob/living/carbon/ear_owner)
+ . = ..()
+ REMOVE_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
+
+// "X-ray ears" that let you hear through walls
+/obj/item/organ/internal/ears/cybernetic/xray
+ name = "wall-penetrating cybernetic ears"
+ icon_state = "ears-c-u"
+ desc = "Throguh the power of modern engineering, allows the user to hear speech through walls. The user becomes extra vulnerable to loud noises, however"
+ // Same sensitivity as felinid ears
+ damage_multiplier = 2
+
+/obj/item/organ/internal/ears/cybernetic/xray/on_insert(mob/living/carbon/ear_owner)
+ . = ..()
+ ADD_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
+
+/obj/item/organ/internal/ears/cybernetic/xray/on_remove(mob/living/carbon/ear_owner)
+ . = ..()
+ REMOVE_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
+
/obj/item/organ/internal/ears/cybernetic/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
diff --git a/modular_skyrat/modules/verbs/code/looc.dm b/modular_skyrat/modules/verbs/code/looc.dm
index e864e19cd1a..3dd47f9741c 100644
--- a/modular_skyrat/modules/verbs/code/looc.dm
+++ b/modular_skyrat/modules/verbs/code/looc.dm
@@ -1,5 +1,3 @@
-#define LOOC_RANGE 7
-
/client/verb/looc(msg as text)
set name = "LOOC"
set desc = "Local OOC, seen only by those in view."
@@ -54,7 +52,7 @@
mob.log_talk(msg,LOG_OOC, tag="LOOC")
var/list/heard
if(wall_pierce)
- heard = get_hearers_in_range(LOOC_RANGE, mob.get_top_level_mob())
+ heard = get_hearers_in_looc_range(mob.get_top_level_mob())
else
heard = get_hearers_in_view(LOOC_RANGE, mob.get_top_level_mob())
@@ -62,7 +60,7 @@
if(istype(mob, /mob/living/silicon/ai))
var/mob/living/silicon/ai/ai = mob
if(wall_pierce)
- heard = get_hearers_in_range(LOOC_RANGE, ai.eyeobj)
+ heard = get_hearers_in_looc_range(ai.eyeobj)
else
heard = get_hearers_in_view(LOOC_RANGE, ai.eyeobj)
//so the ai can see looc text
@@ -90,5 +88,3 @@
to_chat(cli_client, span_looc("[ADMIN_FLW(usr)] LOOC[wall_pierce ? " (WALL PIERCE)" : ""]: [src.key]/[src.mob.name]: [msg]"))
else if (cli_client.prefs.read_preference(/datum/preference/toggle/admin/see_looc))
to_chat(cli_client, span_rlooc("[ADMIN_FLW(usr)] (R)LOOC[wall_pierce ? " (WALL PIERCE)" : ""]: [src.key]/[src.mob.name]: [msg]"))
-
-#undef LOOC_RANGE