From 3103f9c413fe9eea35360c369bf806d30669fc7b Mon Sep 17 00:00:00 2001
From: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Date: Tue, 14 Apr 2026 03:58:40 +0200
Subject: [PATCH] Adds a Tactical IFF Visor and slightly refactors eye
rendering (#95547)
## About The Pull Request
A new cybernetic eye implant, the tactical IFF visor, has been added to
the game, available in the combat implants research node. Its main
features are a cool LED display, dynamic color correction to make
distinguishing important objects easier, and inbuilt IFF systems capable
of distinguishing and highlighting allies and potential threats. They
also give flash/welding protection and mild night-vision capabilities.
However, this comes at a downside of making the user completely unable
to distinguish appearances or voices, or even examine others. Everyone
will simply show up as Unknown and be completely covered in static, bar
the threat assessment outline. Examining will only display their threat
status according to threat settings in the visor.
The parameters for threat assessment (both ID access and security
flag-based), as well as the visor display, can be configured by the user
at any time.
When emagged, the visor will instead completely hide and mute all mobs
except the user themselves, leaving them completely "alone" on the
station.
Deathsquads get an unmodifiable version, configured to treat anyone but
CentCom personnel as hostiles.
Settings can also be adjusted before installation with a multitool, and
locked using a screwdriver, preventing users from accessing them when
installed.
---
Eye rendering has been slightly refactored in order to support the
monovisor, as well as to get rid of duplicate code (and missing
features) on dismembered heads. Also moth eyes once again should block
emissives properly.
## Why It's Good For The Game
New content for both gameplay and roleplay, and fits deathsquad's
purpose very well.
---
.../signals/signals_mob/signals_mob_main.dm | 2 +-
code/__DEFINES/mobs.dm | 3 +
code/__DEFINES/say.dm | 2 +
code/datums/brain_damage/special.dm | 2 +-
code/datums/components/security_vision.dm | 2 +-
code/game/say.dm | 2 +-
.../blood_worm/blood_worm_text_feedback.dm | 2 +-
code/modules/clothing/head/jobs.dm | 2 +-
code/modules/clothing/outfits/ert.dm | 3 +
.../living/carbon/human/human_update_icons.dm | 18 +-
code/modules/mob/living/living_say.dm | 7 +-
code/modules/mob/mob.dm | 5 +-
.../research/designs/medical_designs.dm | 19 +
.../research/techweb/nodes/cyborg_nodes.dm | 1 +
code/modules/surgery/bodyparts/head.dm | 29 +-
.../surgery/organs/internal/eyes/_eyes.dm | 653 +-----------
.../organs/internal/eyes/eyes_augments.dm | 965 ++++++++++++++++++
.../organs/internal/eyes/eyes_species.dm | 113 ++
icons/mob/human/human_eyes.dmi | Bin 3149 -> 3491 bytes
icons/obj/medical/organs/organs.dmi | Bin 58932 -> 50469 bytes
tgstation.dme | 2 +
.../tgui/interfaces/TacVisorEyesMenu.tsx | 75 ++
22 files changed, 1240 insertions(+), 667 deletions(-)
create mode 100644 code/modules/surgery/organs/internal/eyes/eyes_augments.dm
create mode 100644 code/modules/surgery/organs/internal/eyes/eyes_species.dm
create mode 100644 tgui/packages/tgui/interfaces/TacVisorEyesMenu.tsx
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
index c26c80a652f..b3b6acefd23 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
@@ -120,7 +120,7 @@
#define COMSIG_MOB_ATTACK_ALIEN "mob_attack_alien"
///from base of /mob/throw_item(): (atom/target)
#define COMSIG_MOB_THROW "mob_throw"
-///from base of /mob/verb/examinate(): (atom/target, list/examine_strings)
+///from base of /mob/verb/examinate(): (atom/target, list/examine_strings, list/examine_overrides)
#define COMSIG_MOB_EXAMINING "mob_examining"
///from base of /mob/verb/examinate(): (atom/target)
#define COMSIG_MOB_EXAMINATE "mob_examinate"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 7deeeee743d..2e7a1894898 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -608,6 +608,9 @@
/// If you yawn while someone nearby has examined you within this time frame, it will force them to yawn as well. Tradecraft!
#define YAWN_PROPAGATION_EXAMINE_WINDOW (2 SECONDS)
+// Priorities for examine overrides
+#define EXAMINE_OVERRIDE_PRIORITY_IFF 1
+
/// How far away you can be to make eye contact with someone while examining
#define EYE_CONTACT_RANGE 5
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index d1da7ea0976..9b8496f22fd 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -73,6 +73,8 @@
#define MODE_CUSTOM_SAY_ERASE_INPUT "erase_input"
/// Message is being relayed through another object
#define MODE_RELAY "relayed"
+/// Override the mob's name
+#define MODE_SPEAKER_NAME_OVERRIDE "speaker_name_override"
//Spans. Robot speech, italics, etc. Applied in compose_message().
#define SPAN_ROBOT "robot"
diff --git a/code/datums/brain_damage/special.dm b/code/datums/brain_damage/special.dm
index e4e6847011f..469a5b90562 100644
--- a/code/datums/brain_damage/special.dm
+++ b/code/datums/brain_damage/special.dm
@@ -701,7 +701,7 @@
to_chat(owner, span_warning("Should I really leave it here?"))
owner.add_mood_event("fireaxe", /datum/mood_event/axe_neutral)
-/datum/brain_trauma/special/axedoration/proc/on_examine(mob/source, atom/target, list/examine_strings)
+/datum/brain_trauma/special/axedoration/proc/on_examine(mob/source, atom/target, list/examine_strings, list/examine_overrides)
SIGNAL_HANDLER
if(!istype(target, /obj/item/fireaxe))
return
diff --git a/code/datums/components/security_vision.dm b/code/datums/components/security_vision.dm
index 5c2b88fffd3..f26b7121d8e 100644
--- a/code/datums/components/security_vision.dm
+++ b/code/datums/components/security_vision.dm
@@ -19,7 +19,7 @@
UnregisterSignal(parent, COMSIG_MOB_EXAMINING)
/// When we examine something, check if we have any extra data to add
-/datum/component/security_vision/proc/on_examining(mob/source, atom/target, list/examine_strings)
+/datum/component/security_vision/proc/on_examining(mob/source, atom/target, list/examine_strings, list/examine_overrides)
SIGNAL_HANDLER
if (!isliving(target))
return
diff --git a/code/game/say.dm b/code/game/say.dm
index 7199fe3e377..4413e353400 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -149,7 +149,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
//Radio freq/name display
var/freqpart = radio_freq ? "\[[get_radio_name(radio_freq, radio_freq_name)]\] " : ""
//Speaker name
- var/namepart = speaker.get_message_voice(visible_name)
+ var/namepart = message_mods[MODE_SPEAKER_NAME_OVERRIDE] || speaker.get_message_voice(visible_name)
//End name span.
var/endspanpart = ""
diff --git a/code/modules/antagonists/blood_worm/blood_worm_text_feedback.dm b/code/modules/antagonists/blood_worm/blood_worm_text_feedback.dm
index 5be1c9c46c3..d11dc64ce83 100644
--- a/code/modules/antagonists/blood_worm/blood_worm_text_feedback.dm
+++ b/code/modules/antagonists/blood_worm/blood_worm_text_feedback.dm
@@ -4,7 +4,7 @@
/mob/living/basic/blood_worm/examining(atom/target, list/result)
add_special_examining_messages(target, result)
-/mob/living/basic/blood_worm/proc/on_host_examining(datum/source, atom/target, list/examine_strings)
+/mob/living/basic/blood_worm/proc/on_host_examining(datum/source, atom/target, list/examine_strings, list/examine_overrides)
SIGNAL_HANDLER
add_special_examining_messages(target, examine_strings)
diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm
index b7dc17cce85..f83b70bfa2f 100644
--- a/code/modules/clothing/head/jobs.dm
+++ b/code/modules/clothing/head/jobs.dm
@@ -713,7 +713,7 @@
. = ..()
UnregisterSignal(user, COMSIG_MOB_EXAMINING_MORE)
-/obj/item/clothing/head/utility/head_mirror/proc/examining(mob/living/examiner, atom/examining, list/examine_list)
+/obj/item/clothing/head/utility/head_mirror/proc/examining(mob/living/examiner, atom/examining, list/examine_list, list/examine_overrides)
SIGNAL_HANDLER
if(!ishuman(examining) || examining == examiner || examiner.is_blind() || !examiner.Adjacent(examining))
return
diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm
index e9440c2e32c..02bbb550363 100644
--- a/code/modules/clothing/outfits/ert.dm
+++ b/code/modules/clothing/outfits/ert.dm
@@ -457,6 +457,9 @@
)
/datum/outfit/centcom/death_commando/post_equip(mob/living/carbon/human/squaddie, visuals_only = FALSE)
+ var/obj/item/organ/eyes/robotic/tacvisor/deathsquad/visor = new()
+ visor.Insert(squaddie, movement_flags = DELETE_IF_REPLACED)
+
if(visuals_only)
return
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index c55a85b9de8..20a79e80217 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -898,15 +898,19 @@ generate/load female uniform sprites matching all previously decided variables
remove_overlay(EYES_LAYER)
if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
return
- var/obj/item/bodypart/head/noggin = get_bodypart(BODY_ZONE_HEAD)
- if(!(noggin?.head_flags & HEAD_EYESPRITES))
- return
+
// eyes (missing eye sprites get handled by the head itself, but sadly we have to do this stupid shit here, for now)
var/obj/item/organ/eyes/eye_organ = get_organ_slot(ORGAN_SLOT_EYES)
- if(eye_organ)
- eye_organ.refresh(call_update = FALSE)
- overlays_standing[EYES_LAYER] = eye_organ.generate_body_overlay(src)
- apply_overlay(EYES_LAYER)
+ if (!eye_organ)
+ return
+
+ var/obj/item/bodypart/head/noggin = get_bodypart(deprecise_zone(eye_organ.zone)) // Futureproofing for HARS/weird species
+ if(istype(noggin) && !(noggin?.head_flags & HEAD_EYESPRITES))
+ return
+
+ eye_organ.refresh(call_update = FALSE)
+ overlays_standing[EYES_LAYER] = eye_organ.generate_body_overlay(src, noggin)
+ apply_overlay(EYES_LAYER)
/// Updates face (as of now, only eye) offsets
/mob/living/carbon/human/update_face_offset()
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index 6c332091709..6926d6abe99 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -296,6 +296,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
var/dist = get_dist(speaker, src) - message_range
if(dist > 0 && dist <= EAVESDROP_EXTRA_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING))
raw_message = stars(raw_message)
+ var/speaker_name = span_name("[message_mods[MODE_SPEAKER_NAME_OVERRIDE] || speaker]")
if(message_range != INFINITY && dist > EAVESDROP_EXTRA_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING))
// Too far away and don't have good hearing, you can't hear anything
if(is_blind() || HAS_TRAIT(speaker, TRAIT_INVISIBLE_MAN)) // Can't see them speak either
@@ -305,7 +306,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
// But we can still see them speak
if(speaker_is_signing)
- deaf_message = "[span_name("[speaker]")] [speaker.get_default_say_verb()] something, but the motions are too subtle to make out from afar."
+ deaf_message = "[speaker_name] [speaker.get_default_say_verb()] something, but the motions are too subtle to make out from afar."
else if(!HAS_TRAIT(src, TRAIT_DEAF)) // If we can't hear we want to continue to the default deaf message
if(isliving(speaker))
var/mob/living/living_speaker = speaker
@@ -313,7 +314,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
if(mouth_hidden && !HAS_TRAIT(src, TRAIT_SEE_MASK_WHISPER)) // Can't see them speak if their mouth is covered or hidden, unless we're an empath
return FALSE
- deaf_message = "[span_name("[speaker]")] [speaker.verb_whisper] something, but you are too far away to hear [speaker.p_them()]."
+ deaf_message = "[speaker_name] [speaker.verb_whisper] something, but you are too far away to hear [speaker.p_them()]."
if(deaf_message)
deaf_type = MSG_VISUAL
@@ -353,7 +354,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
if(speaker != src)
if(!radio_freq) //These checks have to be separate, else people talking on the radio will make "You can't hear yourself!" appear when hearing people over the radio while deaf.
- deaf_message = "[span_name("[speaker]")] [speaker.get_default_say_verb()] something but you cannot hear [speaker.p_them()]."
+ deaf_message = "[speaker_name] [speaker.get_default_say_verb()] something but you cannot hear [speaker.p_them()]."
deaf_type = MSG_VISUAL
else
deaf_message = span_notice("You can't hear yourself!")
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 4d746c9fdf6..d8285681d95 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -589,7 +589,10 @@
var/list/result = examinify.examine(src)
var/atom_title = examinify.examine_title(src, thats = TRUE)
examining(examinify, result)
- SEND_SIGNAL(src, COMSIG_MOB_EXAMINING, examinify, result)
+ var/alist/overrides = alist()
+ SEND_SIGNAL(src, COMSIG_MOB_EXAMINING, examinify, result, overrides)
+ if (length(overrides))
+ result = overrides[max(overrides)]
if(removes_double_click)
result += span_notice("You can examine [examinify] closer...")
result_combined = (atom_title ? fieldset_block("[atom_title].", jointext(result, "
"), "boxed_message") : boxed_message(jointext(result, "
")))
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index bcc98287e24..b46ce807541 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -581,6 +581,25 @@
id = "ci-thermals-moth"
build_path = /obj/item/organ/eyes/robotic/thermals/moth
+/datum/design/cyberimp_tacvisor
+ name = "Tactical IFF Visor"
+ desc = "A sick IFF visor with an inbuilt LED display. May critically overload the user's prefrontal cortex."
+ id = "ci-tacvisor"
+ build_type = PROTOLATHE | AWAY_LATHE | MECHFAB
+ construction_time = 6 SECONDS
+ materials = list(
+ /datum/material/iron = SMALL_MATERIAL_AMOUNT * 4,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT * 4,
+ /datum/material/silver = SMALL_MATERIAL_AMOUNT * 4,
+ /datum/material/gold = SMALL_MATERIAL_AMOUNT * 6,
+ /datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT,
+ )
+ build_path = /obj/item/organ/eyes/robotic/tacvisor
+ category = list(
+ RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_COMBAT
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
+
/datum/design/cyberimp_antidrop
name = "Anti-Drop Implant"
desc = "This cybernetic brain implant will allow you to force your hand muscles to contract, preventing item dropping. Twitch ear to toggle."
diff --git a/code/modules/research/techweb/nodes/cyborg_nodes.dm b/code/modules/research/techweb/nodes/cyborg_nodes.dm
index fee377af77c..66da933c1ae 100644
--- a/code/modules/research/techweb/nodes/cyborg_nodes.dm
+++ b/code/modules/research/techweb/nodes/cyborg_nodes.dm
@@ -183,6 +183,7 @@
"ci-reviver",
"ci-antidrop",
"ci-antistun",
+ "ci-tacvisor",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
announce_channels = list(RADIO_CHANNEL_SCIENCE, RADIO_CHANNEL_MEDICAL)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 9487373d2cc..ce5e8c669e8 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -222,32 +222,9 @@
. += no_eyes
return
- if(!eyes.eye_icon_state || !(head_flags & HEAD_EYESPRITES))
- return
-
- // This is a bit of copy/paste code from eyes.dm:generate_body_overlay
- var/image/eye_left = image(eyes.eye_icon, "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH)
- var/image/eye_right = image(eyes.eye_icon, "[eyes.eye_icon_state]_r", -EYES_LAYER, SOUTH)
- if(head_flags & HEAD_EYECOLOR)
- if(eyes.eye_color_left)
- eye_left.color = eyes.eye_color_left
- if(eyes.eye_color_right)
- eye_right.color = eyes.eye_color_right
-
- var/list/emissive_overlays = eyes.get_emissive_overlays(eye_left, eye_right, src)
- if(length(emissive_overlays))
- eye_left.overlays += image(emissive_overlays[1], dir = SOUTH)
- eye_right.overlays += image(emissive_overlays[2], dir = SOUTH)
- else if(blocks_emissive != EMISSIVE_BLOCK_NONE)
- eye_left.overlays += image(emissive_blocker(eye_left.icon, eye_left.icon_state, src, alpha = eye_left.alpha), dir = SOUTH)
- eye_right.overlays += image(emissive_blocker(eye_right.icon, eye_right.icon_state, src, alpha = eye_right.alpha), dir = SOUTH)
-
- if(worn_face_offset)
- worn_face_offset.apply_offset(eye_left)
- worn_face_offset.apply_offset(eye_right)
-
- . += eye_left
- . += eye_right
+ if(head_flags & HEAD_EYESPRITES)
+ for (var/mutable_appearance/overlay as anything in eyes.generate_body_overlay(null, src))
+ . += image(overlay, dir = SOUTH)
/obj/item/bodypart/head/get_voice(add_id_name)
return "The head of [get_face_name()]"
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index b6152e8b0ce..64285ea5afc 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -286,45 +286,46 @@
return ..() || (owner.stat != DEAD && !HAS_TRAIT(owner, TRAIT_KNOCKEDOUT) && (owner.is_blind() || owner.is_nearsighted()))
/// This proc generates a list of overlays that the eye should be displayed using for the given parent
-/obj/item/organ/eyes/proc/generate_body_overlay(mob/living/carbon/human/parent)
- if(!istype(parent) || parent.get_organ_by_type(/obj/item/organ/eyes) != src)
- CRASH("Generating a body overlay for [src] targeting an invalid parent '[parent]'.")
-
+/obj/item/organ/eyes/proc/generate_body_overlay(mob/living/carbon/human/parent, obj/item/bodypart/limb)
if(isnull(eye_icon_state))
return list()
- var/mutable_appearance/eye_left = mutable_appearance(eye_icon, "[eye_icon_state]_l", -EYES_LAYER, parent)
- var/mutable_appearance/eye_right = mutable_appearance(eye_icon, "[eye_icon_state]_r", -EYES_LAYER, parent)
+ var/mutable_appearance/eye_left = mutable_appearance(eye_icon, "[eye_icon_state]_l", -EYES_LAYER, parent || limb)
+ var/mutable_appearance/eye_right = mutable_appearance(eye_icon, "[eye_icon_state]_r", -EYES_LAYER, parent || limb)
var/list/overlays = list(eye_left, eye_right)
- if(!(parent.obscured_slots & HIDEEYES))
- overlays += get_emissive_overlays(eye_left, eye_right, parent)
+ if(!(parent?.obscured_slots & HIDEEYES))
+ overlays += get_emissive_overlays(eye_left, eye_right, parent || limb)
- var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD)
-
- if(!my_head)
+ if(!limb)
return overlays
- if(my_head.head_flags & HEAD_EYECOLOR)
- eye_right.color = parent.get_right_eye_color()
- eye_left.color = parent.get_left_eye_color()
- var/list/eyelids = setup_eyelids(eye_left, eye_right, parent)
- if (LAZYLEN(eyelids))
- overlays += eyelids
+ // Futureproofing for HARS/weird species
+ var/obj/item/bodypart/head/head = astype(limb, /obj/item/bodypart/head)
+ if(head?.head_flags & HEAD_EYECOLOR)
+ if (parent)
+ eye_right.color = parent.get_right_eye_color()
+ eye_left.color = parent.get_left_eye_color()
+ var/list/eyelids = setup_eyelids(eye_left, eye_right, parent)
+ if (LAZYLEN(eyelids))
+ overlays += eyelids
+ else
+ eye_right.color = eye_color_right
+ eye_left.color = eye_color_left
if (scarring & RIGHT_EYE_SCAR)
- var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_right", -EYES_LAYER, parent)
- right_scar.color = my_head.draw_color
+ var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_right", -EYES_LAYER, parent || limb)
+ right_scar.color = limb.draw_color
overlays += right_scar
if (scarring & LEFT_EYE_SCAR)
- var/mutable_appearance/left_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_left", -EYES_LAYER, parent)
- left_scar.color = my_head.draw_color
+ var/mutable_appearance/left_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_left", -EYES_LAYER, parent || limb)
+ left_scar.color = limb.draw_color
overlays += left_scar
- if(my_head.worn_face_offset)
+ if(head?.worn_face_offset)
for (var/mutable_appearance/overlay as anything in overlays)
- my_head.worn_face_offset.apply_offset(overlay)
+ head.worn_face_offset.apply_offset(overlay)
return overlays
@@ -336,9 +337,14 @@
emissive_effect = EMISSIVE_BLOOM
else if((owner && HAS_TRAIT(owner, TRAIT_REFLECTIVE_EYES)) || (TRAIT_REFLECTIVE_EYES in organ_traits))
emissive_effect = EMISSIVE_SPECULAR
+
if(emissive_effect)
return_list += emissive_appearance(eye_left.icon, eye_left.icon_state, spokesman, -EYES_LAYER, alpha = eye_left.alpha, effect_type = emissive_effect)
return_list += emissive_appearance(eye_right.icon, eye_right.icon_state, spokesman, -EYES_LAYER, alpha = eye_right.alpha, effect_type = emissive_effect)
+ else
+ return_list += emissive_blocker(eye_left.icon, eye_left.icon_state, spokesman, -EYES_LAYER, alpha = eye_left.alpha)
+ return_list += emissive_blocker(eye_right.icon, eye_right.icon_state, spokesman, -EYES_LAYER, alpha = eye_right.alpha)
+
return return_list
/obj/item/organ/eyes/update_overlays()
@@ -606,604 +612,3 @@
#undef NIGHTVISION_LIGHT_LOW
#undef NIGHTVISION_LIGHT_MID
#undef NIGHTVISION_LIGHT_HIG
-
-/obj/item/organ/eyes/night_vision/mushroom
- name = "fung-eye"
- desc = "While on the outside they look inert and dead, the eyes of mushroom people are actually very advanced."
- low_light_cutoff = list(0, 15, 20)
- medium_light_cutoff = list(0, 20, 35)
- high_light_cutoff = list(0, 40, 50)
- pupils_name = "photosensory openings"
- penlight_message = "are attached to fungal stalks"
-
-/obj/item/organ/eyes/zombie
- name = "undead eyes"
- desc = "Somewhat counterintuitively, these half-rotten eyes actually have superior vision to those of a living human."
- color_cutoffs = list(25, 35, 5)
- penlight_message = "are rotten and decayed!"
-
-/obj/item/organ/eyes/zombie/penlight_examine(mob/living/viewer, obj/item/examtool)
- return span_danger("[owner.p_Their()] eyes [penlight_message]")
-
-/obj/item/organ/eyes/alien
- name = "alien eyes"
- desc = "It turned out they had them after all!"
- sight_flags = SEE_MOBS
- color_cutoffs = list(25, 5, 42)
-
-/obj/item/organ/eyes/golem
- name = "resonating crystal"
- desc = "Golems somehow measure external light levels and detect nearby ore using this sensitive mineral lattice."
- icon_state = "adamantine_cords"
- eye_icon_state = null
- blink_animation = FALSE
- iris_overlay = null
- color = COLOR_GOLEM_GRAY
- visual = FALSE
- organ_flags = ORGAN_MINERAL
- color_cutoffs = list(10, 15, 5)
- actions_types = list(/datum/action/cooldown/golem_ore_sight)
- penlight_message = "glimmer, their crystaline structure refracting light inwards"
- pupils_name = "lensing gems" ///given it says these are a "mineral lattice" that collects light i assume they work like artifical ruby laser foci
-
-/// Send an ore detection pulse on a cooldown
-/datum/action/cooldown/golem_ore_sight
- name = "Ore Resonance"
- desc = "Causes nearby ores to vibrate, revealing their location."
- button_icon = 'icons/obj/devices/scanner.dmi'
- button_icon_state = "manual_mining"
- check_flags = AB_CHECK_CONSCIOUS
- cooldown_time = 10 SECONDS
-
-/datum/action/cooldown/golem_ore_sight/Activate(atom/target)
- . = ..()
- mineral_scan_pulse(get_turf(target), scanner = target)
-
-///Robotic
-
-/obj/item/organ/eyes/robotic
- name = "robotic eyes"
- desc = "Your vision is augmented."
- icon_state = "eyes_cyber"
- organ_flags = ORGAN_ROBOTIC
- failing_desc = "seems to be broken."
- pupils_name = "apertures"
- penlight_message = "are cybernetic, click-whirring as they refocus"
-
-/obj/item/organ/eyes/robotic/emp_act(severity)
- . = ..()
- if((. & EMP_PROTECT_SELF) || !owner)
- return
- if(prob(10 * severity))
- return
- to_chat(owner, span_warning("Static obfuscates your vision!"))
- owner.flash_act(visual = 1)
-
-/obj/item/organ/eyes/robotic/basic
- name = "basic robotic eyes"
- desc = "A pair of basic cybernetic eyes that restore vision, but at some vulnerability to light."
- icon_state = "eyes_cyber_basic"
- iris_overlay = null
- eye_color_left = "#2f3032"
- eye_color_right = "#2f3032"
- flash_protect = FLASH_PROTECTION_SENSITIVE
- penlight_message = "are low grade cybernetics, poorly compensating for the light"
-
-/obj/item/organ/eyes/robotic/basic/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- if(prob(10 * severity))
- apply_organ_damage(20 * severity)
- to_chat(owner, span_warning("Your eyes start to fizzle in their sockets!"))
- do_sparks(2, TRUE, owner)
- owner.emote("scream")
-
-/obj/item/organ/eyes/robotic/xray
- name = "x-ray eyes"
- desc = "These cybernetic eyes will give you X-ray vision. Blinking is futile."
- icon_state = "eyes_cyber_xray"
- iris_overlay = null
- eye_color_left = "#3cb8a5"
- eye_color_right = "#3cb8a5"
- sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
- flash_protect = FLASH_PROTECTION_SENSITIVE
- organ_traits = list(TRAIT_XRAY_VISION)
- penlight_message = "are replaced by small radiation emitters and detectors"
-
-/obj/item/organ/eyes/robotic/thermals
- name = "thermal eyes"
- desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included."
- icon_state = "eyes_cyber_thermal"
- iris_overlay = null
- eye_color_left = "#ce2525"
- eye_color_right = "#ce2525"
- // We're gonna downshift green and blue a bit so darkness looks yellow
- color_cutoffs = list(25, 8, 5)
- sight_flags = SEE_MOBS
- flash_protect = FLASH_PROTECTION_SENSITIVE
- pupils_name = "slit aperatures"
- penlight_message = "are cybernetic, with vertically slit metalic lenses."
-
-/obj/item/organ/eyes/robotic/flashlight
- name = "flashlight eyes"
- desc = "It's two flashlights rigged together with some wire. Why would you put these in someone's head?"
- icon_state = "flashlight_eyes"
- eye_color_left = "#fee5a3"
- eye_color_right = "#fee5a3"
- iris_overlay = null
- flash_protect = FLASH_PROTECTION_WELDER
- tint = INFINITY
- custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.9)
- var/obj/item/flashlight/eyelight/eye
- light_reactive = FALSE
- pupils_name = "flashlights"
- penlight_message = "are actually two flashlights taped together. ...why"
-
-/obj/item/organ/eyes/robotic/flashlight/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
-
-/obj/item/organ/eyes/robotic/flashlight/on_mob_insert(mob/living/carbon/victim)
- . = ..()
- if(!eye)
- eye = new /obj/item/flashlight/eyelight()
- eye.set_light_on(TRUE)
- eye.forceMove(victim)
- eye.update_brightness(victim)
- victim.become_blind(FLASHLIGHT_EYES)
-
-/obj/item/organ/eyes/robotic/flashlight/on_mob_remove(mob/living/carbon/victim)
- . = ..()
- eye.set_light_on(FALSE)
- eye.update_brightness(victim)
- eye.forceMove(src)
- victim.cure_blind(FLASHLIGHT_EYES)
-
-// Welding shield implant
-/obj/item/organ/eyes/robotic/shield
- name = "shielded robotic eyes"
- desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision."
- icon_state = "eyes_cyber_shield"
- iris_overlay = null
- eye_color_left = "#353845"
- eye_color_right = "#353845"
- flash_protect = FLASH_PROTECTION_WELDER
- pupils_name = "flash shields"
- penlight_message = "have polarized cybernetic lenses, blocking bright lights"
-
-/obj/item/organ/eyes/robotic/shield/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
-
-#define MATCH_LIGHT_COLOR 1
-#define USE_CUSTOM_COLOR 0
-#define UPDATE_LIGHT 0
-#define UPDATE_EYES_LEFT 1
-#define UPDATE_EYES_RIGHT 2
-
-/obj/item/organ/eyes/robotic/glow
- name = "high luminosity eyes"
- desc = "Special glowing eyes, used by snowflakes who want to be special."
- icon_state = "eyes_cyber_glow"
- iris_overlay = "eyes_cyber_glow_iris"
- eye_color_left = "#19191a"
- eye_color_right = "#19191a"
- actions_types = list(/datum/action/item_action/organ_action/use, /datum/action/item_action/organ_action/toggle)
- var/max_light_beam_distance = 5
- var/obj/item/flashlight/eyelight/glow/eye
- /// base icon state for eye overlays
- var/base_eye_state = "eyes_glow_gs"
- /// Whether or not to match the eye color to the light or use a custom selection
- var/eye_color_mode = USE_CUSTOM_COLOR
- /// The selected color for the light beam itself
- var/light_color_string = "#ffffff"
- /// The custom selected eye color for the left eye. Defaults to the mob's natural eye color
- var/left_eye_color_string
- /// The custom selected eye color for the right eye. Defaults to the mob's natural eye color
- var/right_eye_color_string
- penlight_message = "shine back with cybernetic LEDs"
-
-/obj/item/organ/eyes/robotic/glow/Initialize(mapload)
- . = ..()
- eye = new /obj/item/flashlight/eyelight/glow
-
-/obj/item/organ/eyes/robotic/glow/Destroy()
- . = ..()
- deactivate(close_ui = TRUE)
- QDEL_NULL(eye)
-
-/obj/item/organ/eyes/robotic/glow/emp_act(severity)
- . = ..()
- if(!eye.light_on || . & EMP_PROTECT_SELF)
- return
- deactivate(close_ui = TRUE)
-
-/// Set the initial color of the eyes on insert to be the mob's previous eye color.
-/obj/item/organ/eyes/robotic/glow/on_mob_insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags)
- . = ..()
- left_eye_color_string = eye_color_left
- right_eye_color_string = eye_color_right
- update_mob_eye_color(eye_recipient)
- deactivate(close_ui = TRUE)
- eye.forceMove(eye_recipient)
-
-/obj/item/organ/eyes/robotic/glow/on_mob_remove(mob/living/carbon/eye_owner)
- deactivate(eye_owner, close_ui = TRUE)
- if(!QDELETED(eye))
- eye.forceMove(src)
- return ..()
-
-/obj/item/organ/eyes/robotic/glow/ui_state(mob/user)
- return GLOB.default_state
-
-/obj/item/organ/eyes/robotic/glow/ui_status(mob/user, datum/ui_state/state)
- if(!QDELETED(owner))
- if(owner == user)
- return min(
- ui_status_user_is_abled(user, src),
- ui_status_only_living(user),
- )
- else return UI_CLOSE
- return ..()
-
-/obj/item/organ/eyes/robotic/glow/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "HighLuminosityEyesMenu")
- ui.autoupdate = FALSE
- ui.open()
-
-/obj/item/organ/eyes/robotic/glow/ui_data(mob/user)
- var/list/data = list()
-
- data["eyeColor"] = list(
- mode = eye_color_mode,
- hasOwner = owner ? TRUE : FALSE,
- left = left_eye_color_string,
- right = right_eye_color_string,
- )
- data["lightColor"] = light_color_string
- data["range"] = eye.light_range
-
- return data
-
-/obj/item/organ/eyes/robotic/glow/ui_act(action, list/params, datum/tgui/ui)
- . = ..()
- if(.)
- return
-
- switch(action)
- if("set_range")
- var/new_range = params["new_range"]
- set_beam_range(new_range)
- return TRUE
- if("pick_color")
- var/new_color = tgui_color_picker(
- usr,
- "Choose eye color color:",
- "High Luminosity Eyes Menu",
- light_color_string
- )
- if(new_color)
- var/to_update = params["to_update"]
- set_beam_color(new_color, to_update)
- return TRUE
- if("enter_color")
- var/new_color = LOWER_TEXT(params["new_color"])
- var/to_update = params["to_update"]
- set_beam_color(new_color, to_update, sanitize = TRUE)
- return TRUE
- if("random_color")
- var/to_update = params["to_update"]
- randomize_color(to_update)
- return TRUE
- if("toggle_eye_color")
- toggle_eye_color_mode()
- return TRUE
-
-/obj/item/organ/eyes/robotic/glow/ui_action_click(mob/user, action)
- if(istype(action, /datum/action/item_action/organ_action/toggle))
- toggle_active()
- else if(istype(action, /datum/action/item_action/organ_action/use))
- ui_interact(user)
-
-/**
- * Activates the light
- *
- * Turns on the attached flashlight object, updates the mob overlay to be added.
- */
-/obj/item/organ/eyes/robotic/glow/proc/activate()
- if(eye.light_range)
- eye.set_light_on(TRUE)
- else
- eye.light_on = TRUE // at range 0 we are just going to make the eyes glow emissively, no light overlay
- update_mob_eye_color()
-
-/**
- * Deactivates the light
- *
- * Turns off the attached flashlight object, closes UIs, updates the mob overlay to be removed.
- * Arguments:
- * * mob/living/carbon/eye_owner - the mob who the eyes belong to
- * * close_ui - whether or not to close the ui
- */
-/obj/item/organ/eyes/robotic/glow/proc/deactivate(mob/living/carbon/eye_owner = owner, close_ui = FALSE)
- if(close_ui)
- SStgui.close_uis(src)
- eye.set_light_on(FALSE)
- update_mob_eye_color(eye_owner)
-
-/**
- * Randomizes the light color
- *
- * Picks a random color and sets the beam color to that
- * Arguments:
- * * to_update - whether we are setting the color for the light beam itself, or the individual eyes
- */
-/obj/item/organ/eyes/robotic/glow/proc/randomize_color(to_update = UPDATE_LIGHT)
- var/new_color = "#"
- for(var/i in 1 to 3)
- new_color += num2hex(rand(0, 255), 2)
- set_beam_color(new_color, to_update)
-
-/**
- * Setter function for the light's range
- *
- * Sets the light range of the attached flashlight object
- * Includes some 'unique' logic to accomodate for some quirks of the lighting system
- * Arguments:
- * * new_range - the new range to set
- */
-/obj/item/organ/eyes/robotic/glow/proc/set_beam_range(new_range)
- var/old_light_range = eye.light_range
- if(old_light_range == 0 && new_range > 0 && eye.light_on) // turn bring back the light overlay if we were previously at 0 (aka emissive eyes only)
- eye.light_on = FALSE // this is stupid, but this has to be FALSE for set_light_on() to work.
- eye.set_light_on(TRUE)
- eye.set_light_range(clamp(new_range, 0, max_light_beam_distance))
-
-/**
- * Setter function for the light's color
- *
- * Sets the light color of the attached flashlight object. Sets the eye color vars of this eye organ as well and then updates the mob's eye color.
- * Arguments:
- * * newcolor - the new color hex string to set
- * * to_update - whether we are setting the color for the light beam itself, or the individual eyes
- * * sanitize - whether the hex string should be sanitized
- */
-/obj/item/organ/eyes/robotic/glow/proc/set_beam_color(newcolor, to_update = UPDATE_LIGHT, sanitize = FALSE)
- var/newcolor_string
- if(sanitize)
- newcolor_string = sanitize_hexcolor(newcolor)
- else
- newcolor_string = newcolor
- switch(to_update)
- if(UPDATE_LIGHT)
- light_color_string = newcolor_string
- eye.set_light_color(newcolor_string)
- if(UPDATE_EYES_LEFT)
- left_eye_color_string = newcolor_string
- if(UPDATE_EYES_RIGHT)
- right_eye_color_string = newcolor_string
-
- update_mob_eye_color()
-
-/**
- * Toggle the attached flashlight object on or off
- */
-/obj/item/organ/eyes/robotic/glow/proc/toggle_active()
- if(eye.light_on)
- deactivate()
- else
- activate()
-
-/**
- * Toggles for the eye color mode
- *
- * Toggles the eye color mode on or off and then calls an update on the mob's eye color
- */
-/obj/item/organ/eyes/robotic/glow/proc/toggle_eye_color_mode()
- eye_color_mode = !eye_color_mode
- update_mob_eye_color()
-
-/**
- * Updates the mob eye color
- *
- * Updates the eye color to reflect on the mob's body if it's possible to do so
- * Arguments:
- * * mob/living/carbon/eye_owner - the mob to update the eye color appearance of
- */
-/obj/item/organ/eyes/robotic/glow/proc/update_mob_eye_color(mob/living/carbon/eye_owner = owner)
- switch(eye_color_mode)
- if(MATCH_LIGHT_COLOR)
- eye_color_left = light_color_string
- eye_color_right = light_color_string
- if(USE_CUSTOM_COLOR)
- eye_color_left = left_eye_color_string
- eye_color_right = right_eye_color_string
-
- if(QDELETED(eye_owner) || !ishuman(eye_owner)) //Other carbon mobs don't have eye color.
- return
-
- var/obj/item/bodypart/head/head = eye_owner.get_bodypart(BODY_ZONE_HEAD) //if we have eyes we definently have a head anyway
- var/previous_flags = head.head_flags
- head.head_flags |= HEAD_EYECOLOR
-
- ///enabling and disabling the TRAIT_LUMINESCENT_EYES trait already calls handle_eyes(), in that case, let's skip that call
- var/skip_call = FALSE
- if(!eye.light_on)
- eye_icon_state = initial(eye_icon_state)
- skip_call = HAS_TRAIT_FROM_ONLY(eye_owner, TRAIT_LUMINESCENT_EYES, REF(src))
- remove_organ_trait(TRAIT_LUMINESCENT_EYES)
- else
- skip_call = !HAS_TRAIT(eye_owner, TRAIT_LUMINESCENT_EYES)
- add_organ_trait(TRAIT_LUMINESCENT_EYES)
- eye_icon_state = base_eye_state
-
- if(!skip_call && ishuman(eye_owner))
- var/mob/living/carbon/human/humie = eye_owner
- humie.update_eyes()
-
- head.head_flags = previous_flags
-
-#undef MATCH_LIGHT_COLOR
-#undef USE_CUSTOM_COLOR
-#undef UPDATE_LIGHT
-#undef UPDATE_EYES_LEFT
-#undef UPDATE_EYES_RIGHT
-
-/obj/item/organ/eyes/moth
- name = "moth eyes"
- desc = "These eyes seem to have increased sensitivity to bright light, with no improvement to low light vision."
- icon_state = "eyes_moth"
- eye_icon_state = "motheyes"
- blink_animation = FALSE
- iris_overlay = null
- flash_protect = FLASH_PROTECTION_SENSITIVE
- pupils_name = "ommatidia" //yes i know compound eyes have no pupils shut up
- penlight_message = "are bulbous and insectoid"
-
-/obj/item/organ/eyes/robotic/moth
- name = "robotic moth eyes"
- desc = "Your vision is augmented. Much like actual moth eyes, very sensitive to bright lights."
- icon_state = "eyes_moth_cyber"
- eye_icon_state = "motheyes_cyber"
- flash_protect = FLASH_PROTECTION_SENSITIVE
- pupils_name = "aperture clusters"
- penlight_message = "are metal hemispheres, resembling insect eyes"
-
-/obj/item/organ/eyes/robotic/basic/moth
- name = "basic robotic moth eyes"
- icon_state = "eyes_moth_cyber_basic"
- eye_icon_state = "motheyes_white"
- eye_color_left = "#65686f"
- eye_color_right = "#65686f"
- blink_animation = FALSE
- flash_protect = FLASH_PROTECTION_SENSITIVE
- pupils_name = "aperture clusters"
- penlight_message = "are metal hemispheres, resembling insect eyes"
-
-/obj/item/organ/eyes/robotic/xray/moth
- name = "moth x-ray eyes"
- desc = "These cybernetic imitation moth eyes will give you X-ray vision. Blinking is futile. Much like actual moth eyes, very sensitive to bright lights."
- icon_state = "eyes_moth_cyber_xray"
- eye_icon_state = "motheyes_white"
- eye_color_left = "#3c4e52"
- eye_color_right = "#3c4e52"
- blink_animation = FALSE
- flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
- pupils_name = "aperture clusters"
-
-/obj/item/organ/eyes/robotic/shield/moth
- name = "shielded robotic moth eyes"
- icon_state = "eyes_moth_cyber_shield"
- eye_icon_state = "motheyes_white"
- eye_color_left = "#353845"
- eye_color_right = "#353845"
- blink_animation = FALSE
- pupils_name = "aperture clusters"
- penlight_message = "have shutters, protecting insectoid compound eyes."
-
-/obj/item/organ/eyes/robotic/glow/moth
- name = "high luminosity moth eyes"
- desc = "Special glowing eyes, to be one with the lamp. Much like actual moth eyes, very sensitive to bright lights."
- icon_state = "eyes_moth_cyber_glow"
- eye_icon_state = "motheyes_cyber"
- iris_overlay = "eyes_moth_cyber_glow_iris"
- blink_animation = FALSE
- base_eye_state = "eyes_mothglow"
- flash_protect = FLASH_PROTECTION_SENSITIVE
- penlight_message = "are bulbous clusters of LEDs and cameras"
- pupils_name = "aperture clusters"
-
-/obj/item/organ/eyes/robotic/thermals/moth
- name = "thermal moth eyes"
- icon_state = "eyes_moth_cyber_thermal"
- eye_icon_state = "motheyes_white"
- eye_color_left = "#901f38"
- eye_color_right = "#901f38"
- blink_animation = FALSE
- flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
- pupils_name = "sensor clusters"
- penlight_message = "are two clustered hemispheres of thermal sensors"
-
-/obj/item/organ/eyes/ghost
- name = "ghost eyes"
- desc = "Despite lacking pupils, these can see pretty well."
- icon_state = "eyes-ghost"
- blink_animation = FALSE
- movement_type = PHASING
- organ_flags = parent_type::organ_flags | ORGAN_GHOST
-
-/obj/item/organ/eyes/snail
- name = "snail eyes"
- desc = "These eyes seem to have a large range, but might be cumbersome with glasses."
- icon_state = "eyes_snail"
- eye_icon_state = "snail_eyes"
- blink_animation = FALSE
- pupils_name = "eyestalks" //many species of snails can retract their eyes into their face! (my lame science excuse for not having better writing here)
- penlight_message = "are sat upon retractable tentacles"
-
-/obj/item/organ/eyes/jelly
- name = "jelly eyes"
- desc = "These eyes are made of a soft jelly. Unlike all other eyes, though, there are three of them."
- icon_state = "eyes_jelly"
- eye_icon_state = "jelleyes"
- blink_animation = FALSE
- iris_overlay = null
- pupils_name = "lensing bubbles" //imagine a water lens physics demo but with goo. thats how these work.
- penlight_message = "are three bubbles of refractive jelly"
-
-/obj/item/organ/eyes/lizard
- name = "reptile eyes"
- desc = "A pair of reptile eyes with thin vertical slits for pupils."
- icon_state = "lizard_eyes"
- synchronized_blinking = FALSE
- pupils_name = "slit pupils"
- penlight_message = "have vertically slit pupils and tinted whites"
-
-/obj/item/organ/eyes/night_vision/maintenance_adapted
- name = "adapted eyes"
- desc = "These red eyes look like two foggy marbles. They give off a particularly worrying glow in the dark."
- icon_state = "eyes_adapted"
- eye_color_left = "#f74a4d"
- eye_color_right = "#f74a4d"
- eye_icon_state = "eyes_glow"
- iris_overlay = null
- organ_traits = list(TRAIT_UNNATURAL_RED_GLOWY_EYES, TRAIT_LUMINESCENT_EYES)
- flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
- low_light_cutoff = list(5, 12, 20)
- medium_light_cutoff = list(15, 20, 30)
- high_light_cutoff = list(30, 35, 50)
- penlight_message = "glow a foggy red, sizzling under the light!"
-
-/obj/item/organ/eyes/night_vision/maintenance_adapted/penlight_examine(mob/living/viewer, obj/item/examtool)
- if(!owner.is_blind())
- to_chat(owner, span_danger("Your eyes sizzle agonizingly as light is shone on them!"))
- apply_organ_damage(20 * examtool.light_power) //that's 0.5 lightpower for a penlight, so one penlight shining is equivalent to two seconds in a lit area
- return span_danger("[owner.p_Their()] eyes [penlight_message]")
-
-/obj/item/organ/eyes/night_vision/maintenance_adapted/on_life(seconds_per_tick)
- if(owner.get_eye_protection() <= FLASH_PROTECTION_SENSITIVE && !owner.is_blind() && isturf(owner.loc) && owner.has_light_nearby(light_amount=0.5)) //we allow a little more than usual so we can produce light from the adapted eyes
- to_chat(owner, span_danger("Your eyes! They burn in the light!"))
- apply_organ_damage(10) //blind quickly
- playsound(owner, 'sound/machines/grill/grillsizzle.ogg', 50)
- else
- apply_organ_damage(-10) //heal quickly
- . = ..()
-
-/obj/item/organ/eyes/pod
- name = "pod eyes"
- desc = "Strangest salad you've ever seen."
- icon_state = "eyes_pod"
- eye_color_left = "#375846"
- eye_color_right = "#375846"
- iris_overlay = null
- foodtype_flags = PODPERSON_ORGAN_FOODTYPES
- penlight_message = "are green and plant-like"
-
-/obj/item/organ/eyes/felinid
- name = "felinid eyes"
- desc = "A pair of highly reflective eyes with slit pupils, like those of a cat."
- pupils_name = "slit pupils"
- penlight_message = "shine under the pearly light"
diff --git a/code/modules/surgery/organs/internal/eyes/eyes_augments.dm b/code/modules/surgery/organs/internal/eyes/eyes_augments.dm
new file mode 100644
index 00000000000..276b8437b88
--- /dev/null
+++ b/code/modules/surgery/organs/internal/eyes/eyes_augments.dm
@@ -0,0 +1,965 @@
+/obj/item/organ/eyes/robotic
+ name = "robotic eyes"
+ desc = "Your vision is augmented."
+ icon_state = "eyes_cyber"
+ organ_flags = ORGAN_ROBOTIC
+ failing_desc = "seems to be broken."
+ pupils_name = "apertures"
+ penlight_message = "are cybernetic, click-whirring as they refocus"
+
+/obj/item/organ/eyes/robotic/emp_act(severity)
+ . = ..()
+ if((. & EMP_PROTECT_SELF) || !owner)
+ return
+ if(prob(10 * severity))
+ return
+ to_chat(owner, span_warning("Static obfuscates your vision!"))
+ owner.flash_act(visual = 1)
+
+/obj/item/organ/eyes/robotic/basic
+ name = "basic robotic eyes"
+ desc = "A pair of basic cybernetic eyes that restore vision, but at some vulnerability to light."
+ icon_state = "eyes_cyber_basic"
+ iris_overlay = null
+ eye_color_left = "#2f3032"
+ eye_color_right = "#2f3032"
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ penlight_message = "are low grade cybernetics, poorly compensating for the light"
+
+/obj/item/organ/eyes/robotic/basic/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ if(prob(10 * severity))
+ apply_organ_damage(20 * severity)
+ to_chat(owner, span_warning("Your eyes start to fizzle in their sockets!"))
+ do_sparks(2, TRUE, owner)
+ owner.emote("scream")
+
+/obj/item/organ/eyes/robotic/xray
+ name = "x-ray eyes"
+ desc = "These cybernetic eyes will give you X-ray vision. Blinking is futile."
+ icon_state = "eyes_cyber_xray"
+ iris_overlay = null
+ eye_color_left = "#3cb8a5"
+ eye_color_right = "#3cb8a5"
+ sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ organ_traits = list(TRAIT_XRAY_VISION)
+ penlight_message = "are replaced by small radiation emitters and detectors"
+
+/obj/item/organ/eyes/robotic/thermals
+ name = "thermal eyes"
+ desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included."
+ icon_state = "eyes_cyber_thermal"
+ iris_overlay = null
+ eye_color_left = "#ce2525"
+ eye_color_right = "#ce2525"
+ // We're gonna downshift green and blue a bit so darkness looks yellow
+ color_cutoffs = list(25, 8, 5)
+ sight_flags = SEE_MOBS
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "slit aperatures"
+ penlight_message = "are cybernetic, with vertically slit metalic lenses."
+
+/obj/item/organ/eyes/robotic/flashlight
+ name = "flashlight eyes"
+ desc = "It's two flashlights rigged together with some wire. Why would you put these in someone's head?"
+ icon_state = "flashlight_eyes"
+ eye_color_left = "#fee5a3"
+ eye_color_right = "#fee5a3"
+ iris_overlay = null
+ flash_protect = FLASH_PROTECTION_WELDER
+ tint = INFINITY
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.9)
+ var/obj/item/flashlight/eyelight/eye
+ light_reactive = FALSE
+ pupils_name = "flashlights"
+ penlight_message = "are actually two flashlights taped together. ...why"
+
+/obj/item/organ/eyes/robotic/flashlight/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
+
+/obj/item/organ/eyes/robotic/flashlight/on_mob_insert(mob/living/carbon/victim)
+ . = ..()
+ if(!eye)
+ eye = new /obj/item/flashlight/eyelight()
+ eye.set_light_on(TRUE)
+ eye.forceMove(victim)
+ eye.update_brightness(victim)
+ victim.become_blind(FLASHLIGHT_EYES)
+
+/obj/item/organ/eyes/robotic/flashlight/on_mob_remove(mob/living/carbon/victim)
+ . = ..()
+ eye.set_light_on(FALSE)
+ eye.update_brightness(victim)
+ eye.forceMove(src)
+ victim.cure_blind(FLASHLIGHT_EYES)
+
+// Welding shield implant
+/obj/item/organ/eyes/robotic/shield
+ name = "shielded robotic eyes"
+ desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision."
+ icon_state = "eyes_cyber_shield"
+ iris_overlay = null
+ eye_color_left = "#353845"
+ eye_color_right = "#353845"
+ flash_protect = FLASH_PROTECTION_WELDER
+ pupils_name = "flash shields"
+ penlight_message = "have polarized cybernetic lenses, blocking bright lights"
+
+/obj/item/organ/eyes/robotic/shield/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
+
+#define MATCH_LIGHT_COLOR 1
+#define USE_CUSTOM_COLOR 0
+#define UPDATE_LIGHT 0
+#define UPDATE_EYES_LEFT 1
+#define UPDATE_EYES_RIGHT 2
+
+/obj/item/organ/eyes/robotic/glow
+ name = "high luminosity eyes"
+ desc = "Special glowing eyes, used by snowflakes who want to be special."
+ icon_state = "eyes_cyber_glow"
+ iris_overlay = "eyes_cyber_glow_iris"
+ eye_color_left = "#19191a"
+ eye_color_right = "#19191a"
+ actions_types = list(/datum/action/item_action/organ_action/use, /datum/action/item_action/organ_action/toggle)
+ var/max_light_beam_distance = 5
+ var/obj/item/flashlight/eyelight/glow/eye
+ /// base icon state for eye overlays
+ var/base_eye_state = "eyes_glow_gs"
+ /// Whether or not to match the eye color to the light or use a custom selection
+ var/eye_color_mode = USE_CUSTOM_COLOR
+ /// The selected color for the light beam itself
+ var/light_color_string = "#ffffff"
+ /// The custom selected eye color for the left eye. Defaults to the mob's natural eye color
+ var/left_eye_color_string
+ /// The custom selected eye color for the right eye. Defaults to the mob's natural eye color
+ var/right_eye_color_string
+ penlight_message = "shine back with cybernetic LEDs"
+
+/obj/item/organ/eyes/robotic/glow/Initialize(mapload)
+ . = ..()
+ eye = new /obj/item/flashlight/eyelight/glow
+
+/obj/item/organ/eyes/robotic/glow/Destroy()
+ . = ..()
+ deactivate(close_ui = TRUE)
+ QDEL_NULL(eye)
+
+/obj/item/organ/eyes/robotic/glow/emp_act(severity)
+ . = ..()
+ if(!eye.light_on || . & EMP_PROTECT_SELF)
+ return
+ deactivate(close_ui = TRUE)
+
+/// Set the initial color of the eyes on insert to be the mob's previous eye color.
+/obj/item/organ/eyes/robotic/glow/on_mob_insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags)
+ . = ..()
+ left_eye_color_string = eye_color_left
+ right_eye_color_string = eye_color_right
+ update_mob_eye_color(eye_recipient)
+ deactivate(close_ui = TRUE)
+ eye.forceMove(eye_recipient)
+
+/obj/item/organ/eyes/robotic/glow/on_mob_remove(mob/living/carbon/eye_owner)
+ deactivate(eye_owner, close_ui = TRUE)
+ if(!QDELETED(eye))
+ eye.forceMove(src)
+ return ..()
+
+/obj/item/organ/eyes/robotic/glow/ui_state(mob/user)
+ return GLOB.default_state
+
+/obj/item/organ/eyes/robotic/glow/ui_status(mob/user, datum/ui_state/state)
+ if(!QDELETED(owner))
+ if(owner == user)
+ return min(
+ ui_status_user_is_abled(user, src),
+ ui_status_only_living(user),
+ )
+ else return UI_CLOSE
+ return ..()
+
+/obj/item/organ/eyes/robotic/glow/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "HighLuminosityEyesMenu")
+ ui.autoupdate = FALSE
+ ui.open()
+
+/obj/item/organ/eyes/robotic/glow/ui_data(mob/user)
+ var/list/data = list()
+
+ data["eyeColor"] = list(
+ mode = eye_color_mode,
+ hasOwner = owner ? TRUE : FALSE,
+ left = left_eye_color_string,
+ right = right_eye_color_string,
+ )
+ data["lightColor"] = light_color_string
+ data["range"] = eye.light_range
+
+ return data
+
+/obj/item/organ/eyes/robotic/glow/ui_act(action, list/params, datum/tgui/ui)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("set_range")
+ var/new_range = params["new_range"]
+ set_beam_range(new_range)
+ return TRUE
+ if("pick_color")
+ var/new_color = tgui_color_picker(
+ usr,
+ "Choose eye color color:",
+ "High Luminosity Eyes Menu",
+ light_color_string
+ )
+ if(new_color)
+ var/to_update = params["to_update"]
+ set_beam_color(new_color, to_update)
+ return TRUE
+ if("enter_color")
+ var/new_color = LOWER_TEXT(params["new_color"])
+ var/to_update = params["to_update"]
+ set_beam_color(new_color, to_update, sanitize = TRUE)
+ return TRUE
+ if("random_color")
+ var/to_update = params["to_update"]
+ randomize_color(to_update)
+ return TRUE
+ if("toggle_eye_color")
+ toggle_eye_color_mode()
+ return TRUE
+
+/obj/item/organ/eyes/robotic/glow/ui_action_click(mob/user, action)
+ if(istype(action, /datum/action/item_action/organ_action/toggle))
+ toggle_active()
+ else if(istype(action, /datum/action/item_action/organ_action/use))
+ ui_interact(user)
+
+/**
+ * Activates the light
+ *
+ * Turns on the attached flashlight object, updates the mob overlay to be added.
+ */
+/obj/item/organ/eyes/robotic/glow/proc/activate()
+ if(eye.light_range)
+ eye.set_light_on(TRUE)
+ else
+ eye.light_on = TRUE // at range 0 we are just going to make the eyes glow emissively, no light overlay
+ update_mob_eye_color()
+
+/**
+ * Deactivates the light
+ *
+ * Turns off the attached flashlight object, closes UIs, updates the mob overlay to be removed.
+ * Arguments:
+ * * mob/living/carbon/eye_owner - the mob who the eyes belong to
+ * * close_ui - whether or not to close the ui
+ */
+/obj/item/organ/eyes/robotic/glow/proc/deactivate(mob/living/carbon/eye_owner = owner, close_ui = FALSE)
+ if(close_ui)
+ SStgui.close_uis(src)
+ eye.set_light_on(FALSE)
+ update_mob_eye_color(eye_owner)
+
+/**
+ * Randomizes the light color
+ *
+ * Picks a random color and sets the beam color to that
+ * Arguments:
+ * * to_update - whether we are setting the color for the light beam itself, or the individual eyes
+ */
+/obj/item/organ/eyes/robotic/glow/proc/randomize_color(to_update = UPDATE_LIGHT)
+ var/new_color = "#"
+ for(var/i in 1 to 3)
+ new_color += num2hex(rand(0, 255), 2)
+ set_beam_color(new_color, to_update)
+
+/**
+ * Setter function for the light's range
+ *
+ * Sets the light range of the attached flashlight object
+ * Includes some 'unique' logic to accomodate for some quirks of the lighting system
+ * Arguments:
+ * * new_range - the new range to set
+ */
+/obj/item/organ/eyes/robotic/glow/proc/set_beam_range(new_range)
+ var/old_light_range = eye.light_range
+ if(old_light_range == 0 && new_range > 0 && eye.light_on) // turn bring back the light overlay if we were previously at 0 (aka emissive eyes only)
+ eye.light_on = FALSE // this is stupid, but this has to be FALSE for set_light_on() to work.
+ eye.set_light_on(TRUE)
+ eye.set_light_range(clamp(new_range, 0, max_light_beam_distance))
+
+/**
+ * Setter function for the light's color
+ *
+ * Sets the light color of the attached flashlight object. Sets the eye color vars of this eye organ as well and then updates the mob's eye color.
+ * Arguments:
+ * * newcolor - the new color hex string to set
+ * * to_update - whether we are setting the color for the light beam itself, or the individual eyes
+ * * sanitize - whether the hex string should be sanitized
+ */
+/obj/item/organ/eyes/robotic/glow/proc/set_beam_color(newcolor, to_update = UPDATE_LIGHT, sanitize = FALSE)
+ var/newcolor_string
+ if(sanitize)
+ newcolor_string = sanitize_hexcolor(newcolor)
+ else
+ newcolor_string = newcolor
+ switch(to_update)
+ if(UPDATE_LIGHT)
+ light_color_string = newcolor_string
+ eye.set_light_color(newcolor_string)
+ if(UPDATE_EYES_LEFT)
+ left_eye_color_string = newcolor_string
+ if(UPDATE_EYES_RIGHT)
+ right_eye_color_string = newcolor_string
+
+ update_mob_eye_color()
+
+/**
+ * Toggle the attached flashlight object on or off
+ */
+/obj/item/organ/eyes/robotic/glow/proc/toggle_active()
+ if(eye.light_on)
+ deactivate()
+ else
+ activate()
+
+/**
+ * Toggles for the eye color mode
+ *
+ * Toggles the eye color mode on or off and then calls an update on the mob's eye color
+ */
+/obj/item/organ/eyes/robotic/glow/proc/toggle_eye_color_mode()
+ eye_color_mode = !eye_color_mode
+ update_mob_eye_color()
+
+/**
+ * Updates the mob eye color
+ *
+ * Updates the eye color to reflect on the mob's body if it's possible to do so
+ * Arguments:
+ * * mob/living/carbon/eye_owner - the mob to update the eye color appearance of
+ */
+/obj/item/organ/eyes/robotic/glow/proc/update_mob_eye_color(mob/living/carbon/eye_owner = owner)
+ switch(eye_color_mode)
+ if(MATCH_LIGHT_COLOR)
+ eye_color_left = light_color_string
+ eye_color_right = light_color_string
+ if(USE_CUSTOM_COLOR)
+ eye_color_left = left_eye_color_string
+ eye_color_right = right_eye_color_string
+
+ if(QDELETED(eye_owner) || !ishuman(eye_owner)) //Other carbon mobs don't have eye color.
+ return
+
+ var/obj/item/bodypart/head/head = eye_owner.get_bodypart(BODY_ZONE_HEAD) //if we have eyes we definently have a head anyway
+ var/previous_flags = head.head_flags
+ head.head_flags |= HEAD_EYECOLOR
+
+ ///enabling and disabling the TRAIT_LUMINESCENT_EYES trait already calls handle_eyes(), in that case, let's skip that call
+ var/skip_call = FALSE
+ if(!eye.light_on)
+ eye_icon_state = initial(eye_icon_state)
+ skip_call = HAS_TRAIT_FROM_ONLY(eye_owner, TRAIT_LUMINESCENT_EYES, REF(src))
+ remove_organ_trait(TRAIT_LUMINESCENT_EYES)
+ else
+ skip_call = !HAS_TRAIT(eye_owner, TRAIT_LUMINESCENT_EYES)
+ add_organ_trait(TRAIT_LUMINESCENT_EYES)
+ eye_icon_state = base_eye_state
+
+ if(!skip_call && ishuman(eye_owner))
+ var/mob/living/carbon/human/humie = eye_owner
+ humie.update_eyes()
+
+ head.head_flags = previous_flags
+
+#undef MATCH_LIGHT_COLOR
+#undef USE_CUSTOM_COLOR
+#undef UPDATE_LIGHT
+#undef UPDATE_EYES_LEFT
+#undef UPDATE_EYES_RIGHT
+
+// Moth variations
+
+/obj/item/organ/eyes/robotic/moth
+ name = "robotic moth eyes"
+ desc = "Your vision is augmented. Much like actual moth eyes, very sensitive to bright lights."
+ icon_state = "eyes_moth_cyber"
+ eye_icon_state = "motheyes_cyber"
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "aperture clusters"
+ penlight_message = "are metal hemispheres, resembling insect eyes"
+
+/obj/item/organ/eyes/robotic/basic/moth
+ name = "basic robotic moth eyes"
+ icon_state = "eyes_moth_cyber_basic"
+ eye_icon_state = "motheyes_white"
+ eye_color_left = "#65686f"
+ eye_color_right = "#65686f"
+ blink_animation = FALSE
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "aperture clusters"
+ penlight_message = "are metal hemispheres, resembling insect eyes"
+
+/obj/item/organ/eyes/robotic/xray/moth
+ name = "moth x-ray eyes"
+ desc = "These cybernetic imitation moth eyes will give you X-ray vision. Blinking is futile. Much like actual moth eyes, very sensitive to bright lights."
+ icon_state = "eyes_moth_cyber_xray"
+ eye_icon_state = "motheyes_white"
+ eye_color_left = "#3c4e52"
+ eye_color_right = "#3c4e52"
+ blink_animation = FALSE
+ flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
+ pupils_name = "aperture clusters"
+
+/obj/item/organ/eyes/robotic/shield/moth
+ name = "shielded robotic moth eyes"
+ icon_state = "eyes_moth_cyber_shield"
+ eye_icon_state = "motheyes_white"
+ eye_color_left = "#353845"
+ eye_color_right = "#353845"
+ blink_animation = FALSE
+ pupils_name = "aperture clusters"
+ penlight_message = "have shutters, protecting insectoid compound eyes."
+
+/obj/item/organ/eyes/robotic/glow/moth
+ name = "high luminosity moth eyes"
+ desc = "Special glowing eyes, to be one with the lamp. Much like actual moth eyes, very sensitive to bright lights."
+ icon_state = "eyes_moth_cyber_glow"
+ eye_icon_state = "motheyes_cyber"
+ iris_overlay = "eyes_moth_cyber_glow_iris"
+ blink_animation = FALSE
+ base_eye_state = "eyes_mothglow"
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ penlight_message = "are bulbous clusters of LEDs and cameras"
+ pupils_name = "aperture clusters"
+
+/obj/item/organ/eyes/robotic/thermals/moth
+ name = "thermal moth eyes"
+ icon_state = "eyes_moth_cyber_thermal"
+ eye_icon_state = "motheyes_white"
+ eye_color_left = "#901f38"
+ eye_color_right = "#901f38"
+ blink_animation = FALSE
+ flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
+ pupils_name = "sensor clusters"
+ penlight_message = "are two clustered hemispheres of thermal sensors"
+
+// Chaplain's special boy
+/obj/item/organ/eyes/night_vision/maintenance_adapted
+ name = "adapted eyes"
+ desc = "These red eyes look like two foggy marbles. They give off a particularly worrying glow in the dark."
+ icon_state = "eyes_adapted"
+ eye_color_left = "#f74a4d"
+ eye_color_right = "#f74a4d"
+ eye_icon_state = "eyes_glow"
+ iris_overlay = null
+ organ_traits = list(TRAIT_UNNATURAL_RED_GLOWY_EYES, TRAIT_LUMINESCENT_EYES)
+ flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
+ low_light_cutoff = list(5, 12, 20)
+ medium_light_cutoff = list(15, 20, 30)
+ high_light_cutoff = list(30, 35, 50)
+ penlight_message = "glow a foggy red, sizzling under the light!"
+
+/obj/item/organ/eyes/night_vision/maintenance_adapted/penlight_examine(mob/living/viewer, obj/item/examtool)
+ if(!owner.is_blind())
+ to_chat(owner, span_danger("Your eyes sizzle agonizingly as light is shone on them!"))
+ apply_organ_damage(20 * examtool.light_power) //that's 0.5 lightpower for a penlight, so one penlight shining is equivalent to two seconds in a lit area
+ return span_danger("[owner.p_Their()] eyes [penlight_message]")
+
+/obj/item/organ/eyes/night_vision/maintenance_adapted/on_life(seconds_per_tick)
+ if(owner.get_eye_protection() <= FLASH_PROTECTION_SENSITIVE && !owner.is_blind() && isturf(owner.loc) && owner.has_light_nearby(light_amount=0.5)) //we allow a little more than usual so we can produce light from the adapted eyes
+ to_chat(owner, span_danger("Your eyes! They burn in the light!"))
+ apply_organ_damage(10) //blind quickly
+ playsound(owner, 'sound/machines/grill/grillsizzle.ogg', 50)
+ else
+ apply_organ_damage(-10) //heal quickly
+ return ..()
+
+#define VISOR_DISPLAY_CROSS "Cross"
+#define VISOR_DISPLAY_EYES "Eyes"
+#define VISOR_DISPLAY_LINE "Line"
+
+#define IFF_HOSTILE 1
+#define IFF_NEUTRAL 2
+#define IFF_FRIENDLY 3
+
+#define IFF_FACTION_NONE "None"
+#define IFF_FACTION_SYNDICATE "Syndicate"
+#define IFF_FACTION_CREW "Crew"
+#define IFF_FACTION_SEC_COMMAND "Security & Command"
+#define IFF_FACTION_CENTCOM "CentCom"
+#define IFF_FACTION_EVERYONE "Non-Allies"
+
+/obj/item/organ/eyes/robotic/tacvisor
+ name = "tactical EFF visor"
+ desc = "A failed attempt at integrating IFF systems directly into soldiers' prefrontal cortex, this complex sensor array has proved to be impractical as the additional load impared the user's ability to recognize people's appearances or voices. The screen is there just for intimidation."
+ icon_state = "eyes_tacvisor"
+ eye_icon_state = "eyes_tacvisor"
+ blink_animation = FALSE
+ no_glasses = TRUE
+ iris_overlay = null
+ eye_color_left = COLOR_WHITE
+ eye_color_right = COLOR_WHITE
+ flash_protect = FLASH_PROTECTION_WELDER
+ lighting_cutoff = LIGHTING_CUTOFF_MEDIUM
+ pupils_name = "faceplate"
+ penlight_message = "are a wide reinforced faceplate with an inbuilt screen and a multitude of combat sensors"
+ light_reactive = FALSE
+ actions_types = list(/datum/action/item_action/organ_action/use)
+ /// Used to detect when unmasked mobs enter range
+ var/datum/proximity_monitor/tacvisor/proximity_monitor
+ /// List of mob refs -> their overlays
+ var/list/mob_overlays = list()
+ /// List of mobs in our direct view range who we need to update dynamically
+ var/list/direct_view_tracking = list()
+ /// Current visual displayed on the screen
+ var/visor_display = VISOR_DISPLAY_CROSS
+ /// Allied faction highlight
+ var/friendly_faction = IFF_FACTION_SEC_COMMAND
+ /// Hostile faction highlight
+ var/hostile_faction = IFF_FACTION_NONE
+ /// Threat flags for hostile detection, if any are chosen
+ var/threat_flags = JUDGE_IDCHECK | JUDGE_WEAPONCHECK | JUDGE_RECORDCHECK
+ /// Can the user change the parameters?
+ var/user_controls = TRUE
+
+ /// Valid options for friendly factions
+ var/static/list/valid_friendly_factions = list(
+ IFF_FACTION_NONE,
+ IFF_FACTION_CREW,
+ IFF_FACTION_SEC_COMMAND,
+ IFF_FACTION_CENTCOM,
+ IFF_FACTION_SYNDICATE,
+ )
+ /// Valid options for hostile factions
+ var/static/list/valid_hostile_factions = list(
+ IFF_FACTION_NONE,
+ IFF_FACTION_CREW,
+ IFF_FACTION_SEC_COMMAND,
+ IFF_FACTION_CENTCOM,
+ IFF_FACTION_SYNDICATE,
+ IFF_FACTION_EVERYONE,
+ )
+ /// Threat flags and their tooltips
+ var/static/list/threat_flag_options = list(
+ "Valid ID" = JUDGE_IDCHECK,
+ "Weapon Permit" = JUDGE_WEAPONCHECK,
+ "Security Status" = JUDGE_RECORDCHECK,
+ )
+
+/obj/item/organ/eyes/robotic/tacvisor/generate_body_overlay(mob/living/carbon/human/parent, obj/item/bodypart/limb)
+ var/mutable_appearance/visor_overlay = mutable_appearance(eye_icon, eye_icon_state, -EYES_LAYER, parent || limb)
+ var/list/eye_overlays = list(visor_overlay)
+
+ if (parent && parent.appears_alive() && !HAS_TRAIT(parent, TRAIT_KNOCKEDOUT))
+ var/mutable_appearance/display_overlay = mutable_appearance(eye_icon, "[eye_icon_state]_[LOWER_TEXT(visor_display)]", -EYES_LAYER, parent)
+ eye_overlays += display_overlay
+ if(!(parent.obscured_slots & HIDEEYES))
+ eye_overlays += emissive_appearance(eye_icon, "[eye_icon_state]_[LOWER_TEXT(visor_display)]", parent, -EYES_LAYER)
+
+ if(!limb)
+ return eye_overlays
+
+ var/obj/item/bodypart/head/head = astype(limb, /obj/item/bodypart/head)
+ if(head?.worn_face_offset)
+ for (var/mutable_appearance/overlay as anything in eye_overlays)
+ head?.worn_face_offset.apply_offset(overlay)
+
+ return eye_overlays
+
+// Hides and mutes all people on the screen
+/obj/item/organ/eyes/robotic/tacvisor/emag_act(mob/user, obj/item/card/emag/emag_card)
+ if(obj_flags & EMAGGED)
+ return FALSE
+ obj_flags |= EMAGGED
+ balloon_alert(user, "perceptual scanners overriden")
+ return TRUE
+
+/obj/item/organ/eyes/robotic/tacvisor/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
+ . = ..()
+ receiver.add_client_colour(/datum/client_colour/tacvisor, REF(src))
+ receiver.apply_status_effect(/datum/status_effect/grouped/see_no_names, REF(src))
+ proximity_monitor = new(receiver, 9)
+ proximity_monitor.owner = src
+
+ RegisterSignal(receiver, COMSIG_MOB_CLIENT_LOGIN, PROC_REF(on_login))
+ RegisterSignal(receiver, COMSIG_MOVABLE_PRE_HEAR, PROC_REF(on_hear))
+ RegisterSignal(receiver, COMSIG_MOB_EXAMINING, PROC_REF(on_examine))
+ if (receiver.client)
+ create_illusions(receiver)
+
+/obj/item/organ/eyes/robotic/tacvisor/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
+ . = ..()
+ UnregisterSignal(organ_owner, list(COMSIG_MOB_CLIENT_LOGIN, COMSIG_MOVABLE_PRE_HEAR, COMSIG_MOB_EXAMINING))
+ organ_owner.remove_client_colour(REF(src))
+ organ_owner.remove_status_effect(/datum/status_effect/grouped/see_no_names, REF(src))
+ QDEL_NULL(proximity_monitor)
+ organ_owner.client?.images -= assoc_to_values(mob_overlays)
+ for (var/mob/living/thing as anything in mob_overlays)
+ UnregisterSignal(thing, list(
+ COMSIG_MOVABLE_Z_CHANGED,
+ COMSIG_QDELETING,
+ COMSIG_ATOM_UPDATE_APPEARANCE,
+ COMSIG_LIVING_POST_UPDATE_TRANSFORM,
+ COMSIG_MOB_EQUIPPED_ITEM,
+ COMSIG_MOB_UNEQUIPPED_ITEM,
+ COMSIG_LIVING_UPDATE_OFFSETS,
+ ))
+ mob_overlays.Cut()
+ direct_view_tracking.Cut()
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/on_hear(datum/source, list/hearing_args)
+ SIGNAL_HANDLER
+
+ if (hearing_args[HEARING_SPEAKER] == owner || !isliving(hearing_args[HEARING_SPEAKER]))
+ return
+
+ if (obj_flags & EMAGGED)
+ return COMSIG_MOVABLE_CANCEL_HEARING
+
+ var/list/message_mods = hearing_args[HEARING_MESSAGE_MODE]
+ message_mods[MODE_SPEAKER_NAME_OVERRIDE] = "Unknown"
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/on_login(mob/living/source, client/user_client)
+ SIGNAL_HANDLER
+
+ if (!length(mob_overlays))
+ create_illusions(source)
+ else
+ user_client.images |= assoc_to_values(mob_overlays)
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/create_illusions(mob/living/user)
+ for(var/mob/living/target as anything in ((obj_flags & EMAGGED) ? GLOB.mob_living_list : GLOB.carbon_list))
+ if (target == user)
+ continue
+
+ if (get_dist(user, target) <= proximity_monitor.current_range)
+ on_entered(target)
+ else
+ refresh_overlay(target)
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/on_mob_delete(mob/living/source)
+ SIGNAL_HANDLER
+ owner.client?.images -= mob_overlays[source]
+ mob_overlays -= source
+ direct_view_tracking -= source
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/refresh_overlay(mob/living/source)
+ SIGNAL_HANDLER
+
+ if (mob_overlays[source])
+ owner.client?.images -= mob_overlays[source]
+ else
+ RegisterSignal(source, COMSIG_QDELETING, PROC_REF(on_mob_delete))
+ RegisterSignal(source, COMSIG_ATOM_UPDATE_APPEARANCE, PROC_REF(refresh_overlay))
+ RegisterSignal(source, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_z_change))
+ // Lying down/being pushed
+ RegisterSignal(source, COMSIG_LIVING_POST_UPDATE_TRANSFORM, PROC_REF(refresh_overlay))
+ RegisterSignal(source, COMSIG_LIVING_UPDATE_OFFSETS, PROC_REF(refresh_overlay))
+
+ mob_overlays[source] = make_overlay(source)
+ owner.client?.images |= mob_overlays[source]
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/on_z_change(mob/living/source)
+ SIGNAL_HANDLER
+ var/image/overlay = mob_overlays[source]
+ SET_PLANE_EXPLICIT(overlay, ABOVE_GAME_PLANE, source)
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/make_overlay(mob/living/target)
+ if (obj_flags & EMAGGED)
+ var/image/overlay_image = image(mutable_appearance('icons/effects/effects.dmi', "nothing"), target)
+ overlay_image.name = "Unknown"
+ overlay_image.override = TRUE
+ overlay_image.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ SET_PLANE_EXPLICIT(overlay_image, ABOVE_GAME_PLANE, target)
+ return overlay_image
+
+ var/mutable_appearance/appearance_copy = new(target.appearance)
+ appearance_copy.appearance_flags |= KEEP_APART|KEEP_TOGETHER
+ var/outline_color = "#FFD500CC"
+ switch (get_iff_signature(target))
+ if (IFF_FRIENDLY)
+ outline_color = "#0099FFCC"
+ if (IFF_HOSTILE)
+ outline_color = "#FF0022CC"
+ appearance_copy.add_filter("target_lock_outline", 2, outline_filter(1, outline_color))
+ var/mutable_appearance/static_effect = mutable_appearance('icons/effects/effects.dmi', "static_base")
+ static_effect.color = "#373642"
+ static_effect.blend_mode = BLEND_INSET_OVERLAY
+ appearance_copy.overlays += static_effect
+ appearance_copy.override = TRUE
+ var/image/overlay_image = image(appearance_copy, target)
+ overlay_image.name = "Unknown"
+ overlay_image.override = TRUE
+ SET_PLANE_EXPLICIT(overlay_image, ABOVE_GAME_PLANE, target)
+ return overlay_image
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/get_iff_signature(mob/living/target)
+ . = IFF_NEUTRAL
+ if (hostile_faction == IFF_FACTION_EVERYONE)
+ . = IFF_HOSTILE
+
+ if (!istype(target))
+ return .
+
+ // This hasn't been used for like, over a decade, but by god will I make it great again
+ var/lasercolor = null
+ if (istype(owner.get_item_by_slot(ITEM_SLOT_OCLOTHING), /obj/item/clothing/suit/redtag) || istype(owner.get_item_by_slot(ITEM_SLOT_BELT), /obj/item/gun/energy/laser/redtag) || owner.is_holding_item_of_type(/obj/item/gun/energy/laser/redtag))
+ lasercolor = "r"
+ else if (istype(owner.get_item_by_slot(ITEM_SLOT_OCLOTHING), /obj/item/clothing/suit/bluetag) || istype(owner.get_item_by_slot(ITEM_SLOT_BELT), /obj/item/gun/energy/laser/bluetag) || owner.is_holding_item_of_type(/obj/item/gun/energy/laser/bluetag))
+ lasercolor = "b"
+
+ if (threat_flags && target.assess_threat(threat_flags, lasercolor) >= THREAT_ASSESS_DANGEROUS)
+ return IFF_HOSTILE
+
+ var/obj/item/card/id/idcard = target.get_idcard()
+ if (!istype(idcard))
+ return .
+
+ // Ignores RETA access so we directly access access instead of using the wrapper
+ if (ACCESS_SYNDICATE in idcard.access)
+ if (hostile_faction == IFF_FACTION_SYNDICATE)
+ return IFF_HOSTILE
+ if (friendly_faction == IFF_FACTION_SYNDICATE)
+ return IFF_FRIENDLY
+
+ if (istype(idcard.trim, /datum/id_trim/job))
+ // Cham cards get a pass
+ if (hostile_faction == IFF_FACTION_CREW && idcard.trim?.threat_modifier >= 0)
+ return IFF_HOSTILE
+ if (friendly_faction == IFF_FACTION_CREW)
+ return IFF_FRIENDLY
+
+ if ((ACCESS_COMMAND in idcard.access) || (ACCESS_SECURITY in idcard.access))
+ if (hostile_faction == IFF_FACTION_SEC_COMMAND)
+ return IFF_HOSTILE
+ if (friendly_faction == IFF_FACTION_SEC_COMMAND)
+ return IFF_FRIENDLY
+
+ if (ACCESS_CENT_GENERAL in idcard.access)
+ if (hostile_faction == IFF_FACTION_CENTCOM)
+ return IFF_HOSTILE
+ if (friendly_faction == IFF_FACTION_CENTCOM)
+ return IFF_FRIENDLY
+
+ return .
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/on_examine(mob/source, atom/target, list/examine_strings, list/examine_overrides)
+ SIGNAL_HANDLER
+
+ if (target == owner || !iscarbon(target) && !(isliving(target) && (obj_flags & EMAGGED)))
+ return
+
+ var/list/override_strings = list(span_warning("You're struggling to make out any details..."))
+
+ if (!threat_flags && !(obj_flags & EMAGGED))
+ examine_overrides[EXAMINE_OVERRIDE_PRIORITY_IFF] = override_strings
+ return
+
+ var/lasercolor = null
+ var/mob/living/victim = target
+ if (istype(owner.get_item_by_slot(ITEM_SLOT_OCLOTHING), /obj/item/clothing/suit/redtag) || istype(owner.get_item_by_slot(ITEM_SLOT_BELT), /obj/item/gun/energy/laser/redtag) || owner.is_holding_item_of_type(/obj/item/gun/energy/laser/redtag))
+ lasercolor = "r"
+ else if (istype(owner.get_item_by_slot(ITEM_SLOT_OCLOTHING), /obj/item/clothing/suit/bluetag) || istype(owner.get_item_by_slot(ITEM_SLOT_BELT), /obj/item/gun/energy/laser/bluetag) || owner.is_holding_item_of_type(/obj/item/gun/energy/laser/bluetag))
+ lasercolor = "b"
+
+ var/threat_level = victim.assess_threat(threat_flags, lasercolor)
+ switch (threat_level)
+ if (THREAT_ASSESS_MAXIMUM to INFINITY)
+ override_strings += span_boldwarning("Assessed threat level of [threat_level]! Extreme danger of criminal activity!")
+ if (THREAT_ASSESS_DANGEROUS to THREAT_ASSESS_MAXIMUM)
+ override_strings += span_warning("Assessed threat level of [threat_level]. Criminal scum detected!")
+ if (1 to THREAT_ASSESS_DANGEROUS)
+ override_strings += span_notice("Assessed threat level of [threat_level]. Probably not dangerous... yet.")
+ else
+ override_strings += span_notice("Seems to be a trustworthy individual.")
+
+ examine_overrides[EXAMINE_OVERRIDE_PRIORITY_IFF] = override_strings
+
+/obj/item/organ/eyes/robotic/tacvisor/ui_state(mob/user)
+ return GLOB.default_state
+
+/obj/item/organ/eyes/robotic/tacvisor/ui_status(mob/user, datum/ui_state/state)
+ if(!QDELETED(owner))
+ if(owner == user && user_controls)
+ return min(
+ ui_status_user_is_abled(user, src),
+ ui_status_only_living(user),
+ )
+ return UI_CLOSE
+ return ..()
+
+/obj/item/organ/eyes/robotic/tacvisor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "TacVisorEyesMenu")
+ ui.open()
+
+/obj/item/organ/eyes/robotic/tacvisor/ui_data(mob/user)
+ var/list/data = list()
+ data["friendlyFaction"] = friendly_faction
+ data["hostileFaction"] = hostile_faction
+ data["visorDisplay"] = visor_display
+ data["threatFlags"] = threat_flags
+ return data
+
+/obj/item/organ/eyes/robotic/tacvisor/ui_static_data(mob/user)
+ var/list/data = list()
+ data["validFriendlyFactions"] = valid_friendly_factions
+ data["validHostileFactions"] = valid_hostile_factions
+ data["visorOptions"] = list(VISOR_DISPLAY_CROSS, VISOR_DISPLAY_EYES, VISOR_DISPLAY_LINE)
+ data["threatOptions"] = threat_flag_options
+ return data
+
+/obj/item/organ/eyes/robotic/tacvisor/ui_act(action, list/params, datum/tgui/ui)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if ("set_friendly")
+ var/chosen_faction = params["faction"]
+ if (!(chosen_faction in valid_friendly_factions))
+ chosen_faction = IFF_FACTION_NONE
+ friendly_faction = chosen_faction
+ update_mob_overlays()
+
+ if ("set_hostile")
+ var/chosen_faction = params["faction"]
+ if (!(chosen_faction in valid_hostile_factions))
+ chosen_faction = IFF_FACTION_NONE
+ hostile_faction = chosen_faction
+ update_mob_overlays()
+
+ if ("set_threat_flags")
+ threat_flags = text2num(params["threat_flags"])
+ update_mob_overlays()
+
+ if ("set_display")
+ visor_display = params["display"]
+ if (visor_display != VISOR_DISPLAY_EYES && visor_display != VISOR_DISPLAY_LINE && visor_display != VISOR_DISPLAY_CROSS)
+ visor_display = VISOR_DISPLAY_CROSS
+ owner?.update_body()
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/update_mob_overlays()
+ for (var/mob/living/target as anything in direct_view_tracking)
+ refresh_overlay(target)
+
+/obj/item/organ/eyes/robotic/tacvisor/ui_action_click(mob/user, action)
+ if(istype(action, /datum/action/item_action/organ_action/use) && user_controls)
+ ui_interact(user)
+
+/obj/item/organ/eyes/robotic/tacvisor/multitool_act(mob/living/user, obj/item/tool)
+ ui_interact(user)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/organ/eyes/robotic/tacvisor/screwdriver_act(mob/living/user, obj/item/tool)
+ user_controls = !user_controls
+ balloon_alert(user, "user controls [user_controls ? "enabled" : "disabled"]")
+ if (user_controls)
+ add_item_action(/datum/action/item_action/organ_action/use)
+ else
+ for(var/datum/action/action as anything in actions)
+ remove_item_action(action)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/organ/eyes/robotic/tacvisor/examine(mob/user)
+ . = ..()
+ . += span_notice("Its settings can be changed with a [EXAMINE_HINT("multitool")].")
+ . += span_notice("User configuration switch is currently in the [user_controls ? "on" : "off"] position, and could be flipped wtih a [EXAMINE_HINT("screwdriver")].")
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/on_entered(mob/living/source)
+ if (source in direct_view_tracking)
+ return
+
+ refresh_overlay(source)
+ direct_view_tracking += source
+ // Track equipping/unequipping items for threat levels/ID identity
+ RegisterSignal(source, COMSIG_MOB_EQUIPPED_ITEM, PROC_REF(check_equippped_item))
+ RegisterSignal(source, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(refresh_overlay)) // Can't see the slot of the dropped item so we need to blanket update
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/check_equippped_item(mob/living/source, obj/item/equipped_item, slot)
+ SIGNAL_HANDLER
+
+ // Anywhere where an ID or a gun would count
+ if (slot & (ITEM_SLOT_BELT | ITEM_SLOT_ID | ITEM_SLOT_HANDS | ITEM_SLOT_BACK))
+ refresh_overlay(source)
+
+/obj/item/organ/eyes/robotic/tacvisor/proc/on_exited(mob/living/source)
+ if (!(source in direct_view_tracking))
+ return
+
+ direct_view_tracking -= source
+ UnregisterSignal(source, list(
+ COMSIG_MOB_EQUIPPED_ITEM,
+ COMSIG_MOB_UNEQUIPPED_ITEM,
+ ))
+
+/datum/proximity_monitor/tacvisor
+ var/obj/item/organ/eyes/robotic/tacvisor/owner
+
+/datum/proximity_monitor/tacvisor/Destroy()
+ owner = null
+ return ..()
+
+/datum/proximity_monitor/tacvisor/on_moved(atom/movable/source, atom/old_loc)
+ return
+
+/datum/proximity_monitor/tacvisor/on_entered(atom/source, atom/movable/arrived, turf/old_loc)
+ if (arrived != host && (iscarbon(arrived) || isliving(arrived) && (owner.obj_flags & EMAGGED)))
+ owner.on_entered(arrived)
+
+/datum/proximity_monitor/tacvisor/on_uncrossed/on_uncrossed(turf/old_location, mob/exited, direction)
+ if (exited != host && (iscarbon(exited) || isliving(exited) && (owner.obj_flags & EMAGGED)) && get_dist(exited, host) > current_range)
+ owner.on_exited(exited)
+
+/datum/proximity_monitor/tacvisor/on_initialized(turf/location, atom/created, init_flags)
+ if (created != host && (iscarbon(created) || isliving(created) && (owner.obj_flags & EMAGGED)))
+ owner.on_entered(created)
+
+/datum/client_colour/tacvisor
+ priority = CLIENT_COLOR_ORGAN_PRIORITY
+
+/datum/client_colour/tacvisor/New(mob/owner)
+ . = ..()
+ color = color_matrix_filter(list(
+ 1, 0, 0,
+ 0, 1.75, 0,
+ 0, 0, 0.75,
+ 0, -0.75, 0,
+ ), COLORSPACE_HSL)
+
+/obj/item/organ/eyes/robotic/tacvisor/deathsquad
+ friendly_faction = IFF_FACTION_CENTCOM
+ hostile_faction = IFF_FACTION_EVERYONE
+ actions_types = null
+ user_controls = FALSE
+
+/obj/item/organ/eyes/robotic/tacvisor/deathsquad/ui_status(mob/user, datum/ui_state/state)
+ return UI_CLOSE
+
+#undef VISOR_DISPLAY_CROSS
+#undef VISOR_DISPLAY_EYES
+#undef VISOR_DISPLAY_LINE
+
+#undef IFF_HOSTILE
+#undef IFF_NEUTRAL
+#undef IFF_FRIENDLY
+
+#undef IFF_FACTION_NONE
+#undef IFF_FACTION_SYNDICATE
+#undef IFF_FACTION_CREW
+#undef IFF_FACTION_SEC_COMMAND
+#undef IFF_FACTION_CENTCOM
+#undef IFF_FACTION_EVERYONE
diff --git a/code/modules/surgery/organs/internal/eyes/eyes_species.dm b/code/modules/surgery/organs/internal/eyes/eyes_species.dm
new file mode 100644
index 00000000000..7c3fc298330
--- /dev/null
+++ b/code/modules/surgery/organs/internal/eyes/eyes_species.dm
@@ -0,0 +1,113 @@
+/obj/item/organ/eyes/night_vision/mushroom
+ name = "fung-eye"
+ desc = "While on the outside they look inert and dead, the eyes of mushroom people are actually very advanced."
+ low_light_cutoff = list(0, 15, 20)
+ medium_light_cutoff = list(0, 20, 35)
+ high_light_cutoff = list(0, 40, 50)
+ pupils_name = "photosensory openings"
+ penlight_message = "are attached to fungal stalks"
+
+/obj/item/organ/eyes/zombie
+ name = "undead eyes"
+ desc = "Somewhat counterintuitively, these half-rotten eyes actually have superior vision to those of a living human."
+ color_cutoffs = list(25, 35, 5)
+ penlight_message = "are rotten and decayed!"
+
+/obj/item/organ/eyes/zombie/penlight_examine(mob/living/viewer, obj/item/examtool)
+ return span_danger("[owner.p_Their()] eyes [penlight_message]")
+
+/obj/item/organ/eyes/alien
+ name = "alien eyes"
+ desc = "It turned out they had them after all!"
+ sight_flags = SEE_MOBS
+ color_cutoffs = list(25, 5, 42)
+
+/obj/item/organ/eyes/golem
+ name = "resonating crystal"
+ desc = "Golems somehow measure external light levels and detect nearby ore using this sensitive mineral lattice."
+ icon_state = "adamantine_cords"
+ eye_icon_state = null
+ blink_animation = FALSE
+ iris_overlay = null
+ color = COLOR_GOLEM_GRAY
+ visual = FALSE
+ organ_flags = ORGAN_MINERAL
+ color_cutoffs = list(10, 15, 5)
+ actions_types = list(/datum/action/cooldown/golem_ore_sight)
+ penlight_message = "glimmer, their crystaline structure refracting light inwards"
+ pupils_name = "lensing gems" // Given it says these are a "mineral lattice" that collects light i assume they work like artifical ruby laser foci
+
+/// Send an ore detection pulse on a cooldown
+/datum/action/cooldown/golem_ore_sight
+ name = "Ore Resonance"
+ desc = "Causes nearby ores to vibrate, revealing their location."
+ button_icon = 'icons/obj/devices/scanner.dmi'
+ button_icon_state = "manual_mining"
+ check_flags = AB_CHECK_CONSCIOUS
+ cooldown_time = 10 SECONDS
+
+/datum/action/cooldown/golem_ore_sight/Activate(atom/target)
+ . = ..()
+ mineral_scan_pulse(get_turf(target), scanner = target)
+
+/obj/item/organ/eyes/moth
+ name = "moth eyes"
+ desc = "These eyes seem to have increased sensitivity to bright light, with no improvement to low light vision."
+ icon_state = "eyes_moth"
+ eye_icon_state = "motheyes"
+ blink_animation = FALSE
+ iris_overlay = null
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ pupils_name = "ommatidia" //yes i know compound eyes have no pupils shut up
+ penlight_message = "are bulbous and insectoid"
+
+/obj/item/organ/eyes/ghost
+ name = "ghost eyes"
+ desc = "Despite lacking pupils, these can see pretty well."
+ icon_state = "eyes-ghost"
+ blink_animation = FALSE
+ movement_type = PHASING
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
+/obj/item/organ/eyes/snail
+ name = "snail eyes"
+ desc = "These eyes seem to have a large range, but might be cumbersome with glasses."
+ icon_state = "eyes_snail"
+ eye_icon_state = "snail_eyes"
+ blink_animation = FALSE
+ pupils_name = "eyestalks" //many species of snails can retract their eyes into their face! (my lame science excuse for not having better writing here)
+ penlight_message = "are sat upon retractable tentacles"
+
+/obj/item/organ/eyes/jelly
+ name = "jelly eyes"
+ desc = "These eyes are made of a soft jelly. Unlike all other eyes, though, there are three of them."
+ icon_state = "eyes_jelly"
+ eye_icon_state = "jelleyes"
+ blink_animation = FALSE
+ iris_overlay = null
+ pupils_name = "lensing bubbles" //imagine a water lens physics demo but with goo. thats how these work.
+ penlight_message = "are three bubbles of refractive jelly"
+
+/obj/item/organ/eyes/lizard
+ name = "reptile eyes"
+ desc = "A pair of reptile eyes with thin vertical slits for pupils."
+ icon_state = "lizard_eyes"
+ synchronized_blinking = FALSE
+ pupils_name = "slit pupils"
+ penlight_message = "have vertically slit pupils and tinted whites"
+
+/obj/item/organ/eyes/pod
+ name = "pod eyes"
+ desc = "Strangest salad you've ever seen."
+ icon_state = "eyes_pod"
+ eye_color_left = "#375846"
+ eye_color_right = "#375846"
+ iris_overlay = null
+ foodtype_flags = PODPERSON_ORGAN_FOODTYPES
+ penlight_message = "are green and plant-like"
+
+/obj/item/organ/eyes/felinid
+ name = "felinid eyes"
+ desc = "A pair of highly reflective eyes with slit pupils, like those of a cat."
+ pupils_name = "slit pupils"
+ penlight_message = "shine under the pearly light"
diff --git a/icons/mob/human/human_eyes.dmi b/icons/mob/human/human_eyes.dmi
index 1ed4e37917aff56ecba4115a7d2e361a7bffbee3..9f8b07cdc97e03659fa6602c7b4126fdc21553b4 100644
GIT binary patch
literal 3491
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
VyOFv>E1xgm;8(sL)$S+1`x$&cin0Iz
literal 3149
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