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

🆑
add: Hardhats now squish down excessively tall hairstyles while being
worn.
/🆑
This commit is contained in:
Cruix
2024-10-25 02:01:06 +02:00
committed by GitHub
parent 1170b87f7b
commit 7e2f7ee3ad
8 changed files with 33 additions and 5 deletions
+3
View File
@@ -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
+3
View File
@@ -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
@@ -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
+5
View File
@@ -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()
@@ -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 .
+2
View File
@@ -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"
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B