SPECIES NUKING 2023: Head flags 3 & Knuckles: Fixes some growing pains with head flags (#76440)

## About The Pull Request

Fixes https://github.com/tgstation/tgstation/issues/76422
This was caused by me somehow not using the wrapper there and not
noticing it

Also fixes hair gradients and facial hair gradients. I am pretty sure
they were uhh, being hidden behind the actual hair/facial hair. Oops.

Also also fixes spawning yourself as a human as admin and getting random
hair colors. That was just a failure to update the icon after updating
everything, I think?

Additionally, to totally babyproof all of this, ensures that head_flags
involved stuff gets applied AFTER species by creating a new preference
priority, and uses two separate wrappers to apply gradient style and
color.

Here's this absolute hellspawn to prove that everything works.

![image](https://github.com/tgstation/tgstation/assets/82850673/7ed29a68-cb60-4b28-996c-3be0e7331be8)

![image](https://github.com/tgstation/tgstation/assets/82850673/e57128be-0d7c-46ad-90dd-ee25981d0fea)

![image](https://github.com/tgstation/tgstation/assets/82850673/5c3619a8-fe6f-42b3-9fdc-12277d568e8d)

![image](https://github.com/tgstation/tgstation/assets/82850673/fdd13000-2220-47ad-8e02-44bc75a4a907)

Sorry for being so damn good at breaking this codebase.

## Why It's Good For The Game

Bugs are bad they make you mad

## Changelog

🆑
fix: Hair and facial hair gradients work again now
fix: Facial hair colors apply properly again
fix: Admin spawned characters will get hair color preferences applied
properly
/🆑
This commit is contained in:
ChungusGamer666
2023-07-01 14:15:25 +02:00
committed by GitHub
parent f25571e8e6
commit d85e44c69c
9 changed files with 117 additions and 57 deletions
+11 -10
View File
@@ -29,19 +29,18 @@
#define DNA_BLOCK_SIZE_COLOR DEFAULT_HEX_COLOR_LEN
#define DNA_EYE_COLOR_LEFT_BLOCK 4
#define DNA_GENDER_BLOCK 1
#define DNA_SKIN_TONE_BLOCK 2
#define DNA_EYE_COLOR_LEFT_BLOCK 3
#define DNA_EYE_COLOR_RIGHT_BLOCK 4
#define DNA_FACIAL_HAIR_COLOR_BLOCK 2
#define DNA_FACIAL_HAIRSTYLE_BLOCK 6
#define DNA_GENDER_BLOCK 5
#define DNA_HAIR_COLOR_BLOCK 1
#define DNA_HAIRSTYLE_BLOCK 7
#define DNA_SKIN_TONE_BLOCK 3
#define DNA_UNI_IDENTITY_BLOCKS 7
#define DNA_HAIRSTYLE_BLOCK 5
#define DNA_HAIR_COLOR_BLOCK 6
#define DNA_FACIAL_HAIRSTYLE_BLOCK 7
#define DNA_FACIAL_HAIR_COLOR_BLOCK 8
#define DNA_UNI_IDENTITY_BLOCKS 8
/// This number needs to equal the total number of DNA blocks
#define DNA_FEATURE_BLOCKS 15
#define DNA_MUTANT_COLOR_BLOCK 1
#define DNA_ETHEREAL_COLOR_BLOCK 2
#define DNA_LIZARD_MARKINGS_BLOCK 3
@@ -58,6 +57,8 @@
#define DNA_MUSHROOM_CAPS_BLOCK 14
#define DNA_POD_HAIR_BLOCK 15
#define DNA_FEATURE_BLOCKS 15
#define DNA_SEQUENCE_LENGTH 4
#define DNA_MUTATION_BLOCKS 8
#define DNA_UNIQUE_ENZYMES_LEN 32
+5 -5
View File
@@ -586,10 +586,10 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
..()
var/structure = dna.unique_identity
skin_tone = GLOB.skin_tones[deconstruct_block(get_uni_identity_block(structure, DNA_SKIN_TONE_BLOCK), GLOB.skin_tones.len)]
set_haircolor(sanitize_hexcolor(get_uni_identity_block(structure, DNA_HAIR_COLOR_BLOCK)), update = FALSE)
set_facial_haircolor(sanitize_hexcolor(get_uni_identity_block(structure, DNA_FACIAL_HAIR_COLOR_BLOCK)), update = FALSE)
eye_color_left = sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_LEFT_BLOCK))
eye_color_right = sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_RIGHT_BLOCK))
set_haircolor(sanitize_hexcolor(get_uni_identity_block(structure, DNA_HAIR_COLOR_BLOCK)), update = FALSE)
set_facial_haircolor(sanitize_hexcolor(get_uni_identity_block(structure, DNA_FACIAL_HAIR_COLOR_BLOCK)), update = FALSE)
if(HAS_TRAIT(src, TRAIT_SHAVED))
set_facial_hairstyle("Shaved", update = FALSE)
else
@@ -640,10 +640,10 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
external_organ.mutate_feature(features, src)
if(icon_update)
dna.species.handle_body(src) // We want 'update_body_parts(update_limb_data = TRUE)' to be called only if mutcolor_update is TRUE, so no 'update_body()' here.
update_body_parts() //We can call this because it doesnt refresh limb data, and it handles hair and such.
if(mutcolor_update)
update_body_parts(update_limb_data = TRUE)
update_body(is_creating = TRUE)
else
update_body()
if(mutations_overlay_update)
update_mutations_overlay()
+4 -2
View File
@@ -42,7 +42,9 @@
if(!do_after(user, 3 SECONDS, target))
return
if(beard_or_hair == "Hair")
human_target.set_hair_gradient(new_grad_style, new_grad_color, update = TRUE)
human_target.set_hair_gradient_style(new_grad_style, update = FALSE)
human_target.set_hair_gradient_color(new_grad_color, update = TRUE)
else
human_target.set_facial_hair_gradient(new_grad_style, new_grad_color, update = TRUE)
human_target.set_facial_hair_gradient_style(new_grad_style, update = FALSE)
human_target.set_facial_hair_gradient_color(new_grad_color, update = TRUE)
playsound(src, 'sound/effects/spray.ogg', 10, vary = TRUE)
+10 -4
View File
@@ -5,18 +5,24 @@
/// The priority at which species runs, needed for external organs to apply properly.
#define PREFERENCE_PRIORITY_SPECIES 2
/**
* Some preferences get applied directly to bodyparts (anything head_flags related right now).
* These must apply after species, as species gaining might replace the bodyparts of the human.
*/
#define PREFERENCE_PRIORITY_BODYPARTS 3
/// The priority at which gender is determined, needed for proper randomization.
#define PREFERENCE_PRIORITY_GENDER 3
#define PREFERENCE_PRIORITY_GENDER 4
/// The priority at which body type is decided, applied after gender so we can
/// support the "use gender" option.
#define PREFERENCE_PRIORITY_BODY_TYPE 4
#define PREFERENCE_PRIORITY_BODY_TYPE 5
/// The priority at which names are decided, needed for proper randomization.
#define PREFERENCE_PRIORITY_NAMES 5
#define PREFERENCE_PRIORITY_NAMES 6
/// Preferences that aren't names, but change the name changes set by PREFERENCE_PRIORITY_NAMES.
#define PREFERENCE_PRIORITY_NAME_MODIFICATIONS 6
#define PREFERENCE_PRIORITY_NAME_MODIFICATIONS 7
/// The maximum preference priority, keep this updated, but don't use it for `priority`.
#define MAX_PREFERENCE_PRIORITY PREFERENCE_PRIORITY_NAME_MODIFICATIONS
@@ -23,6 +23,7 @@
return values
/datum/preference/color/eye_color
priority = PREFERENCE_PRIORITY_BODYPARTS
savefile_key = "eye_color"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
@@ -54,6 +55,7 @@
return random_eye_color()
/datum/preference/choiced/facial_hairstyle
priority = PREFERENCE_PRIORITY_BODYPARTS
savefile_key = "facial_style_name"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -65,8 +67,7 @@
return generate_possible_values_for_sprite_accessories_on_head(GLOB.facial_hairstyles_list)
/datum/preference/choiced/facial_hairstyle/apply_to_human(mob/living/carbon/human/target, value)
target.facial_hairstyle = value
target.update_body_parts()
target.set_facial_hairstyle(value, update = FALSE)
/datum/preference/choiced/facial_hairstyle/compile_constant_data()
var/list/data = ..()
@@ -76,15 +77,17 @@
return data
/datum/preference/color/facial_hair_color
priority = PREFERENCE_PRIORITY_BODYPARTS
savefile_key = "facial_hair_color"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES
relevant_head_flag = HEAD_FACIAL_HAIR
/datum/preference/color/facial_hair_color/apply_to_human(mob/living/carbon/human/target, value)
target.set_facial_haircolor(value, update = TRUE)
target.set_facial_haircolor(value, update = FALSE)
/datum/preference/choiced/facial_hair_gradient
priority = PREFERENCE_PRIORITY_BODYPARTS
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "facial_hair_gradient"
@@ -94,19 +97,20 @@
return assoc_to_keys_features(GLOB.facial_hair_gradients_list)
/datum/preference/choiced/facial_hair_gradient/apply_to_human(mob/living/carbon/human/target, value)
target.set_facial_hair_gradient(new_style = value, update = TRUE)
target.set_facial_hair_gradient_style(new_style = value, update = FALSE)
/datum/preference/choiced/facial_hair_gradient/create_default_value()
return "None"
/datum/preference/color/facial_hair_gradient
priority = PREFERENCE_PRIORITY_BODYPARTS
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "facial_hair_gradient_color"
relevant_head_flag = HEAD_FACIAL_HAIR
/datum/preference/color/facial_hair_gradient/apply_to_human(mob/living/carbon/human/target, value)
target.set_facial_hair_gradient(new_color = value, update = TRUE)
target.set_facial_hair_gradient_color(new_color = value, update = FALSE)
/datum/preference/color/facial_hair_gradient/is_accessible(datum/preferences/preferences)
if (!..(preferences))
@@ -114,15 +118,17 @@
return preferences.read_preference(/datum/preference/choiced/facial_hair_gradient) != "None"
/datum/preference/color/hair_color
priority = PREFERENCE_PRIORITY_BODYPARTS
savefile_key = "hair_color"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES
relevant_head_flag = HEAD_HAIR
/datum/preference/color/hair_color/apply_to_human(mob/living/carbon/human/target, value)
target.set_haircolor(value, update = TRUE)
target.set_haircolor(value, update = FALSE)
/datum/preference/choiced/hairstyle
priority = PREFERENCE_PRIORITY_BODYPARTS
savefile_key = "hairstyle_name"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -134,7 +140,7 @@
return generate_possible_values_for_sprite_accessories_on_head(GLOB.hairstyles_list)
/datum/preference/choiced/hairstyle/apply_to_human(mob/living/carbon/human/target, value)
target.set_hairstyle(value, update = TRUE)
target.set_hairstyle(value, update = FALSE)
/datum/preference/choiced/hairstyle/compile_constant_data()
var/list/data = ..()
@@ -144,6 +150,7 @@
return data
/datum/preference/choiced/hair_gradient
priority = PREFERENCE_PRIORITY_BODYPARTS
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "hair_gradient"
@@ -153,19 +160,20 @@
return assoc_to_keys_features(GLOB.hair_gradients_list)
/datum/preference/choiced/hair_gradient/apply_to_human(mob/living/carbon/human/target, value)
target.set_hair_gradient(new_style = value, update = TRUE)
target.set_hair_gradient_style(new_style = value, update = FALSE)
/datum/preference/choiced/hair_gradient/create_default_value()
return "None"
/datum/preference/color/hair_gradient
priority = PREFERENCE_PRIORITY_BODYPARTS
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "hair_gradient_color"
relevant_head_flag = HEAD_HAIR
/datum/preference/color/hair_gradient/apply_to_human(mob/living/carbon/human/target, value)
target.set_hair_gradient(new_color = value, update = TRUE)
target.set_hair_gradient_color(new_color = value, update = FALSE)
/datum/preference/color/hair_gradient/is_accessible(datum/preferences/preferences)
if (!..(preferences))
+1 -1
View File
@@ -974,7 +974,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/dead/observer/CtrlShiftClick(mob/user)
if(isobserver(user) && check_rights(R_SPAWN))
change_mob_type( /mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
change_mob_type(/mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
/mob/dead/observer/examine(mob/user)
. = ..()
@@ -256,6 +256,7 @@
preference.apply_to_human(src, preference.create_random_value(preferences))
fully_replace_character_name(real_name, dna.species.random_name())
/**
* Setter for mob height
*
@@ -37,7 +37,7 @@
qdel(desired_mob)
return
if( istext(new_name) )
if(istext(new_name))
desired_mob.name = new_name
desired_mob.real_name = new_name
else
@@ -48,12 +48,12 @@
var/mob/living/carbon/old_mob = src
var/mob/living/carbon/new_mob = desired_mob
old_mob.dna.transfer_identity(new_mob, transfer_species = FALSE)
new_mob.updateappearance(mutcolor_update=1, mutations_overlay_update=1)
new_mob.updateappearance(icon_update = TRUE, mutcolor_update = TRUE, mutations_overlay_update = TRUE)
else if(ishuman(desired_mob) && (!ismonkey(desired_mob)))
var/mob/living/carbon/human/new_human = desired_mob
client?.prefs.safe_transfer_prefs_to(new_human)
new_human.dna.update_dna_identity()
new_human.updateappearance(mutcolor_update=1, mutations_overlay_update=1)
new_human.updateappearance(icon_update = TRUE, mutcolor_update = TRUE, mutations_overlay_update = TRUE)
//Ghosts have copys of their minds, but if an admin put somebody else in their og body, the mind will have a new mind.key
// and transfer_to will transfer the wrong person since it uses mind.key
@@ -59,7 +59,7 @@
else
show_eyeless = FALSE
if(!is_creating)
if(!is_creating || !owner)
return
lip_style = human_head_owner.lip_style
@@ -104,18 +104,18 @@
//Overlay
facial_hair_overlay = image(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, image_dir)
facial_hair_overlay.alpha = facial_hair_alpha
//Gradients
var/facial_hair_gradient_style = LAZYACCESS(gradient_styles, GRADIENT_FACIAL_HAIR_KEY)
if(facial_hair_gradient_style)
var/facial_hair_gradient_color = LAZYACCESS(gradient_colors, GRADIENT_FACIAL_HAIR_KEY)
var/image/facial_hair_gradient_overlay = get_gradient_overlay(sprite_accessory.icon, sprite_accessory.icon_state, HAIR_LAYER, GLOB.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color)
. += facial_hair_gradient_overlay
//Emissive blocker
if(blocks_emissive)
facial_hair_overlay.overlays += emissive_blocker(facial_hair_overlay.icon, facial_hair_overlay.icon_state, location, alpha = facial_hair_alpha)
//Offsets
worn_face_offset?.apply_offset(facial_hair_overlay)
. += facial_hair_overlay
//Gradients
var/facial_hair_gradient_style = LAZYACCESS(gradient_styles, GRADIENT_FACIAL_HAIR_KEY)
if(facial_hair_gradient_style)
var/facial_hair_gradient_color = LAZYACCESS(gradient_colors, GRADIENT_FACIAL_HAIR_KEY)
var/image/facial_hair_gradient_overlay = get_gradient_overlay(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, GLOB.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color)
. += facial_hair_gradient_overlay
var/image/hair_overlay
if(!(show_debrained && (head_flags & HEAD_DEBRAIN)) && !hair_hidden && hairstyle && (head_flags & HEAD_HAIR))
@@ -124,18 +124,18 @@
//Overlay
hair_overlay = image(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, image_dir)
hair_overlay.alpha = hair_alpha
//Gradients
var/hair_gradient_style = LAZYACCESS(gradient_styles, GRADIENT_HAIR_KEY)
if(hair_gradient_style)
var/hair_gradient_color = LAZYACCESS(gradient_colors, GRADIENT_HAIR_KEY)
var/image/hair_gradient_overlay = get_gradient_overlay(sprite_accessory.icon, sprite_accessory.icon_state, HAIR_LAYER, GLOB.hair_gradients_list[hair_gradient_style], hair_gradient_color)
. += hair_gradient_overlay
//Emissive blocker
if(blocks_emissive)
hair_overlay.overlays += emissive_blocker(hair_overlay.icon, hair_overlay.icon_state, location, alpha = hair_alpha)
//Offsets
worn_face_offset?.apply_offset(hair_overlay)
. += hair_overlay
//Gradients
var/hair_gradient_style = LAZYACCESS(gradient_styles, GRADIENT_HAIR_KEY)
if(hair_gradient_style)
var/hair_gradient_color = LAZYACCESS(gradient_colors, GRADIENT_HAIR_KEY)
var/image/hair_gradient_overlay = get_gradient_overlay(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, GLOB.hair_gradients_list[hair_gradient_style], hair_gradient_color)
. += hair_gradient_overlay
if(show_debrained && (head_flags & HEAD_DEBRAIN))
. += get_debrain_overlay(can_rotate = !dropped)
@@ -147,6 +147,9 @@
if(override_hair_color)
SET_OVERLAY_VALUE(facial_hair_overlay, color, override_hair_color)
SET_OVERLAY_VALUE(hair_overlay, color, override_hair_color)
else if(fixed_hair_color)
SET_OVERLAY_VALUE(facial_hair_overlay, color, fixed_hair_color)
SET_OVERLAY_VALUE(hair_overlay, color, fixed_hair_color)
else
SET_OVERLAY_VALUE(facial_hair_overlay, color, facial_hair_color)
SET_OVERLAY_VALUE(hair_overlay, color, hair_color)
@@ -197,7 +200,7 @@
/obj/item/bodypart/head/proc/get_gradient_overlay(file, icon, layer, datum/sprite_accessory/gradient, grad_color)
RETURN_TYPE(/mutable_appearance)
var/mutable_appearance/gradient_overlay = mutable_appearance(layer = -layer)
var/mutable_appearance/gradient_overlay = mutable_appearance(layer = layer)
var/icon/temp = icon(gradient.icon, gradient.icon_state)
var/icon/temp_hair = icon(file, icon)
temp.Blend(temp_hair, ICON_ADD)
@@ -279,22 +282,42 @@
update_body_parts()
/**
* Set the hair gradient style and color of a human.
* Set the hair gradient style of a human.
* Update calls update_body_parts().
**/
/mob/living/proc/set_hair_gradient(new_style, new_color, update = TRUE)
/mob/living/proc/set_hair_gradient_style(new_style, update = TRUE)
return
/mob/living/carbon/human/set_hair_gradient(new_style, new_color, update = TRUE)
/mob/living/carbon/human/set_hair_gradient_style(new_style, update = TRUE)
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
LAZYSETLEN(grad_style, GRADIENTS_LEN)
LAZYSETLEN(grad_color, GRADIENTS_LEN)
grad_style[GRADIENT_HAIR_KEY] = new_style
if(my_head)
LAZYSETLEN(my_head.gradient_styles, GRADIENTS_LEN)
LAZYSETLEN(my_head.gradient_colors, GRADIENTS_LEN)
my_head.gradient_styles[GRADIENT_HAIR_KEY] = new_style
if(update)
update_body_parts()
/**
* Set the hair gradient color of a human.
* Update calls update_body_parts().
**/
/mob/living/proc/set_hair_gradient_color(new_color, update = TRUE)
return
/mob/living/carbon/human/set_hair_gradient_color(new_color, update = TRUE)
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
LAZYSETLEN(grad_style, GRADIENTS_LEN)
LAZYSETLEN(grad_color, GRADIENTS_LEN)
grad_color[GRADIENT_HAIR_KEY] = new_color
if(my_head)
LAZYSETLEN(my_head.gradient_styles, GRADIENTS_LEN)
my_head.gradient_styles[GRADIENT_HAIR_KEY] = new_style
LAZYSETLEN(my_head.gradient_colors, GRADIENTS_LEN)
my_head.gradient_colors[GRADIENT_HAIR_KEY] = new_color
@@ -332,29 +355,48 @@
// so no head? tough luck
my_head?.override_hair_color = hex_string
else
hair_color = hex_string
facial_hair_color = hex_string
my_head?.facial_hair_color = hex_string
if(update)
update_body_parts()
/**
* Set the facial hair gradient style and color of a human.
* Set the facial hair gradient style of a human.
* Update calls update_body_parts().
**/
/mob/living/proc/set_facial_hair_gradient(new_style, new_color, update = TRUE)
/mob/living/proc/set_facial_hair_gradient_style(new_style, update = TRUE)
return
/mob/living/carbon/human/set_facial_hair_gradient(new_style, new_color, update = TRUE)
/mob/living/carbon/human/set_facial_hair_gradient_style(new_style, update = TRUE)
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
LAZYSETLEN(grad_style, GRADIENTS_LEN)
LAZYSETLEN(grad_color, GRADIENTS_LEN)
grad_style[GRADIENT_FACIAL_HAIR_KEY] = new_style
if(my_head)
LAZYSETLEN(my_head.gradient_styles, GRADIENTS_LEN)
LAZYSETLEN(my_head.gradient_colors, GRADIENTS_LEN)
my_head.gradient_styles[GRADIENT_FACIAL_HAIR_KEY] = new_style
if(update)
update_body_parts()
/**
* Set the facial hair gradient color of a human.
* Update calls update_body_parts().
**/
/mob/living/proc/set_facial_hair_gradient_color(new_color, update = TRUE)
return
/mob/living/carbon/human/set_facial_hair_gradient_color(new_color, update = TRUE)
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
LAZYSETLEN(grad_style, GRADIENTS_LEN)
LAZYSETLEN(grad_color, GRADIENTS_LEN)
grad_color[GRADIENT_FACIAL_HAIR_KEY] = new_color
if(my_head)
LAZYSETLEN(my_head.gradient_styles, GRADIENTS_LEN)
my_head.gradient_styles[GRADIENT_FACIAL_HAIR_KEY] = new_style
LAZYSETLEN(my_head.gradient_colors, GRADIENTS_LEN)
my_head.gradient_colors[GRADIENT_FACIAL_HAIR_KEY] = new_color