From 3450f8ba1ed5036c4d6a198152d4b8cd3d28b005 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sat, 9 May 2026 06:23:48 -0500 Subject: [PATCH] Lizard frills are hidden by `HIDEHAIR` rather than `HIDEEARS` / Lizard frills are masked like hair by hats (#95986) --- .../bodypart_overlays/bodypart_overlay.dm | 2 +- .../markings_bodypart_overlay.dm | 3 +- .../mutant_bodypart_overlay.dm | 25 +++++++--------- .../simple_bodypart_overlay.dm | 5 ++-- .../texture_bodypart_overlay.dm | 4 +-- .../mob/living/carbon/carbon_update_icons.dm | 4 +-- code/modules/mob/living/carbon/inventory.dm | 2 ++ .../surgery/organs/external/_visual_organs.dm | 27 +++++++++++++++++- .../organs/external/wings/functional_wings.dm | 2 +- .../internal/cyberimp/augments_internal.dm | 2 +- ...enshot_humanoids__datum_species_lizard.png | Bin 1368 -> 1370 bytes 11 files changed, 48 insertions(+), 28 deletions(-) diff --git a/code/datums/bodypart_overlays/bodypart_overlay.dm b/code/datums/bodypart_overlays/bodypart_overlay.dm index 19bf44f39a3..164c6908cb2 100644 --- a/code/datums/bodypart_overlays/bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/bodypart_overlay.dm @@ -90,7 +90,7 @@ CRASH("External organ color set to override with no override proc.") ///Generate a unique identifier to cache with. If you change something about the image, but the icon cache stays the same, it'll simply pull the unchanged image out of the cache -/datum/bodypart_overlay/proc/generate_icon_cache() +/datum/bodypart_overlay/proc/generate_icon_cache(obj/item/bodypart/limb) return list() /// Additionally color or texture the limb diff --git a/code/datums/bodypart_overlays/markings_bodypart_overlay.dm b/code/datums/bodypart_overlays/markings_bodypart_overlay.dm index 0cc780794f1..efa407c57e3 100644 --- a/code/datums/bodypart_overlays/markings_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/markings_bodypart_overlay.dm @@ -28,9 +28,8 @@ icon_state = accessory.icon_state use_gender = accessory.gender_specific draw_color = accessory.color_src ? set_color : null - cache_key = jointext(generate_icon_cache(), "_") -/datum/bodypart_overlay/simple/body_marking/generate_icon_cache() +/datum/bodypart_overlay/simple/body_marking/generate_icon_cache(obj/item/bodypart/limb) . = ..() . += use_gender . += draw_color diff --git a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm index 40b303b1a4f..40b770d87bf 100644 --- a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm @@ -72,22 +72,20 @@ /datum/bodypart_overlay/mutant/proc/get_base_icon_state() return sprite_datum.icon_state +///Used to build the final incon state for the sprite +/datum/bodypart_overlay/mutant/proc/build_icon_state(image_layer, obj/item/bodypart/limb) + PROTECTED_PROC(TRUE) + var/gender_key = (sprite_datum.gender_specific && limb?.limb_gender) || "m" // Male is default because sprite accessories are so ancient they predate the concept of not hardcoding gender + var/base_state = get_base_icon_state() + var/layer_key = mutant_bodyparts_layertext(image_layer) + return "[gender_key]_[feature_key]_[base_state]_[layer_key]" + ///Get the image we need to draw on the person. Called from get_overlay() which is called from _bodyparts.dm. Limb can be null /datum/bodypart_overlay/mutant/get_image(image_layer, obj/item/bodypart/limb) if(!sprite_datum) CRASH("Trying to call get_image() on [type] while it didn't have a sprite_datum. This shouldn't happen, report it as soon as possible.") - var/gender = limb?.limb_gender || "m" - var/list/icon_state_builder = list() - icon_state_builder += sprite_datum.gender_specific ? gender : "m" //Male is default because sprite accessories are so ancient they predate the concept of not hardcoding gender - icon_state_builder += feature_key - icon_state_builder += get_base_icon_state() - icon_state_builder += mutant_bodyparts_layertext(image_layer) - - var/finished_icon_state = icon_state_builder.Join("_") - - var/mutable_appearance/appearance = mutable_appearance(sprite_datum.icon, finished_icon_state, layer = image_layer) - + var/mutable_appearance/appearance = mutable_appearance(sprite_datum.icon, build_icon_state(image_layer, limb), layer = image_layer) if(sprite_datum.center) center_image(appearance, sprite_datum.dimension_x, sprite_datum.dimension_y) @@ -102,20 +100,17 @@ ///Change our accessory sprite, using the accesssory type. If you need to change the sprite for something, use simple_change_sprite() /datum/bodypart_overlay/mutant/set_appearance(accessory_type) sprite_datum = fetch_sprite_datum(accessory_type) - cache_key = jointext(generate_icon_cache(), "_") ///In a lot of cases, appearances are stored in DNA as the Name, instead of the path. Use set_appearance instead of possible /datum/bodypart_overlay/mutant/proc/set_appearance_from_name(accessory_name) sprite_datum = fetch_sprite_datum_from_name(accessory_name) - cache_key = jointext(generate_icon_cache(), "_") ///Generate a unique key based on our sprites. So that if we've aleady drawn these sprites, they can be found in the cache and wont have to be drawn again (blessing and curse, but mostly curse) -/datum/bodypart_overlay/mutant/generate_icon_cache() +/datum/bodypart_overlay/mutant/generate_icon_cache(obj/item/bodypart/limb) . = list() . += "[get_base_icon_state()]" . += "[feature_key]" . += "[dye_color || draw_color]" - return . ///Return a dumb glob list for this specific feature (called from parse_sprite) /datum/bodypart_overlay/mutant/proc/get_global_feature_list() diff --git a/code/datums/bodypart_overlays/simple_bodypart_overlay.dm b/code/datums/bodypart_overlays/simple_bodypart_overlay.dm index a8004811b1a..fa5222f5adf 100644 --- a/code/datums/bodypart_overlays/simple_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/simple_bodypart_overlay.dm @@ -15,10 +15,9 @@ overlay.color = draw_color -/datum/bodypart_overlay/simple/generate_icon_cache() +/datum/bodypart_overlay/simple/generate_icon_cache(obj/item/bodypart/limb) . = ..() - - . += "[icon_state]" + . += icon_state ///A sixpack drawn on the chest /datum/bodypart_overlay/simple/sixpack diff --git a/code/datums/bodypart_overlays/texture_bodypart_overlay.dm b/code/datums/bodypart_overlays/texture_bodypart_overlay.dm index 920c2973ac8..23b8aa6b780 100644 --- a/code/datums/bodypart_overlays/texture_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/texture_bodypart_overlay.dm @@ -16,9 +16,9 @@ /datum/bodypart_overlay/texture/modify_bodypart_appearance(datum/appearance) appearance.add_filter("bodypart_texture_[texture_icon_state]", 1, layering_filter(icon = cached_texture_icon, blend_mode = BLEND_INSET_OVERLAY)) -/datum/bodypart_overlay/texture/generate_icon_cache() +/datum/bodypart_overlay/texture/generate_icon_cache(obj/item/bodypart/limb) . = ..() - . += "[type]" + . += type /datum/bodypart_overlay/texture/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner, mob/living/carbon/owner, is_husked = FALSE) if (!..()) diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 171e53c65af..0c1cae2fe17 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -545,7 +545,7 @@ for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays) if(!overlay.can_draw_on_bodypart(src, owner, is_husked)) continue - . += overlay.generate_icon_cache() + . += overlay.generate_icon_cache(src) if(ishuman(owner)) var/mob/living/carbon/human/human_owner = owner . += "[human_owner.mob_height]" @@ -574,7 +574,7 @@ for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays) if(!overlay.can_draw_on_bodypart(src, owner, TRUE)) continue - . += overlay.generate_icon_cache() + . += overlay.generate_icon_cache(src) if(ishuman(owner)) var/mob/living/carbon/human/human_owner = owner . += "[human_owner.mob_height]" diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index a35b9b9db60..049b0ccb42a 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -174,6 +174,7 @@ if(item.hair_mask) LAZYADD(hair_masks, item.hair_mask) update_hair() + update_body() // this is solely for lizard frills add_item_coverage(item) /mob/living/carbon/has_unequipped(obj/item/item) @@ -185,6 +186,7 @@ if(item.hair_mask) LAZYREMOVE(hair_masks, item.hair_mask) update_hair() + update_body() // this is solely for lizard frills 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/surgery/organs/external/_visual_organs.dm b/code/modules/surgery/organs/external/_visual_organs.dm index 45df1a4a29d..e3fcdc92500 100644 --- a/code/modules/surgery/organs/external/_visual_organs.dm +++ b/code/modules/surgery/organs/external/_visual_organs.dm @@ -144,7 +144,32 @@ Unlike normal organs, we're actually inside a persons limbs at all times feature_key = FEATURE_FRILLS /datum/bodypart_overlay/mutant/frills/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner, mob/living/carbon/owner, is_husked = FALSE) - return ..() && !(bodypart_owner.owner?.obscured_slots & HIDEEARS) + return ..() && !(bodypart_owner.owner?.obscured_slots & HIDEHAIR) + +/datum/bodypart_overlay/mutant/frills/generate_icon_cache(obj/item/bodypart/limb) + . = ..() + if(LAZYLEN(limb?.owner?.hair_masks)) + . += jointext(limb.owner.hair_masks, ",") + +/datum/bodypart_overlay/mutant/frills/get_image(image_layer, obj/item/bodypart/limb) + if(!LAZYLEN(limb?.owner?.hair_masks)) + return ..() + + var/list/hair_masks_to_use = limb.owner.hair_masks + var/icon_state_to_use = build_icon_state(image_layer, limb) + var/frill_cache_key = "[sprite_datum.type]-[icon_state_to_use]-[jointext(hair_masks_to_use, ",")]" + var/static/list/cached_frill_icons + var/icon/cached_icon = LAZYACCESS(cached_frill_icons, frill_cache_key) + if(isnull(cached_icon)) + cached_icon = icon(sprite_datum.icon, build_icon_state(image_layer, limb)) + for(var/datum/hair_mask/mask as anything in hair_masks_to_use) + cached_icon.Blend(icon(mask::icon, mask::icon_state), ICON_ADD) + LAZYSET(cached_frill_icons, frill_cache_key, cached_icon) + + var/mutable_appearance/uncached_appearance = mutable_appearance(cached_icon, layer = image_layer) + if(sprite_datum.center) + center_image(uncached_appearance, sprite_datum.dimension_x, sprite_datum.dimension_y) + return uncached_appearance ///Guess what part of the lizard this is? /obj/item/organ/snout diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm index f2dcdf6851a..ee3887a2096 100644 --- a/code/modules/surgery/organs/external/wings/functional_wings.dm +++ b/code/modules/surgery/organs/external/wings/functional_wings.dm @@ -183,7 +183,7 @@ feature_key = initial(feature_key) set_appearance_from_name(sprite_datum.name) -/datum/bodypart_overlay/mutant/wings/functional/generate_icon_cache() +/datum/bodypart_overlay/mutant/wings/functional/generate_icon_cache(obj/item/bodypart/limb) . = ..() . += wings_open ? "open" : "closed" diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm index d3991d7aa99..16d5e7cd306 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm @@ -56,7 +56,7 @@ implant = null return ..() -/datum/bodypart_overlay/augment/generate_icon_cache() +/datum/bodypart_overlay/augment/generate_icon_cache(obj/item/bodypart/limb) . = ..() . += implant.get_overlay_state() diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard.png index c0a3c30b8bb38e8d158a68098780184936065ae7..02d3799db6955dfba2c839590e36e1bb9b3f6427 100644 GIT binary patch delta 1120 zcmV-m1fToZ3fc;gBqNPbOjJd{z`(b8G2XU4>f3`gH$GimWQ<25Cn_#%T1o(yn2|?p z2LLH4Vdh&Tk+*?=3Q0skR9JN$lqtk8T@_^7+jcV>t6OR&i-Y8(zwZ|@tnqmc-=>QZyDUW zh`U!VUzoki-Jauz24ZkV@C=Ryn-7NkOU~zuLMda6QU!w_e2IHp`U96=6`9OshR-za zk0p(xPvcTm^o;SWt7I)o6^`Zch<8ZhhdQgMO7O0-dZ`k4C_M;#8B(}7oa(ARWjc$W z34&7{e838S@IQr1uojpe`$ZL)Z7vdc@wM{dE%@qdvBa@LiM-S4a5{lWE>?+OLRCZs zMHQAfc8V771o3K#L;Ung;KJqBd{f`Bgr;eX&FAHW8f-!tj(C*;n_^LJsA2d0{Qdm* zx%u(Kxdee}f`2RjDc^i>W=UY2GneMl{M%g5XS!H_PH+W7iANb4vz&l7tGLLE%9OfZ z>0%+#UJsXWYj54iR<9ArjwJjE z!6bpfJ0j@U33N9?LzLzv|9BKKar2#tTx*;zTFUtke}@Qk^*+yr!@Yw-DNR^cOXi^ zf9M^jUH#`iBT5>wx~0z~UZ4+ghtM zs#NN4wCxi4*lQ@%H3rH@F^3pDC%{kWjY^Zt;0VZkXocnphB*OtY0&8VvZ~hz>Po_Y zC}@hFVbuJw*JNF+D=2Ezqb83q6A%@k%nP)5{@e!!@1UZB|4Z=b+FoNEJ|Z|!6q@Yp zCg7AhdX27Y;Sm&5t46^*z&aR>;Mrp!|K!gw!-hNpSBYdEU=8fqlhvA`SXE=4vE&gr z1!NwqaTZ1)dGhOvwU_L&T1eNcwb%52anN-=4wL$$Qlf3`gH$GimWQ<250GF62DlTkVN|8rw z1Pw`~-jTY2e+x-OK~z|UrB;o4n>Y|e!WNDq#g}3M!cD2Kr8a->|9Uqo?6e13lJBb= z$Huce8o^3CN%H3qG!lM%h|4rhB_AOqjV(Og?RPwb5-AO#7(7#{I6NIhsxpQ^g(WDC z0I=Ne_EIoYsIWp>Bg2rr&k$FTsrxUlmn_w~ykdIxVS6@GjGOtrEB|90*+DV|a2p*JXW9RT@4c z1n1g2e~%U5e+(C3%`rXpi_9_GQY7&FYw7%(cjec7jbnuZdFS)#diDKw~ucN2gyAG$#96(=U`Bibpi?JWT(a$pa#{lq95S2gzXB^+ge(dRQ zbu$Fl81F-E`-5N*h8FDT%!e~RNz`$HN+cn2aS z{D(Sl3Jx#;sbu}Z-5m(Ym>5bH_aH`sf3eRv?Ou-YHEIp>kY&bl?H?iLmHa_=j2<`^ z0JV3+_kZ(hvNn(!1e-tnZ zilP@7HGk|iQRmwV@*4H1$RjKS1O+Jb94($d_kqDX$e`f=5*)g=Qy8a@2u>7*CcC%^ zIHfA5&~*(wf@Eq{OPB{(2dxmicnsv9+y!P>lSkkxk<0_Efw?%cS_>4bYK+x}JOZbH z%!AR^z$ipde$^V&rtM}SU9ZMmf8)nN-w!xU>W@m98sxA(&I7{@4E0t%G1SPv#quHG zHhqp@x(@4gJZO!ijLI+O}JWUUZU=POG_n<&y$JOurs=C)OQOpkEC`%Z3yFvcO k#mh7hkVANEf^p9L52N~Ln2+u$Z~y=R07*qoM6N<$f)Dx=WdHyG