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 1ed4e37917a..9f8b07cdc97 100644
Binary files a/icons/mob/human/human_eyes.dmi and b/icons/mob/human/human_eyes.dmi differ
diff --git a/icons/obj/medical/organs/organs.dmi b/icons/obj/medical/organs/organs.dmi
index 6197442f622..24d9ef9cbe2 100644
Binary files a/icons/obj/medical/organs/organs.dmi and b/icons/obj/medical/organs/organs.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 7bd506ce5c8..76e3fafc18c 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -6519,6 +6519,8 @@
#include "code\modules\surgery\organs\internal\cyberimp\augments_internal.dm"
#include "code\modules\surgery\organs\internal\ears\_ears.dm"
#include "code\modules\surgery\organs\internal\eyes\_eyes.dm"
+#include "code\modules\surgery\organs\internal\eyes\eyes_augments.dm"
+#include "code\modules\surgery\organs\internal\eyes\eyes_species.dm"
#include "code\modules\surgery\organs\internal\heart\_heart.dm"
#include "code\modules\surgery\organs\internal\heart\heart_anomalock.dm"
#include "code\modules\surgery\organs\internal\heart\heart_ethereal.dm"
diff --git a/tgui/packages/tgui/interfaces/TacVisorEyesMenu.tsx b/tgui/packages/tgui/interfaces/TacVisorEyesMenu.tsx
new file mode 100644
index 00000000000..4ba29091667
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/TacVisorEyesMenu.tsx
@@ -0,0 +1,75 @@
+import { Button, Dropdown, LabeledList, Section } from 'tgui-core/components';
+import { useBackend } from '../backend';
+import { Window } from '../layouts';
+
+type Data = {
+ friendlyFaction: string;
+ hostileFaction: string;
+ visorDisplay: string;
+ threatFlags: number;
+
+ validFriendlyFactions: string[];
+ validHostileFactions: string[];
+ visorOptions: string[];
+ threatOptions: Record;
+};
+
+export const TacVisorEyesMenu = (props) => {
+ const { act, data } = useBackend();
+ const {
+ friendlyFaction,
+ hostileFaction,
+ visorDisplay,
+ threatFlags,
+ validFriendlyFactions,
+ validHostileFactions,
+ visorOptions,
+ threatOptions,
+ } = data;
+ return (
+
+
+
+
+
+ act('set_friendly', { faction: value })}
+ />
+
+
+ act('set_hostile', { faction: value })}
+ />
+
+
+ act('set_display', { display: value })}
+ />
+
+
+
+
+ {Object.keys(threatOptions).map((option) => (
+
+ act('set_threat_flags', {
+ threat_flags: threatFlags ^ threatOptions[option],
+ })
+ }
+ >
+ {option}
+
+ ))}
+
+
+
+ );
+};