diff --git a/code/game/objects/items/tools/tools.dm b/code/game/objects/items/tools/tools.dm index be52fdb1f4b..e2135203296 100644 --- a/code/game/objects/items/tools/tools.dm +++ b/code/game/objects/items/tools/tools.dm @@ -320,12 +320,35 @@ update_icon() /obj/item/weldingtool/use_tool(atom/target, mob/living/user, delay, amount, volume, datum/callback/extra_checks) + var/welding_blind = user.is_blind() + if(welding_blind) + delay *= 3 + var/image/welding_sparks = image('icons/effects/effects.dmi', welding_state) welding_sparks.plane = ABOVE_LIGHTING_PLANE target.AddOverlays(welding_sparks) . = ..() target.CutOverlays(welding_sparks) + if(. && welding_blind && prob(80)) + to_chat(user, SPAN_WARNING("Unable to see your work, you botch the weld!")) + if(prob(50)) + var/burn_zone + if(ishuman(user)) + switch(get_equip_slot()) + if(slot_l_hand) + burn_zone = BP_L_HAND + if(slot_r_hand) + burn_zone = BP_R_HAND + else + burn_zone = pick(BP_L_HAND, BP_R_HAND) + user.apply_damage(rand(5, 10), DAMAGE_BURN, burn_zone, src) + user.visible_message( + SPAN_DANGER("[user] jerks back after burning themselves with \the [src]!"), + SPAN_DANGER("Your blind welding slips, burning your hand!") + ) + return FALSE + /obj/item/weldingtool/proc/update_torch() if(welding) AddOverlays("[initial(icon_state)]-on") @@ -439,7 +462,7 @@ user.visible_message(SPAN_NOTICE("\The [user] finishes repairing the physical damage on \the [target]'s [affecting.name].")) return - if(do_mob(user, target, 30)) + if(use_tool(target, user, 30, volume = 15)) if(use(0)) var/static/list/repair_messages = list( "patches some dents", @@ -951,4 +974,3 @@ var/mutable_appearance/handle = mutable_appearance('icons/obj/tools.dmi', "hammer_handle") handle.color = pick(COLOR_BLUE, COLOR_RED, COLOR_PURPLE, COLOR_BROWN, COLOR_GREEN, COLOR_CYAN, COLOR_YELLOW) AddOverlays(handle) - diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 2d1bd0ff9fa..76603598123 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -209,6 +209,10 @@ if(wrists && !skipwrists) msg += "[get_pronoun("He")] [get_pronoun("is")] wearing [icon2html(wrists, user)] \a [wrists] [wrists.get_wrist_examine_text(src)].\n" + //closed eyes + if(!skipeyes && eyes_are_closed()) + msg += "[get_pronoun("His")] eyes are closed.\n" + //Jitters if(is_jittery) if(jitteriness >= 300) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 67cf6bc9707..f2904f8314a 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -925,6 +925,9 @@ return base_flash_protection /mob/living/carbon/human/flash_act(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = FALSE, ignore_inherent = FALSE, type = /atom/movable/screen/fullscreen/flash, length = 2.5 SECONDS) + if(eyes_are_closed()) + return FALSE + if(..()) var/obj/item/organ/E = get_eyes(no_synthetic = !affect_silicon) if(istype(E)) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 255e7cb532c..0196c3539fd 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -205,6 +205,14 @@ return eyes +/// Returns TRUE when this mob's vision organ has been voluntarily closed. +/mob/living/carbon/human/proc/eyes_are_closed() + var/obj/item/organ/internal/eyes/eyes = get_eyes() + return eyes?.eyes_closed + +/mob/living/carbon/human/is_blind() + return ..() || eyes_are_closed() + /mob/living/carbon/human/proc/awaken_psi_basic(var/source) var/static/list/psi_operancy_messages = list( "There's something in your skull!", diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 48ce3be721c..73cc4b1acc4 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -771,7 +771,7 @@ if(!H.client)//no client, no screen to update return 1 - H.set_fullscreen(H.eye_blind, "blind", /atom/movable/screen/fullscreen/blind) + H.set_fullscreen(H.eye_blind || H.eyes_are_closed(), "blind", /atom/movable/screen/fullscreen/blind) H.set_fullscreen(H.stat == UNCONSCIOUS, "blackout", /atom/movable/screen/fullscreen/blackout) if(GLOB.config.welder_vision) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index d4b4ee2e3b2..3105229cb51 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -315,7 +315,7 @@ There are several things that need to be remembered: var/icon_key = "[species.race_key][g][s_tone][r_skin][g_skin][b_skin][lipstick_color || "nolips"][!!husk][!!fat][!!skeleton][is_frenzied]" var/obj/item/organ/internal/eyes/eyes = get_eyes() if(eyes) - icon_key += "[rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])]" + icon_key += "[rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])][eyes.eyes_closed ? "closed" : "open"]" else icon_key += "#000000" diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index dbfdbd77b15..242605da4ac 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -12,6 +12,26 @@ toxin_type = CE_OCULOTOXIC var/list/eye_colour = list(0,0,0) var/singular_name = "eye" + /// Whether the owner has voluntarily closed this organ. + var/eyes_closed = FALSE + +/obj/item/organ/internal/eyes/verb/toggle_eyes() + set name = "Open/Close Eyes" + set desc = "Close your eyes to block your vision and protect them from bright flashes." + set category = "IC" + set src in usr + + if(!owner || usr != owner || owner.incapacitated()) + return + + eyes_closed = !eyes_closed + if(eyes_closed) + owner.custom_emote(VISIBLE_MESSAGE, "closes their eyes.") + else + owner.custom_emote(VISIBLE_MESSAGE, "opens their eyes.") + + owner.update_body() + owner.handle_vision() /obj/item/organ/internal/eyes/proc/update_colour() if(!owner) diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index ad49a9ec910..7b70bf3a39b 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -60,17 +60,22 @@ is_frenzied = TRUE if(owner.species.has_organ[owner.species.vision_organ]) var/obj/item/organ/internal/eyes/eyes = owner.get_eyes() - if(eyes && species.eyes && !is_frenzied) + if(eyes && species.eyes && (!is_frenzied || eyes.eyes_closed)) var/eyecolor - if (eyes.eye_colour) + if(eyes.eye_colour && !eyes.eyes_closed) eyecolor = rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]) - var/cache_key = "[species.eyes]_[eyecolor || "nocolor"]" + var/cache_key = "[species.eyes]_[eyes.eyes_closed ? "closed" : (eyecolor || "nocolor")]" var/icon/eyes_icon = SSicon_cache.human_eye_cache[cache_key] if (!eyes_icon) eyes_icon = new/icon(species.eyes_icons, species.eyes) - if(eyecolor) + if(eyes.eyes_closed) + // A translucent black eye mask darkens the skin beneath it, creating eyelids + // which follow both colour-based skin and tone-shaded human complexions. + eyes_icon.Blend(rgb(0, 0, 0), ICON_MULTIPLY) + eyes_icon.ChangeOpacity(0.2) + else if(eyecolor) eyes_icon.Blend(eyecolor, species.eyes_icon_blend) else eyes_icon.Blend(rgb(128,0,0), species.eyes_icon_blend) @@ -109,6 +114,9 @@ var/list/bonus_overlays = list() var/obj/item/organ/internal/eyes/eyes = H.get_eyes() + if(eyes?.eyes_closed) + return mob_overlays + if(eyes && BP_IS_ROBOTIC(eyes)) var/datum/robolimb/robolimb_data = GLOB.all_robolimbs[eyes.model] if(robolimb_data.emissive) diff --git a/code/unit_tests/mob_tests.dm b/code/unit_tests/mob_tests.dm index ea46246e9b5..0edcd05e3a2 100644 --- a/code/unit_tests/mob_tests.dm +++ b/code/unit_tests/mob_tests.dm @@ -122,6 +122,45 @@ return 1 // return 1 to show we're done and don't want to recheck the result. +/datum/unit_test/closed_eyes + name = "MOB: Closing eyes blinds and protects from flashes" + groups = list("mob") + +/datum/unit_test/closed_eyes/start_test() + var/mob/living/carbon/human/H = new(pick(GLOB.tdome1)) + var/obj/item/organ/internal/eyes/eyes = H.get_eyes() + + if(!eyes) + TEST_FAIL("Test human spawned without an eye organ.") + qdel(H) + return TRUE + + eyes.eyes_closed = TRUE + if(!H.is_blind()) + TEST_FAIL("A human with closed eyes was not considered blind.") + qdel(H) + return TRUE + + if(H.flash_act(affect_silicon = TRUE, ignore_inherent = TRUE)) + TEST_FAIL("A flash affected a human with closed eyes.") + qdel(H) + return TRUE + + eyes.eyes_closed = FALSE + if(H.is_blind()) + TEST_FAIL("Opening healthy eyes did not restore the human's vision.") + qdel(H) + return TRUE + + if(!H.flash_act(ignore_inherent = TRUE)) + TEST_FAIL("A flash did not affect a human with open, unprotected eyes.") + qdel(H) + return TRUE + + TEST_PASS("Closed eyes blind their owner and prevent flash effects.") + qdel(H) + return TRUE + // ============================================================================ /proc/create_test_mob_with_mind(var/turf/mobloc = null, var/mobtype = /mob/living/carbon/human, var/add_to_playerlist = FALSE) diff --git a/html/changelogs/geeves-closing_eyes.yml b/html/changelogs/geeves-closing_eyes.yml new file mode 100644 index 00000000000..1bbafd0a301 --- /dev/null +++ b/html/changelogs/geeves-closing_eyes.yml @@ -0,0 +1,7 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "You can now close your eyes. This blinds you, but gives you protection against flash effects." + - balance: "Welding while blind takes three times as long and has an 80% chance to fail, with a risk of burning yourself."