diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index 80c6f89a108..ee157b9d51a 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -39,6 +39,9 @@ /// Called from bodypart changing owner, which could be on attach or detachment. Either argument can be null. (mob/living/carbon/new_owner, mob/living/carbon/old_owner) #define COMSIG_BODYPART_CHANGED_OWNER "bodypart_changed_owner" +/// Called from /obj/item/bodypart/proc/update_part_wound_overlay() +#define COMSIG_BODYPART_UPDATE_WOUND_OVERLAY "bodypart_update_wound_overlay" + #define COMPONENT_PREVENT_WOUND_OVERLAY_UPDATE (1 << 0) /// Called from update_health_hud, whenever a bodypart is being updated on the health doll #define COMSIG_BODYPART_UPDATING_HEALTH_HUD "bodypart_updating_health_hud" diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm index 237e956ca7f..b930c3711de 100644 --- a/code/__DEFINES/surgery.dm +++ b/code/__DEFINES/surgery.dm @@ -31,6 +31,11 @@ /// An organ that is ostensibly dangerous when inside a body #define ORGAN_HAZARDOUS (1<<12) +/// Scarring on the right eye +#define RIGHT_EYE_SCAR (1<<0) +/// Scarring on the left eye +#define LEFT_EYE_SCAR (1<<1) + /// Helper to figure out if a limb is organic #define IS_ORGANIC_LIMB(limb) (limb.bodytype & BODYTYPE_ORGANIC) /// Helper to figure out if a limb is robotic diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 690dabd42c5..400359ad06d 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -501,6 +501,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_USER_SCOPED "user_scoped" /// Mob is unable to feel pain #define TRAIT_ANALGESIA "analgesia" +/// Mob has a scar on their left/right eye +#define TRAIT_RIGHT_EYE_SCAR "right_eye_scar" +#define TRAIT_LEFT_EYE_SCAR "left_eye_scar" /// Trait added when a revenant is visible. #define TRAIT_REVENANT_REVEALED "revenant_revealed" diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 8b3ac10f950..11bbf07f626 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -231,6 +231,7 @@ #define SPEED_TRAIT "speed_trait" /// Trait given to mobs that have been autopsied #define AUTOPSY_TRAIT "autopsy_trait" +#define EYE_SCARRING_TRAIT "eye_scarring_trait" ///From the market_crash event #define MARKET_CRASH_EVENT_TRAIT "crashed_market_event" diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index 91614c7d8a7..93ded9aec5d 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -25,6 +25,11 @@ /// outright dismemberment of limb #define WOUND_SEVERITY_LOSS 4 +// how much blood the limb needs to be losing per tick (not counting laying down/self grasping modifiers) to get the different bleed icons +#define BLEED_OVERLAY_LOW 0.5 +#define BLEED_OVERLAY_MED 1.5 +#define BLEED_OVERLAY_GUSH 3.25 + /// A "chronological" list of wound severities, starting at the least severe. GLOBAL_LIST_INIT(wound_severities_chronological, list( "[WOUND_SEVERITY_TRIVIAL]", diff --git a/code/_globalvars/lists/quirks.dm b/code/_globalvars/lists/quirks.dm index 9c3b6878d03..c052f761b03 100644 --- a/code/_globalvars/lists/quirks.dm +++ b/code/_globalvars/lists/quirks.dm @@ -99,6 +99,14 @@ GLOBAL_LIST_INIT(paraplegic_choice, list( "Amputee" = TRUE, )) +///Scarred Eye Quirk +GLOBAL_LIST_INIT(scarred_eye_choice, list( + "Random", + "Left Eye", + "Right Eye", + "Double", +)) + ///chipped Quirk GLOBAL_LIST_INIT(quirk_chipped_choice, list( "Basketsoft 3000" = /obj/item/skillchip/basketweaving, diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index a003be7ccb8..eef2f1a2994 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -563,6 +563,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING, "TRAIT_ILLUSORY_EFFECT" = TRAIT_ILLUSORY_EFFECT, "TRAIT_IGNORE_FIRE_PROTECTION" = TRAIT_IGNORE_FIRE_PROTECTION, + "TRAIT_LEFT_EYE_SCAR" = TRAIT_LEFT_EYE_SCAR, + "TRAIT_RIGHT_EYE_SCAR" = TRAIT_RIGHT_EYE_SCAR, ), /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 09633867376..7ede706f157 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -326,6 +326,8 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_XRAY_VISION" = TRAIT_XRAY_VISION, "TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING, "TRAIT_IGNORE_FIRE_PROTECTION" = TRAIT_IGNORE_FIRE_PROTECTION, + "TRAIT_LEFT_EYE_SCAR" = TRAIT_LEFT_EYE_SCAR, + "TRAIT_RIGHT_EYE_SCAR" = TRAIT_RIGHT_EYE_SCAR, ), /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index aca5ca557cd..0199b7498cc 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -4,7 +4,7 @@ // Shifted to glob so they are generated at world start instead of risking players doing preference stuff before the subsystem inits GLOBAL_LIST_INIT_TYPED(quirk_blacklist, /list/datum/quirk, list( - list(/datum/quirk/item_quirk/blindness, /datum/quirk/item_quirk/nearsighted), + list(/datum/quirk/item_quirk/blindness, /datum/quirk/item_quirk/nearsighted, /datum/quirk/item_quirk/scarred_eye), list(/datum/quirk/item_quirk/blindness, /datum/quirk/touchy), list(/datum/quirk/jolly, /datum/quirk/depression, /datum/quirk/apathetic, /datum/quirk/hypersensitive), list(/datum/quirk/no_taste, /datum/quirk/vegetarian, /datum/quirk/deviant_tastes, /datum/quirk/gamer), diff --git a/code/datums/elements/eyestab.dm b/code/datums/elements/eyestab.dm index b8c0d78c4ae..a6757f67fb3 100644 --- a/code/datums/elements/eyestab.dm +++ b/code/datums/elements/eyestab.dm @@ -2,6 +2,8 @@ #define CLUMSY_ATTACK_SELF_CHANCE 50 /// The damage threshold (of the victim's eyes) after which they start taking more serious effects #define EYESTAB_BLEEDING_THRESHOLD 10 +/// The damage threshold (of the victim's eyes) after which they can go blind +#define EYESTAB_BLINDING_THRESHOLD 30 /// How much blur we can apply #define EYESTAB_MAX_BLUR (4 MINUTES) @@ -80,13 +82,14 @@ return target.adjust_eye_blur_up_to(6 SECONDS, EYESTAB_MAX_BLUR) + var/started_bleeding = eyes.damage < EYESTAB_BLEEDING_THRESHOLD eyes.apply_organ_damage(rand(2, 4)) if(eyes.damage < EYESTAB_BLEEDING_THRESHOLD) return // At over 10 damage we apply a lot of eye blur target.adjust_eye_blur_up_to(30 SECONDS, EYESTAB_MAX_BLUR) - if (target.stat != DEAD) + if (target.stat != DEAD && started_bleeding) to_chat(target, span_danger("Your eyes start to bleed profusely!")) // At over 10 damage, we cause at least enough eye damage to force nearsightedness @@ -102,8 +105,26 @@ target.Unconscious(2 SECONDS) target.Paralyze(4 SECONDS) - // At over 10 damage, there is a chance (based on eye damage) of going blind - if (prob(eyes.damage - eyes.low_threshold + 1)) + // A solid chance of getting a permanent scar over one of your eyes, if you have at least one unscarred eyeball + if (prob(eyes.damage - EYESTAB_BLEEDING_THRESHOLD + 1)) + var/valid_sides = list() + if (!(eyes.scarring & RIGHT_EYE_SCAR)) + valid_sides += RIGHT_EYE_SCAR + if (!(eyes.scarring & LEFT_EYE_SCAR)) + valid_sides += LEFT_EYE_SCAR + if (length(valid_sides)) + var/picked_side = pick(valid_sides) + to_chat(target, span_userdanger("You feel searing pain shoot though your [picked_side == RIGHT_EYE_SCAR ? "right" : "left"] eye!")) + // oof ouch my eyes + var/datum/wound/pierce/bleed/severe/eye/eye_puncture = new + eye_puncture.apply_wound(eyes.bodypart_owner, wound_source = "eye stab", right_side = picked_side) + eyes.apply_scar(picked_side) + + if (eyes.damage < EYESTAB_BLINDING_THRESHOLD) + return + + // At over 30 damage, there is a chance (based on eye damage) of going blind + if (prob(eyes.damage - EYESTAB_BLINDING_THRESHOLD + 1)) if (!target.is_blind_from(EYE_DAMAGE)) eyes.set_organ_damage(eyes.maxHealth) // Also cause some temp blindness, so that they're still blind even if they get healed @@ -111,4 +132,5 @@ #undef CLUMSY_ATTACK_SELF_CHANCE #undef EYESTAB_BLEEDING_THRESHOLD +#undef EYESTAB_BLINDING_THRESHOLD #undef EYESTAB_MAX_BLUR diff --git a/code/datums/quirks/negative_quirks/blindness.dm b/code/datums/quirks/negative_quirks/blindness.dm index d0af915dc32..54a4f154474 100644 --- a/code/datums/quirks/negative_quirks/blindness.dm +++ b/code/datums/quirks/negative_quirks/blindness.dm @@ -1,7 +1,7 @@ /datum/quirk/item_quirk/blindness name = "Blind" desc = "You are completely blind, nothing can counteract this." - icon = FA_ICON_EYE_SLASH + icon = FA_ICON_BLIND value = -16 gain_text = span_danger("You can't see anything.") lose_text = span_notice("You miraculously gain back your vision.") diff --git a/code/datums/quirks/negative_quirks/scarred_eye.dm b/code/datums/quirks/negative_quirks/scarred_eye.dm new file mode 100644 index 00000000000..49628545cfa --- /dev/null +++ b/code/datums/quirks/negative_quirks/scarred_eye.dm @@ -0,0 +1,63 @@ +/datum/quirk/item_quirk/scarred_eye + name = "Scarred Eye" + desc = "An accident in your past has cost you one of your eyes, but you got a cool eyepatch. Yarr!" + icon = FA_ICON_EYE_SLASH + value = -2 + gain_text = span_danger("After all this time, your eye still stings a bit...") + lose_text = span_notice("Your peripherial vision grows by about thirty percent.") + medical_record_text = "Patient has severe scarring on one of their eyes, resulting in partial vision loss." + hardcore_value = 2 + quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_CHANGES_APPEARANCE + mail_goodies = list(/obj/item/reagent_containers/cup/glass/bottle/rum, /obj/item/clothing/mask/bandana/red) + +/datum/quirk_constant_data/eye_scarring + associated_typepath = /datum/quirk/item_quirk/scarred_eye + customization_options = list(/datum/preference/choiced/scarred_eye) + +/datum/quirk/item_quirk/scarred_eye/add_unique(client/client_source) + if (client_source?.prefs.read_preference(/datum/preference/choiced/scarred_eye) == "Double") + give_item_to_holder(new /obj/item/clothing/glasses/blindfold/white(get_turf(quirk_holder)), list( + LOCATION_EYES = ITEM_SLOT_EYES, + LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, + LOCATION_HANDS = ITEM_SLOT_HANDS, + )) + return + + var/mob/living/carbon/human/human_holder = quirk_holder + var/obj/item/clothing/glasses/eyepatch/eyepatch = new(get_turf(quirk_holder)) + if (human_holder.get_eye_scars() & LEFT_EYE_SCAR) + eyepatch.flip_eyepatch() + give_item_to_holder(eyepatch, list( + LOCATION_EYES = ITEM_SLOT_EYES, + LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, + LOCATION_HANDS = ITEM_SLOT_HANDS, + )) + +/datum/quirk/item_quirk/scarred_eye/add(client/client_source) + var/mob/living/carbon/human/human_owner = quirk_holder + var/obj/item/organ/internal/eyes/eyes = human_owner.get_organ_slot(ORGAN_SLOT_EYES) + if (isnull(eyes)) + return + + var/eye_side = client_source?.prefs.read_preference(/datum/preference/choiced/scarred_eye) || "Random" + + if (eye_side == "Double") + eyes.apply_scar(RIGHT_EYE_SCAR) + eyes.apply_scar(LEFT_EYE_SCAR) + return + + switch (eye_side) + if ("Random") + eye_side = pick(RIGHT_EYE_SCAR, LEFT_EYE_SCAR) + if ("Right Eye") + eye_side = RIGHT_EYE_SCAR + if ("Left Eye") + eye_side = LEFT_EYE_SCAR + eyes.apply_scar(eye_side) + +/datum/quirk/item_quirk/scarred_eye/remove() + var/mob/living/carbon/human/human_owner = quirk_holder + var/obj/item/organ/internal/eyes/eyes = human_owner.get_organ_slot(ORGAN_SLOT_EYES) + if (!isnull(eyes)) + eyes.fix_scar(RIGHT_EYE_SCAR) + eyes.fix_scar(LEFT_EYE_SCAR) diff --git a/code/datums/status_effects/debuffs/blindness.dm b/code/datums/status_effects/debuffs/blindness.dm index e36f2e45dfa..fb87d2fde85 100644 --- a/code/datums/status_effects/debuffs/blindness.dm +++ b/code/datums/status_effects/debuffs/blindness.dm @@ -13,7 +13,7 @@ /// Static list of signals that, when received, we force an update to our nearsighted overlay var/static/list/update_signals = list(SIGNAL_ADDTRAIT(TRAIT_NEARSIGHTED_CORRECTED), SIGNAL_REMOVETRAIT(TRAIT_NEARSIGHTED_CORRECTED)) /// How severe is our nearsightedness right now - var/overlay_severity = 1 + var/overlay_severity = 2 /datum/status_effect/grouped/nearsighted/on_apply() RegisterSignals(owner, update_signals, PROC_REF(update_nearsightedness)) @@ -33,6 +33,10 @@ /// Checks if we should be nearsighted currently, or if we should clear the overlay /datum/status_effect/grouped/nearsighted/proc/should_be_nearsighted() + if (ishuman(owner)) + var/mob/living/carbon/human/human_owner = owner + if (human_owner.get_eye_scars()) + return TRUE return !HAS_TRAIT(owner, TRAIT_NEARSIGHTED_CORRECTED) /// Updates our nearsightd overlay, either removing it if we have the trait or adding it if we don't diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm index 2cdc2bab382..7a0fa12f447 100644 --- a/code/datums/wounds/pierce.dm +++ b/code/datums/wounds/pierce.dm @@ -256,6 +256,49 @@ if(!limb.can_bleed()) occur_text = "tears a hole open" +/datum/wound/pierce/bleed/severe/eye + name = "Eyeball Puncture" + desc = "Patient's eye has sustained extreme damage, causing severe bleeding from the ocular cavity." + occur_text = "looses a violent spray of blood, revealing a crushed eyeball" + var/right_side = FALSE + +/datum/wound/pierce/bleed/severe/eye/apply_wound(obj/item/bodypart/limb, silent, datum/wound/old_wound, smited, attack_direction, wound_source, replacing, right_side) + var/obj/item/organ/internal/eyes/eyes = locate() in limb + if (!istype(eyes)) + return FALSE + . = ..() + src.right_side = right_side + examine_desc = "has its [right_side ? "right" : "left"] eye pierced clean through, blood spewing from the cavity" + RegisterSignal(limb, COMSIG_BODYPART_UPDATE_WOUND_OVERLAY, PROC_REF(wound_overlay)) + limb.update_part_wound_overlay() + +/datum/wound/pierce/bleed/severe/eye/remove_wound(ignore_limb, replaced) + if (!isnull(limb)) + UnregisterSignal(limb, COMSIG_BODYPART_UPDATE_WOUND_OVERLAY) + return ..() + +/datum/wound/pierce/bleed/severe/eye/proc/wound_overlay(obj/item/bodypart/source, limb_bleed_rate) + SIGNAL_HANDLER + + if (limb_bleed_rate <= BLEED_OVERLAY_LOW || limb_bleed_rate > BLEED_OVERLAY_GUSH) + return + + if (blood_flow <= BLEED_OVERLAY_LOW) + return + + source.bleed_overlay_icon = right_side ? "r_eye" : "l_eye" + return COMPONENT_PREVENT_WOUND_OVERLAY_UPDATE + +/datum/wound_pregen_data/flesh_pierce/open_puncture/eye + wound_path_to_generate = /datum/wound/pierce/bleed/severe/eye + viable_zones = list(BODY_ZONE_HEAD) + can_be_randomly_generated = FALSE + +/datum/wound_pregen_data/flesh_pierce/open_puncture/eye/can_be_applied_to(obj/item/bodypart/limb, list/suggested_wounding_types, datum/wound/old_wound, random_roll, duplicates_allowed, care_about_existing_wounds) + if (isnull(locate(/obj/item/organ/internal/eyes) in limb)) + return FALSE + return ..() + /datum/wound/pierce/bleed/critical name = "Ruptured Cavity" desc = "Patient's internal tissue and circulatory system is shredded, causing significant internal bleeding and damage to internal organs." diff --git a/code/modules/client/preferences/scarred_eye.dm b/code/modules/client/preferences/scarred_eye.dm new file mode 100644 index 00000000000..6cb0286fe86 --- /dev/null +++ b/code/modules/client/preferences/scarred_eye.dm @@ -0,0 +1,20 @@ +/datum/preference/choiced/scarred_eye + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "scarred_eye" + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/choiced/scarred_eye/init_possible_values() + return GLOB.scarred_eye_choice + +/datum/preference/choiced/scarred_eye/create_default_value() + return "Random" + +/datum/preference/choiced/scarred_eye/is_accessible(datum/preferences/preferences) + . = ..() + if (!.) + return FALSE + + return "Scarred Eye" in preferences.all_quirks + +/datum/preference/choiced/scarred_eye/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 4a5ac9b6572..4cff2c52f00 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -182,11 +182,46 @@ inhand_icon_state = null actions_types = list(/datum/action/item_action/flip) dog_fashion = /datum/dog_fashion/head/eyepatch + var/flipped = FALSE -/obj/item/clothing/glasses/eyepatch/attack_self(mob/user, modifiers) +/obj/item/clothing/glasses/eyepatch/click_alt(mob/user) . = ..() - icon_state = (icon_state == base_icon_state) ? "[base_icon_state]_flipped" : base_icon_state + flip_eyepatch() + +/obj/item/clothing/glasses/eyepatch/attack_self(mob/user) + . = ..() + flip_eyepatch() + +/obj/item/clothing/glasses/eyepatch/proc/flip_eyepatch() + flipped = !flipped + icon_state = flipped ? "[base_icon_state]_flipped" : base_icon_state + if (!ismob(loc)) + return + var/mob/user = loc user.update_worn_glasses() + if (!ishuman(user)) + return + var/mob/living/carbon/human/human_user = user + if (human_user.get_eye_scars() & (flipped ? RIGHT_EYE_SCAR : LEFT_EYE_SCAR)) + tint = INFINITY + else + tint = initial(tint) + human_user.update_tint() + +/obj/item/clothing/glasses/eyepatch/equipped(mob/living/user, slot) + if (!ishuman(user)) + return ..() + var/mob/living/carbon/human/human_user = user + // lol lmao + if (human_user.get_eye_scars() & (flipped ? RIGHT_EYE_SCAR : LEFT_EYE_SCAR)) + tint = INFINITY + else + tint = initial(tint) + return ..() + +/obj/item/clothing/glasses/eyepatch/dropped(mob/living/user) + . = ..() + tint = initial(tint) /obj/item/clothing/glasses/eyepatch/medical name = "medical eyepatch" diff --git a/code/modules/loadout/categories/glasses.dm b/code/modules/loadout/categories/glasses.dm index 5b8ff856200..78f9b45fb9d 100644 --- a/code/modules/loadout/categories/glasses.dm +++ b/code/modules/loadout/categories/glasses.dm @@ -53,3 +53,7 @@ /datum/loadout_item/glasses/eyepatch name = "Eyepatch" item_path = /obj/item/clothing/glasses/eyepatch + +/datum/loadout_item/glasses/eyepatch/medical + name = "Medical Eyepatch" + item_path = /obj/item/clothing/glasses/eyepatch/medical diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 3e2248914dc..07325c97137 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -136,6 +136,11 @@ . = ..() . += "[dna.species.type]" +/mob/living/carbon/human/proc/get_eye_scars() + var/obj/item/organ/internal/eyes/eyes = get_organ_slot(ORGAN_SLOT_EYES) + if (!isnull(eyes)) + return eyes.scarring + /// When we're joining the game in [/mob/dead/new_player/proc/create_character], we increment our scar slot then store the slot in our mind datum. /mob/living/carbon/human/proc/increment_scar_slot() var/check_ckey = ckey || client?.ckey diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 17377f5f398..667e6f1e7be 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -1210,11 +1210,6 @@ bleed_rate *= 0.7 return bleed_rate -// how much blood the limb needs to be losing per tick (not counting laying down/self grasping modifiers) to get the different bleed icons -#define BLEED_OVERLAY_LOW 0.5 -#define BLEED_OVERLAY_MED 1.5 -#define BLEED_OVERLAY_GUSH 3.25 - /obj/item/bodypart/proc/update_part_wound_overlay() if(!owner) return FALSE @@ -1224,6 +1219,9 @@ owner.update_wound_overlays() return FALSE + if (SEND_SIGNAL(src, COMSIG_BODYPART_UPDATE_WOUND_OVERLAY, cached_bleed_rate) & COMPONENT_PREVENT_WOUND_OVERLAY_UPDATE) + return + var/bleed_rate = cached_bleed_rate var/new_bleed_icon = null @@ -1247,10 +1245,6 @@ bleed_overlay_icon = new_bleed_icon owner.update_wound_overlays() -#undef BLEED_OVERLAY_LOW -#undef BLEED_OVERLAY_MED -#undef BLEED_OVERLAY_GUSH - /obj/item/bodypart/proc/can_bleed() SHOULD_BE_PURE(TRUE) diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index acae5f1b71f..eba862bca8a 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -51,6 +51,8 @@ var/damaged = FALSE /// Native FOV that will be applied if a config is enabled var/native_fov = FOV_90_DEGREES + /// Scarring on this organ + var/scarring = NONE /obj/item/organ/internal/eyes/mob_insert(mob/living/carbon/receiver, special, movement_flags) // If we don't do this before everything else, heterochromia will be reset leading to eye_color_right no longer being accurate @@ -64,6 +66,7 @@ receiver.cure_blind(NO_EYES) apply_damaged_eye_effects() refresh(receiver, call_update = TRUE) + RegisterSignal(receiver, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) /// Refreshes the visuals of the eyes /// If call_update is TRUE, we also will call update_body @@ -117,6 +120,30 @@ organ_owner.update_tint() organ_owner.update_sight() + UnregisterSignal(organ_owner, COMSIG_ATOM_BULLET_ACT) + +/obj/item/organ/internal/eyes/proc/on_bullet_act(datum/source, obj/projectile/proj, def_zone) + SIGNAL_HANDLER + + // Once-a-dozen-rounds level of rare + if (def_zone != BODY_ZONE_HEAD || !prob(proj.damage * 0.1) || !(proj.damage_type == BRUTE || proj.damage_type == BURN)) + return + + var/valid_sides = list() + if (!(scarring & RIGHT_EYE_SCAR)) + valid_sides += RIGHT_EYE_SCAR + if (!(scarring & LEFT_EYE_SCAR)) + valid_sides += LEFT_EYE_SCAR + if (!length(valid_sides)) + return + + var/picked_side = pick(valid_sides) + to_chat(owner, span_userdanger("You feel searing pain shoot though your [picked_side == RIGHT_EYE_SCAR ? "right" : "left"] eye!")) + // oof ouch my eyes + apply_organ_damage(rand((maxHealth - high_threshold) * 0.5, maxHealth - low_threshold)) + var/datum/wound/pierce/bleed/severe/eye/eye_puncture = new + eye_puncture.apply_wound(bodypart_owner, wound_source = "bullet impact", right_side = picked_side) + apply_scar(picked_side) #define OFFSET_X 1 #define OFFSET_Y 2 @@ -129,6 +156,8 @@ if(advanced) if(owner.is_blind_from(QUIRK_TRAIT)) return conditional_tooltip("Subject is permanently blind.", "Irreparable under normal circumstances.", add_tooltips) + if(owner.is_blind_from(EYE_SCARRING_TRAIT)) + return conditional_tooltip("Subject is blind from widespread ocular scarring.", "Surgically replace eyes, irreparable otherwise.", add_tooltips) if(owner.is_blind_from(TRAUMA_TRAIT)) return conditional_tooltip("Subject is blind from mental trauma.", "Repair via treatment of associated trauma.", add_tooltips) if(owner.is_blind_from(GENETIC_MUTATION)) @@ -140,6 +169,8 @@ if(advanced) if(owner.is_nearsighted_from(QUIRK_TRAIT)) return conditional_tooltip("Subject is permanently nearsighted.", "Irreparable under normal circumstances. Prescription glasses will assuage the effects.", add_tooltips) + if(owner.is_nearsighted_from(TRAIT_RIGHT_EYE_SCAR) || owner.is_nearsighted_from(TRAIT_LEFT_EYE_SCAR)) + return conditional_tooltip("Subject is nearsighted from severe ocular scarring.", "Surgically replace eyes, irreparable otherwise.", add_tooltips) if(owner.is_nearsighted_from(GENETIC_MUTATION)) return conditional_tooltip("Subject is genetically nearsighted.", "Use medication such as [/datum/reagent/medicine/mutadone::name]. Prescription glasses will assuage the effects.", add_tooltips) if(owner.is_nearsighted_from(EYE_DAMAGE)) @@ -167,28 +198,97 @@ if(overlay_ignore_lighting && !(obscured & ITEM_SLOT_EYES)) overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, parent, -BODY_LAYER, alpha = eye_left.alpha) overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, parent, -BODY_LAYER, alpha = eye_right.alpha) - var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD) - if(my_head) - if(my_head.head_flags & HEAD_EYECOLOR) - if(IS_ROBOTIC_ORGAN(src) || !my_head.draw_color || (parent.appears_alive() && !HAS_TRAIT(parent, TRAIT_KNOCKEDOUT))) - // show the eyes as open - eye_right.color = eye_color_right - eye_left.color = eye_color_left - else - // show the eyes as closed, and as such color them like eyelids wound be colored - var/list/base_color = rgb2num(my_head.draw_color, COLORSPACE_HSL) - base_color[2] *= 0.85 - base_color[3] *= 0.85 - var/eyelid_color = rgb(base_color[1], base_color[2], base_color[3], (length(base_color) >= 4 ? base_color[4] : null), COLORSPACE_HSL) - eye_right.color = eyelid_color - eye_left.color = eyelid_color - if(my_head.worn_face_offset) - my_head.worn_face_offset.apply_offset(eye_left) - my_head.worn_face_offset.apply_offset(eye_right) + var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD) + + if(!my_head) + return overlays + + if(my_head.head_flags & HEAD_EYECOLOR) + if(IS_ROBOTIC_ORGAN(src) || !my_head.draw_color || (parent.appears_alive() && !HAS_TRAIT(parent, TRAIT_KNOCKEDOUT))) + // show the eyes as open + eye_right.color = eye_color_right + eye_left.color = eye_color_left + else + // show the eyes as closed, and as such color them like eyelids wound be colored + var/list/base_color = rgb2num(my_head.draw_color, COLORSPACE_HSL) + base_color[2] *= 0.85 + base_color[3] *= 0.85 + var/eyelid_color = rgb(base_color[1], base_color[2], base_color[3], (length(base_color) >= 4 ? base_color[4] : null), COLORSPACE_HSL) + eye_right.color = eyelid_color + eye_left.color = eyelid_color + + if (scarring & RIGHT_EYE_SCAR) + var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_face.dmi', "eye_scar_right", -BODY_LAYER) + right_scar.color = my_head.draw_color + overlays += right_scar + + if (scarring & LEFT_EYE_SCAR) + var/mutable_appearance/left_scar = mutable_appearance('icons/mob/human/human_face.dmi', "eye_scar_left", -BODY_LAYER) + left_scar.color = my_head.draw_color + overlays += left_scar + + if(my_head.worn_face_offset) + my_head.worn_face_offset.apply_offset(eye_left) + my_head.worn_face_offset.apply_offset(eye_right) return overlays +/obj/item/organ/internal/eyes/update_overlays() + . = ..() + if (scarring & RIGHT_EYE_SCAR) + . += mutable_appearance('icons/obj/medical/organs/organs.dmi', "eye_scar_right") + if (scarring & LEFT_EYE_SCAR) + . += mutable_appearance('icons/obj/medical/organs/organs.dmi', "eye_scar_left") + +/obj/item/organ/internal/eyes/proc/apply_scar(side) + if (scarring & side) + return + scarring |= side + maxHealth -= 15 + update_appearance() + apply_scarring_effects() + +/obj/item/organ/internal/eyes/proc/apply_scarring_effects() + if (!owner) + return + var/datum/status_effect/grouped/nearsighted/nearsightedness = owner.is_nearsighted() + // Even if eyes have enough health, our owner still becomes nearsighted + if (scarring & RIGHT_EYE_SCAR) + owner.become_nearsighted(TRAIT_RIGHT_EYE_SCAR) + if (scarring & LEFT_EYE_SCAR) + owner.become_nearsighted(TRAIT_LEFT_EYE_SCAR) + if (isnull(nearsightedness)) // We aren't nearsighted from any other source + nearsightedness = owner.is_nearsighted() + nearsightedness.set_nearsighted_severity(1) + if ((scarring & RIGHT_EYE_SCAR) && (scarring & LEFT_EYE_SCAR)) + owner.become_blind(EYE_SCARRING_TRAIT) + owner.update_body() + +/obj/item/organ/internal/eyes/proc/fix_scar(side) + if (!(scarring & side)) + return + scarring &= ~side + maxHealth += 15 + update_appearance() + if (!owner) + return + owner.cure_nearsighted(side == RIGHT_EYE_SCAR ? TRAIT_RIGHT_EYE_SCAR : TRAIT_LEFT_EYE_SCAR) + owner.cure_blind(EYE_SCARRING_TRAIT) + owner.update_body() + +/obj/item/organ/internal/eyes/on_mob_insert(mob/living/carbon/eye_owner) + . = ..() + if (scarring) + apply_scarring_effects() + +/obj/item/organ/internal/eyes/on_mob_remove(mob/living/carbon/eye_owner) + . = ..() + if (scarring) + owner.cure_nearsighted(TRAIT_RIGHT_EYE_SCAR) + owner.cure_nearsighted(TRAIT_LEFT_EYE_SCAR) + owner.cure_blind(EYE_SCARRING_TRAIT) + #undef OFFSET_X #undef OFFSET_Y @@ -233,7 +333,7 @@ owner.become_nearsighted(EYE_DAMAGE) // update the severity of our nearsightedness based on our eye damage var/datum/status_effect/grouped/nearsighted/nearsightedness = owner.is_nearsighted() - nearsightedness.set_nearsighted_severity(damage > high_threshold ? 2 : 1) + nearsightedness.set_nearsighted_severity(damage > high_threshold ? 3 : 2) damaged = TRUE diff --git a/code/modules/unit_tests/blindness.dm b/code/modules/unit_tests/blindness.dm index f7ae16c7e0a..88f5eece575 100644 --- a/code/modules/unit_tests/blindness.dm +++ b/code/modules/unit_tests/blindness.dm @@ -125,7 +125,7 @@ TEST_ASSERT(dummy.is_nearsighted(), "After sustaining minor eye damage ([minor_damage]), the dummy was not nearsighted.") // Check that the severity is correct nearsightedness = dummy.is_nearsighted() - TEST_ASSERT_EQUAL(nearsightedness.overlay_severity, 1, "After taking minor eye damage, the dummy's nearsightedness was the incorrect severity.") + TEST_ASSERT_EQUAL(nearsightedness.overlay_severity, 2, "After taking minor eye damage, the dummy's nearsightedness was the incorrect severity.") nearsightedness = null // Heal eye damage eyes.set_organ_damage(0) @@ -137,7 +137,7 @@ TEST_ASSERT(dummy.is_nearsighted(), "After sustaining major eye damage ([major_damage]), the dummy was not nearsighted.") // Check that the severity is correct nearsightedness = dummy.is_nearsighted() - TEST_ASSERT_EQUAL(nearsightedness.overlay_severity, 2, "After taking major eye damage, the dummy's nearsightedness was the incorrect severity.") + TEST_ASSERT_EQUAL(nearsightedness.overlay_severity, 3, "After taking major eye damage, the dummy's nearsightedness was the incorrect severity.") nearsightedness = null // Heal eye damage eyes.set_organ_damage(0) diff --git a/icons/hud/screen_full.dmi b/icons/hud/screen_full.dmi index ac33631e1a0..dfe3bd12091 100644 Binary files a/icons/hud/screen_full.dmi and b/icons/hud/screen_full.dmi differ diff --git a/icons/mob/effects/bleed_overlays.dmi b/icons/mob/effects/bleed_overlays.dmi index aa82d5578f8..68c167e11ef 100644 Binary files a/icons/mob/effects/bleed_overlays.dmi and b/icons/mob/effects/bleed_overlays.dmi differ diff --git a/icons/mob/human/human_face.dmi b/icons/mob/human/human_face.dmi index 9c6c333f05b..99bdf00910c 100644 Binary files a/icons/mob/human/human_face.dmi and b/icons/mob/human/human_face.dmi differ diff --git a/icons/obj/medical/organs/organs.dmi b/icons/obj/medical/organs/organs.dmi index 06ed0e7058d..8e1dccba1ed 100644 Binary files a/icons/obj/medical/organs/organs.dmi and b/icons/obj/medical/organs/organs.dmi differ diff --git a/tgstation.dme b/tgstation.dme index f6ab0f39f88..e4d5d5a3f96 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1771,6 +1771,7 @@ #include "code\datums\quirks\negative_quirks\prosthetic_organ.dm" #include "code\datums\quirks\negative_quirks\pushover.dm" #include "code\datums\quirks\negative_quirks\quadruple_amputee.dm" +#include "code\datums\quirks\negative_quirks\scarred_eye.dm" #include "code\datums\quirks\negative_quirks\social_anxiety.dm" #include "code\datums\quirks\negative_quirks\softspoken.dm" #include "code\datums\quirks\negative_quirks\tin_man.dm" @@ -3818,6 +3819,7 @@ #include "code\modules\client\preferences\random.dm" #include "code\modules\client\preferences\runechat.dm" #include "code\modules\client\preferences\scaling_method.dm" +#include "code\modules\client\preferences\scarred_eye.dm" #include "code\modules\client\preferences\screentips.dm" #include "code\modules\client\preferences\security_department.dm" #include "code\modules\client\preferences\skin_tone.dm" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/scarred_eye.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/scarred_eye.tsx new file mode 100644 index 00000000000..35376d16f96 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/scarred_eye.tsx @@ -0,0 +1,7 @@ +import { FeatureChoiced } from '../base'; +import { FeatureDropdownInput } from '../dropdowns'; + +export const scarred_eye: FeatureChoiced = { + name: 'Scarred Eye', + component: FeatureDropdownInput, +};