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 4dbe974e3f1..62f59572f57 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -63,7 +63,7 @@ /// Sent to a limb when something *attempts* to change its surgery state (old_state, new_state, changed_states) #define COMSIG_BODYPART_UPDATING_SURGERY_STATE "bodypart_updating_surgery_state" -/// Called from /obj/item/bodypart/proc/get_limb_icon(dropped, mob/living/carbon/update_on) : (list/limb_icons, dropped, mob/living/carbon/update_on) +/// Called from /obj/item/bodypart/proc/get_limb_icon(dropped) : (list/limb_icons, dropped, mob/living/carbon/update_on) #define COMSIG_BODYPART_GET_LIMB_ICON "bodypart_get_limb_icon" /// Called from /obj/item/bodypart/proc/generate_icon_key() : (list/icon_keys) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index d51f29f4072..bcc17a7dca1 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -837,7 +837,8 @@ GLOBAL_LIST_INIT(layers_to_offset, list( "[HEAD_LAYER]" = UPPER_BODY, // Hair will get cut off by filter "[HAIR_LAYER]" = UPPER_BODY, - "[BENEATH_HAIR_LAYER]" = UPPER_BODY, + // Doesn't do much + "[EYES_LAYER]" = UPPER_BODY, // Long belts (sabre sheathe) will get cut off by filter "[BELT_LAYER]" = LOWER_BODY, // Everything below looks fine with or without a filter, so we can skip it and just offset @@ -854,6 +855,7 @@ GLOBAL_LIST_INIT(layers_to_offset, list( "[ID_LAYER]" = UPPER_BODY, "[FACEMASK_LAYER]" = UPPER_BODY, "[LOW_FACEMASK_LAYER]" = UPPER_BODY, + "[BENEATH_HAIR_LAYER]" = UPPER_BODY, // alt mask layer // These two are cached, and have their appearance shared(?), so it's safer to just not touch it "[MUTATIONS_LAYER]" = NO_MODIFY, "[FRONT_MUTATIONS_LAYER]" = NO_MODIFY, @@ -862,7 +864,6 @@ GLOBAL_LIST_INIT(layers_to_offset, list( // BACK_LAYER (backpacks are big) // BODYPARTS_HIGH_LAYER (arms) // BODY_LAYER (body markings (full body), underwear (full body)) - // EYES_LAYER, // BODY_ADJ_LAYER (external organs like wings) // BODY_BEHIND_LAYER (external organs like wings) // BODY_FRONT_LAYER (external organs like wings) diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index bb1c9478514..7d31cb5d7af 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -220,8 +220,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NO_BLOOD_OVERLAY "no_blood_overlay" /// Humans with this trait cannot have underwear #define TRAIT_NO_UNDERWEAR "no_underwear" -/// This carbon doesn't show an overlay when they have no brain -#define TRAIT_NO_DEBRAIN_OVERLAY "no_debrain_overlay" /// Humans with this trait cannot get augmentation surgery #define TRAIT_NO_AUGMENTS "no_augments" /// This carbon doesn't get hungry diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 5f1fa7993c7..5a5db3d0d8a 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -441,7 +441,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NO_BLOOD_OVERLAY" = TRAIT_NO_BLOOD_OVERLAY, "TRAIT_NO_BREATHLESS_DAMAGE" = TRAIT_NO_BREATHLESS_DAMAGE, "TRAIT_NO_DAMAGE_OVERLAY" = TRAIT_NO_DAMAGE_OVERLAY, - "TRAIT_NO_DEBRAIN_OVERLAY" = TRAIT_NO_DEBRAIN_OVERLAY, "TRAIT_NO_DNA_COPY" = TRAIT_NO_DNA_COPY, "TRAIT_NO_DNA_SCRAMBLE" = TRAIT_NO_DNA_SCRAMBLE, "TRAIT_NO_EXTINGUISH" = TRAIT_NO_EXTINGUISH, diff --git a/code/datums/components/manual_blinking.dm b/code/datums/components/manual_blinking.dm index aed2c531e3d..b02646dc686 100644 --- a/code/datums/components/manual_blinking.dm +++ b/code/datums/components/manual_blinking.dm @@ -28,7 +28,7 @@ var/mob/living/carbon/carbon_parent = parent ADD_TRAIT(carbon_parent, TRAIT_PREVENT_BLINK_LOOPS, REF(src)) - carbon_parent.update_body() + carbon_parent.update_eyes() parent_eyes = carbon_parent.get_organ_slot(ORGAN_SLOT_EYES) if(!parent_eyes || IS_ROBOTIC_ORGAN(parent_eyes)) @@ -47,7 +47,7 @@ to_chat(parent, span_notice("You revert back to automatic blinking.")) var/mob/living/carbon/carbon_parent = parent carbon_parent.cure_blind(REF(src)) - carbon_parent.update_body() + carbon_parent.update_eyes() return ..() /datum/component/manual_blinking/RegisterWithParent() diff --git a/code/datums/elements/cult_eyes.dm b/code/datums/elements/cult_eyes.dm index 68d70a47092..6e95d9b1bca 100644 --- a/code/datums/elements/cult_eyes.dm +++ b/code/datums/elements/cult_eyes.dm @@ -30,7 +30,7 @@ return var/mob/living/carbon/human/human_parent = target human_parent.add_eye_color(BLOODCULT_EYE, EYE_COLOR_CULT_PRIORITY) - human_parent.update_body() + human_parent.update_eyes() /** * Detach proc @@ -42,6 +42,6 @@ if (ishuman(target)) var/mob/living/carbon/human/human_parent = target human_parent.remove_eye_color(EYE_COLOR_CULT_PRIORITY) - human_parent.update_body() + human_parent.update_eyes() UnregisterSignal(target, list(COMSIG_CHANGELING_TRANSFORM, COMSIG_HUMAN_MONKEYIZE, COMSIG_MONKEY_HUMANIZE)) return ..() diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm index a576138f128..b89da65d5eb 100644 --- a/code/datums/sprite_accessories.dm +++ b/code/datums/sprite_accessories.dm @@ -998,7 +998,7 @@ GLOBAL_LIST_EMPTY(blended_hair_icons_cache) /datum/sprite_accessory/gradient/none name = SPRITE_ACCESSORY_NONE - icon_state = "none" + icon_state = SPRITE_ACCESSORY_NONE /datum/sprite_accessory/gradient/full name = "Full" @@ -1245,7 +1245,7 @@ GLOBAL_LIST_EMPTY(blended_hair_icons_cache) /datum/sprite_accessory/facial_hair/shaved name = "Shaved" - icon_state = null + icon_state = SPRITE_ACCESSORY_NONE gender = NEUTER /datum/sprite_accessory/clothing diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm index 50a9473e19f..0615ea579da 100644 --- a/code/game/objects/structures/guillotine.dm +++ b/code/game/objects/structures/guillotine.dm @@ -244,8 +244,7 @@ var/datum/species/S = victim.dna.species if (istype(S)) - victim.cut_overlays() - victim.update_body_parts_head_only() + victim.render_only_head() victim.remove_overlay(BODY_ADJ_LAYER) victim.pixel_y += -GUILLOTINE_HEAD_OFFSET // Offset their body so it looks like they're in the guillotine victim.layer += GUILLOTINE_LAYER_DIFF diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index dff81692a5f..718a527a85c 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -262,7 +262,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) return user.set_eye_color(sanitize_hexcolor(new_eye_color)) user.dna.update_ui_block(/datum/dna_block/identity/eye_colors) - user.update_body() + user.update_eyes() to_chat(user, span_notice("You gaze at your new eyes with your new eyes. Perfect!")) /obj/structure/mirror/examine(mob/user) diff --git a/code/modules/antagonists/cult/cult_other.dm b/code/modules/antagonists/cult/cult_other.dm index 0e286d75e81..4f4ddd7aee0 100644 --- a/code/modules/antagonists/cult/cult_other.dm +++ b/code/modules/antagonists/cult/cult_other.dm @@ -9,7 +9,7 @@ /datum/outfit/cultist/post_equip(mob/living/carbon/human/equipped, visuals_only) equipped.set_eye_color(BLOODCULT_EYE) - equipped.update_body() + equipped.update_eyes() ///Returns whether the given mob is convertable to the blood cult /proc/is_convertable_to_cult(mob/living/target, datum/team/cult/specific_cult) diff --git a/code/modules/antagonists/spy/spy.dm b/code/modules/antagonists/spy/spy.dm index 3685800dcf1..224f083029c 100644 --- a/code/modules/antagonists/spy/spy.dm +++ b/code/modules/antagonists/spy/spy.dm @@ -196,6 +196,7 @@ var/mob/living/carbon/human/dummy/consistent/dummy = new() dummy.set_haircolor(COLOR_SILVER, update = FALSE) dummy.set_hairstyle("CIA", update = FALSE) + dummy.update_hair() var/datum/universal_icon/dummy_icon = render_preview_outfit(preview_outfit, dummy) qdel(dummy) return finish_preview_icon(dummy_icon) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 1a8e2a7c00f..002f2fa312e 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -221,18 +221,16 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) if(ghost_accs == GHOST_ACCS_FULL && (icon_state in GLOB.ghost_forms_with_accessories_list)) //check if this form supports accessories and if the client wants to show them if(facial_hairstyle) var/datum/sprite_accessory/S = SSaccessories.facial_hairstyles_list[facial_hairstyle] - if(S) - facial_hair_overlay = mutable_appearance(S.icon, "[S.icon_state]", -HAIR_LAYER) - if(facial_hair_color) - facial_hair_overlay.color = facial_hair_color + if(S && S.icon_state != SPRITE_ACCESSORY_NONE) + facial_hair_overlay = mutable_appearance(S.icon, S.icon_state, -HAIR_LAYER) + facial_hair_overlay.color = facial_hair_color facial_hair_overlay.alpha = 200 add_overlay(facial_hair_overlay) if(hairstyle) var/datum/sprite_accessory/hair/S = SSaccessories.hairstyles_list[hairstyle] - if(S) - hair_overlay = mutable_appearance(S.icon, "[S.icon_state]", -HAIR_LAYER) - if(hair_color) - hair_overlay.color = hair_color + if(S && S.icon_state != SPRITE_ACCESSORY_NONE) + hair_overlay = mutable_appearance(S.icon, S.icon_state, -HAIR_LAYER) + hair_overlay.color = hair_color hair_overlay.alpha = 200 hair_overlay.pixel_z = S.y_offset add_overlay(hair_overlay) diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index e02cf298603..7df3f25c317 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -130,8 +130,6 @@ if((!QDELETED(src) || !QDELETED(owner)) && !(movement_flags & NO_ID_TRANSFER)) transfer_identity(organ_owner) if(!special) - if(!(organ_owner.living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS)) - organ_owner.update_body_parts() organ_owner.clear_mood_event("brain_damage") organ_owner.med_hud_set_status() @@ -348,9 +346,21 @@ owner.investigate_log("has been killed by brain damage.", INVESTIGATE_DEATHS) owner.death() +/obj/item/organ/brain/on_bodypart_insert(obj/item/bodypart/limb) + . = ..() + if(ishuman(limb.owner)) + limb.owner.update_hair() + else + limb.update_icon_dropped() + /obj/item/organ/brain/on_bodypart_remove(obj/item/bodypart/limb, movement_flags) . = ..() - update_brain_color(animate = FALSE) // once it's out in the world we need to make sure it's the right color + if(ishuman(limb.owner)) + limb.owner.update_hair() + else + limb.update_icon_dropped() + // once it's out in the world we need to make sure it's the right color + update_brain_color(animate = FALSE) /obj/item/organ/brain/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag = NONE) . = ..() diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 3038c9b76fb..0e6efd54357 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -131,3 +131,6 @@ var/obscured_slots = NONE /// Covered hide flags (hideflags that can be seen, BUT can't be interacted with) var/covered_slots = NONE + + /// Lazylist of all hair masks applied to this mob's hairstyles + var/list/hair_masks diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index fb1ceda9558..171e53c65af 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -464,10 +464,13 @@ update_damage_overlays() update_wound_overlays() var/limb_count_update = 0 + var/head_update = FALSE var/list/new_limbs = list() for(var/body_zone, limb_untyped in get_bodyparts_by_zones()) var/obj/item/bodypart/limb = limb_untyped if(isnull(limb) || IS_STUMP(limb)) + if(body_zone == BODY_ZONE_HEAD) + head_update = TRUE if(icon_render_keys[body_zone]) icon_render_keys -= body_zone limb_count_update += 1 @@ -483,6 +486,8 @@ new_limbs += limb_icon_cache[new_key] else + if(body_zone == BODY_ZONE_HEAD) + head_update = TRUE limb_icon_cache[new_key] ||= limb.get_limb_icon(dropped = FALSE) new_limbs += limb_icon_cache[new_key] icon_render_keys[limb.body_zone] = new_key @@ -498,6 +503,10 @@ overlays_standing[BODYPARTS_LAYER] = new_limbs apply_overlay(BODYPARTS_LAYER) + // for legacy support, head changes triggers an eye/hair update + if(head_update) + update_eyes() + update_hair() /mob/living/carbon/proc/update_face_offset() return @@ -543,6 +552,12 @@ SEND_SIGNAL(src, COMSIG_BODYPART_GENERATE_ICON_KEY, .) return . +/obj/item/bodypart/head/generate_icon_key() + . = ..() + if(lip_style) + . += lip_color + . += lip_style + ///Generates a cache key specifically for husks /obj/item/bodypart/proc/generate_husk_key() RETURN_TYPE(/list) @@ -565,44 +580,6 @@ . += "[human_owner.mob_height]" return . -/obj/item/bodypart/head/generate_icon_key() - . = ..() - if(lip_style) - . += "-[lip_style]" - . += "-[lip_color]" - - if(facial_hair_hidden) - . += "-FACIAL_HAIR_HIDDEN" - else - . += "-[facial_hairstyle]" - . += "-[override_hair_color || fixed_hair_color || facial_hair_color]" - . += "-[facial_hair_alpha]" - var/facial_hair_gradient_style = get_hair_gradient_style(GRADIENT_FACIAL_HAIR_KEY) - if(facial_hair_gradient_style) - . += "-[facial_hair_gradient_style]" - . += "-[get_hair_gradient_color(GRADIENT_FACIAL_HAIR_KEY)]" - - if(show_eyeless) - . += "-SHOW_EYELESS" - if(show_debrained) - . += "-SHOW_DEBRAINED" - return . - - if(hair_hidden) - . += "-HAIR_HIDDEN" - else - . += "-[hairstyle]" - . += "-[override_hair_color || fixed_hair_color || hair_color]" - . += "-[hair_alpha]" - var/hair_gradient_style = get_hair_gradient_style(GRADIENT_HAIR_KEY) - if(hair_gradient_style) - . += "-[hair_gradient_style]" - . += "-[get_hair_gradient_color(GRADIENT_HAIR_KEY)]" - if(LAZYLEN(hair_masks)) - . += "-[jointext(hair_masks, "-")]" - - return . - GLOBAL_LIST_EMPTY(masked_leg_icons_cache) /** diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm index 1cb072c7080..2d99beba919 100644 --- a/code/modules/mob/living/carbon/human/dummy.dm +++ b/code/modules/mob/living/carbon/human/dummy.dm @@ -76,8 +76,12 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) item.item_flags |= IN_INVENTORY if(!item.visual_equipped(src, slot, initial)) return FALSE - + if(!(slot & item.slot_flags)) // Things below only update if slotted in (ie: not held) + return TRUE add_item_coverage(item) + if(item.hair_mask) + LAZYADD(hair_masks, item.hair_mask) + update_hair() return TRUE /mob/living/carbon/human/dummy/proc/wipe_state() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 0a3ab890fd8..598770ef778 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -980,12 +980,12 @@ /mob/living/carbon/human/proc/add_eye_color_left(color, color_priority, update_body = TRUE) LAZYSET(eye_color_left_overrides, "[color_priority]", color) if (update_body) - update_body() + update_eyes() /mob/living/carbon/human/proc/add_eye_color_right(color, color_priority, update_body = TRUE) LAZYSET(eye_color_right_overrides, "[color_priority]", color) if (update_body) - update_body() + update_eyes() /mob/living/carbon/human/proc/add_eye_color(color, color_priority, update_body = TRUE) add_eye_color_left(color, color_priority, update_body = FALSE) @@ -995,9 +995,12 @@ LAZYREMOVE(eye_color_left_overrides, "[color_priority]") LAZYREMOVE(eye_color_right_overrides, "[color_priority]") if (update_body) - update_body() + update_eyes() -/mob/living/carbon/human/proc/get_right_eye_color() +/mob/living/carbon/proc/get_right_eye_color() + return + +/mob/living/carbon/human/get_right_eye_color() if (!LAZYLEN(eye_color_right_overrides)) return eye_color_right @@ -1010,7 +1013,10 @@ eye_color = eye_color_right_overrides[override_priority] return eye_color -/mob/living/carbon/human/proc/get_left_eye_color() +/mob/living/carbon/proc/get_left_eye_color() + return + +/mob/living/carbon/human/get_left_eye_color() if (!LAZYLEN(eye_color_left_overrides)) return eye_color_left 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 20a79e80217..5eaf99419f1 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -48,27 +48,27 @@ There are several things that need to be remembered: /* --------------------------------------- */ //For legacy support. /mob/living/carbon/human/regenerate_icons() - - if(!..()) - update_worn_undersuit() - update_worn_id() - update_worn_glasses() - update_worn_gloves() - update_worn_ears() - update_worn_shoes() - update_suit_storage() - update_worn_mask() - update_worn_head() - update_worn_belt() - update_worn_back() - update_worn_oversuit() - update_pockets() - update_worn_neck() - update_transform() - //mutations - update_mutations_overlay() - //damage overlays - update_damage_overlays() + . = ..() + update_worn_undersuit() + update_worn_id() + update_worn_glasses() + update_worn_gloves() + update_worn_ears() + update_worn_shoes() + update_suit_storage() + update_worn_mask() + update_worn_head() + update_worn_belt() + update_worn_back() + update_worn_oversuit() + update_pockets() + update_worn_neck() + update_transform() + update_mutations_overlay() + update_damage_overlays() + // These are done via parent call update_body(), keeping them here for clarity + // update_hair() + // update_eyes() /* --------------------------------------- */ //vvvvvv UPDATE_INV PROCS vvvvvv @@ -860,57 +860,78 @@ generate/load female uniform sprites matching all previously decided variables break /mob/living/carbon/human/update_body(is_creating = FALSE) - update_eyes() - update_underwear() + remove_overlay(BODY_LAYER) + + var/list/body_overlays = list() + body_overlays += get_underwear_overlays() + + if(length(body_overlays)) + overlays_standing[BODY_LAYER] = body_overlays + apply_overlay(BODY_LAYER) + + // parent call will update the actual bodyparts return ..() -/mob/living/carbon/human/proc/update_underwear() - remove_overlay(BODY_LAYER) +/// Returns a list of all underclothing overlays to be applied to the mob +/mob/living/carbon/human/proc/get_underwear_overlays() + . = list() if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN) || HAS_TRAIT(src, TRAIT_NO_UNDERWEAR)) - return + return . + // Underwear, Undershirts & Socks - var/list/standing = list() var/active_bodyshapes = get_active_bodyshapes() if(underwear) var/datum/sprite_accessory/clothing/underwear/undie_accessory = SSaccessories.underwear_list[underwear] var/mutable_appearance/underwear_overlay = undie_accessory?.make_appearance(underwear_color, physique, active_bodyshapes) if(underwear_overlay) - standing += underwear_overlay + . += underwear_overlay if(undershirt) var/datum/sprite_accessory/clothing/undershirt/shirt_accessory = SSaccessories.undershirt_list[undershirt] var/mutable_appearance/shirt_overlay = shirt_accessory?.make_appearance(null, physique, active_bodyshapes) if(shirt_overlay) - standing += shirt_overlay + . += shirt_overlay if(socks && num_legs >= 2 && !(bodyshape & BODYSHAPE_DIGITIGRADE)) var/datum/sprite_accessory/clothing/socks/sock_accessory = SSaccessories.socks_list[socks] var/mutable_appearance/socks_overlay = sock_accessory?.make_appearance(null, physique, active_bodyshapes) if(socks_overlay) - standing += socks_overlay + . += socks_overlay - if(standing.len) - overlays_standing[BODY_LAYER] = standing + return . - apply_overlay(BODY_LAYER) +/// Updates eye sprites if relevant +/mob/living/proc/update_eyes(refresh = TRUE) + return -/mob/living/carbon/human/proc/update_eyes() +/mob/living/carbon/human/update_eyes(refresh = TRUE) remove_overlay(EYES_LAYER) - if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) + + var/obj/item/bodypart/head/noggin = get_bodypart(BODY_ZONE_HEAD) + if(isnull(noggin) || noggin.is_husked || noggin.is_invisible) 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) - return + if(refresh) + var/obj/item/organ/eyes/eyes = locate() in noggin + eyes?.refresh(src, call_update = FALSE) - var/obj/item/bodypart/head/noggin = get_bodypart(deprecise_zone(eye_organ.zone)) // Futureproofing for HARS/weird species - if(istype(noggin) && !(noggin?.head_flags & HEAD_EYESPRITES)) - return + var/list/eye_overlays = noggin.get_eye_overlays() + if(length(eye_overlays)) + overlays_standing[EYES_LAYER] = eye_overlays + apply_overlay(EYES_LAYER) - eye_organ.refresh(call_update = FALSE) - overlays_standing[EYES_LAYER] = eye_organ.generate_body_overlay(src, noggin) - apply_overlay(EYES_LAYER) +/// Updates hair sprites if relevant +/mob/living/proc/update_hair() + return + +/mob/living/carbon/human/update_hair() + remove_overlay(HAIR_LAYER) + + var/obj/item/bodypart/head/head = get_bodypart(BODY_ZONE_HEAD) + var/list/head_overlays = head?.get_hair_overlays() + if(length(head_overlays)) + overlays_standing[HAIR_LAYER] = head_overlays + apply_overlay(HAIR_LAYER) /// Updates face (as of now, only eye) offsets /mob/living/carbon/human/update_face_offset() @@ -931,8 +952,8 @@ generate/load female uniform sprites matching all previously decided variables apply_overlay(EYES_LAYER) -// Only renders the head of the human -/mob/living/carbon/human/proc/update_body_parts_head_only(update_limb_data) +/// Makes all aspects of the mob invisibile but the head and its associated sprites +/mob/living/carbon/human/proc/render_only_head() if(!dna?.species) return @@ -941,11 +962,13 @@ generate/load female uniform sprites matching all previously decided variables if(!istype(my_head)) return - my_head.update_limb(is_creating = update_limb_data) - - add_overlay(my_head.get_limb_icon(dropped = FALSE, update_on = src)) + cut_overlays() + my_head.update_limb() + add_overlay(my_head.get_limb_icon()) update_worn_head() update_worn_mask() + update_eyes() + update_hair() /** * Used to perform regular updates to the limbs of humans with special bodyshapes diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm index 55ab5d6b6b8..9f864884723 100644 --- a/code/modules/mob/living/carbon/human/init_signals.dm +++ b/code/modules/mob/living/carbon/human/init_signals.dm @@ -12,8 +12,9 @@ RegisterSignal(src, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(clean_face)) - RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_HUSK), SIGNAL_REMOVETRAIT(TRAIT_HUSK)), PROC_REF(refresh_obscured)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_HUSK), SIGNAL_REMOVETRAIT(TRAIT_HUSK)), PROC_REF(husk_trait_toggle)) RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_INVISIBLE_MAN), SIGNAL_REMOVETRAIT(TRAIT_INVISIBLE_MAN)), PROC_REF(invisible_man_toggle)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_NO_UNDERWEAR), SIGNAL_REMOVETRAIT(TRAIT_NO_UNDERWEAR)), PROC_REF(no_underwear_toggle)) /// Gaining or losing [TRAIT_DWARF] updates our height and grants passtable /mob/living/carbon/human/proc/on_dwarf_trait(datum/source) @@ -67,8 +68,19 @@ ) playsound(src, SFX_RUSTLE, 50, TRUE, -5, frequency = 0.8) +/mob/living/carbon/human/proc/husk_trait_toggle(datum/source) + SIGNAL_HANDLER + refresh_obscured() + update_body() + /// When [TRAIT_INVISIBLE_MAN] is added or removed we need to update a few things /mob/living/carbon/human/proc/invisible_man_toggle(datum/source) SIGNAL_HANDLER refresh_obscured() update_visible_name() + update_body() + +/// When [TRAIT_NO_UNDERWEAR] is added or removed we need to update our body to hide or show underwear sprites +/mob/living/carbon/human/proc/no_underwear_toggle(datum/source) + SIGNAL_HANDLER + update_body() diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index fb751eefe2e..777e2ca64bd 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -60,7 +60,6 @@ eyes.eye_color_left = human.eye_color_left eyes.eye_color_right = human.eye_color_right eyes.bodypart_insert(head) - human.update_body() head.update_limb() head.update_icon_dropped() RegisterSignal(head, COMSIG_QDELETING, PROC_REF(on_head_destroyed)) diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index bfa1ae5649b..2de3d7fc799 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -112,16 +112,16 @@ currently_flickered = FALSE ethereal_light.set_light_on(TRUE) fixed_mut_color = current_color - ethereal.update_body() ethereal.set_facial_haircolor(current_color, override = TRUE, update = FALSE) - ethereal.set_haircolor(current_color, override = TRUE, update = TRUE) + ethereal.set_haircolor(current_color, override = TRUE, update = FALSE) + ethereal.update_body() else ethereal_light.set_light_on(FALSE) var/dead_color = rgb(128,128,128) fixed_mut_color = dead_color - ethereal.update_body() ethereal.set_facial_haircolor(dead_color, override = TRUE, update = FALSE) - ethereal.set_haircolor(dead_color, override = TRUE, update = TRUE) + ethereal.set_haircolor(dead_color, override = TRUE, update = FALSE) + ethereal.update_body() /datum/species/ethereal/proc/on_emp_act(mob/living/carbon/human/source, severity, protection) SIGNAL_HANDLER 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 28f676cb13f..124196d02af 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -128,7 +128,7 @@ var/obj/item/organ/ears/cat/cat_ears = human_for_preview.get_organ_by_type(/obj/item/organ/ears/cat) if (cat_ears) cat_ears.color = human_for_preview.hair_color - human_for_preview.update_body() + human_for_preview.update_hair() /datum/species/human/felinid/get_physical_attributes() return "Felinids are very similar to humans in almost all respects, with their biggest differences being the ability to lick their wounds, \ diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 6013c6d2b92..fb891df0f26 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -31,7 +31,6 @@ . = ..() to_chat(new_vampire, "[info_text]") new_vampire.skin_tone = "albino" - new_vampire.update_body(0) RegisterSignal(new_vampire, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby)) RegisterSignal(new_vampire, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created)) if(new_vampire.hud_used) diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index fe4dcaca550..b8dfca193ba 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -172,7 +172,8 @@ if(!(slot & item.slot_flags)) // Things below only update if slotted in (ie: not held) return if(item.hair_mask) - update_body() + LAZYADD(hair_masks, item.hair_mask) + update_hair() add_item_coverage(item) /mob/living/carbon/has_unequipped(obj/item/item) @@ -182,7 +183,8 @@ hud_used?.update_locked_slots() if(item.hair_mask) - update_body() + LAZYREMOVE(hair_masks, item.hair_mask) + update_hair() remove_item_coverage(item) /mob/living/carbon/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index c04a8c4c282..58ad4aac12a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2413,7 +2413,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) return if(. <= UNCONSCIOUS || new_stat >= UNCONSCIOUS) - update_body() // to update eyes + update_eyes() switch(.) //Previous stat. if(CONSCIOUS) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 765bfe5b89f..2c5f0af4451 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -513,19 +513,17 @@ REMOVE_TRAIT(src, TRAIT_HUSK, source) if(HAS_TRAIT(src, TRAIT_HUSK)) return FALSE - update_body() UnregisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_UNHUSKABLE)) return TRUE /mob/living/proc/become_husk(source) if(HAS_TRAIT(src, TRAIT_UNHUSKABLE)) - return + return FALSE var/was_husk = HAS_TRAIT(src, TRAIT_HUSK) ADD_TRAIT(src, TRAIT_HUSK, source) - if (was_husk) - return - update_body() - RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_UNHUSKABLE), PROC_REF(became_unhuskable)) + if (!was_husk) + RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_UNHUSKABLE), PROC_REF(became_unhuskable)) + return TRUE /// Called when we become unhuskable while already husked /mob/living/proc/became_unhuskable() diff --git a/code/modules/mob/mob_update_icons.dm b/code/modules/mob/mob_update_icons.dm index 210c041408c..bbd617f97f7 100644 --- a/code/modules/mob/mob_update_icons.dm +++ b/code/modules/mob/mob_update_icons.dm @@ -95,9 +95,6 @@ /mob/proc/update_body() return -/mob/proc/update_hair() - return - ///Updates the glasses overlay & HUD element. /mob/proc/update_worn_glasses() return diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 5b1cced95ad..2e776ebfb06 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -769,7 +769,6 @@ invisible_man.add_traits(list(TRAIT_INVISIBLE_MAN, TRAIT_HIDE_EXTERNAL_ORGANS, TRAIT_NO_BLOOD_OVERLAY), type) - invisible_man.update_body() invisible_man.remove_from_all_data_huds() invisible_man.sound_environment_override = SOUND_ENVIROMENT_PHASED @@ -781,7 +780,6 @@ to_chat(invisible_man, span_notice("As you sober up, opacity once again returns to your body meats.")) - invisible_man.update_body() invisible_man.sound_environment_override = NONE if(!invisible_man.hud_used) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index f8d821b0b84..a43c37a9018 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -3501,7 +3501,7 @@ if (eyes && !IS_ROBOTIC_ORGAN(eyes)) eyes.eye_color_left = color eyes.eye_color_right = color - affected_human.update_body() + affected_human.update_eyes() /datum/reagent/luminescent_fluid/red name = "Red Luminiscent Fluid" diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 72facbf11ac..fad8a3ada56 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -281,6 +281,7 @@ if (length(drop_results)) butcher_drops = string_list(drop_results) butcher_drop_cache[type] = butcher_drops + update_limb(TRUE) update_icon_dropped() refresh_bleed_rate() @@ -1168,7 +1169,7 @@ update_draw_color() if(!is_creating || !owner) - return + return FALSE // There should technically to be an ishuman(owner) check here, but it is absent because no basetype carbons use bodyparts // No, xenos don't actually use bodyparts. Don't ask. diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index d0a3d18cfa2..2ae41bd5eae 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -328,7 +328,7 @@ // behavior within said bodyparts list. We sort it here, as it's the only place we make changes to bodyparts. new_limb_owner.bodyparts = sort_list(new_limb_owner.bodyparts, GLOBAL_PROC_REF(cmp_bodypart_by_body_part_asc)) new_limb_owner.updatehealth() - new_limb_owner.update_body() + new_limb_owner.update_body() // updates lips + hair + eyes new_limb_owner.update_damage_overlays() if(!special) new_limb_owner.hud_used?.update_locked_slots() @@ -372,7 +372,7 @@ sexy_chad.lip_color = lip_color new_head_owner.updatehealth() - new_head_owner.update_body() + new_head_owner.update_body() // updates lips + hair + eyes new_head_owner.update_damage_overlays() /obj/item/bodypart/arm/try_attach_limb(mob/living/carbon/new_arm_owner, special, lazy) diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 034c1f8e752..3a2f4ef01fa 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -44,10 +44,6 @@ var/hair_color = COLOR_BLACK /// Hair alpha var/hair_alpha = 255 - /// Is the hair currently hidden by something? - var/hair_hidden = FALSE - /// Lazy initialized hashset of all hair mask types that should be applied - var/list/hair_masks ///Facial hair style var/facial_hairstyle = "Shaved" @@ -55,8 +51,6 @@ var/facial_hair_color = COLOR_BLACK ///Facial hair alpha var/facial_hair_alpha = 255 - ///Is the facial hair currently hidden by something? - var/facial_hair_hidden = FALSE /// Gradient styles, if any var/list/gradient_styles @@ -89,15 +83,8 @@ /// Offset to apply to overlays placed on the face var/datum/worn_feature_offset/worn_face_offset - VAR_PROTECTED - /// Draw this head as "debrained" - show_debrained = FALSE - - /// Draw this head as missing eyes - show_eyeless = FALSE - - /// Can this head be dismembered normally? - can_dismember = FALSE + /// Can this head be dismembered normally? + VAR_PROTECTED/can_dismember = FALSE /obj/item/bodypart/head/Initialize(mapload) . = ..() @@ -188,50 +175,25 @@ /obj/item/bodypart/head/update_limb(dropping_limb, is_creating) . = ..() - if(!isnull(owner)) + if(isnull(owner)) + return + if(is_husked) + ADD_TRAIT(src, TRAIT_DISFIGURED, HUSK_TRAIT) + else + REMOVE_TRAIT(src, TRAIT_DISFIGURED, HUSK_TRAIT) + if(is_creating) real_name = owner.real_name - if(is_husked) - ADD_TRAIT(src, TRAIT_DISFIGURED, HUSK_TRAIT) - else - REMOVE_TRAIT(src, TRAIT_DISFIGURED, HUSK_TRAIT) - update_hair_and_lips(dropping_limb, is_creating) - -// Ensures putting organs in and removing organs from our head always updates the limb -/obj/item/bodypart/head/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - . = ..() - if(isorgan(arrived) && !ismob(loc)) - addtimer(CALLBACK(src, PROC_REF(update_head_on_organ_movement)), 1, TIMER_UNIQUE|TIMER_DELETE_ME) - -/obj/item/bodypart/head/Exited(atom/movable/gone, direction) - . = ..() - if(isorgan(gone) && !ismob(loc)) - addtimer(CALLBACK(src, PROC_REF(update_head_on_organ_movement)), 1, TIMER_UNIQUE|TIMER_DELETE_ME) - -/obj/item/bodypart/head/proc/update_head_on_organ_movement() - update_limb() - update_icon_dropped() + copy_appearance_from(owner) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/obj/item/bodypart/head/get_limb_icon(dropped, mob/living/carbon/update_on) +/obj/item/bodypart/head/get_limb_icon(dropped) . = ..() - . += get_hair_and_lips_icon(dropped) - // We need to get the eyes if we are dropped (ugh) - if(!dropped) - return + if(dropped) // These overlays are applied as standing overlays so we only need them if dropped + . += get_hair_overlays(dropped) + . += get_eye_overlays(dropped) - var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in src - if(!eyes) - if (!(head_flags & HEAD_EYEHOLES)) - return - var/image/no_eyes = image('icons/mob/human/human_eyes.dmi', "eyes_missing", -EYES_LAYER, SOUTH) - worn_face_offset?.apply_offset(no_eyes) - . += no_eyes - return - - if(head_flags & HEAD_EYESPRITES) - for (var/mutable_appearance/overlay as anything in eyes.generate_body_overlay(null, src)) - . += image(overlay, dir = SOUTH) + . += get_lips_overlays(dropped) /obj/item/bodypart/head/get_voice(add_id_name) return "The head of [get_face_name()]" diff --git a/code/modules/surgery/bodyparts/head_hair_and_lips.dm b/code/modules/surgery/bodyparts/head_hair_and_lips.dm index f2af67ebac7..43689b1852b 100644 --- a/code/modules/surgery/bodyparts/head_hair_and_lips.dm +++ b/code/modules/surgery/bodyparts/head_hair_and_lips.dm @@ -1,49 +1,3 @@ -#define SET_OVERLAY_VALUE(overlay,variable,value) if(overlay) overlay.variable = value - -/// Part of `update_limb()`, basically does all the head specific icon stuff. -/obj/item/bodypart/head/proc/update_hair_and_lips(dropping_limb, is_creating) - var/mob/living/carbon/human/human_head_owner = owner - - //HIDDEN CHECKS START - hair_hidden = FALSE - facial_hair_hidden = FALSE - LAZYNULL(hair_masks) - if(human_head_owner) - for(var/obj/item/worn_item in human_head_owner.get_equipped_items(INCLUDE_ABSTRACT)) - if(worn_item.hair_mask) - LAZYSET(hair_masks, worn_item.hair_mask, TRUE) - hair_hidden = !!(human_head_owner.obscured_slots & HIDEHAIR) - facial_hair_hidden = !!(human_head_owner.obscured_slots & HIDEFACIALHAIR) - if(is_husked) - hair_hidden = TRUE - facial_hair_hidden = TRUE - //HIDDEN CHECKS END - - if(owner) - if(!hair_hidden && !owner.get_organ_slot(ORGAN_SLOT_BRAIN) && !HAS_TRAIT(owner, TRAIT_NO_DEBRAIN_OVERLAY)) - show_debrained = TRUE - else - show_debrained = FALSE - - if(!owner.get_organ_slot(ORGAN_SLOT_EYES)) - show_eyeless = TRUE - else - show_eyeless = FALSE - else - if(!hair_hidden && !(locate(/obj/item/organ/brain) in src)) - show_debrained = TRUE - else - show_debrained = FALSE - - if(!(locate(/obj/item/organ/eyes) in src)) - show_eyeless = TRUE - else - show_eyeless = FALSE - - if(!is_creating || !owner) - return - copy_appearance_from(human_head_owner) - /obj/item/bodypart/head/proc/copy_appearance_from(mob/living/carbon/human/target, overwrite_eyes = FALSE) var/datum/species/target_species = target.dna.species @@ -77,110 +31,145 @@ skin_tone = "" species_color = "" -/obj/item/bodypart/head/proc/get_hair_and_lips_icon(dropped) - SHOULD_CALL_PARENT(TRUE) - RETURN_TYPE(/list) +/// Returns a list of all overlays associated with the lips +/obj/item/bodypart/head/proc/get_lips_overlays(dropped) . = list() + if(!lip_style || is_husked || is_invisible || (owner?.obscured_slots & HIDEFACIALHAIR) || !(head_flags & HEAD_LIPS)) + return . - var/atom/location = loc || owner || src - var/image_dir = null - if (dropped) - image_dir = SOUTH + var/image/lip_overlay = image('icons/mob/human/human_face.dmi', "lips_[lip_style]", -BODY_LAYER, dir = (dropped ? SOUTH : null)) + lip_overlay.color = lip_color + worn_face_offset?.apply_offset(lip_overlay) + . += lip_overlay + return . - var/datum/sprite_accessory/sprite_accessory - if(!facial_hair_hidden && lip_style && (head_flags & HEAD_LIPS)) - //not a sprite accessory, don't ask - //Overlay - var/image/lip_overlay = image('icons/mob/human/human_face.dmi', "lips_[lip_style]", -BODY_LAYER, dir = image_dir) - lip_overlay.color = lip_color - //Offsets - worn_face_offset?.apply_offset(lip_overlay) - . += lip_overlay +/// Returns a list of all hair/facial hair related overlays, or alternatively the debrained overlay if applicable +/obj/item/bodypart/head/proc/get_hair_overlays(dropped) + . = list() + var/hair_hidden = is_husked || is_invisible || (owner?.obscured_slots & HIDEHAIR) + var/facial_hair_hidden = is_husked || is_invisible || (owner?.obscured_slots & HIDEFACIALHAIR) - var/image/facial_hair_overlay - if(!facial_hair_hidden && facial_hairstyle && (head_flags & HEAD_FACIAL_HAIR)) - sprite_accessory = SSaccessories.facial_hairstyles_list[facial_hairstyle] - if(sprite_accessory) - //Overlay - facial_hair_overlay = image(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, dir = image_dir) - facial_hair_overlay.alpha = facial_hair_alpha - //Emissive blocker - if(blocks_emissive != EMISSIVE_BLOCK_NONE) - var/mutable_appearance/em_block = emissive_blocker(facial_hair_overlay.icon, facial_hair_overlay.icon_state, location, alpha = facial_hair_alpha) - if (dropped) - em_block = image(em_block, dir = SOUTH) - facial_hair_overlay.overlays += em_block - //Offsets - worn_face_offset?.apply_offset(facial_hair_overlay) - . += facial_hair_overlay - //Gradients - var/facial_hair_gradient_style = get_hair_gradient_style(GRADIENT_FACIAL_HAIR_KEY) - if(facial_hair_gradient_style != "None") - var/facial_hair_gradient_color = get_hair_gradient_color(GRADIENT_FACIAL_HAIR_KEY) - var/image/facial_hair_gradient_overlay = get_gradient_overlay(icon(sprite_accessory.icon, sprite_accessory.icon_state), -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color, dropped) - . += facial_hair_gradient_overlay + if(!facial_hair_hidden && (head_flags & HEAD_FACIAL_HAIR)) + . += get_base_facial_hair_overlays(dropped) - var/list/all_hair_overlays = list() - if(!(show_debrained && (head_flags & HEAD_DEBRAIN)) && !hair_hidden && hairstyle && (head_flags & HEAD_HAIR)) - var/datum/sprite_accessory/hair/hair_sprite_accessory = SSaccessories.hairstyles_list[hairstyle] - if(hair_sprite_accessory) - //Hair masks - var/icon/base_icon = icon(hair_sprite_accessory.getCachedIcon(hair_masks)) - //Overlay - all_hair_overlays += image(base_icon, layer=-HAIR_LAYER, dir = image_dir) - //If we have any hair appendages (ponytails, etc.) sticking out on a particular side, we need to add an additional hair layer to go above hats/helmets for the sides they stick out on - if(LAZYLEN(hair_sprite_accessory.hair_appendages_outer)) - var/strictly_masked_zones = NONE - for(var/datum/hair_mask/mask as anything in hair_masks) - strictly_masked_zones |= mask.strict_coverage_zones - for(var/appendage_icon_state in hair_sprite_accessory.hair_appendages_outer) - var/appendage_zone = hair_sprite_accessory.hair_appendages_outer[appendage_icon_state] - if(!(appendage_zone & strictly_masked_zones)) // if there are no strict masks in this zone - all_hair_overlays += image(hair_sprite_accessory.icon, icon_state=appendage_icon_state, layer=-OUTER_HAIR_LAYER, dir = image_dir) - for(var/image/hair_overlay as anything in all_hair_overlays) - hair_overlay.alpha = hair_alpha - hair_overlay.pixel_z = hair_sprite_accessory.y_offset - //Emissive blocker - if(blocks_emissive != EMISSIVE_BLOCK_NONE) - var/mutable_appearance/em_block = emissive_blocker(hair_overlay.icon, hair_overlay.icon_state, location, alpha = hair_alpha) - if (dropped) - em_block = image(em_block, dir = SOUTH) - hair_overlay.overlays += em_block - //Offsets - worn_face_offset?.apply_offset(hair_overlay) - . += hair_overlay - //Gradients - var/hair_gradient_style = get_hair_gradient_style(GRADIENT_HAIR_KEY) - if(hair_gradient_style != "None") - var/hair_gradient_color = get_hair_gradient_color(GRADIENT_HAIR_KEY) - var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, hair_overlay.layer, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, dropped) - hair_gradient_overlay.pixel_z = hair_sprite_accessory.y_offset - . += hair_gradient_overlay - - if(show_debrained && (head_flags & HEAD_DEBRAIN)) - . += get_debrain_overlay(dropped) - - if(show_eyeless && (head_flags & HEAD_EYEHOLES)) - . += get_eyeless_overlay(dropped) - - //HAIR COLOR START - if(override_hair_color) - SET_OVERLAY_VALUE(facial_hair_overlay, color, override_hair_color) - for(var/image/hair_overlay as anything in all_hair_overlays) - SET_OVERLAY_VALUE(hair_overlay, color, override_hair_color) - else if(fixed_hair_color) - SET_OVERLAY_VALUE(facial_hair_overlay, color, fixed_hair_color) - for(var/image/hair_overlay as anything in all_hair_overlays) - SET_OVERLAY_VALUE(hair_overlay, color, fixed_hair_color) - else - SET_OVERLAY_VALUE(facial_hair_overlay, color, facial_hair_color) - for(var/image/hair_overlay as anything in all_hair_overlays) - SET_OVERLAY_VALUE(hair_overlay, color, hair_color) - //HAIR COLOR END + if(!hair_hidden) + var/obj/item/organ/brain/brain = locate() in src + if(QDELETED(brain) && (head_flags & HEAD_DEBRAIN)) + . += get_debrain_overlay(dropped) + else if(head_flags & HEAD_HAIR) + . += get_base_hair_overlays(dropped) return . -#undef SET_OVERLAY_VALUE +/// Used in constructing the hair overlays - handles just facial hair +/obj/item/bodypart/head/proc/get_base_facial_hair_overlays(dropped) + PRIVATE_PROC(TRUE) + . = list() + var/datum/sprite_accessory/facial_hair/sprite_accessory = SSaccessories.facial_hairstyles_list[facial_hairstyle] + if(!sprite_accessory || sprite_accessory.icon_state == SPRITE_ACCESSORY_NONE) + return . + + var/atom/location = loc || owner || src + var/image_dir = dropped ? SOUTH : null + + // Overlay + var/image/facial_hair_overlay = image(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, dir = image_dir) + facial_hair_overlay.alpha = facial_hair_alpha + set_overlay_hair_color(facial_hair_overlay) + // Emissive blocker + if(blocks_emissive != EMISSIVE_BLOCK_NONE) + var/mutable_appearance/em_block = emissive_blocker(facial_hair_overlay.icon, facial_hair_overlay.icon_state, location, alpha = facial_hair_alpha) + if (dropped) + em_block = image(em_block, dir = SOUTH) + facial_hair_overlay.overlays += em_block + + //Offsets + worn_face_offset?.apply_offset(facial_hair_overlay) + . += facial_hair_overlay + + //Gradients + var/facial_hair_gradient_style = get_hair_gradient_style(GRADIENT_FACIAL_HAIR_KEY) + if(facial_hair_gradient_style != SPRITE_ACCESSORY_NONE) + var/facial_hair_gradient_color = get_hair_gradient_color(GRADIENT_FACIAL_HAIR_KEY) + var/image/facial_hair_gradient_overlay = get_gradient_overlay(icon(sprite_accessory.icon, sprite_accessory.icon_state), -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color, dropped) + . += facial_hair_gradient_overlay + + return . + +/// Used in constructing the hair overlays - handles just the hair on top of the head +/obj/item/bodypart/head/proc/get_base_hair_overlays(dropped) + PRIVATE_PROC(TRUE) + . = list() + var/datum/sprite_accessory/hair/hair_sprite_accessory = SSaccessories.hairstyles_list[hairstyle] + if(!hair_sprite_accessory || hair_sprite_accessory.icon_state == SPRITE_ACCESSORY_NONE) + return . + + var/atom/location = loc || owner || src + var/image_dir = dropped ? SOUTH : null + + var/list/all_hair_overlays = list() + // Hair masks + var/icon/base_icon = icon(hair_sprite_accessory.getCachedIcon(owner?.hair_masks)) + // Overlay + all_hair_overlays += image(base_icon, layer = -HAIR_LAYER, dir = image_dir) + // If we have any hair appendages (ponytails, etc.) sticking out on a particular side, + // we need to add an additional hair layer to go above hats/helmets for the sides they stick out on + if(LAZYLEN(hair_sprite_accessory.hair_appendages_outer)) + var/strictly_masked_zones = NONE + for(var/datum/hair_mask/mask as anything in owner?.hair_masks) + strictly_masked_zones |= mask.strict_coverage_zones + for(var/appendage_icon_state in hair_sprite_accessory.hair_appendages_outer) + var/appendage_zone = hair_sprite_accessory.hair_appendages_outer[appendage_icon_state] + if(!(appendage_zone & strictly_masked_zones)) // if there are no strict masks in this zone + all_hair_overlays += image(hair_sprite_accessory.icon, icon_state = appendage_icon_state, layer = -OUTER_HAIR_LAYER, dir = image_dir) + + for(var/image/hair_overlay as anything in all_hair_overlays) + set_overlay_hair_color(hair_overlay) + hair_overlay.alpha = hair_alpha + hair_overlay.pixel_z = hair_sprite_accessory.y_offset + // Emissive blocker + if(blocks_emissive != EMISSIVE_BLOCK_NONE) + var/mutable_appearance/em_block = emissive_blocker(hair_overlay.icon, hair_overlay.icon_state, location, alpha = hair_alpha) + if (dropped) + em_block = image(em_block, dir = SOUTH) + hair_overlay.overlays += em_block + // Offsets + worn_face_offset?.apply_offset(hair_overlay) + . += hair_overlay + // Gradients + var/hair_gradient_style = get_hair_gradient_style(GRADIENT_HAIR_KEY) + if(hair_gradient_style != SPRITE_ACCESSORY_NONE) + var/hair_gradient_color = get_hair_gradient_color(GRADIENT_HAIR_KEY) + var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, hair_overlay.layer, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, dropped) + hair_gradient_overlay.pixel_z = hair_sprite_accessory.y_offset + . += hair_gradient_overlay + + return . + +/// Helper for setting hair color of an overlay appropriately +/obj/item/bodypart/head/proc/set_overlay_hair_color(image/hair_overlay) + PRIVATE_PROC(TRUE) + if(override_hair_color) + hair_overlay.color = override_hair_color + else if(fixed_hair_color) + hair_overlay.color = fixed_hair_color + else + hair_overlay.color = hair_color + +/// Returns a list of all eye related overlays, or an eyeless overlay if applicable +/obj/item/bodypart/head/proc/get_eye_overlays(dropped) + . = list() + + var/obj/item/organ/eyes/eyes = locate() in src + if(QDELETED(eyes)) + if(head_flags & HEAD_EYEHOLES) + . += get_eyeless_overlay(dropped) + return . + + if(head_flags & HEAD_EYESPRITES) + . += eyes.generate_body_overlay(src) + + return . /// Returns an appropriate debrained overlay /obj/item/bodypart/head/proc/get_debrain_overlay(dropped) @@ -251,7 +240,7 @@ hopefully_a_head?.stored_lipstick_trait = apply_trait if(update) - update_body_parts() + update_body() // lips is done as a body layer /** * A wrapper for [mob/living/carbon/human/proc/update_lips] that sets the lip style and color to null. @@ -279,7 +268,7 @@ my_head?.hairstyle = new_style if(update) - update_body_parts() + update_hair() /** * Set the hair color of a human. @@ -300,7 +289,7 @@ my_head?.hair_color = hex_string if(update) - update_body_parts() + update_hair() /** * Get the hair gradient style of a human. @@ -352,7 +341,7 @@ LAZYSET(my_head.gradient_styles, GRADIENT_HAIR_KEY, new_style) if(update) - update_body_parts() + update_hair() /** * Get the hair gradient color of a human. @@ -406,7 +395,7 @@ LAZYSET(my_head.gradient_colors, GRADIENT_HAIR_KEY, new_color) if(update) - update_body_parts() + update_hair() /** * Set the facial hair style of a human. @@ -422,7 +411,7 @@ my_head?.facial_hairstyle = new_style if(update) - update_body_parts() + update_hair() /** * Set the facial hair color of a human. @@ -443,7 +432,7 @@ my_head?.facial_hair_color = hex_string if(update) - update_body_parts() + update_hair() /** * Set the facial hair gradient style of a human. @@ -462,7 +451,7 @@ LAZYSET(my_head.gradient_styles, GRADIENT_FACIAL_HAIR_KEY, new_style) if(update) - update_body_parts() + update_hair() /** * Set the facial hair gradient color of a human. @@ -481,4 +470,4 @@ LAZYSET(my_head.gradient_colors, GRADIENT_FACIAL_HAIR_KEY, new_color) if(update) - update_body_parts() + update_hair() diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index 64285ea5afc..a23b7ed0683 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -112,8 +112,11 @@ /// Refreshes the visuals of the eyes /// If call_update is TRUE, we also will call update_body /obj/item/organ/eyes/proc/refresh(mob/living/carbon/eye_owner = owner, call_update = TRUE) - owner.update_sight() - owner.update_tint() + if(isnull(eye_owner)) + return + + eye_owner.update_sight() + eye_owner.update_tint() if(!ishuman(eye_owner)) return @@ -131,7 +134,7 @@ affected_human.add_fov_trait(type, native_fov) if(call_update) - affected_human.update_body() + affected_human.update_eyes() /obj/item/organ/eyes/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() @@ -144,7 +147,7 @@ if(native_fov) organ_owner.remove_fov_trait(type) if(!special) - human_owner.update_body() + human_owner.update_eyes(refresh = FALSE) // become blind (if not special) if(!special) @@ -173,6 +176,20 @@ SIGNAL_REMOVETRAIT(TRAIT_REFLECTIVE_EYES), )) +/obj/item/organ/eyes/on_bodypart_insert(obj/item/bodypart/limb) + . = ..() + if(ishuman(limb.owner)) + limb.owner.update_eyes(refresh = FALSE) + else + limb.update_icon_dropped() + +/obj/item/organ/eyes/on_bodypart_remove(obj/item/bodypart/limb, movement_flags) + . = ..() + if(ishuman(limb.owner)) + limb.owner.update_eyes(refresh = FALSE) + else + limb.update_icon_dropped() + ///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 @@ -182,7 +199,7 @@ . = ..() if (ishuman(owner)) refresh_atom_color_overrides() - owner.update_body() + owner.update_eyes() /// Adds eye color overrides to our owner from our atom color /obj/item/organ/eyes/proc/refresh_atom_color_overrides() @@ -285,47 +302,42 @@ // Always show if we have an appendix return ..() || (owner.stat != DEAD && !HAS_TRAIT(owner, TRAIT_KNOCKEDOUT) && (owner.is_blind() || owner.is_nearsighted())) -/// This proc generates a list of overlays that the eye should be displayed using for the given parent -/obj/item/organ/eyes/proc/generate_body_overlay(mob/living/carbon/human/parent, obj/item/bodypart/limb) - if(isnull(eye_icon_state)) +/// This proc generates a list of overlays that the eye displays on the given head +/obj/item/organ/eyes/proc/generate_body_overlay(obj/item/bodypart/head/my_head) + if(!eye_icon_state || isnull(my_head)) return list() - var/mutable_appearance/eye_left = mutable_appearance(eye_icon, "[eye_icon_state]_l", -EYES_LAYER, parent || limb) - var/mutable_appearance/eye_right = mutable_appearance(eye_icon, "[eye_icon_state]_r", -EYES_LAYER, parent || limb) + var/eye_dir = my_head.owner ? null : SOUTH + var/mutable_appearance/eye_left = mutable_appearance(eye_icon, "[eye_icon_state]_l", -EYES_LAYER) + var/mutable_appearance/eye_right = mutable_appearance(eye_icon, "[eye_icon_state]_r", -EYES_LAYER) + eye_left.dir = eye_dir + eye_right.dir = eye_dir + var/list/overlays = list(eye_left, eye_right) - if(!(parent?.obscured_slots & HIDEEYES)) - overlays += get_emissive_overlays(eye_left, eye_right, parent || limb) + if(my_head.owner && !(my_head.owner.obscured_slots & HIDEEYES)) + overlays += get_emissive_overlays(eye_left, eye_right, my_head) - if(!limb) - return overlays - - // Futureproofing for HARS/weird species - var/obj/item/bodypart/head/head = astype(limb, /obj/item/bodypart/head) - if(head?.head_flags & HEAD_EYECOLOR) - if (parent) - eye_right.color = parent.get_right_eye_color() - eye_left.color = parent.get_left_eye_color() - var/list/eyelids = setup_eyelids(eye_left, eye_right, parent) - if (LAZYLEN(eyelids)) - overlays += eyelids - else - eye_right.color = eye_color_right - eye_left.color = eye_color_left + if(my_head.head_flags & HEAD_EYECOLOR) + eye_right.color = eye_color_right || my_head.owner?.get_right_eye_color() + eye_left.color = eye_color_left || my_head.owner?.get_left_eye_color() + var/list/eyelids = get_eyelid_overlays(eye_left, eye_right, my_head) + if (LAZYLEN(eyelids)) + overlays += eyelids if (scarring & RIGHT_EYE_SCAR) - var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_right", -EYES_LAYER, parent || limb) - right_scar.color = limb.draw_color + var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_right", -EYES_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_eyes.dmi', "eye_scar_left", -EYES_LAYER, parent || limb) - left_scar.color = limb.draw_color + var/mutable_appearance/left_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_left", -EYES_LAYER) + left_scar.color = my_head.draw_color overlays += left_scar - if(head?.worn_face_offset) + if(my_head.worn_face_offset) for (var/mutable_appearance/overlay as anything in overlays) - head.worn_face_offset.apply_offset(overlay) + my_head.worn_face_offset.apply_offset(overlay) return overlays @@ -392,7 +404,7 @@ owner.assign_nearsightedness(TRAIT_LEFT_EYE_SCAR, 1, FALSE) if((scarring & RIGHT_EYE_SCAR) && (scarring & LEFT_EYE_SCAR)) owner.become_blind(EYE_SCARRING_TRAIT) - owner.update_body() + owner.update_eyes() /obj/item/organ/eyes/proc/fix_scar(side) if (!(scarring & side)) @@ -404,7 +416,7 @@ 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() + owner.update_eyes() #undef OFFSET_X #undef OFFSET_Y @@ -448,10 +460,10 @@ #define BLINK_LOOPS 5 /// Modifies eye overlays to also act as eyelids, both for blinking and for when you're knocked out cold -/obj/item/organ/eyes/proc/setup_eyelids(mutable_appearance/eye_left, mutable_appearance/eye_right, mob/living/carbon/human/parent) - var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD) +/obj/item/organ/eyes/proc/get_eyelid_overlays(mutable_appearance/eye_left, mutable_appearance/eye_right, obj/item/bodypart/head/my_head) + var/mob/living/carbon/human/parent = my_head.owner // Robotic eyes or colorless heads don't get the privelege of having eyelids - if (IS_ROBOTIC_ORGAN(src) || !my_head.draw_color || HAS_TRAIT(parent, TRAIT_NO_EYELIDS)) + if (isnull(parent) || IS_ROBOTIC_ORGAN(src) || !my_head.draw_color || HAS_TRAIT(parent, TRAIT_NO_EYELIDS)) return var/list/base_color = rgb2num(my_head.draw_color, COLORSPACE_HSL) diff --git a/code/modules/surgery/organs/internal/eyes/eyes_augments.dm b/code/modules/surgery/organs/internal/eyes/eyes_augments.dm index 276b8437b88..0cce64e4c0c 100644 --- a/code/modules/surgery/organs/internal/eyes/eyes_augments.dm +++ b/code/modules/surgery/organs/internal/eyes/eyes_augments.dm @@ -558,12 +558,12 @@ "Security Status" = JUDGE_RECORDCHECK, ) -/obj/item/organ/eyes/robotic/tacvisor/generate_body_overlay(mob/living/carbon/human/parent, obj/item/bodypart/limb) - var/mutable_appearance/visor_overlay = mutable_appearance(eye_icon, eye_icon_state, -EYES_LAYER, parent || limb) +/obj/item/organ/eyes/robotic/tacvisor/generate_body_overlay(obj/item/bodypart/limb) + var/mutable_appearance/visor_overlay = mutable_appearance(eye_icon, eye_icon_state, -EYES_LAYER) var/list/eye_overlays = list(visor_overlay) - + var/mob/living/carbon/human/parent = limb.owner if (parent && parent.appears_alive() && !HAS_TRAIT(parent, TRAIT_KNOCKEDOUT)) - var/mutable_appearance/display_overlay = mutable_appearance(eye_icon, "[eye_icon_state]_[LOWER_TEXT(visor_display)]", -EYES_LAYER, parent) + var/mutable_appearance/display_overlay = mutable_appearance(eye_icon, "[eye_icon_state]_[LOWER_TEXT(visor_display)]", -EYES_LAYER) eye_overlays += display_overlay if(!(parent.obscured_slots & HIDEEYES)) eye_overlays += emissive_appearance(eye_icon, "[eye_icon_state]_[LOWER_TEXT(visor_display)]", parent, -EYES_LAYER) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index abc4c37e8bf..5800b131c58 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -287,6 +287,7 @@ #include "screenshot_airlocks.dm" #include "screenshot_antag_icons.dm" #include "screenshot_basic.dm" +#include "screenshot_debrain.dm" #include "screenshot_digi.dm" #include "screenshot_dynamic_human_icons.dm" #include "screenshot_high_luminosity_eyes.dm" diff --git a/code/modules/unit_tests/screenshot_debrain.dm b/code/modules/unit_tests/screenshot_debrain.dm new file mode 100644 index 00000000000..2eca2abd916 --- /dev/null +++ b/code/modules/unit_tests/screenshot_debrain.dm @@ -0,0 +1,32 @@ +/// Tests debrained overlay. And also eyeless since we're here +/datum/unit_test/screenshot_debrain + var/last_frame = 1 + +/datum/unit_test/screenshot_debrain/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human/dummy/consistent) + var/obj/item/organ/brain/their_brain = human.get_organ_by_type(__IMPLIED_TYPE__) + var/obj/item/organ/eyes/their_eyes = human.get_organ_by_type(__IMPLIED_TYPE__) + human.set_hairstyle(/datum/sprite_accessory/hair/bedheadv4::name) + human.set_facial_hairstyle(/datum/sprite_accessory/facial_hair/vlongbeard::name) + human.set_haircolor(COLOR_BLACK) + human.set_facial_haircolor(COLOR_BLACK) + + var/icon/final_icon = icon('icons/effects/effects.dmi', "nothing") + // record pre-test appearance + final_icon.Insert(getFlatIcon(human, no_anim = TRUE), dir = SOUTH, frame = 1) + + // remove brain, record appearance + their_brain.Remove(human) + final_icon.Insert(getFlatIcon(human, no_anim = TRUE), dir = NORTH, frame = 1) + + // remove eyes, record appearance + their_eyes.Remove(human) + final_icon.Insert(getFlatIcon(human, no_anim = TRUE), dir = EAST, frame = 1) + + // re-add organs, record appearance + their_brain.Insert(human) + their_eyes.Insert(human) + final_icon.Insert(getFlatIcon(human, no_anim = TRUE), dir = WEST, frame = 1) + + // test the final screenshot + test_screenshot("head_organs", final_icon) diff --git a/code/modules/unit_tests/screenshots/screenshot_debrain_head_organs.png b/code/modules/unit_tests/screenshots/screenshot_debrain_head_organs.png new file mode 100644 index 00000000000..fad6e7e38bd Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_debrain_head_organs.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_silverscale.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_silverscale.png index f7f6ddf47d5..c5bc9261ec5 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_silverscale.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_silverscale.png differ