Organ damage refactor/cleanup (#93436)

## About The Pull Request
So, my original goal was just a refactor for the emissive overlays of
eyes, as a way to implement the specular emissive introduced by smartkar
some time ago, but somehow I found myself dragged into a bigger refactor
or cleanup of organ damage, thresholds, failures. One of the main
problem was that there were no procs called when a organ suffered enough
damage to fail or when recovering from failure. It'd just enable or
disable a bitflag, leaving it up to subtypes to decide how to tackle
organ failure their own ways: diverse, funky and sometimes incompatible.
More often than not relying on their very own "update_thingamajig" kinda
procs that run whenever the organ takes damage, rather than just when
the threshold is reached (low, high, failure. There are however a couple
organs with their own quirky thresholds, I let those slide).

There's also a bit of old code, especially for ears, with the
`AdjustEarDamage` and temporary deafness both predating the framework
for organ damage as far as I know. It really needed a coat of fresh
paint.

Oh, there were also more than a handful of organs that still heavily
relied on some ORGAN_TRAIT source instead of the `organ_traits` list and
the two add/remove procs `add_organ_trait` or `remove_organ_trait`. This
include organs that lose or gain specific traits when failing et
viceversa.

~~Lastly, felinids (and the halloween ghost species) having reflective
eyes. It's just a nod to the tapetum lucidum that animals with night
vision often have (including cats), which is why their eyes are a bit
brighter in the dark. Felinids however, do not have night vision (nor do
ghosts). This is merely cosmetic.~~ Cut out for the time being due to
issues with the specular emissive...

## Why It's Good For The Game
Refactoring / cleaning up old organ code.

## Changelog

🆑
refactor: Refactored organ damage code a little. Hopefully there won't
be issues (otherwise report them).
/🆑
This commit is contained in:
Ghom
2025-10-21 23:52:28 +02:00
committed by GitHub
parent 593fe47c1c
commit ca2cc70322
34 changed files with 422 additions and 388 deletions
+61 -23
View File
@@ -117,7 +117,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/// Add a Trait to an organ that it will give its owner.
/obj/item/organ/proc/add_organ_trait(trait)
LAZYADD(organ_traits, trait)
LAZYOR(organ_traits, trait)
if(isnull(owner))
return
ADD_TRAIT(owner, trait, REF(src))
@@ -251,14 +251,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
damage = clamp(damage + damage_amount, 0, maximum)
SEND_SIGNAL(src, COMSIG_ORGAN_ADJUST_DAMAGE, damage_amount, maximum, required_organ_flag)
. = (prev_damage - damage) // return net damage
var/message = check_damage_thresholds(owner)
var/message = check_damage_thresholds()
prev_damage = damage
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
else
organ_flags &= ~ORGAN_FAILING
if(message && owner && owner.stat <= SOFT_CRIT)
to_chat(owner, message)
@@ -272,24 +267,66 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
* description: By checking our current damage against our previous damage, we can decide whether we've passed an organ threshold.
* If we have, send the corresponding threshold message to the owner, if such a message exists.
*/
/obj/item/organ/proc/check_damage_thresholds(mob/organ_owner)
/obj/item/organ/proc/check_damage_thresholds()
SHOULD_CALL_PARENT(TRUE)
if(damage == prev_damage)
return
var/delta = damage - prev_damage
var/message = ""
if(delta > 0)
if(damage >= maxHealth)
return now_failing
if(damage > high_threshold && prev_damage <= high_threshold)
return high_threshold_passed
if(damage > low_threshold && prev_damage <= low_threshold)
return low_threshold_passed
else
if(prev_damage > low_threshold && damage <= low_threshold)
return low_threshold_cleared
if(prev_damage > high_threshold && damage <= high_threshold)
return high_threshold_cleared
if(prev_damage == maxHealth)
return now_fixed
on_low_damage_received()
message = low_threshold_passed
if(damage > high_threshold && prev_damage <= high_threshold)
on_high_damage_received()
message = high_threshold_passed
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
on_begin_failure()
message = now_failing
return message
if(prev_damage == maxHealth)
organ_flags &= ~ORGAN_FAILING
on_failure_recovery()
message = now_fixed
if(prev_damage > high_threshold && damage <= high_threshold)
on_high_damage_healed()
message = high_threshold_cleared
if(prev_damage > low_threshold && damage <= low_threshold)
on_low_damage_healed()
message = low_threshold_cleared
return message
/**
* Called when the damage surpasses the low damage threshold.
*
* This and other procs like this one merely exist to make it easier to keep a standard on
* damage thresholds for organs. This doesn't mean you cannot make custom thresholds for various stuff,
* and you're more than welcome to improve or refactor any portion of the code around these mechanics
*/
/obj/item/organ/proc/on_low_damage_received()
return
///Called when the damage goes below the low damage threshold
/obj/item/organ/proc/on_low_damage_healed()
return
///Called when the damage surpasses the high damage threshold
/obj/item/organ/proc/on_high_damage_received()
return
///Called when the damage goes below the high damage threshold
/obj/item/organ/proc/on_high_damage_healed()
return
///Called when the organ enters failing stage
/obj/item/organ/proc/on_begin_failure()
return
///Called when the organ recovers from failing stage
/obj/item/organ/proc/on_failure_recovery()
return
//Looking for brains?
//Try code/modules/mob/living/carbon/brain/brain_item.dm
@@ -313,9 +350,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
dna.species.regenerate_organs(src, replace_current = FALSE)
set_heartattack(FALSE)
// Ears have aditional vаr "deaf", need to update it too
// Ears have aditional var "deaf", need to update it too
var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS)
ears?.adjustEarDamage(0, -INFINITY) // full heal ears deafness
ears.adjust_temporary_deafness(-INFINITY)
return
@@ -352,7 +389,8 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
if(!ears)
ears = new()
ears.Insert(src)
ears.adjustEarDamage(-INFINITY, -INFINITY) // actually do: set_organ_damage(0) and deaf = 0
ears.set_organ_damage(0)
ears.adjust_temporary_deafness(-INFINITY)
///Organs don't die instantly, and neither should you when you get fucked up
/obj/item/organ/proc/handle_failing_organs(seconds_per_tick)
@@ -18,32 +18,36 @@
/// Eyecolor from the HUD
var/hud_color = "#3CB8A5"
/obj/item/organ/cyberimp/eyes/hud/Initialize(mapload)
. = ..()
if(toggled_on)
for(var/hud_trait in HUD_traits)
add_organ_trait(hud_trait)
/obj/item/organ/cyberimp/eyes/hud/proc/toggle_hud(mob/living/carbon/human/eye_owner)
if(toggled_on)
toggled_on = FALSE
eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT)
for(var/hud_trait in HUD_traits)
remove_organ_trait(hud_trait)
balloon_alert(eye_owner, "hud disabled")
if(hud_color)
eye_owner.remove_eye_color(EYE_COLOR_HUD_PRIORITY)
return
toggled_on = TRUE
eye_owner.add_traits(HUD_traits, ORGAN_TRAIT)
for(var/hud_trait in HUD_traits)
add_organ_trait(hud_trait)
balloon_alert(eye_owner, "hud enabled")
if(hud_color)
eye_owner.add_eye_color_right(hud_color, EYE_COLOR_HUD_PRIORITY)
/obj/item/organ/cyberimp/eyes/hud/on_mob_insert(mob/living/carbon/human/eye_owner, special = FALSE, movement_flags)
. = ..()
eye_owner.add_traits(HUD_traits, ORGAN_TRAIT)
toggled_on = TRUE
if(hud_color)
if(toggled_on && hud_color)
eye_owner.add_eye_color_right(hud_color, EYE_COLOR_HUD_PRIORITY, !special)
/obj/item/organ/cyberimp/eyes/hud/on_mob_remove(mob/living/carbon/human/eye_owner, special, movement_flags)
. = ..()
eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT)
toggled_on = FALSE
if(hud_color)
if(toggled_on && hud_color)
eye_owner.remove_eye_color(EYE_COLOR_HUD_PRIORITY, !special)
/obj/item/organ/cyberimp/eyes/hud/medical
@@ -14,8 +14,8 @@
now_fixed = span_info("Noise slowly begins filling your ears once more.")
low_threshold_cleared = span_info("The ringing in your ears has died down.")
/// `deaf` measures "ticks" of deafness. While > 0, the person is unable to hear anything.
var/deaf = 0
/// temporary deafness, measured in seconds. While > 0, the person is unable to hear anything.
var/temporary_deafness = 0
// `damage` in this case measures long term damage to the ears, if too high,
// the person will not have either `deaf` or `ear_damage` decrease
@@ -39,23 +39,20 @@
// no healing if failing
if(organ_flags & ORGAN_FAILING)
return
adjustEarDamage(0, -0.5 * seconds_per_tick)
adjust_temporary_deafness(-seconds_per_tick SECONDS)
if((damage > low_threshold) && SPT_PROB(damage / 60, seconds_per_tick))
adjustEarDamage(0, 4)
adjust_temporary_deafness(4 SECONDS)
SEND_SOUND(owner, sound('sound/items/weapons/flash_ring.ogg'))
/obj/item/organ/ears/apply_organ_damage(damage_amount, maximum, required_organ_flag)
. = ..()
update_temp_deafness()
/obj/item/organ/ears/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
update_temp_deafness()
if(temporary_deafness)
on_deafened()
/obj/item/organ/ears/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE)
if(temporary_deafness)
on_undeafened(organ_owner)
/obj/item/organ/ears/get_status_appendix(advanced, add_tooltips)
if(owner.stat == DEAD || !HAS_TRAIT(owner, TRAIT_DEAF))
@@ -65,7 +62,7 @@
return conditional_tooltip("Subject is permanently deaf.", "Irreparable under normal circumstances.", add_tooltips)
if(HAS_TRAIT_FROM(owner, TRAIT_DEAF, GENETIC_MUTATION))
return conditional_tooltip("Subject is genetically deaf.", "Use medication such as [/datum/reagent/medicine/mutadone::name].", add_tooltips)
if(HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
if((organ_flags & ORGAN_FAILING) || HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
return conditional_tooltip("Subject is [(organ_flags & ORGAN_FAILING) ? "permanently": "temporarily"] deaf from ear damage.", "Repair surgically, use medication such as [/datum/reagent/medicine/inacusiate::name], or protect ears with earmuffs.", add_tooltips)
return "Subject is deaf."
@@ -73,52 +70,43 @@
// Always show if we have an appendix
return ..() || (owner.stat != DEAD && HAS_TRAIT(owner, TRAIT_DEAF))
/**
* Snowflake proc to handle temporary deafness
*
* * ddmg: Handles normal organ damage
* * ddeaf: Handles temporary deafness, 1 ddeaf = 2 seconds of deafness, by default (with no multiplier)
*/
/obj/item/organ/ears/proc/adjustEarDamage(ddmg = 0, ddeaf = 0)
if(HAS_TRAIT(owner, TRAIT_GODMODE))
update_temp_deafness()
///Adjust the temporary deafness of the person, up or down
/obj/item/organ/ears/proc/adjust_temporary_deafness(amount)
// organ failure makes us permanently deafened. Also, doesn't do anything if not in someone or during godmode
if(amount <= 0 || (owner && HAS_TRAIT(owner, TRAIT_GODMODE)))
return
var/mod_damage = ddmg > 0 ? (ddmg * damage_multiplier) : ddmg
if(mod_damage)
apply_organ_damage(mod_damage)
var/mod_deaf = ddeaf > 0 ? (ddeaf * damage_multiplier) : ddeaf
if(mod_deaf)
deaf = max(deaf + mod_deaf, 0)
update_temp_deafness()
temporary_deafness += max(amount * damage_multiplier, 0)
/// Updates status of deafness
/obj/item/organ/ears/proc/update_temp_deafness()
// if we're failing we always have at least some deaf stacks (and thus deafness)
if(organ_flags & ORGAN_FAILING)
deaf = max(deaf, 1 * damage_multiplier)
if(isnull(owner))
if(!owner)
return
if(HAS_TRAIT(owner, TRAIT_GODMODE))
deaf = 0
if(temporary_deafness && !HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
on_deafened()
else if(!temporary_deafness && HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
on_undeafened()
if(deaf > 0)
if(!HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(adjust_speech))
ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
else
REMOVE_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
UnregisterSignal(owner, COMSIG_MOB_SAY)
///Called when temporary deafness begins
/obj/item/organ/ears/proc/on_deafened()
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(adjust_speech))
ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
///Called when temporary deafness reaches zero. Has to have an 'organ_owner' arg, because by the time it's called on 'on_mob_remove', owner is already null
/obj/item/organ/ears/proc/on_undeafened(mob/living/organ_owner = owner)
REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE)
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
/obj/item/organ/ears/on_begin_failure()
add_organ_trait(TRAIT_DEAF)
/obj/item/organ/ears/on_failure_recovery()
remove_organ_trait(TRAIT_DEAF)
/// Being deafened by loud noises makes you shout
/obj/item/organ/ears/proc/adjust_speech(datum/source, list/speech_args)
SIGNAL_HANDLER
if(HAS_TRAIT_NOT_FROM(source, TRAIT_DEAF, EAR_DAMAGE))
return
if(HAS_TRAIT(source, TRAIT_SIGN_LANG))
if(HAS_TRAIT_NOT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE) || HAS_TRAIT(owner, TRAIT_SIGN_LANG))
return
var/message = speech_args[SPEECH_MESSAGE]
@@ -240,16 +228,9 @@
desc = "Allows the user to more easily hear whispers. The user becomes extra vulnerable to loud noises, however"
// Same sensitivity as felinid ears
damage_multiplier = 2
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
/obj/item/organ/ears/cybernetic/whisper/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
/obj/item/organ/ears/cybernetic/whisper/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
organ_traits = list(TRAIT_GOOD_HEARING)
/obj/item/organ/ears/cybernetic/volume
name = "volume-adjusting cybernetic ears"
@@ -265,14 +246,7 @@
desc = "Through the power of modern engineering, allows the user to hear speech through walls. The user becomes extra vulnerable to loud noises, however"
// Same sensitivity as felinid ears
damage_multiplier = 2
/obj/item/organ/ears/cybernetic/xray/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
/obj/item/organ/ears/cybernetic/xray/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
organ_traits = list(TRAIT_XRAY_HEARING)
/obj/item/organ/ears/cybernetic/emp_act(severity)
. = ..()
@@ -22,8 +22,6 @@
/// Sight flags this eye pair imparts on its user.
var/sight_flags = NONE
/// changes how the eyes overlay is applied, makes it apply over the lighting layer
var/overlay_ignore_lighting = FALSE
/// How much innate tint these eyes have
var/tint = 0
/// How much innare flash protection these eyes have, usually paired with tint
@@ -52,8 +50,6 @@
/// Glasses cannot be worn over these eyes. Currently unused
var/no_glasses = FALSE
/// indication that the eyes are undergoing some negative effect
var/damaged = FALSE
/// Native FOV that will be applied if a config is enabled
var/native_fov = FOV_90_DEGREES
/// Scarring on this organ
@@ -82,8 +78,13 @@
/obj/item/organ/eyes/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(organ_flags & ORGAN_FAILING)
receiver.become_blind(EYE_DAMAGE)
if(damage >= low_threshold)
receiver.assign_nearsightedness(EYE_DAMAGE, damage >= high_threshold ? 3 : 2, TRUE)
receiver.cure_blind(NO_EYES)
apply_damaged_eye_effects()
// Ensures that non-player mobs get their eye colors assigned, as players get them from prefs
if (ishuman(receiver))
var/mob/living/carbon/human/as_human = receiver
@@ -91,9 +92,17 @@
eye_color_left = as_human.eye_color_left
if (!eye_color_right)
eye_color_right = as_human.eye_color_right
RegisterSignals(receiver, list(
SIGNAL_ADDTRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_REMOVETRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_ADDTRAIT(TRAIT_REFLECTIVE_EYES),
SIGNAL_REMOVETRAIT(TRAIT_REFLECTIVE_EYES),
), PROC_REF(on_shiny_eyes_trait_update))
refresh(receiver, call_update = TRUE)
RegisterSignal(receiver, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act))
RegisterSignal(receiver, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(on_face_wash))
if (scarring)
apply_scarring_effects()
@@ -134,19 +143,37 @@
if(!special)
human_owner.update_body()
// become blind (if not special)
if(!special)
organ_owner.become_blind(NO_EYES)
// Cure blindness from eye damage
organ_owner.cure_blind(EYE_DAMAGE)
organ_owner.cure_nearsighted(EYE_DAMAGE)
// Eye blind and temp blind go to, even if this is a bit of cheesy way to clear blindness
organ_owner.remove_status_effect(/datum/status_effect/eye_blur)
organ_owner.remove_status_effect(/datum/status_effect/temporary_blindness)
// Then become blind anyways (if not special)
if(!special)
organ_owner.become_blind(NO_EYES)
if (scarring)
owner.cure_nearsighted(TRAIT_RIGHT_EYE_SCAR)
owner.cure_nearsighted(TRAIT_LEFT_EYE_SCAR)
owner.cure_blind(EYE_SCARRING_TRAIT)
organ_owner.update_tint()
organ_owner.update_sight()
UnregisterSignal(organ_owner, list(COMSIG_ATOM_BULLET_ACT, COMSIG_COMPONENT_CLEAN_FACE_ACT))
UnregisterSignal(organ_owner, list(
COMSIG_ATOM_BULLET_ACT,
COMSIG_COMPONENT_CLEAN_FACE_ACT,
SIGNAL_ADDTRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_REMOVETRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_ADDTRAIT(TRAIT_REFLECTIVE_EYES),
SIGNAL_REMOVETRAIT(TRAIT_REFLECTIVE_EYES),
))
///Called whenever the luminescent and/or reflective eyes traits are added or removed
/obj/item/organ/eyes/proc/on_shiny_eyes_trait_update(mob/living/carbon/human/source)
SIGNAL_HANDLER
source.update_eyes()
/obj/item/organ/eyes/update_atom_colour()
. = ..()
@@ -267,9 +294,8 @@
var/mutable_appearance/eye_right = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_r", -EYES_LAYER, parent)
var/list/overlays = list(eye_left, eye_right)
if(overlay_ignore_lighting && !(parent.obscured_slots & HIDEEYES))
overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, parent, -EYES_LAYER, alpha = eye_left.alpha)
overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, parent, -EYES_LAYER, alpha = eye_right.alpha)
if(!(parent.obscured_slots & HIDEEYES))
overlays += get_emissive_overlays(eye_left, eye_right, parent)
var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD)
@@ -299,6 +325,19 @@
return overlays
///Returns the two emissive overlays built for the left and right eyes, in order.
/obj/item/organ/eyes/proc/get_emissive_overlays(mutable_appearance/eye_left, mutable_appearance/eye_right, atom/spokesman)
var/list/return_list = list()
var/emissive_effect
if((owner && HAS_TRAIT(owner, TRAIT_LUMINESCENT_EYES)) || (TRAIT_LUMINESCENT_EYES in organ_traits))
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)
return return_list
/obj/item/organ/eyes/update_overlays()
. = ..()
if (scarring & RIGHT_EYE_SCAR)
@@ -358,13 +397,6 @@
owner.cure_blind(EYE_SCARRING_TRAIT)
owner.update_body()
/obj/item/organ/eyes/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
if (scarring)
eye_owner.cure_nearsighted(TRAIT_RIGHT_EYE_SCAR)
eye_owner.cure_nearsighted(TRAIT_LEFT_EYE_SCAR)
eye_owner.cure_blind(EYE_SCARRING_TRAIT)
#undef OFFSET_X
#undef OFFSET_Y
@@ -374,38 +406,28 @@
eye_color_left = initial(eye_color_left)
eye_color_right = initial(eye_color_right)
/obj/item/organ/eyes/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag)
. = ..()
if(!owner)
return FALSE
apply_damaged_eye_effects()
/// Applies effects to our owner based on how damaged our eyes are
/obj/item/organ/eyes/proc/apply_damaged_eye_effects()
// we're in healthy threshold, either try to heal (if damaged) or do nothing
if(damage <= low_threshold)
if(damaged)
damaged = FALSE
// clear nearsightedness from damage
owner.cure_nearsighted(EYE_DAMAGE)
// and cure blindness from damage
owner.cure_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_low_damage_received()
if(damage >= high_threshold)
return
owner?.assign_nearsightedness(EYE_DAMAGE, 2, TRUE)
//various degrees of "oh fuck my eyes", from "point a laser at your eye" to "staring at the Sun" intensities
// 50 - blind
// 49-31 - nearsighted (2 severity)
// 30-20 - nearsighted (1 severity)
if(organ_flags & ORGAN_FAILING)
// become blind from damage
owner.become_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_high_damage_received()
owner?.assign_nearsightedness(EYE_DAMAGE, 3, TRUE)
else
// become nearsighted from damage
var/severity = damage > high_threshold ? 3 : 2
owner.assign_nearsightedness(EYE_DAMAGE, severity, TRUE)
/obj/item/organ/eyes/on_begin_failure()
owner?.become_blind(EYE_DAMAGE)
damaged = TRUE
/obj/item/organ/eyes/on_failure_recovery()
owner?.cure_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_high_damage_healed()
if(damage <= low_threshold)
return
owner?.assign_nearsightedness(EYE_DAMAGE, 2, TRUE)
/obj/item/organ/eyes/on_low_damage_healed()
// clear nearsightedness from damage
owner?.cure_nearsighted(EYE_DAMAGE)
/obj/item/organ/eyes/feel_for_damage(self_aware)
// Eye damage has visual effects, so we don't really need to "feel" it when self-examining
@@ -681,16 +703,9 @@
eye_color_left = "#3cb8a5"
eye_color_right = "#3cb8a5"
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
organ_traits = list(TRAIT_XRAY_VISION)
penlight_message = "replaced by small radiation emitters and detectors"
/obj/item/organ/eyes/robotic/xray/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
/obj/item/organ/eyes/robotic/xray/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
REMOVE_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
/obj/item/organ/eyes/robotic/thermals
name = "thermal eyes"
desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included."
@@ -1004,17 +1019,25 @@
if(QDELETED(eye_owner) || !ishuman(eye_owner)) //Other carbon mobs don't have eye color.
return
if(!eye.light_on)
eye_icon_state = initial(eye_icon_state)
overlay_ignore_lighting = FALSE
else
overlay_ignore_lighting = TRUE
eye_icon_state = base_eye_state
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 = previous_flags | HEAD_EYECOLOR
eye_owner.dna.species.handle_body(eye_owner)
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
@@ -1140,7 +1163,7 @@
eye_color_right = "#f74a4d"
eye_icon_state = "eyes_glow"
iris_overlay = null
overlay_ignore_lighting = TRUE
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)
@@ -1153,10 +1176,6 @@
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_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT)
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_life(seconds_per_tick, times_fired)
if(!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!"))
@@ -1166,10 +1185,6 @@
apply_organ_damage(-10) //heal quickly
. = ..()
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_remove(mob/living/carbon/unadapted, special = FALSE, movement_flags)
REMOVE_TRAIT(unadapted, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT)
return ..()
/obj/item/organ/eyes/pod
name = "pod eyes"
desc = "Strangest salad you've ever seen."
@@ -1179,3 +1194,9 @@
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"
@@ -8,6 +8,7 @@
attack_verb_continuous = list("licks", "slobbers", "slaps", "frenches", "tongues")
attack_verb_simple = list("lick", "slobber", "slap", "french", "tongue")
voice_filter = ""
organ_traits = list(TRAIT_SPEAKS_CLEARLY)
/**
* A cached list of paths of all the languages this tongue is capable of speaking
*
@@ -51,6 +52,8 @@
// - then we cache it via string list
// this results in tongues with identical possible languages sharing a cached list instance
languages_possible = string_list(get_possible_languages())
if(!sense_of_taste)
add_organ_trait(TRAIT_AGEUSIA)
/obj/item/organ/tongue/examine(mob/user)
. = ..()
@@ -137,40 +140,24 @@
* ageusia from having a non-tasting tongue.
*/
REMOVE_TRAIT(receiver, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
apply_tongue_effects()
/obj/item/organ/tongue/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
temp_say_mod = ""
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
REMOVE_TRAIT(organ_owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
REMOVE_TRAIT(organ_owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
// Carbons by default start with NO_TONGUE_TRAIT caused TRAIT_AGEUSIA
ADD_TRAIT(organ_owner, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
organ_owner.voice_filter = initial(organ_owner.voice_filter)
/obj/item/organ/tongue/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag)
. = ..()
if(!owner)
return FALSE
apply_tongue_effects()
/obj/item/organ/tongue/on_begin_failure()
remove_organ_trait(TRAIT_SPEAKS_CLEARLY)
add_organ_trait(TRAIT_AGEUSIA)
/// Applies effects to our owner based on how damaged our tongue is
/obj/item/organ/tongue/proc/apply_tongue_effects()
/obj/item/organ/tongue/on_failure_recovery()
add_organ_trait(TRAIT_SPEAKS_CLEARLY)
if(sense_of_taste)
//tongues can't taste food when they are failing
if(organ_flags & ORGAN_FAILING)
ADD_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
else
REMOVE_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
else
//tongues can't taste food when they lack a sense of taste
ADD_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
if(organ_flags & ORGAN_FAILING)
REMOVE_TRAIT(owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
else
ADD_TRAIT(owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
remove_organ_trait(TRAIT_AGEUSIA)
/obj/item/organ/tongue/could_speak_language(datum/language/language_path)
return (language_path in languages_possible)