From ca2cc70322edc8db27a0cfe1d641e67f78076d59 Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Tue, 21 Oct 2025 23:52:28 +0200 Subject: [PATCH] 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 :cl: refactor: Refactored organ damage code a little. Hopefully there won't be issues (otherwise report them). /:cl: --- code/__DEFINES/traits/declarations.dm | 4 + code/__HELPERS/honkerblast.dm | 9 +- code/_globalvars/traits/_traits.dm | 2 + code/_globalvars/traits/admin_tooling.dm | 2 + .../diseases/advance/symptoms/deafness.dm | 10 +- .../diseases/advance/symptoms/sensory.dm | 3 +- code/datums/elements/earhealing.dm | 5 +- .../antagonists/changeling/powers/shriek.dm | 4 +- .../heretic/items/corrupted_organs.dm | 3 +- .../antagonists/heretic/magic/mind_gate.dm | 12 +- code/modules/clothing/head/jobs.dm | 2 +- code/modules/fishing/fish/types/rift.dm | 2 +- .../mob/living/basic/bots/honkbots/honkbot.dm | 3 +- .../basic/lavaland/legion/legion_tumour.dm | 10 +- code/modules/mob/living/brain/brain_item.dm | 55 +++--- .../mob/living/carbon/alien/alien_defense.dm | 7 +- .../mob/living/carbon/carbon_defense.dm | 15 +- .../mob/living/carbon/carbon_update_icons.dm | 6 +- .../mob/living/carbon/human/_species.dm | 78 -------- .../mob/living/carbon/human/human_defense.dm | 9 +- .../living/carbon/human/human_update_icons.dm | 76 ++++++++ .../carbon/human/species_types/felinid.dm | 1 + code/modules/mob/living/living_defense.dm | 4 + .../chemistry/reagents/medicine_reagents.dm | 6 +- .../chemistry/reagents/other_reagents.dm | 10 +- code/modules/surgery/bodyparts/head.dm | 58 +++--- code/modules/surgery/ear_surgery.dm | 4 +- code/modules/surgery/organs/_organ.dm | 84 ++++++--- .../organs/internal/cyberimp/augments_eyes.dm | 20 ++- .../surgery/organs/internal/ears/_ears.dm | 104 ++++------- .../surgery/organs/internal/eyes/_eyes.dm | 167 ++++++++++-------- .../surgery/organs/internal/tongue/_tongue.dm | 31 +--- ...ot_high_luminosity_eyes_light_emissive.png | Bin 1374 -> 989 bytes .../mecha/equipment/weapons/weapons.dm | 4 +- 34 files changed, 422 insertions(+), 388 deletions(-) diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 5e10a84417c..4d2eab10640 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -550,6 +550,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_CULT_HALO "cult_halo" /// Their eyes glow an unnatural red colour. Currently used to set special examine text on humans. Does not guarantee the mob's eyes are coloured red, nor that there is any visible glow on their character sprite. #define TRAIT_UNNATURAL_RED_GLOWY_EYES "unnatural_red_glowy_eyes" +/// Their eyes glow in the dark (aka emissive appearance) +#define TRAIT_LUMINESCENT_EYES "luminescent_eyes" +/// Their eyes reflect light, making them shine faintly in dim, shadowy areas. Kinda like nocturnal animals. +#define TRAIT_REFLECTIVE_EYES "reflective_eyes" /// Their eyes are bloodshot. Currently used to set special examine text on humans. Examine text is overridden by TRAIT_UNNATURAL_RED_GLOWY_EYES. #define TRAIT_BLOODSHOT_EYES "bloodshot_eyes" /// This mob should never close UI even if it doesn't have a client diff --git a/code/__HELPERS/honkerblast.dm b/code/__HELPERS/honkerblast.dm index ea8eea00e59..e76d8178228 100644 --- a/code/__HELPERS/honkerblast.dm +++ b/code/__HELPERS/honkerblast.dm @@ -23,8 +23,7 @@ victim.Stun(100) victim.adjust_stutter(30 SECONDS) victim.set_jitter_if_lower(1000 SECONDS) - var/obj/item/organ/ears/ears = victim.get_organ_slot(ORGAN_SLOT_EARS) - ears?.adjustEarDamage(10, 15) + victim.sound_damage(10, 30 SECONDS) to_chat(victim, "HONK") var/obj/item/clothing/shoes/victim_shoes = victim.get_item_by_slot(ITEM_SLOT_FEET) if(!victim_shoes || victim_shoes.fastening_type == SHOES_SLIPON) @@ -35,13 +34,11 @@ victim.Paralyze(20) victim.Stun(50) victim.set_jitter_if_lower(500 SECONDS) - var/obj/item/organ/ears/ears = victim.get_organ_slot(ORGAN_SLOT_EARS) - ears?.adjustEarDamage(7, 10) + victim.sound_damage(7, 20 SECONDS) to_chat(victim, "HONK") for(var/mob/living/carbon/victim in lightly_honked) victim.Knockdown(20) victim.set_jitter_if_lower(200 SECONDS) - var/obj/item/organ/ears/ears = victim.get_organ_slot(ORGAN_SLOT_EARS) - ears?.adjustEarDamage(4, 5) + victim.sound_damage(4, 10 SECONDS) to_chat(victim, "HONK") diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 22969385579..73bd13ff62c 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -372,6 +372,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_LITERATE" = TRAIT_LITERATE, "TRAIT_LIVERLESS_METABOLISM" = TRAIT_LIVERLESS_METABOLISM, "TRAIT_LOUD_BINARY" = TRAIT_LOUD_BINARY, + "TRAIT_LUMINESCENT_EYES" = TRAIT_LUMINESCENT_EYES, "TRAIT_MADNESS_IMMUNE" = TRAIT_MADNESS_IMMUNE, "TRAIT_MAFIAINITIATE" = TRAIT_MAFIAINITIATE, "TRAIT_MAGICALLY_GIFTED" = TRAIT_MAGICALLY_GIFTED, @@ -498,6 +499,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_REAGENT_SCANNER" = TRAIT_REAGENT_SCANNER, "TRAIT_RECENTLY_BLOCKED_MAGIC" = TRAIT_RECENTLY_BLOCKED_MAGIC, "TRAIT_RECENTLY_TREATED" = TRAIT_RECENTLY_TREATED, + "TRAIT_REFLECTIVE_EYES" = TRAIT_REFLECTIVE_EYES, "TRAIT_REGEN_SHIELD" = TRAIT_REGEN_SHIELD, "TRAIT_RELAYING_ATTACKER" = TRAIT_RELAYING_ATTACKER, "TRAIT_REMOTE_TASTING" = TRAIT_REMOTE_TASTING, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 40241195347..eb54b2259dd 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -170,6 +170,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_LIMBATTACHMENT" = TRAIT_LIMBATTACHMENT, "TRAIT_LITERATE" = TRAIT_LITERATE, "TRAIT_LIVERLESS_METABOLISM" = TRAIT_LIVERLESS_METABOLISM, + "TRAIT_LUMINESCENT_EYES" = TRAIT_LUMINESCENT_EYES, "TRAIT_MADNESS_IMMUNE" = TRAIT_MADNESS_IMMUNE, "TRAIT_MAGICALLY_GIFTED" = TRAIT_MAGICALLY_GIFTED, "TRAIT_MARTIAL_ARTS_IMMUNE" = TRAIT_MARTIAL_ARTS_IMMUNE, @@ -256,6 +257,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_QUICK_CARRY" = TRAIT_QUICK_CARRY, "TRAIT_QUICKER_CARRY" = TRAIT_QUICKER_CARRY, "TRAIT_RADIMMUNE" = TRAIT_RADIMMUNE, + "TRAIT_REFLECTIVE_EYES" = TRAIT_REFLECTIVE_EYES, "TRAIT_REMOTE_TASTING" = TRAIT_REMOTE_TASTING, "TRAIT_RESISTCOLD" = TRAIT_RESISTCOLD, "TRAIT_RESISTHEAT" = TRAIT_RESISTHEAT, diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index bf096a9fb4a..89755c17c4b 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -45,7 +45,6 @@ return var/mob/living/carbon/infected_mob = advanced_disease.affected_mob - var/obj/item/organ/ears/ears = infected_mob.get_organ_slot(ORGAN_SLOT_EARS) switch(advanced_disease.stage) if(3, 4) @@ -53,15 +52,18 @@ to_chat(infected_mob, span_warning("[pick("You hear a ringing in your ear.", "Your ears pop.")]")) if(5) if(causes_permanent_deafness) - if(ears.damage < ears.maxHealth) + if(!HAS_TRAIT_FROM(infected_mob, TRAIT_DEAF, DISEASE_TRAIT)) to_chat(infected_mob, span_userdanger("Your ears pop painfully and start bleeding!")) // Just absolutely murder me man - ears.apply_organ_damage(ears.maxHealth) + infected_mob.adjustOrganLoss(ORGAN_SLOT_EARS, INFINITY) infected_mob.emote("scream") ADD_TRAIT(infected_mob, TRAIT_DEAF, DISEASE_TRAIT) else to_chat(infected_mob, span_userdanger("Your ears pop and begin ringing loudly!")) - ears.deaf = min(20, ears.deaf + 15) + var/obj/item/organ/ears/ears = infected_mob.get_organ_slot(ORGAN_SLOT_EARS) + var/deafness_to_add = min(40 SECONDS - ears.temporary_deafness, 30 SECONDS) + if(deafness_to_add > 0) + ears.adjust_temporary_deafness(deafness_to_add) /datum/symptom/deafness/on_stage_change(datum/disease/advance/advanced_disease) . = ..() diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 2848ec484b3..e0677571599 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -96,7 +96,8 @@ if(4, 5) if(advanced_disease.has_required_infectious_organ(infected_mob, ORGAN_SLOT_EARS)) var/obj/item/organ/ears/ears = infected_mob.get_organ_slot(ORGAN_SLOT_EARS) - ears.adjustEarDamage(-4, -4) + ears?.apply_organ_damage(-4) + ears?.adjust_temporary_deafness(-8 SECONDS) if(!advanced_disease.has_required_infectious_organ(infected_mob, ORGAN_SLOT_EYES)) return diff --git a/code/datums/elements/earhealing.dm b/code/datums/elements/earhealing.dm index ac69e8cc18e..33e599eddbe 100644 --- a/code/datums/elements/earhealing.dm +++ b/code/datums/elements/earhealing.dm @@ -27,8 +27,9 @@ for(var/i in user_by_item) var/mob/living/carbon/user = user_by_item[i] var/obj/item/organ/ears/ears = user.get_organ_slot(ORGAN_SLOT_EARS) - if(!ears || !ears.damage || (ears.organ_flags & ORGAN_FAILING) || IS_ROBOTIC_ORGAN(ears)) + if(!ears || (ears.organ_flags & ORGAN_FAILING) || IS_ROBOTIC_ORGAN(ears)) continue - ears.deaf = max(ears.deaf - 0.25 * seconds_per_tick, (ears.damage < ears.maxHealth ? 0 : 1)) // Do not clear deafness if our ears are too damaged + + ears.adjust_temporary_deafness(-0.5 SECONDS * seconds_per_tick) ears.apply_organ_damage(-0.025 * seconds_per_tick) CHECK_TICK diff --git a/code/modules/antagonists/changeling/powers/shriek.dm b/code/modules/antagonists/changeling/powers/shriek.dm index 09ebee22d59..2a704e0baac 100644 --- a/code/modules/antagonists/changeling/powers/shriek.dm +++ b/code/modules/antagonists/changeling/powers/shriek.dm @@ -18,9 +18,7 @@ if(iscarbon(M)) var/mob/living/carbon/C = M if(!IS_CHANGELING(C)) - var/obj/item/organ/ears/ears = C.get_organ_slot(ORGAN_SLOT_EARS) - if(ears) - ears.adjustEarDamage(0, 30) + C.sound_damage(deafen = 30 SECONDS) C.adjust_confusion(25 SECONDS) C.set_jitter_if_lower(100 SECONDS) else diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm index 5ffe055de80..59cd6ccc067 100644 --- a/code/modules/antagonists/heretic/items/corrupted_organs.dm +++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm @@ -264,8 +264,7 @@ hearer.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic) hearer.emote("scream") hearer.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus) - var/obj/item/organ/ears/regret = hearer.get_organ_slot(ORGAN_SLOT_EARS) - regret?.adjustEarDamage(10,20) + hearer.sound_damage(10, 40 SECONDS) return "[owner.p_Their()] lungs emit [span_hypnophrase(breath_noise)]" /// It's full of worms diff --git a/code/modules/antagonists/heretic/magic/mind_gate.dm b/code/modules/antagonists/heretic/magic/mind_gate.dm index 81215979468..cf6a48f4a62 100644 --- a/code/modules/antagonists/heretic/magic/mind_gate.dm +++ b/code/modules/antagonists/heretic/magic/mind_gate.dm @@ -44,16 +44,14 @@ /// The duration of these effects are based on sanity, mainly for flavor but also to make it a weaker alpha strike var/maximum_duration = 15 SECONDS - var/mind_gate_duration = ((SANITY_MAXIMUM - cast_on.mob_mood.sanity) / (SANITY_MAXIMUM - SANITY_INSANE)) * maximum_duration + var/mind_gate_duration = ((SANITY_MAXIMUM - cast_on.mob_mood.sanity) / (SANITY_MAXIMUM - SANITY_INSANE)) * maximum_duration + 1 SECONDS to_chat(cast_on, span_warning("Your eyes cry out in pain, your ears bleed and your lips seal! THE MOON SMILES UPON YOU!")) - cast_on.adjust_temp_blindness(mind_gate_duration + 1 SECONDS) - cast_on.set_eye_blur_if_lower(mind_gate_duration + 2 SECONDS) + cast_on.adjust_temp_blindness(mind_gate_duration) + cast_on.set_eye_blur_if_lower(mind_gate_duration + 1 SECONDS) - var/obj/item/organ/ears/ears = cast_on.get_organ_slot(ORGAN_SLOT_EARS) - //adjustEarDamage takes deafness duration parameter in one unit per two seconds, instead of the normal time, so we divide by two seconds - ears?.adjustEarDamage(0, (mind_gate_duration + 1 SECONDS) / (2 SECONDS)) + cast_on.sound_damage(0, mind_gate_duration) - cast_on.adjust_silence(mind_gate_duration + 1 SECONDS) + cast_on.adjust_silence(mind_gate_duration) cast_on.add_mood_event("moon_smile", /datum/mood_event/moon_smile) // Only knocksdown if the target has a low enough sanity diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 61285e3801b..03f1df6a268 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -740,7 +740,7 @@ else var/obj/item/organ/ears/has_ears = human_examined.get_organ_slot(ORGAN_SLOT_EARS) if(has_ears) - if(has_ears.deaf) + if(has_ears.temporary_deafness) final_message += "\tDamaged eardrums in [examining.p_their()] ear canals." else final_message += "\tA set of [has_ears.damage ? "" : "healthy "][has_ears.name]." diff --git a/code/modules/fishing/fish/types/rift.dm b/code/modules/fishing/fish/types/rift.dm index ba6a0142ed4..530a32ced23 100644 --- a/code/modules/fishing/fish/types/rift.dm +++ b/code/modules/fishing/fish/types/rift.dm @@ -815,7 +815,7 @@ /obj/item/organ/ears/babbelfish/proc/on_drain_magic(mob/user) to_chat(user, span_noticealien("Your [src] pop as they protect your mind from psychic phenomena!")) - adjustEarDamage(ddeaf = 20) + adjust_temporary_deafness(40 SECONDS) /obj/item/organ/ears/babbelfish/proc/on_expire(mob/user) to_chat(user, span_noticealien("Your [src] suddenly burst apart!")) diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm index 68fdb431a96..cc4469f8971 100644 --- a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm +++ b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm @@ -83,8 +83,7 @@ if(HAS_TRAIT(current_target, TRAIT_DEAF)) return - var/obj/item/organ/ears/target_ears = current_target.get_organ_slot(ORGAN_SLOT_EARS) - target_ears?.adjustEarDamage(0, 5) + sound_damage(deafen = 10 SECONDS) /mob/living/basic/bot/honkbot/ui_data(mob/user) var/list/data = ..() diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm index 3505f7a368a..c6b5f574532 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm @@ -31,14 +31,10 @@ . = ..() animate_pulse() -/obj/item/organ/legion_tumour/apply_organ_damage(damage_amount, maximum, required_organ_flag) - var/was_failing = organ_flags & ORGAN_FAILING - . = ..() - if (was_failing != (organ_flags & ORGAN_FAILING)) - animate_pulse() +/obj/item/organ/legion_tumour/on_begin_failure() + animate_pulse() -/obj/item/organ/legion_tumour/set_organ_damage(damage_amount, required_organ_flag) - . = ..() +/obj/item/organ/legion_tumour/on_failure_recovery() animate_pulse() /// Do a heartbeat animation depending on if we're failing or not diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 1a35fe9cedf..a1e5affe573 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -25,8 +25,6 @@ var/mob/living/brain/brainmob = null /// If it's a fake brain with no brainmob assigned. Feedback messages will be faked as if it does have a brainmob. See changelings & dullahans. var/decoy_override = FALSE - /// Two variables necessary for calculating whether we get a brain trauma or not - var/damage_delta = 0 var/list/datum/brain_trauma/traumas = list() @@ -345,34 +343,42 @@ owner.investigate_log("has been killed by brain damage.", INVESTIGATE_DEATHS) owner.death() -/obj/item/organ/brain/check_damage_thresholds(mob/M) +/obj/item/organ/brain/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag = NONE) . = ..() + var/delta_dam = . //for the sake of clarity + if(delta_dam <= 0 || damage < BRAIN_DAMAGE_MILD) + return + + if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit + gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE) + + var/is_boosted = (owner && HAS_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST)) + if(damage < BRAIN_DAMAGE_SEVERE) + return + if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% + if(prob(20 + (is_boosted * 30))) + gain_trauma_type(BRAIN_TRAUMA_SPECIAL, is_boosted ? TRAUMA_RESILIENCE_SURGERY : null, natural_gain = TRUE) + else + gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE) + +/obj/item/organ/brain/check_damage_thresholds() + . = ..() + if(!owner) + return // If we crossed blinking brain damage thresholds either way, update our blinking - if (owner && ((prev_damage > BRAIN_DAMAGE_ASYNC_BLINKING && damage < BRAIN_DAMAGE_ASYNC_BLINKING) || (prev_damage < BRAIN_DAMAGE_ASYNC_BLINKING && damage > BRAIN_DAMAGE_ASYNC_BLINKING))) + if((prev_damage >= BRAIN_DAMAGE_ASYNC_BLINKING && damage < BRAIN_DAMAGE_ASYNC_BLINKING) || (prev_damage < BRAIN_DAMAGE_ASYNC_BLINKING && damage >= BRAIN_DAMAGE_ASYNC_BLINKING)) var/obj/item/organ/eyes/eyes = owner.get_organ_slot(ORGAN_SLOT_EYES) if(eyes?.blink_animation) eyes.animate_eyelids(owner) + if(damage >= 60 && prev_damage < 60) + owner.add_mood_event("brain_damage", /datum/mood_event/brain_damage) + else if(prev_damage >= 60 && damage < 60) + owner.clear_mood_event("brain_damage") // If we're not more injured than before, return without gambling for a trauma if(damage <= prev_damage) return - damage_delta = damage - prev_damage - if(damage > BRAIN_DAMAGE_MILD) - if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit - gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE) - - var/is_boosted = (owner && HAS_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST)) - if(damage > BRAIN_DAMAGE_SEVERE) - if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% - if(prob(20 + (is_boosted * 30))) - gain_trauma_type(BRAIN_TRAUMA_SPECIAL, is_boosted ? TRAUMA_RESILIENCE_SURGERY : null, natural_gain = TRUE) - else - gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE) - - if (!owner || owner.stat > UNCONSCIOUS) - return - // Conscious or soft-crit var/brain_message if(prev_damage < BRAIN_DAMAGE_MILD && damage >= BRAIN_DAMAGE_MILD) @@ -665,15 +671,6 @@ amount_cured++ return amount_cured -/obj/item/organ/brain/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag = NONE) - . = ..() - if(!owner) - return FALSE - if(damage >= 60) - owner.add_mood_event("brain_damage", /datum/mood_event/brain_damage) - else - owner.clear_mood_event("brain_damage") - /// This proc lets the mob's brain decide what bodypart to attack with in an unarmed strike. /obj/item/organ/brain/proc/get_attacking_limb(mob/living/carbon/human/target) var/obj/item/bodypart/arm/active_hand = owner.get_active_hand() diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm index e5c5731dc03..e0892cbaf8e 100644 --- a/code/modules/mob/living/carbon/alien/alien_defense.dm +++ b/code/modules/mob/living/carbon/alien/alien_defense.dm @@ -71,22 +71,19 @@ In all, this is a lot like the monkey code. /N if(!. || QDELETED(src)) return FALSE - var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) switch (severity) if (EXPLODE_DEVASTATE) gib(DROP_ALL_REMAINS) if (EXPLODE_HEAVY) take_overall_damage(60, 60) - if(ears) - ears.adjustEarDamage(30,120) + sound_damage(30, 240 SECONDS) if(EXPLODE_LIGHT) take_overall_damage(30,0) if(prob(50)) Unconscious(20) - if(ears) - ears.adjustEarDamage(15,60) + sound_damage(15, 120 SECONDS) return TRUE diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 518ba53e769..ed9c794ce54 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -16,6 +16,17 @@ if(isclothing(wear_mask)) //Mask . += wear_mask.flash_protect +/mob/living/carbon/sound_damage(damage, deafen) + if(HAS_TRAIT(src, TRAIT_GODMODE)) + return + var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) + if(QDELETED(ears)) + return + if(damage) + ears.apply_organ_damage(damage * ears.damage_multiplier) + if(deafen) + ears.adjust_temporary_deafness(deafen) + /mob/living/carbon/get_ear_protection() . = ..() if(HAS_TRAIT(src, TRAIT_DEAF)) @@ -523,8 +534,8 @@ if(ears && (deafen_pwr || damage_pwr)) var/ear_damage = damage_pwr * effect_amount - var/deaf = deafen_pwr * effect_amount - ears.adjustEarDamage(ear_damage,deaf) + var/deaf = deafen_pwr * effect_amount * 2 SECONDS + sound_damage(ear_damage, deaf) . = effect_amount //how soundbanged we are SEND_SOUND(src, sound('sound/items/weapons/flash_ring.ogg',0,1,0,250)) diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 78f5d38205b..8a5f9abd050 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -20,7 +20,7 @@ if(OFFSET_HEAD) update_worn_head() if(OFFSET_FACE) - dna?.species?.update_face_offset(src) // updates eye and lipstick icon + update_face_offset() // updates eye and lipstick icon update_worn_mask() if(OFFSET_BELT) update_worn_belt() @@ -49,7 +49,6 @@ SEND_SIGNAL(src, COMSIG_CARBON_REMOVE_OVERLAY, cache_index, I) /mob/living/carbon/update_body(is_creating = FALSE) - dna?.species.handle_body(src) update_body_parts(is_creating) /mob/living/carbon/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) @@ -504,6 +503,9 @@ apply_overlay(BODYPARTS_LAYER) +/mob/living/carbon/proc/update_face_offset() + return + ///////////////////////// // Limb Icon Cache 2.0 // ///////////////////////// diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 485dc3ca0f8..6db14908e70 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -485,84 +485,6 @@ GLOBAL_LIST_EMPTY(features_by_species) human.living_flags &= ~STOP_OVERLAY_UPDATE_BODY_PARTS -/** - * Handles the body of a human - * - * Handles lipstick, having no eyes, eye color, undergarnments like underwear, undershirts, and socks, and body layers. - * Arguments: - * * species_human - Human, whoever we're handling the body for - */ -/datum/species/proc/handle_body(mob/living/carbon/human/species_human) - species_human.remove_overlay(BODY_LAYER) - species_human.remove_overlay(EYES_LAYER) - - if(HAS_TRAIT(species_human, TRAIT_INVISIBLE_MAN)) - return - - if(!HAS_TRAIT(species_human, TRAIT_HUSK)) - var/obj/item/bodypart/head/noggin = species_human.get_bodypart(BODY_ZONE_HEAD) - if(noggin?.head_flags & HEAD_EYESPRITES) - // 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 = species_human.get_organ_slot(ORGAN_SLOT_EYES) - if(eye_organ) - eye_organ.refresh(call_update = FALSE) - species_human.overlays_standing[EYES_LAYER] = eye_organ.generate_body_overlay(species_human) - species_human.apply_overlay(EYES_LAYER) - - if(HAS_TRAIT(species_human, TRAIT_NO_UNDERWEAR)) - return - - // Underwear, Undershirts & Socks - var/list/standing = list() - if(species_human.underwear) - var/datum/sprite_accessory/underwear/underwear = SSaccessories.underwear_list[species_human.underwear] - var/mutable_appearance/underwear_overlay - if(underwear) - if(species_human.dna.species.sexes && species_human.physique == FEMALE && (underwear.gender == MALE)) - underwear_overlay = mutable_appearance(wear_female_version(underwear.icon_state, underwear.icon, FEMALE_UNIFORM_FULL), layer = -BODY_LAYER) - else - underwear_overlay = mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER) - if(!underwear.use_static) - underwear_overlay.color = species_human.underwear_color - standing += underwear_overlay - - if(species_human.undershirt) - var/datum/sprite_accessory/undershirt/undershirt = SSaccessories.undershirt_list[species_human.undershirt] - if(undershirt) - var/mutable_appearance/working_shirt - if(species_human.dna.species.sexes && species_human.physique == FEMALE) - working_shirt = mutable_appearance(wear_female_version(undershirt.icon_state, undershirt.icon), layer = -BODY_LAYER) - else - working_shirt = mutable_appearance(undershirt.icon, undershirt.icon_state, layer = -BODY_LAYER) - standing += working_shirt - - if(species_human.socks && species_human.num_legs >= 2 && !(species_human.bodyshape & BODYSHAPE_DIGITIGRADE)) - var/datum/sprite_accessory/socks/socks = SSaccessories.socks_list[species_human.socks] - if(socks) - standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER) - - if(standing.len) - species_human.overlays_standing[BODY_LAYER] = standing - - species_human.apply_overlay(BODY_LAYER) - -/// Updates face (as of now, only eye) offsets -/datum/species/proc/update_face_offset(mob/living/carbon/human/species_human) - var/list/eye_overlays = species_human.overlays_standing[EYES_LAYER] - species_human.remove_overlay(EYES_LAYER) - - if(HAS_TRAIT(species_human, TRAIT_INVISIBLE_MAN) || HAS_TRAIT(species_human, TRAIT_HUSK) || !length(eye_overlays)) - return - - var/obj/item/bodypart/head/noggin = species_human.get_bodypart(BODY_ZONE_HEAD) - for (var/mutable_appearance/overlay as anything in eye_overlays) - overlay.pixel_w = 0 - overlay.pixel_z = 0 - noggin.worn_face_offset.apply_offset(overlay) - - species_human.overlays_standing[EYES_LAYER] = eye_overlays - species_human.apply_overlay(EYES_LAYER) - // This exists so sprite accessories can still be per-layer without having to include that layer's // number in their sprite name, which causes issues when those numbers change. /datum/species/proc/mutant_bodyparts_layertext(layer) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index d4cac5e8c9a..95bb123c67a 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -271,7 +271,6 @@ //200 max knockdown for EXPLODE_HEAVY //160 max knockdown for EXPLODE_LIGHT - var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) switch (severity) if (EXPLODE_DEVASTATE) if(bomb_armor < EXPLODE_GIB_THRESHOLD) //gibs the mob if their bomb armor is lower than EXPLODE_GIB_THRESHOLD @@ -299,8 +298,8 @@ brute_loss = 30*(2 - round(bomb_armor*0.01, 0.05)) burn_loss = brute_loss //damage gets reduced from 120 to up to 60 combined brute+burn damage_clothes(200 - bomb_armor, BRUTE, BOMB) - if (ears && !HAS_TRAIT_FROM_ONLY(src, TRAIT_DEAF, EAR_DAMAGE)) - ears.adjustEarDamage(30, 120) + if (!HAS_TRAIT_FROM(src, TRAIT_DEAF, EAR_DAMAGE)) + sound_damage(30, 240 SECONDS) Unconscious(20) //short amount of time for follow up attacks against elusive enemies like wizards Knockdown(200 - (bomb_armor * 1.6)) //between ~4 and ~20 seconds of knockdown depending on bomb armor @@ -309,8 +308,8 @@ if(bomb_armor) brute_loss = 15*(2 - round(bomb_armor*0.01, 0.05)) damage_clothes(max(50 - bomb_armor, 0), BRUTE, BOMB) - if (ears && !HAS_TRAIT_FROM_ONLY(src, TRAIT_DEAF, EAR_DAMAGE)) - ears.adjustEarDamage(15,60) + if (!HAS_TRAIT_FROM(src, TRAIT_DEAF, EAR_DAMAGE)) + sound_damage(15, 120 SECONDS) Knockdown(160 - (bomb_armor * 1.6)) //100 bomb armor will prevent knockdown altogether take_overall_damage(brute_loss,burn_loss) 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 044315bd2b8..64698c9422a 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -859,6 +859,82 @@ generate/load female uniform sprites matching all previously decided variables observers = null break +/mob/living/carbon/human/update_body(is_creating = FALSE) + update_eyes() + update_underwear() + return ..() + +/mob/living/carbon/human/proc/update_underwear() + remove_overlay(BODY_LAYER) + if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) + return + // Underwear, Undershirts & Socks + var/list/standing = list() + if(underwear) + var/datum/sprite_accessory/underwear/undie_accessory = SSaccessories.underwear_list[underwear] + var/mutable_appearance/underwear_overlay + if(undie_accessory) + if(dna.species.sexes && physique == FEMALE && (undie_accessory.gender == MALE)) + underwear_overlay = mutable_appearance(wear_female_version(undie_accessory.icon_state, undie_accessory.icon, FEMALE_UNIFORM_FULL), layer = -BODY_LAYER) + else + underwear_overlay = mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, -BODY_LAYER) + if(!undie_accessory.use_static) + underwear_overlay.color = underwear_color + standing += underwear_overlay + + if(undershirt) + var/datum/sprite_accessory/undershirt/undie_accessory = SSaccessories.undershirt_list[undershirt] + if(undie_accessory) + var/mutable_appearance/working_shirt + if(dna.species.sexes && physique == FEMALE) + working_shirt = mutable_appearance(wear_female_version(undie_accessory.icon_state, undie_accessory.icon), layer = -BODY_LAYER) + else + working_shirt = mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, layer = -BODY_LAYER) + standing += working_shirt + + if(socks && num_legs >= 2 && !(bodyshape & BODYSHAPE_DIGITIGRADE)) + var/datum/sprite_accessory/socks/undie_accessory = SSaccessories.socks_list[socks] + if(undie_accessory) + standing += mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, -BODY_LAYER) + + if(standing.len) + overlays_standing[BODY_LAYER] = standing + + apply_overlay(BODY_LAYER) + +/mob/living/carbon/human/proc/update_eyes() + 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) + +/// Updates face (as of now, only eye) offsets +/mob/living/carbon/human/update_face_offset() + var/list/eye_overlays = overlays_standing[EYES_LAYER] + + if(HAS_TRAIT(src, TRAIT_INVISIBLE_MAN) || HAS_TRAIT(src, TRAIT_HUSK) || !length(eye_overlays)) + return + + remove_overlay(EYES_LAYER) + + var/obj/item/bodypart/head/noggin = get_bodypart(BODY_ZONE_HEAD) + for (var/mutable_appearance/overlay as anything in eye_overlays) + overlay.pixel_w = 0 + overlay.pixel_z = 0 + noggin.worn_face_offset.apply_offset(overlay) + + overlays_standing[EYES_LAYER] = eye_overlays + apply_overlay(EYES_LAYER) + + // Only renders the head of the human /mob/living/carbon/human/proc/update_body_parts_head_only(update_limb_data) if(!dna?.species) diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index e288bb6b146..f15f65c1972 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -6,6 +6,7 @@ mutantbrain = /obj/item/organ/brain/felinid mutanttongue = /obj/item/organ/tongue/cat mutantears = /obj/item/organ/ears/cat + mutanteyes = /obj/item/organ/eyes/felinid mutant_organs = list( /obj/item/organ/tail/cat = "Cat", ) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 3496ffabf3e..821ad98dd78 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -35,6 +35,10 @@ /mob/living/proc/get_eye_protection() return 0 +///A easy to use proc to apply both organ damage and temporary deafness at once, so you don't have to get the ears everytime. +/mob/living/proc/sound_damage(damage, deafen) + return + //this returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears) /mob/living/proc/get_ear_protection() var/turf/current_turf = get_turf(src) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 9a01339012d..9dda07ae412 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -829,7 +829,7 @@ if(eyes.apply_organ_damage(-2 * REM * seconds_per_tick * normalise_creation_purity(), required_organ_flag = affected_organ_flags)) . = UPDATE_MOB_HEALTH // If our eyes are seriously damaged, we have a probability of causing eye blur while healing depending on purity - if(eyes.damaged && IS_ORGANIC_ORGAN(eyes) && SPT_PROB(16 - min(normalized_purity * 6, 12), seconds_per_tick)) + if(eyes.damage >= eyes.low_threshold && IS_ORGANIC_ORGAN(eyes) && SPT_PROB(16 - min(normalized_purity * 6, 12), seconds_per_tick)) // While healing, gives some eye blur if(affected_mob.is_blind_from(EYE_DAMAGE)) to_chat(affected_mob, span_warning("Your vision slowly returns...")) @@ -929,7 +929,9 @@ var/obj/item/organ/ears/ears = affected_mob.get_organ_slot(ORGAN_SLOT_EARS) if(!ears) return - ears.adjustEarDamage(-4 * REM * seconds_per_tick * normalise_creation_purity(), -4 * REM * seconds_per_tick * normalise_creation_purity()) + var/multiplier = REM * seconds_per_tick * normalise_creation_purity() + ears.apply_organ_damage(-4 * multiplier) + ears.adjust_temporary_deafness(-8 * multiplier) return UPDATE_MOB_HEALTH /datum/reagent/medicine/inacusiate/on_mob_delete(mob/living/affected_mob) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 48d16e3644c..9d6baed2e51 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -3230,8 +3230,7 @@ RegisterSignal(affected_human, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_organ_removed)) var/obj/item/organ/eyes/eyes = affected_human.get_organ_slot(ORGAN_SLOT_EYES) if (eyes && !IS_ROBOTIC_ORGAN(eyes)) - prev_ignore_lighting = eyes.overlay_ignore_lighting - eyes.overlay_ignore_lighting = TRUE + ADD_TRAIT(affected_human, TRAIT_LUMINESCENT_EYES, REF(src)) /datum/reagent/luminescent_fluid/on_mob_end_metabolize(mob/living/affected_mob) . = ..() @@ -3243,7 +3242,7 @@ affected_human.remove_eye_color(EYE_COLOR_LUMINESCENT_PRIORITY) var/obj/item/organ/eyes/eyes = affected_human.get_organ_slot(ORGAN_SLOT_EYES) if (eyes && !IS_ROBOTIC_ORGAN(eyes) && !overdosed) - eyes.overlay_ignore_lighting = prev_ignore_lighting + REMOVE_TRAIT(affected_human, TRAIT_LUMINESCENT_EYES, REF(src)) /datum/reagent/luminescent_fluid/on_mob_life(mob/living/affected_mob, seconds_per_tick, times_fired) . = ..() @@ -3262,14 +3261,13 @@ SIGNAL_HANDLER if (istype(new_eyes) && !IS_ROBOTIC_ORGAN(new_eyes)) - prev_ignore_lighting = new_eyes.overlay_ignore_lighting - new_eyes.overlay_ignore_lighting = TRUE + ADD_TRAIT(source, TRAIT_LUMINESCENT_EYES, REF(src)) /datum/reagent/luminescent_fluid/proc/on_organ_removed(mob/living/source, obj/item/organ/eyes/old_eyes) SIGNAL_HANDLER if (istype(old_eyes) && !IS_ROBOTIC_ORGAN(old_eyes) && !overdosed) - old_eyes.overlay_ignore_lighting = prev_ignore_lighting + REMOVE_TRAIT(source, TRAIT_LUMINESCENT_EYES, REF(src)) /datum/reagent/luminescent_fluid/overdose_start(mob/living/affected_mob) . = ..() diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 79524d43a12..5dc46ddea0c 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -176,35 +176,37 @@ . += get_hair_and_lips_icon(dropped) // We need to get the eyes if we are dropped (ugh) - if(dropped) - var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in src - // This is a bit of copy/paste code from eyes.dm:generate_body_overlay - if(eyes?.eye_icon_state && (head_flags & HEAD_EYESPRITES)) - var/image/eye_left = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH) - var/image/eye_right = image('icons/mob/human/human_face.dmi', "[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 - if(eyes.overlay_ignore_lighting) - eye_left.overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, src, alpha = eye_left.alpha) - eye_right.overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, src, alpha = eye_right.alpha) - else if(blocks_emissive != EMISSIVE_BLOCK_NONE) - var/atom/location = loc || owner || src - eye_left.overlays += emissive_blocker(eye_left.icon, eye_left.icon_state, location, alpha = eye_left.alpha) - eye_right.overlays += emissive_blocker(eye_right.icon, eye_right.icon_state, location, alpha = eye_right.alpha) - if(worn_face_offset) - worn_face_offset.apply_offset(eye_left) - worn_face_offset.apply_offset(eye_right) - . += eye_left - . += eye_right - else if(!eyes && (head_flags & HEAD_EYEHOLES)) - var/image/no_eyes = image('icons/mob/human/human_face.dmi', "eyes_missing", -EYES_LAYER, SOUTH) - worn_face_offset?.apply_offset(no_eyes) - . += no_eyes + if(!dropped) + return + var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in src + // This is a bit of copy/paste code from eyes.dm:generate_body_overlay + if(eyes?.eye_icon_state && (head_flags & HEAD_EYESPRITES)) + var/image/eye_left = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH) + var/image/eye_right = image('icons/mob/human/human_face.dmi', "[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 - return + var/list/emissive_overlays = eyes.get_emissive_overlays(eye_left, eye_right, src) + if(length(emissive_overlays)) + eye_left.overlays += emissive_overlays[1] //left first + eye_right.overlays += emissive_overlays[2] //right second + else if(blocks_emissive != EMISSIVE_BLOCK_NONE) + var/atom/location = loc || owner || src + eye_left.overlays += emissive_blocker(eye_left.icon, eye_left.icon_state, location, alpha = eye_left.alpha) + eye_right.overlays += emissive_blocker(eye_right.icon, eye_right.icon_state, location, alpha = eye_right.alpha) + + if(worn_face_offset) + worn_face_offset.apply_offset(eye_left) + worn_face_offset.apply_offset(eye_right) + . += eye_left + . += eye_right + else if(!eyes && (head_flags & HEAD_EYEHOLES)) + var/image/no_eyes = image('icons/mob/human/human_face.dmi', "eyes_missing", -EYES_LAYER, SOUTH) + worn_face_offset?.apply_offset(no_eyes) + . += no_eyes /obj/item/bodypart/head/Initialize(mapload) . = ..() diff --git a/code/modules/surgery/ear_surgery.dm b/code/modules/surgery/ear_surgery.dm index 9f41949f763..41afe23eb5f 100644 --- a/code/modules/surgery/ear_surgery.dm +++ b/code/modules/surgery/ear_surgery.dm @@ -45,8 +45,10 @@ span_notice("[user] completes the surgery on [target]'s ears."), ) display_pain(target, "Your head swims, but it seems like you can feel your hearing coming back!") - target_ears.deaf = (20) //deafness works off ticks, so this should work out to about 30-40s target_ears.set_organ_damage(0) + ///makes you temporarily deaf for a duration post-surgery + var/deaf_change = 40 SECONDS - target_ears.temporary_deafness + target_ears.adjust_temporary_deafness(deaf_change) return ..() /datum/surgery_step/fix_ears/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 7e969ee4843..3b2edfea15d 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -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) diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm index 9763b22e4b0..19ff1031d79 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm @@ -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 diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm index da1d7faff63..e50961a5d8b 100644 --- a/code/modules/surgery/organs/internal/ears/_ears.dm +++ b/code/modules/surgery/organs/internal/ears/_ears.dm @@ -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) . = ..() diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index 5d1d37ca9f9..9306abf8f63 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -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" diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index 9a540532388..34c2b7df085 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -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) diff --git a/code/modules/unit_tests/screenshots/screenshot_high_luminosity_eyes_light_emissive.png b/code/modules/unit_tests/screenshots/screenshot_high_luminosity_eyes_light_emissive.png index 912f16e6839b256d62904bd65e3012421dac7e8e..7445f2cfffa1fb222ead7921d882fd36488a711a 100644 GIT binary patch delta 914 zcmV;D18w}?3f%{PiBL{Q4GJ0x0000DNk~Le0000$0000$2m=5B0G+pi>;M1&V^B;~ zMZmzo+Mbg6(Z>Ja(dxgnl)tvgZNCTbB#DWM;jgB!u&}&)aWyzWj*pTW8X;C%W3#if zb9R4sdxKF=TM-l#A|@sn92}>rw$#_(iHo8mBq=Q|Gt12)(eCc<9UUa4skU@+f`IjM zng9R*0d!JMQvg8b*k%9#0A-QCT7Nd=QdV&Fa{-$O0Gj_84mqJ=L;wH+w@E}nR9J=W zmSJ<-APj~9(>4TTU?gehrcVCRR<_U<0t5b_&ExcBv=KN6A~gCz64*I|9gLH<(9ZC3JAv<&cYg+dUJTK* z0{C85fPw4}1!IluuL6;CjwlFxrofA4$v=jaZBF>?`^WH&SBQOuOfeiC_;ogagwSw>6R68gpy159hFiY-n!_-+91oiF zB@-2*T)b-@Ic>e>3G|dR>3Gntc_1o3o#1^s^*AiF;s_E)ptQ(I z;=DMCl)9(nk2wF<8M|~&$xngGhXO_Q4#~R!qA#5@@DB;xF}N#Guz$9<&+%n>eO(m4 zEuZ4&x3}}Ms&(OuE?<|*m$l2)<$S(euIhT>i%;+`v-F(-;4U}g0f6U&(Ew5XC%HTN zjSmKh=>h%!Q8ds`W*nl|yNOMN5d6f{3gxI;?6tLn9gqTRQC>v>rT5m_Q4WCR83`4; zWIU{c4LoozDDGW|t%X8=`pzqG8W>i>25@6@e zC^l0Hw=J-evBf}u=3KbZ!@P~4wtc!60d12)6G5l~!~qNi5CT&-Ie-2@qSw zc9P@fUwBMqBk3qt2BX3=!bgg<6buQPd!KODHip@uv^lI22WJcc{&;+Tks^3`J|5ZT zAk3;Idp&O7Oto(Zq+bd{|C@sB&k95Tsxb2ZFTesQN$W^pML`+~)aE}>ShpErWWdA0 oRl?WX3?Z+r`O5;g8R7T!1Iai(IxrpASpWb407*qoM6N<$g6ih9WdHyG delta 1302 zcmV+x1?l?T2i^*QiBL{Q4GJ0x0000DNk~Le0001B0001B2m=5B0M$l^3IG5Afly3T zMZmzo+Mbg6(Z>Ja(dxgn?YhLk*q^+6ag@Kd$Zfv6&!q?NB*|^R){~uyiHYH_rt-zj z#p9^3u&^~aL5`4<8X6%{Pg`_%fL2;#v$L~zdxH@a6e1=-CKwzXr>eHp*WZbYq8%P2 zBqS;=Ei=o_(eCcK~!jg?O5A(+At85WCxL^ zjZ<`Wfus-u&Hw*kcWp>cu~w@h`p`DR2@pn}u`FbER)`P$9;B4De0G3Tjmiy>P19s~ zl^-D68#Qa`0kVbTb3E!dH=5$-^5*t`)7?xef>%<(43(Ud2F<)tH=n;y{C%e$ny;#( zPEmFW(cM!5THeD6JT$}<7D9Il9^?#>SI93Yej(d7vk2e-0<=CTs8-SXFvmsPwzPt= zw(B};4Z%BW5Y`cg8|Oo?f!2?v4{hk-HLV}bH-w2v2Oy(!uD5~E4>tC|+P;r}$uYs2 z7(qWqlj1?&I1nHmKp6~r(1+K=7~mZ^08C25#pt~4tw)ozL2o07csMxPK4H*X;|yE_ zcuw$0fPoLrrVZeD4+r)EP5?)qD-3*u^Mt81I zuR&(LPVs25gh6_>dVEUmWi18HpvM^qIURE3EyW95CoOGHZ{=Z;a|1Yk9?u|0<99va z7}{XqGYUTB^oPwP5}WAV0IrUo?t%ZwfDx!Rz^y($UB=^x5H=*c-9A5W1wL&r;&BL) zl*8`jWe4PDi*J(rW;^iPO_E292k+D5VYfRR_F{j)Hy7}Tkt7U0PzJWXugwooedhu~ zZGO*FV7En3ELKa-qUP- ze!%h{@CWmQ&~Psf9*UDBTbUmaSVB7oMS4o3GC!R3p)PPHK%@AH=afmpXX?uaF;34(SCFSI7_J z7*hWV`GGg6tiJz!gX;VMD|Ay-S>!!)(hXMnp|DVqf zC1CphOn%_N_W!y3!0B`Sf5GGjUO(Ue7fgO&!1w<}K0mMqT>pPGpz{N3!1ez}12R9b zVEg|^11dih7%=_+lL3(*Sg`$nkHONK") M.SetSleeping(0) M.adjust_stutter(40 SECONDS) - var/obj/item/organ/ears/ears = M.get_organ_slot(ORGAN_SLOT_EARS) - if(ears) - ears.adjustEarDamage(0, 30) + M.sound_damage(deafen = 30 SECONDS) M.Paralyze(60) if(prob(30)) M.Stun(200)