diff --git a/code/__HELPERS/~skyrat_helpers/unsorted.dm b/code/__HELPERS/~skyrat_helpers/unsorted.dm
index 041934e8ee7..4f0cda57ecc 100644
--- a/code/__HELPERS/~skyrat_helpers/unsorted.dm
+++ b/code/__HELPERS/~skyrat_helpers/unsorted.dm
@@ -13,3 +13,36 @@
M.Scale(squishx, squishy)
animate(src, transform = M, time = halftime, easing = BOUNCE_EASING)
animate(src, transform = OM, time = halftime, easing = BOUNCE_EASING)
+
+/** 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)
+ var/turf/center_turf = get_turf(source)
+ if(!center_turf)
+ return
+
+ . = list()
+ var/old_luminosity = center_turf.luminosity
+ if(range_radius <= 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_radius)
+
+ 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 .
+
+ var/list/assigned_oranges_ears = SSspatial_grid.assign_oranges_ears(hearables_from_grid)
+
+ for(var/mob/oranges_ear/ear in range(range_radius, center_turf))
+ . += ear.references
+
+ for(var/mob/oranges_ear/remaining_ear as anything in assigned_oranges_ears) //we need to clean up our mess
+ remaining_ear.unassign()
+
+ center_turf.luminosity = old_luminosity
+ return .
diff --git a/modular_skyrat/modules/verbs/code/looc.dm b/modular_skyrat/modules/verbs/code/looc.dm
index c1df1e968e1..6c07d89c8e1 100644
--- a/modular_skyrat/modules/verbs/code/looc.dm
+++ b/modular_skyrat/modules/verbs/code/looc.dm
@@ -1,10 +1,22 @@
+#define LOOC_RANGE 7
+
/client/verb/looc(msg as text)
set name = "LOOC"
set desc = "Local OOC, seen only by those in view."
set category = "OOC"
- if(GLOB.say_disabled) //This is here to try to identify lag problems
- to_chat(usr, span_danger(" Speech is currently admin-disabled."))
+ looc_message(msg)
+
+/client/verb/looc_wallpierce(msg as text)
+ set name = "LOOC (Wallpierce)"
+ set desc = "Local OOC, seen by anyone within 7 tiles of you."
+ set category = "OOC"
+
+ looc_message(msg, TRUE)
+
+/client/proc/looc_message(msg, wall_pierce)
+ if(GLOB.say_disabled)
+ to_chat(usr, span_danger("Speech is currently admin-disabled."))
return
if(!mob)
@@ -16,9 +28,9 @@
if(!holder)
if(!GLOB.looc_allowed)
- to_chat(src, span_danger(" LOOC is globally muted"))
+ to_chat(src, span_danger("LOOC is globally muted."))
return
- if(handle_spam_prevention(msg,MUTE_OOC))
+ if(handle_spam_prevention(msg, MUTE_OOC))
return
if(findtext(msg, "byond://"))
to_chat(src, span_boldannounce("Advertising other servers is not allowed."))
@@ -28,7 +40,7 @@
to_chat(src, span_danger("You cannot use LOOC (muted)."))
return
if(mob.stat)
- to_chat(src, span_danger("You cannot use LOOC while unconscious or dead.")) //Skyrat change
+ to_chat(src, span_danger("You cannot use LOOC while unconscious or dead."))
return
if(istype(mob, /mob/dead))
to_chat(src, span_danger("You cannot use LOOC while ghosting."))
@@ -37,35 +49,43 @@
msg = emoji_parse(msg)
mob.log_talk(msg,LOG_OOC, tag="LOOC")
-
- var/list/heard = get_hearers_in_view(7, get_top_level_mob(src.mob))
+ var/list/heard
+ if(wall_pierce)
+ heard = get_hearers_in_range(LOOC_RANGE, get_top_level_mob(src.mob))
+ else
+ heard = get_hearers_in_view(LOOC_RANGE, get_top_level_mob(src.mob))
//so the ai can post looc text
- if(istype(mob,/mob/living/silicon/ai))
+ if(istype(mob, /mob/living/silicon/ai))
var/mob/living/silicon/ai/ai = mob
- heard = get_hearers_in_view(7, ai.eyeobj)
+ if(wall_pierce)
+ heard = get_hearers_in_range(LOOC_RANGE, ai.eyeobj)
+ else
+ heard = get_hearers_in_view(LOOC_RANGE, ai.eyeobj)
//so the ai can see looc text
for(var/mob/living/silicon/ai/ai as anything in GLOB.ai_list)
if(ai.client && !(ai in heard) && (ai.eyeobj in heard))
heard += ai
var/list/admin_seen = list()
- for(var/mob/M in heard)
- if(!M.client)
+ for(var/mob/hearing in heard)
+ if(!hearing.client)
continue
- var/client/C = M.client
- if (C.holder)
- admin_seen[C] = TRUE
+ var/client/hearing_client = hearing.client
+ if (hearing_client.holder)
+ admin_seen[hearing_client] = TRUE
continue //they are handled after that
- if (isobserver(M))
+ if (isobserver(hearing))
continue //Also handled later.
- to_chat(C, span_looc(span_prefix("LOOC: [src.mob.name]: [msg]")))
+ to_chat(hearing_client, span_looc(span_prefix("LOOC: [src.mob.name]: [msg]")))
for(var/cli in GLOB.admins)
- var/client/C = cli
- if (admin_seen[C])
- to_chat(C, span_looc("[ADMIN_FLW(usr)] LOOC: [src.key]/[src.mob.name]: [msg]"))
- else if (C.prefs.read_preference(/datum/preference/toggle/admin/see_looc))
- to_chat(C, span_rlooc("[ADMIN_FLW(usr)] (R)LOOC: [src.key]/[src.mob.name]: [msg]"))
+ var/client/cli_client = cli
+ if (admin_seen[cli_client])
+ to_chat(cli_client, span_looc("[ADMIN_FLW(usr)] LOOC: [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: [src.key]/[src.mob.name]: [msg]"))
+
+#undef LOOC_RANGE