From b9f559776e4fe5e498170f99bc980efd36f6a8b7 Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Thu, 3 Aug 2023 12:51:26 +0200
Subject: [PATCH] [MIRROR] Replaces Upgraded Cybernetic Ears with two new
variants [MDB IGNORE] (#22854)
* Replaces Upgraded Cybernetic Ears with two new variants (#75931)
## About The Pull Request
Adjusts the placement of basic and upgraded cybernetic ears in the
research tree and adds two new variants: Whisper-sensitive Cybernetic
Ears, which make it slightly easier to hear whispers from a tile away,
at the cost of higher vulnerability to flashbangs and other loud noises;
and Wall-penetrating Cybernetic Ears, which allow you to 'hear through
walls' so to speak, also at the cost of higher vulnerability to loud
noises.
Basic cybernetic ears are now in basic medical tech node, meaning that
medbay can print them roundstart the same as other basic cybernetics.
The upgraded cybernetic ears are now unlocked with the other tier 2
cybernetics. The two new ear variants are unlocked with the other tier 3
cybernetic organs, and the luminiscent and welding shield eyes have also
been moved there from the cybernetic implants node for consistency
reasons.
The whisper ears allow you to clearly hear whispers from up to seven
tiles away, the same range where you can hear normal speech. The
wall-penetrating ears allow you to hear normal speech within seven tiles
even through walls. Due to technical limitations, runechat popups do not
show up for people you can't see, but the messages will still show up in
chat.
## Why It's Good For The Game
Currently, upgraded cybernetic ears are very underwhelming compared to
other high-tier cybernetic organs. All other high tier organs provide
some sort of benefit; even if the benefit is minor like a built-in
flashlight, a slightly higher tolerance to alcohol and toxins, or higher
tolerance to disgusting food. This change is intended to grant similarly
minor but useful benefits to the cybernetic ears.
## Changelog
:cl:
add: Added whisper-sensitive cybernetic ears, which make it much easier
for the user to hear whispers at the cost of being more vulnerable to
loud noises
add: Added wall-penetrating cybernetic ears, which allow you to hear
speech through walls
balance: Basic cybernetic ears and upgraded cybernetic ears are now
unlocked with the other basic/normal cybernetics
balance: The welding shield and luminiscent cybernetic eyes are now
unlocked with the other upgraded cybernetics
/:cl:
* Replaces Upgraded Cybernetic Ears with two new variants
* Modular adjustments
* Linters
* Linters
---------
Co-authored-by: GPeckman <21979502+GPeckman@users.noreply.github.com>
Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com>
---
code/__DEFINES/say.dm | 6 +-
code/__DEFINES/traits.dm | 4 +
code/__DEFINES/~skyrat_defines/chat.dm | 1 +
code/__HELPERS/spatial_info.dm | 34 +++++++
code/__HELPERS/~skyrat_helpers/unsorted.dm | 2 +-
code/_globalvars/traits.dm | 2 +
code/datums/brain_damage/imaginary_friend.dm | 2 +-
code/datums/station_traits/positive_traits.dm | 4 +-
code/modules/cargo/bounties/medical.dm | 2 +
code/modules/mob/living/living_say.dm | 21 ++++-
.../research/designs/medical_designs.dm | 92 +++++++++++++------
code/modules/research/techweb/all_nodes.dm | 10 +-
.../surgery/organs/internal/ears/_ears.dm | 39 +++++++-
modular_skyrat/modules/verbs/code/looc.dm | 8 +-
14 files changed, 177 insertions(+), 50 deletions(-)
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