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

@@ -265,3 +265,6 @@
/// from /mob/update_incapacitated(): (old_incap, new_incap)
#define COMSIG_MOB_INCAPACITATE_CHANGED "mob_incapacitated"
/// from /obj/item/reagent_containers/dropper/interact_with_atom(atom/target, mob/living/user, list/modifiers): (mob/living/user, atom/dropper, datum/reagents/reagents, fraction)
#define COMSIG_MOB_REAGENTS_DROPPED_INTO_EYES "mob_reagents_drop_into_eyes"

View File

@@ -1415,4 +1415,13 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Apply to movables to say "hey, this movable is technically flat on the floor, so it'd be mopped up by a mop"
#define TRAIT_MOPABLE "mopable"
/// Humans with this trait do not blink
#define TRAIT_PREVENT_BLINKING "prevent_blinking"
/// Prevents animations for blinking from looping
#define TRAIT_PREVENT_BLINK_LOOPS "prevent_blink_loops"
/// Mob doesn't get closed eyelids overlay when it gets knocked out cold or dies
#define TRAIT_NO_EYELIDS "no_eyelids"
// END TRAIT DEFINES

View File

@@ -585,6 +585,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_IGNORE_FIRE_PROTECTION" = TRAIT_IGNORE_FIRE_PROTECTION,
"TRAIT_LEFT_EYE_SCAR" = TRAIT_LEFT_EYE_SCAR,
"TRAIT_RIGHT_EYE_SCAR" = TRAIT_RIGHT_EYE_SCAR,
"TRAIT_PREVENT_BLINKING" = TRAIT_PREVENT_BLINKING,
"TRAIT_PREVENT_BLINK_LOOPS" = TRAIT_PREVENT_BLINK_LOOPS,
"TRAIT_NO_EYELIDS" = TRAIT_NO_EYELIDS,
),
/obj/item = list(
"TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING,

View File

@@ -5,6 +5,7 @@
// Shifted to glob so they are generated at world start instead of risking players doing preference stuff before the subsystem inits
GLOBAL_LIST_INIT_TYPED(quirk_blacklist, /list/datum/quirk, list(
list(/datum/quirk/item_quirk/blindness, /datum/quirk/item_quirk/nearsighted, /datum/quirk/item_quirk/scarred_eye),
list(/datum/quirk/item_quirk/blindness, /datum/quirk/item_quirk/fluoride_stare),
list(/datum/quirk/item_quirk/blindness, /datum/quirk/touchy),
list(/datum/quirk/jolly, /datum/quirk/depression, /datum/quirk/apathetic, /datum/quirk/hypersensitive),
list(/datum/quirk/no_taste, /datum/quirk/vegetarian, /datum/quirk/deviant_tastes, /datum/quirk/gamer),

View File

@@ -1,31 +1,51 @@
/datum/component/manual_blinking
dupe_mode = COMPONENT_DUPE_UNIQUE
var/obj/item/organ/eyes/E
var/obj/item/organ/eyes/parent_eyes
var/warn_grace = FALSE
var/warn_dying = FALSE
var/last_blink
var/check_every = 20 SECONDS
var/grace_period = 6 SECONDS
var/damage_rate = 1 // organ damage taken per tick
var/list/valid_emotes = list(/datum/emote/living/carbon/blink, /datum/emote/living/carbon/blink_r)
/// Organ damage taken per tick
var/damage_rate = 1
/// How much saline needs to be dropper at once for it to count as "blinking"
var/min_saline = 1
/// Do we display a message when adding/removing the component
var/display_message = TRUE
var/list/valid_emotes = list(/datum/emote/living/carbon/human/blink, /datum/emote/living/carbon/human/blink_r)
/datum/component/manual_blinking/Initialize()
/datum/component/manual_blinking/Initialize(damage_rate = 1, check_every = 20 SECONDS, grace_period = 6 SECONDS, display_message = TRUE)
if(!iscarbon(parent))
return COMPONENT_INCOMPATIBLE
var/mob/living/carbon/C = parent
E = C.get_organ_slot(ORGAN_SLOT_EYES)
src.damage_rate = damage_rate
src.check_every = check_every
src.grace_period = grace_period
src.display_message = display_message
if(E)
START_PROCESSING(SSdcs, src)
last_blink = world.time
to_chat(C, span_notice("You suddenly realize you're blinking manually."))
var/mob/living/carbon/carbon_parent = parent
ADD_TRAIT(carbon_parent, TRAIT_PREVENT_BLINK_LOOPS, REF(src))
carbon_parent.update_body()
parent_eyes = carbon_parent.get_organ_slot(ORGAN_SLOT_EYES)
if(!parent_eyes || IS_ROBOTIC_ORGAN(parent_eyes))
return
START_PROCESSING(SSdcs, src)
last_blink = world.time
if (display_message)
to_chat(carbon_parent, span_notice("You suddenly realize you're blinking manually."))
/datum/component/manual_blinking/Destroy(force)
E = null
REMOVE_TRAIT(parent, TRAIT_PREVENT_BLINK_LOOPS, REF(src))
parent_eyes = null
STOP_PROCESSING(SSdcs, src)
to_chat(parent, span_notice("You revert back to automatic blinking."))
if (display_message)
to_chat(parent, span_notice("You revert back to automatic blinking."))
var/mob/living/carbon/carbon_parent = parent
carbon_parent.cure_blind(REF(src))
carbon_parent.update_body()
return ..()
/datum/component/manual_blinking/RegisterWithParent()
@@ -34,13 +54,10 @@
RegisterSignal(parent, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(check_removed_organ))
RegisterSignal(parent, COMSIG_LIVING_REVIVE, PROC_REF(restart))
RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(pause))
RegisterSignal(parent, COMSIG_MOB_REAGENTS_DROPPED_INTO_EYES, PROC_REF(on_dropper))
/datum/component/manual_blinking/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_MOB_EMOTE)
UnregisterSignal(parent, COMSIG_CARBON_GAIN_ORGAN)
UnregisterSignal(parent, COMSIG_CARBON_LOSE_ORGAN)
UnregisterSignal(parent, COMSIG_LIVING_REVIVE)
UnregisterSignal(parent, COMSIG_LIVING_DEATH)
UnregisterSignal(parent, list(COMSIG_MOB_EMOTE, COMSIG_CARBON_GAIN_ORGAN, COMSIG_CARBON_LOSE_ORGAN, COMSIG_LIVING_REVIVE, COMSIG_LIVING_DEATH, COMSIG_MOB_REAGENTS_DROPPED_INTO_EYES))
/datum/component/manual_blinking/proc/restart()
SIGNAL_HANDLER
@@ -53,41 +70,51 @@
STOP_PROCESSING(SSdcs, src)
/datum/component/manual_blinking/process()
var/mob/living/carbon/C = parent
if(world.time > (last_blink + check_every + grace_period))
if(!warn_dying)
to_chat(C, span_userdanger("Your eyes begin to wither, you need to blink!"))
to_chat(parent, span_userdanger("Your eyes begin to wither, you need to blink!"))
warn_dying = TRUE
E.apply_organ_damage(damage_rate)
parent_eyes.apply_organ_damage(damage_rate)
else if(world.time > (last_blink + check_every))
if(!warn_grace)
to_chat(C, span_danger("You feel a need to blink!"))
to_chat(parent, span_danger("You feel a need to blink!"))
warn_grace = TRUE
/datum/component/manual_blinking/proc/check_added_organ(mob/who_cares, obj/item/organ/O)
/datum/component/manual_blinking/proc/check_added_organ(mob/who_cares, obj/item/organ/added_organ)
SIGNAL_HANDLER
var/obj/item/organ/eyes/new_eyes = O
if(istype(new_eyes,/obj/item/organ/eyes))
E = new_eyes
if(istype(added_organ, /obj/item/organ/eyes))
parent_eyes = added_organ
if (IS_ROBOTIC_ORGAN(parent_eyes))
parent_eyes = null
return
last_blink = world.time
START_PROCESSING(SSdcs, src)
/datum/component/manual_blinking/proc/check_removed_organ(mob/who_cares, obj/item/organ/O)
/datum/component/manual_blinking/proc/check_removed_organ(mob/who_cares, obj/item/organ/removed_organ)
SIGNAL_HANDLER
var/obj/item/organ/eyes/bye_beyes = O // oh come on, that's pretty good
if(istype(bye_beyes, /obj/item/organ/eyes))
E = null
if(removed_organ == parent_eyes)
parent_eyes = null
STOP_PROCESSING(SSdcs, src)
/datum/component/manual_blinking/proc/check_emote(mob/living/carbon/user, datum/emote/emote)
SIGNAL_HANDLER
if(emote.type in valid_emotes)
if(!(emote.type in valid_emotes))
return
warn_grace = FALSE
warn_dying = FALSE
last_blink = world.time
user.become_blind(REF(src))
addtimer(CALLBACK(user, TYPE_PROC_REF(/mob/living, remove_status_effect), /datum/status_effect/grouped/blindness, REF(src)), 0.15 SECONDS)
/datum/component/manual_blinking/proc/on_dropper(datum/source, mob/living/user, atom/dropper, datum/reagents/reagents, fraction)
SIGNAL_HANDLER
var/saline_amount = reagents.get_reagent_amount(/datum/reagent/medicine/salglu_solution) * fraction
if (saline_amount >= min_saline)
warn_grace = FALSE
warn_dying = FALSE
last_blink = world.time

View File

@@ -0,0 +1,36 @@
/datum/quirk/item_quirk/fluoride_stare
name = "Fluoride Stare"
desc = "You have lost your eyelids in a horrible accident, or so you tell others. You need to manually wet your eyes with a saline solution every once in a while!"
icon = FA_ICON_EYE_DROPPER
value = -6
gain_text = span_danger("Your eyes feel itchy and dry...")
lose_text = span_notice("You realize that sudden darkness that has just enveloped you was just your eyelids growing back.")
medical_record_text = "Patient has lost their eyelids in a grueling accident."
hardcore_value = 6
quirk_flags = QUIRK_HUMAN_ONLY
mail_goodies = list(/obj/item/reagent_containers/cup/bottle/salglu_solution, /obj/item/light/bulb)
/datum/quirk/item_quirk/fluoride_stare/add_unique(client/client_source)
var/obj/item/reagent_containers/cup/bottle/salglu_solution/saline = new(get_turf(quirk_holder))
give_item_to_holder(saline, list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS,
))
var/obj/item/reagent_containers/dropper/dropper = new(get_turf(quirk_holder))
give_item_to_holder(dropper, list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS,
))
/datum/quirk/item_quirk/fluoride_stare/add(client/client_source)
ADD_TRAIT(quirk_holder, TRAIT_NO_EYELIDS, QUIRK_TRAIT)
quirk_holder.AddComponent(/datum/component/manual_blinking, 1, 30 SECONDS, 10 SECONDS, FALSE)
/datum/quirk/item_quirk/fluoride_stare/remove()
REMOVE_TRAIT(quirk_holder, TRAIT_NO_EYELIDS, QUIRK_TRAIT)
if (!HAS_TRAIT(quirk_holder, TRAIT_NO_EYELIDS))
qdel(quirk_holder.GetComponent(/datum/component/manual_blinking))

View File

@@ -26,6 +26,8 @@
icon_state = "eyeballs-fly"
flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
native_fov = NONE //flies can see all around themselves.
blink_animation = FALSE
iris_overlays = FALSE
/obj/item/organ/eyes/fly/Initialize(mapload)
. = ..()

View File

@@ -18,6 +18,7 @@
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "eyes"
iris_overlays = FALSE
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = GOLIATH_COLORS

View File

@@ -21,6 +21,7 @@
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "eyes"
iris_overlays = FALSE
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = RAT_COLORS
low_light_cutoff = list(16, 11, 0)

View File

@@ -3,6 +3,8 @@
name = "blackened orbs"
desc = "These orbs will withstand the light of the sun, yet still see within the darkest voids."
eye_icon_state = null
blink_animation = FALSE
iris_overlays = FALSE
pepperspray_protect = TRUE
flash_protect = FLASH_PROTECTION_WELDER
color_cutoffs = list(20, 10, 40)

View File

@@ -6,16 +6,6 @@
message = "is strumming the air and headbanging like a safari chimp."
hands_use_check = TRUE
/datum/emote/living/carbon/blink
key = "blink"
key_third_person = "blinks"
message = "blinks."
/datum/emote/living/carbon/blink_r
key = "blink_r"
name = "blink (Rapid)"
message = "blinks rapidly."
/datum/emote/living/carbon/clap
key = "clap"
key_third_person = "claps"

View File

@@ -171,6 +171,41 @@
key_third_person = "clears throat"
message = "clears their throat."
/datum/emote/living/carbon/human/blink
key = "blink"
key_third_person = "blinks"
message = "blinks."
/datum/emote/living/carbon/human/blink/can_run_emote(mob/living/carbon/human/user, status_check, intentional, params)
if (!ishuman(user) || HAS_TRAIT(user, TRAIT_PREVENT_BLINKING) || HAS_TRAIT(user, TRAIT_NO_EYELIDS))
return FALSE
var/obj/item/organ/eyes/eyes = user.get_organ_slot(ORGAN_SLOT_EYES)
if (!eyes)
return FALSE
return ..()
/datum/emote/living/carbon/human/blink/run_emote(mob/living/carbon/human/user, params, type_override, intentional)
. = ..()
user.update_body_parts_head_only() // Refreshing instantly makes the user blink
/datum/emote/living/carbon/human/blink_r
key = "blink_r"
name = "blink (Rapid)"
message = "blinks rapidly."
/datum/emote/living/carbon/human/blink_r/can_run_emote(mob/living/carbon/human/user, status_check, intentional, params)
if (!ishuman(user) || HAS_TRAIT(user, TRAIT_PREVENT_BLINKING) || HAS_TRAIT(user, TRAIT_NO_EYELIDS))
return FALSE
var/obj/item/organ/eyes/eyes = user.get_organ_slot(ORGAN_SLOT_EYES)
if (!eyes)
return FALSE
return ..()
/datum/emote/living/carbon/human/blink_r/run_emote(mob/user, params, type_override, intentional)
. = ..()
for (var/i in 1 to 3)
addtimer(CALLBACK(user, TYPE_PROC_REF(/mob/living/carbon/human, update_body_parts_head_only)), i * 0.3 SECONDS)
///Snowflake emotes only for le epic chimp
/datum/emote/living/carbon/human/monkey

View File

@@ -16,6 +16,7 @@
/obj/item/organ/tail/lizard = "Smooth",
)
mutanttongue = /obj/item/organ/tongue/lizard
mutanteyes = /obj/item/organ/eyes/lizard
coldmod = 1.5
heatmod = 0.67
payday_modifier = 1.0

View File

@@ -93,6 +93,7 @@
name = "burning red eyes"
desc = "Even without their shadowy owner, looking at these eyes gives you a sense of dread."
icon = 'icons/obj/medical/organs/shadow_organs.dmi'
iris_overlays = FALSE
color_cutoffs = list(20, 10, 40)
pepperspray_protect = TRUE
flash_protect = FLASH_PROTECTION_SENSITIVE

View File

@@ -41,8 +41,8 @@
target.visible_message(span_danger("[user] tries to squirt something into [target]'s eyes, but fails!"), \
span_userdanger("[user] tries to squirt something into your eyes, but fails!"))
to_chat(user, span_notice("You transfer [trans] unit\s of the solution."))
if(trans)
to_chat(user, span_notice("You transfer [trans] unit\s of the solution."))
update_appearance()
return ITEM_INTERACT_BLOCKING
else if(isalien(target)) //hiss-hiss has no eyes!
@@ -52,6 +52,7 @@
target.visible_message(span_danger("[user] squirts something into [target]'s eyes!"), \
span_userdanger("[user] squirts something into your eyes!"))
SEND_SIGNAL(target, COMSIG_MOB_REAGENTS_DROPPED_INTO_EYES, user, src, reagents, fraction)
reagents.expose(target, TOUCH, fraction)
var/mob/M = target
var/R

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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -1773,6 +1773,7 @@
#include "code\datums\quirks\negative_quirks\deafness.dm"
#include "code\datums\quirks\negative_quirks\depression.dm"
#include "code\datums\quirks\negative_quirks\family_heirloom.dm"
#include "code\datums\quirks\negative_quirks\fluoride_stare.dm"
#include "code\datums\quirks\negative_quirks\food_allergy.dm"
#include "code\datums\quirks\negative_quirks\frail.dm"
#include "code\datums\quirks\negative_quirks\glass_jaw.dm"