From 7e2f7ee3ad2d7e71938805d58d41fdfcde04702b Mon Sep 17 00:00:00 2001 From: Cruix Date: Thu, 24 Oct 2024 17:01:06 -0700 Subject: [PATCH] Added a system to hide parts of hair when wearing hats (#87263) ## About The Pull Request Added a system to allow clothing to apply a mask to your hair sprite, using the same blend procs that hair gradients do. This allows us to hide the upper part of people's hair without affecting portions below a person's hat. Currently this is only implemented on hardhats as an example. This is the mask they use: ![Mask_Example](https://github.com/user-attachments/assets/aef11885-33e4-48b2-90de-d10447a34b0f) This hides any hair above the brow line and above a 45 degree angle along the sides of the head. For hats with a thinner or wider profile, a thinner or wider mask could be made. Alternatively, you could create a mask for a suit slot item that makes it appear like long, draping hair has been tucked into the suit. You could also color the mask to create items that change the color of a person's hair, if you wanted to do that for some reason. If multiple items of clothing have masks, they will all be applied in sequence. Before: ![before](https://github.com/user-attachments/assets/e4f13255-c6dc-4eb7-8b75-ada84380a739) After: ![after](https://github.com/user-attachments/assets/59e5db78-e5ac-455a-af5b-cb1b460f5f43) Removing the hardhat will cause your afro to poof back out completely unharmed. ## Why It's Good For The Game It looks better than having tall hairstyles show up behind your headwear, but it does not hide your hair completely. ## Changelog :cl: add: Hardhats now squish down excessively tall hairstyles while being worn. /:cl: --- code/__DEFINES/mobs.dm | 3 +++ code/game/objects/items.dm | 3 +++ .../clothing/chameleon/_chameleon_action.dm | 1 + code/modules/clothing/head/hardhat.dm | 5 ++++ .../mob/living/carbon/carbon_update_icons.dm | 2 ++ code/modules/surgery/bodyparts/head.dm | 2 ++ .../surgery/bodyparts/head_hair_and_lips.dm | 22 ++++++++++++++---- icons/mob/human/hair_masks.dmi | Bin 0 -> 327 bytes 8 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 icons/mob/human/hair_masks.dmi diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index df9b7cc2296..7c082f0b228 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -647,6 +647,9 @@ #define GRADIENT_APPLIES_TO_HAIR (1<<0) #define GRADIENT_APPLIES_TO_FACIAL_HAIR (1<<1) +// Hair masks +#define HAIR_MASK_HIDE_ABOVE_45_DEG_MEDIUM "hide_above_45deg" + // Height defines // - They are numbers so you can compare height values (x height < y height) // - They do not start at 0 for futureproofing diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 58b1b1ae346..fe3441dd02b 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -130,6 +130,9 @@ var/flags_inv ///you can see someone's mask through their transparent visor, but you can't reach it var/transparent_protection = NONE + ///Name of a mask in icons\mob\human\hair_masks.dmi to apply to hair when this item is worn + ///Used by certain hats to give the appearance of squishing down tall hairstyles without hiding the hair completely + var/hair_mask = "" ///flags for what should be done when you click on the item, default is picking it up var/interaction_flags_item = INTERACT_ITEM_ATTACK_HAND_PICKUP diff --git a/code/modules/clothing/chameleon/_chameleon_action.dm b/code/modules/clothing/chameleon/_chameleon_action.dm index b9a1697976b..fbbe480e59a 100644 --- a/code/modules/clothing/chameleon/_chameleon_action.dm +++ b/code/modules/clothing/chameleon/_chameleon_action.dm @@ -156,6 +156,7 @@ ) item_target.flags_inv = initial(picked_item.flags_inv) + item_target.hair_mask = initial(picked_item.hair_mask) item_target.transparent_protection = initial(picked_item.transparent_protection) if(isclothing(item_target) && ispath(picked_item, /obj/item/clothing)) var/obj/item/clothing/clothing_target = item_target diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 6cd88c1746c..56a8661a412 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -9,6 +9,7 @@ inhand_icon_state = null armor_type = /datum/armor/utility_hardhat flags_inv = 0 + hair_mask = HAIR_MASK_HIDE_ABOVE_45_DEG_MEDIUM actions_types = list(/datum/action/item_action/toggle_helmet_light) clothing_flags = SNUG_FIT | STACKABLE_HELMET_EXEMPT resistance_flags = FIRE_PROOF @@ -211,6 +212,7 @@ flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF visor_flags_cover = NONE flags_inv = HIDEEARS|HIDEHAIR|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT + hair_mask = "" transparent_protection = HIDEMASK|HIDEEYES visor_flags_inv = NONE visor_state = "weldvisor_atmos" @@ -230,6 +232,8 @@ hat_type = "pumpkin" clothing_flags = SNUG_FIT | STACKABLE_HELMET_EXEMPT flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT + hair_mask = "" + armor_type = /datum/armor/none light_range = 2 //luminosity when on flags_cover = HEADCOVERSEYES @@ -296,6 +300,7 @@ inhand_icon_state = null hat_type = "reindeer" flags_inv = 0 + hair_mask = "" armor_type = /datum/armor/none light_range = 1 //luminosity when on clothing_traits = list() diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 8863dedd43e..aebba0ae2a9 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -580,6 +580,8 @@ if(gradient_styles?[GRADIENT_HAIR_KEY]) . += "-[gradient_styles[GRADIENT_HAIR_KEY]]" . += "-[gradient_colors[GRADIENT_HAIR_KEY]]" + if(LAZYLEN(hair_masks)) + . += "-[jointext(hair_masks, "-")]" return . diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index cce8935f803..30c841147a7 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -43,6 +43,8 @@ var/hair_alpha = 255 /// Is the hair currently hidden by something? var/hair_hidden = FALSE + /// Lazy initialized hashset of all hair masks that should be applied + var/list/hair_masks ///Facial hair style var/facial_hairstyle = "Shaved" diff --git a/code/modules/surgery/bodyparts/head_hair_and_lips.dm b/code/modules/surgery/bodyparts/head_hair_and_lips.dm index 7f81a615f35..861076e184b 100644 --- a/code/modules/surgery/bodyparts/head_hair_and_lips.dm +++ b/code/modules/surgery/bodyparts/head_hair_and_lips.dm @@ -8,12 +8,15 @@ //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()) if(worn_item.flags_inv & HIDEHAIR) hair_hidden = TRUE if(worn_item.flags_inv & HIDEFACIALHAIR) facial_hair_hidden = TRUE + if(worn_item.hair_mask) + LAZYSET(hair_masks, worn_item.hair_mask, TRUE) //invisibility and husk stuff if(HAS_TRAIT(human_head_owner, TRAIT_INVISIBLE_MAN) || HAS_TRAIT(human_head_owner, TRAIT_HUSK)) hair_hidden = TRUE @@ -99,15 +102,24 @@ var/facial_hair_gradient_style = gradient_styles[GRADIENT_FACIAL_HAIR_KEY] if(facial_hair_gradient_style != "None") var/facial_hair_gradient_color = gradient_colors[GRADIENT_FACIAL_HAIR_KEY] - var/image/facial_hair_gradient_overlay = get_gradient_overlay(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color, image_dir) + 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, image_dir) . += facial_hair_gradient_overlay var/image/hair_overlay 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) + var/icon/base_icon + if(LAZYLEN(hair_masks)) + base_icon = icon(hair_sprite_accessory.icon, hair_sprite_accessory.icon_state) + for(var/mask in hair_masks) + var/icon/blend_with = icon('icons/mob/human/hair_masks.dmi', mask) + blend_with.Shift(SOUTH, hair_sprite_accessory.y_offset) + base_icon.Blend(blend_with, ICON_ADD) + else + base_icon = icon(hair_sprite_accessory.icon, hair_sprite_accessory.icon_state) //Overlay - hair_overlay = image(hair_sprite_accessory.icon, hair_sprite_accessory.icon_state, -HAIR_LAYER, image_dir) + hair_overlay = image(base_icon, layer=-HAIR_LAYER, dir=image_dir) hair_overlay.alpha = hair_alpha hair_overlay.pixel_y = hair_sprite_accessory.y_offset //Emissive blocker @@ -120,7 +132,7 @@ var/hair_gradient_style = gradient_styles[GRADIENT_HAIR_KEY] if(hair_gradient_style != "None") var/hair_gradient_color = gradient_colors[GRADIENT_HAIR_KEY] - var/image/hair_gradient_overlay = get_gradient_overlay(hair_sprite_accessory.icon, hair_sprite_accessory.icon_state, -HAIR_LAYER, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, image_dir) + var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, -HAIR_LAYER, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, image_dir) hair_gradient_overlay.pixel_y = hair_sprite_accessory.y_offset . += hair_gradient_overlay @@ -184,12 +196,12 @@ return eyeless_overlay /// Returns an appropriate hair/facial hair gradient overlay -/obj/item/bodypart/head/proc/get_gradient_overlay(file, icon, layer, datum/sprite_accessory/gradient, grad_color, image_dir) +/obj/item/bodypart/head/proc/get_gradient_overlay(icon/base_icon, layer, datum/sprite_accessory/gradient, grad_color, image_dir) RETURN_TYPE(/mutable_appearance) var/mutable_appearance/gradient_overlay = mutable_appearance(layer = layer) var/icon/temp = icon(gradient.icon, gradient.icon_state, image_dir) - var/icon/temp_hair = icon(file, icon, image_dir) + var/icon/temp_hair = icon(base_icon, dir=image_dir) temp.Blend(temp_hair, ICON_ADD) gradient_overlay.icon = temp gradient_overlay.color = grad_color diff --git a/icons/mob/human/hair_masks.dmi b/icons/mob/human/hair_masks.dmi new file mode 100644 index 0000000000000000000000000000000000000000..45ecb761d9a54f950e18bc6651586b87b9f766f9 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxE?rg0Law7zHk|kVk`;r z3ubV5b|VeQDXj{LC~-+GPAM56C74hRQml(aKXo~Pqe&swa%S6AG{&d z;G*$^M>^+yG*2=V_4MwrFb*=lZ0w~l=h37ilR^}NHIPBl0ePM-jv*Qo&tBNb+hD-s za?x32O3#EqBdMNa8a)m(l};VpuD;;3XMEAqN1pq`s+T3FY}$O(&U1C`M6=&&Z@)Qf zl_`hrp3GN$;k%Og-H-XK^Vvk~7+d|F9tsEiU;#57V9cY-nyVQ7%!}rKk5_sQw3)%v L)z4*}Q$iB}mqdD| literal 0 HcmV?d00001