Adds blinking and a new Fluoride Stare quirk (#88927)

## About The Pull Request

Spessmen now need to blink! If you have non-robotic eyes, you'll
automatically blink every once in a while. Lizards have asynchronous
blinking, and whenever they blink one of their eyes (chosen at random)
will blink slightly sooner.


https://github.com/user-attachments/assets/e62020ef-d2f8-4634-9399-a27244326cfe

You can also blink manually, as emotes now fire the animations.


https://github.com/user-attachments/assets/80d6304f-f3c2-424a-a5aa-96a4aee7acdc

Adds a new eye-related quirk, Fluoride Stare! It will spawn you without
eyelids, preventing random or manual blinking and forcing you to wet
your eyes with some saline solution (of which you get a bottle, and a
dropper to apply it) every minute or so.
Additionally, eyes now display their color on their organ sprite,
instead of always showing up as blue.

(Don't tell roleplayers, but Fully Immerse smite now blinds you when you
blink, for true full immersion)

## Why It's Good For The Game

Spessmen blinking is just soulful, and brings some life into the game.
As for the quirk, its just a funny bit/reference that people can use
to... torture themselves?

## Changelog
🆑
add: Spessmen now blink.
add: Added a new Fluoride Stare quirk, keep those eyeballs wet, lest
they crack!
image: Eyes now display their color on their organ sprite, instead of
always being displayed as blue.
/🆑
This commit is contained in:
SmArtKar
2025-01-09 12:24:49 +03:00
committed by GitHub
parent 5fbc0d8fa8
commit 640a1229ca
18 changed files with 322 additions and 75 deletions

View File

@@ -40,6 +40,15 @@
var/eye_color_left = "" //set to a hex code to override a mob's left eye color
var/eye_color_right = "" //set to a hex code to override a mob's right eye color
var/eye_icon_state = "eyes"
/// Do these eyes have blinking animations
var/blink_animation = TRUE
/// Do these eyes have iris overlays
var/iris_overlays = TRUE
/// Should our blinking be synchronized or can separate eyes have (slightly) separate blinking times
var/synchronized_blinking = TRUE
// A pair of abstract eyelid objects (yes, really) used to animate blinking
var/obj/effect/abstract/eyelid_effect/eyelid_left
var/obj/effect/abstract/eyelid_effect/eyelid_right
/// Glasses cannot be worn over these eyes. Currently unused
var/no_glasses = FALSE
@@ -50,6 +59,17 @@
/// Scarring on this organ
var/scarring = NONE
/obj/item/organ/eyes/Initialize(mapload)
. = ..()
if (blink_animation)
eyelid_left = new(src, "[eye_icon_state]_l")
eyelid_right = new(src, "[eye_icon_state]_r")
/obj/item/organ/eyes/Destroy()
QDEL_NULL(eyelid_left)
QDEL_NULL(eyelid_right)
return ..()
/obj/item/organ/eyes/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
receiver.cure_blind(NO_EYES)
@@ -226,8 +246,8 @@
if(isnull(eye_icon_state))
return list()
var/mutable_appearance/eye_left = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_l", -BODY_LAYER)
var/mutable_appearance/eye_right = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_r", -BODY_LAYER)
var/mutable_appearance/eye_left = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_l", -BODY_LAYER, parent)
var/mutable_appearance/eye_right = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_r", -BODY_LAYER, parent)
var/list/overlays = list(eye_left, eye_right)
var/obscured = parent.check_obscured_slots(TRUE)
@@ -241,18 +261,11 @@
return overlays
if(my_head.head_flags & HEAD_EYECOLOR)
if(IS_ROBOTIC_ORGAN(src) || !my_head.draw_color || (parent.appears_alive() && !HAS_TRAIT(parent, TRAIT_KNOCKEDOUT)))
// show the eyes as open
eye_right.color = parent.get_right_eye_color()
eye_left.color = parent.get_left_eye_color()
else
// show the eyes as closed, and as such color them like eyelids wound be colored
var/list/base_color = rgb2num(my_head.draw_color, COLORSPACE_HSL)
base_color[2] *= 0.85
base_color[3] *= 0.85
var/eyelid_color = rgb(base_color[1], base_color[2], base_color[3], (length(base_color) >= 4 ? base_color[4] : null), COLORSPACE_HSL)
eye_right.color = eyelid_color
eye_left.color = eyelid_color
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
if (scarring & RIGHT_EYE_SCAR)
var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_face.dmi', "eye_scar_right", -BODY_LAYER)
@@ -276,6 +289,18 @@
. += mutable_appearance('icons/obj/medical/organs/organs.dmi', "eye_scar_right")
if (scarring & LEFT_EYE_SCAR)
. += mutable_appearance('icons/obj/medical/organs/organs.dmi', "eye_scar_left")
if (iris_overlays && eye_color_left && eye_color_right)
var/mutable_appearance/left_iris = mutable_appearance(icon, "[icon_state]_iris_l")
var/mutable_appearance/right_iris = mutable_appearance(icon, "[icon_state]_iris_r")
var/list/color_left = rgb2num(eye_color_left, COLORSPACE_HSL)
var/list/color_right = rgb2num(eye_color_right, COLORSPACE_HSL)
// Ugly as sin? Indeed it is! But otherwise eyeballs turn out to be super dark, and this way even lighter colors are mostly preserved
color_left[3] /= sqrt(color_left[3] * 0.01)
color_right[3] /= sqrt(color_right[3] * 0.01)
left_iris.color = rgb(color_left[1], color_left[2], color_left[3], space = COLORSPACE_HSL)
right_iris.color = rgb(color_right[1], color_right[2], color_right[3], space = COLORSPACE_HSL)
. += left_iris
. += right_iris
/obj/item/organ/eyes/proc/apply_scar(side)
if (scarring & side)
@@ -368,6 +393,92 @@
damaged = TRUE
#define BASE_BLINKING_DELAY 5 SECONDS
#define RAND_BLINKING_DELAY 1 SECONDS
#define BLINK_DURATION 0.15 SECONDS
#define BLINK_LOOPS 5
#define ASYNC_BLINKING_BRAIN_DAMAGE 60
/// Modifies eye overlays to also act as eyelids, both for blinking and for when you're knocked out cold
/obj/item/organ/eyes/proc/setup_eyelids(mutable_appearance/eye_left, mutable_appearance/eye_right, mob/living/carbon/human/parent)
var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD)
// Robotic eyes or colorless heads don't get the privelege of having eyelids
if (IS_ROBOTIC_ORGAN(src) || !my_head.draw_color || HAS_TRAIT(parent, TRAIT_NO_EYELIDS))
return
var/list/base_color = rgb2num(my_head.draw_color, COLORSPACE_HSL)
base_color[2] *= 0.85
base_color[3] *= 0.85
var/eyelid_color = rgb(base_color[1], base_color[2], base_color[3], (length(base_color) >= 4 ? base_color[4] : null), COLORSPACE_HSL)
// If we're knocked out, just color the eyes
if (!parent.appears_alive() || HAS_TRAIT(parent, TRAIT_KNOCKEDOUT))
eye_right.color = eyelid_color
eye_left.color = eyelid_color
return
if (!blink_animation || HAS_TRAIT(parent, TRAIT_PREVENT_BLINKING))
return
eyelid_left.color = eyelid_color
eyelid_right.color = eyelid_color
eyelid_left.render_target = "*[REF(parent)]_eyelid_left"
eyelid_right.render_target = "*[REF(parent)]_eyelid_right"
parent.vis_contents += eyelid_left
parent.vis_contents += eyelid_right
var/sync_blinking = synchronized_blinking && (parent.get_organ_loss(ORGAN_SLOT_BRAIN) < ASYNC_BLINKING_BRAIN_DAMAGE)
// Randomize order for unsynched animations
if (sync_blinking || prob(50))
var/list/anim_times = animate_eyelid(eyelid_left, parent, sync_blinking)
animate_eyelid(eyelid_right, parent, sync_blinking, anim_times)
else
var/list/anim_times = animate_eyelid(eyelid_right, parent, sync_blinking)
animate_eyelid(eyelid_left, parent, sync_blinking, anim_times)
var/mutable_appearance/left_eyelid_overlay = mutable_appearance(layer = -BODY_LAYER, offset_spokesman = parent)
var/mutable_appearance/right_eyelid_overlay = mutable_appearance(layer = -BODY_LAYER, offset_spokesman = parent)
left_eyelid_overlay.render_source = "*[REF(parent)]_eyelid_left"
right_eyelid_overlay.render_source = "*[REF(parent)]_eyelid_right"
return list(left_eyelid_overlay, right_eyelid_overlay)
/// Animates one eyelid at a time, thanks BYOND and thanks animation chains
/obj/item/organ/eyes/proc/animate_eyelid(obj/effect/abstract/eyelid_effect/eyelid, mob/living/carbon/human/parent, sync_blinking = TRUE, list/anim_times = null)
. = list()
var/prevent_loops = HAS_TRAIT(parent, TRAIT_PREVENT_BLINK_LOOPS)
animate(eyelid, alpha = 0, time = 0, loop = (prevent_loops ? 0 : -1))
for (var/i in 1 to (prevent_loops ? 1 : BLINK_LOOPS))
var/wait_time = rand(BASE_BLINKING_DELAY - RAND_BLINKING_DELAY, BASE_BLINKING_DELAY + RAND_BLINKING_DELAY)
if (anim_times)
if (sync_blinking)
wait_time = anim_times[1]
anim_times.Cut(1, 2)
else
wait_time = rand(max(BASE_BLINKING_DELAY - RAND_BLINKING_DELAY, anim_times[1] - RAND_BLINKING_DELAY), anim_times[1])
. += wait_time
if (anim_times && !sync_blinking)
// Make sure that we're somewhat in sync with the other eye
animate(time = anim_times[1] - wait_time)
anim_times.Cut(1, 2)
animate(alpha = 255, time = 0)
animate(time = BLINK_DURATION)
animate(alpha = 0, time = 0)
animate(time = wait_time)
/obj/effect/abstract/eyelid_effect
name = "eyelid"
icon = 'icons/mob/human/human_face.dmi'
layer = -BODY_LAYER
vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE | VIS_INHERIT_ID
/obj/effect/abstract/eyelid_effect/Initialize(mapload, new_state)
. = ..()
icon_state = new_state
#undef BASE_BLINKING_DELAY
#undef RAND_BLINKING_DELAY
#undef BLINK_DURATION
#undef BLINK_LOOPS
#undef ASYNC_BLINKING_BRAIN_DAMAGE
#define NIGHTVISION_LIGHT_OFF 0
#define NIGHTVISION_LIGHT_LOW 1
#define NIGHTVISION_LIGHT_MID 2
@@ -434,9 +545,11 @@
/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
desc = "Golems somehow measure external light levels and detect nearby ore using this sensitive mineral lattice."
blink_animation = FALSE
iris_overlays = FALSE
color = COLOR_GOLEM_GRAY
visual = FALSE
organ_flags = ORGAN_MINERAL
@@ -460,8 +573,9 @@
/obj/item/organ/eyes/robotic
name = "robotic eyes"
icon_state = "cybernetic_eyeballs"
desc = "Your vision is augmented."
icon_state = "cybernetic_eyeballs"
iris_overlays = FALSE
organ_flags = ORGAN_ROBOTIC
failing_desc = "seems to be broken."
@@ -825,59 +939,77 @@
/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."
eye_icon_state = "motheyes"
icon_state = "eyeballs-moth"
eye_icon_state = "motheyes"
blink_animation = FALSE
iris_overlays = FALSE
flash_protect = FLASH_PROTECTION_SENSITIVE
/obj/item/organ/eyes/robotic/moth
name = "robotic moth eyes"
eye_icon_state = "motheyes"
icon_state = "eyeballs-cybermoth"
desc = "Your vision is augmented. Much like actual moth eyes, very sensitive to bright lights."
icon_state = "eyeballs-cybermoth"
eye_icon_state = "motheyes"
blink_animation = FALSE
flash_protect = FLASH_PROTECTION_SENSITIVE
/obj/item/organ/eyes/robotic/basic/moth
name = "basic robotic moth eyes"
eye_icon_state = "motheyes"
icon_state = "eyeballs-cybermoth"
eye_icon_state = "motheyes"
blink_animation = FALSE
flash_protect = FLASH_PROTECTION_SENSITIVE
/obj/item/organ/eyes/robotic/xray/moth
name = "moth x-ray eyes"
eye_icon_state = "motheyes"
icon_state = "eyeballs-cybermoth"
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 = "eyeballs-cybermoth"
eye_icon_state = "motheyes"
blink_animation = FALSE
flash_protect = FLASH_PROTECTION_SENSITIVE
/obj/item/organ/eyes/robotic/shield/moth
name = "shielded robotic moth eyes"
eye_icon_state = "motheyes"
icon_state = "eyeballs-cybermoth"
eye_icon_state = "motheyes"
blink_animation = FALSE
/obj/item/organ/eyes/robotic/glow/moth
name = "high luminosity moth eyes"
eye_icon_state = "motheyes"
base_eye_state = "eyes_mothglow"
icon_state = "eyeballs-cybermoth"
desc = "Special glowing eyes, to be one with the lamp. Much like actual moth eyes, very sensitive to bright lights."
icon_state = "eyeballs-cybermoth"
eye_icon_state = "motheyes"
blink_animation = FALSE
base_eye_state = "eyes_mothglow"
flash_protect = FLASH_PROTECTION_SENSITIVE
/obj/item/organ/eyes/robotic/thermals/moth //we inherit flash weakness from thermals
name = "thermal moth eyes"
eye_icon_state = "motheyes"
icon_state = "eyeballs-cybermoth"
eye_icon_state = "motheyes"
blink_animation = FALSE
/obj/item/organ/eyes/snail
name = "snail eyes"
desc = "These eyes seem to have a large range, but might be cumbersome with glasses."
eye_icon_state = "snail_eyes"
icon_state = "snail_eyeballs"
eye_icon_state = "snail_eyes"
blink_animation = FALSE
iris_overlays = FALSE
/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."
eye_icon_state = "jelleyes"
icon_state = "eyeballs-jelly"
eye_icon_state = "jelleyes"
blink_animation = FALSE
iris_overlays = FALSE
/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
/obj/item/organ/eyes/night_vision/maintenance_adapted
name = "adapted eyes"
@@ -887,6 +1019,7 @@
eye_color_right = "f00"
icon_state = "adapted_eyes"
eye_icon_state = "eyes_glow"
iris_overlays = FALSE
overlay_ignore_lighting = TRUE
low_light_cutoff = list(5, 12, 20)
medium_light_cutoff = list(15, 20, 30)