diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 47d04aa39ce..5bb897169eb 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -75,6 +75,10 @@ CREATE TABLE `characters` ( `body_accessory` longtext COLLATE utf8mb4_unicode_ci NOT NULL, `gear` longtext COLLATE utf8mb4_unicode_ci NOT NULL, `autohiss` tinyint(1) NOT NULL, + `hair_gradient` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL, + `hair_gradient_offset` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0,0', + `hair_gradient_colour` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '#000000', + `hair_gradient_alpha` tinyint(3) UNSIGNED NOT NULL DEFAULT '255', PRIMARY KEY (`id`), KEY `ckey` (`ckey`) ) ENGINE=InnoDB AUTO_INCREMENT=125467 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/SQL/updates/27-28.sql b/SQL/updates/27-28.sql new file mode 100644 index 00000000000..c8685aed3e1 --- /dev/null +++ b/SQL/updates/27-28.sql @@ -0,0 +1,6 @@ +# Updates DB from 27 to 28 -dearmochi +# Adds support for hair gradient +ALTER TABLE `characters` ADD COLUMN `hair_gradient` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL; +ALTER TABLE `characters` ADD COLUMN `hair_gradient_offset` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0,0'; +ALTER TABLE `characters` ADD COLUMN `hair_gradient_colour` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '#000000'; +ALTER TABLE `characters` ADD COLUMN `hair_gradient_alpha` tinyint(3) UNSIGNED NOT NULL DEFAULT '255' diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index f399915e3e7..bd0fdf96e25 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -123,3 +123,7 @@ #define COLOR_THEME_OPERATIVE "#B8221F" #define COLOR_THEME_GLASS "#75A4C4" #define COLOR_THEME_CLOCKWORK "#CFBA47" + +// Color matrix utilities +#define COLOR_MATRIX_ADD(C) list(COLOR_RED, COLOR_GREEN, COLOR_BLUE, C) +#define COLOR_MATRIX_OVERLAY(C) list(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, C) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index b05cdbb7635..5d3dbac9c50 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -365,7 +365,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 27 +#define SQL_VERSION 28 // Vending machine stuff #define CAT_NORMAL 1 diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 118f6c28188..8996c196b85 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -10,6 +10,8 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list) //hair init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list) + //hair gradients + init_sprite_accessory_subtypes(/datum/sprite_accessory/hair_gradient, GLOB.hair_gradients_list) //facial hair init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) //underwear diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index fc18f2d83cd..ad0f11f97f1 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -11,7 +11,7 @@ GLOBAL_LIST_INIT(hair_styles_full_list, list()) //fluff hair styles GLOBAL_LIST_INIT(facial_hair_styles_list, list()) //stores /datum/sprite_accessory/facial_hair indexed by name GLOBAL_LIST_INIT(facial_hair_styles_male_list, list()) GLOBAL_LIST_INIT(facial_hair_styles_female_list, list()) -GLOBAL_LIST_INIT(skin_styles_female_list, list()) //unused +GLOBAL_LIST_EMPTY(hair_gradients_list) //stores /datum/sprite_accessory/hair_gradient indexed by name //Underwear GLOBAL_LIST_INIT(underwear_list, list()) //stores /datum/sprite_accessory/underwear indexed by name GLOBAL_LIST_INIT(underwear_m, list()) //stores only underwear name diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 6efbb726cff..495c46a72c8 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -23,6 +23,10 @@ if(WEST) pixel_x = 32 +/obj/structure/mirror/Destroy() + QDEL_LIST_ASSOC_VAL(ui_users) + return ..() + /obj/structure/mirror/attack_hand(mob/user) if(broken) return diff --git a/code/modules/client/login_processing/20-load_characters.dm b/code/modules/client/login_processing/20-load_characters.dm index 68bd1458c47..d2871e61b3b 100644 --- a/code/modules/client/login_processing/20-load_characters.dm +++ b/code/modules/client/login_processing/20-load_characters.dm @@ -56,7 +56,11 @@ body_accessory, gear, autohiss, - slot + slot, + hair_gradient, + hair_gradient_offset, + hair_gradient_colour, + hair_gradient_alpha FROM characters WHERE ckey=:ckey"}, list( "ckey" = C.ckey )) diff --git a/code/modules/client/preference/character.dm b/code/modules/client/preference/character.dm index 9a98c853d1b..2f22075d938 100644 --- a/code/modules/client/preference/character.dm +++ b/code/modules/client/preference/character.dm @@ -95,6 +95,13 @@ /// Character slot number, used for saves and stuff. var/slot_number = 0 + // Hair gradient + var/h_grad_style = "None" + var/h_grad_offset_x = 0 + var/h_grad_offset_y = 0 + var/h_grad_colour = "#000000" + var/h_grad_alpha = 255 + // Fuckery to prevent null characters /datum/character_save/New() randomise() @@ -178,7 +185,11 @@ socks=:socks, body_accessory=:body_accessory, gear=:gearlist, - autohiss=:autohiss_mode + autohiss=:autohiss_mode, + hair_gradient=:h_grad_style, + hair_gradient_offset=:h_grad_offset, + hair_gradient_colour=:h_grad_colour, + hair_gradient_alpha=:h_grad_alpha WHERE ckey=:ckey AND slot=:slot"}, list( // OH GOD SO MANY PARAMETERS @@ -234,6 +245,10 @@ "body_accessory" = (body_accessory ? body_accessory : ""), "gearlist" = (gearlist ? gearlist : ""), "autohiss_mode" = autohiss_mode, + "h_grad_style" = h_grad_style, + "h_grad_offset" = "[h_grad_offset_x],[h_grad_offset_y]", + "h_grad_colour" = h_grad_colour, + "h_grad_alpha" = h_grad_alpha, "ckey" = C.ckey, "slot" = slot_number )) @@ -274,7 +289,8 @@ gen_record, player_alt_titles, disabilities, organ_data, rlimb_data, nanotrasen_relation, speciesprefs, - socks, body_accessory, gear, autohiss) + socks, body_accessory, gear, autohiss, + hair_gradient, hair_gradient_offset, hair_gradient_colour, hair_gradient_alpha) VALUES (:ckey, :slot, :metadata, :name, :be_random_name, :gender, :age, :species, :language, @@ -301,7 +317,8 @@ :gen_record, :playertitlelist, :disabilities, :organlist, :rlimblist, :nanotrasen_relation, :speciesprefs, - :socks, :body_accessory, :gearlist, :autohiss_mode) + :socks, :body_accessory, :gearlist, :autohiss_mode, + :h_grad_style, :h_grad_offset, :h_grad_colour, :h_grad_alpha) "}, list( // This has too many params for anyone to look at this without going insae "ckey" = C.ckey, @@ -357,7 +374,11 @@ "socks" = socks, "body_accessory" = (body_accessory ? body_accessory : ""), "gearlist" = (gearlist ? gearlist : ""), - "autohiss_mode" = autohiss_mode + "autohiss_mode" = autohiss_mode, + "h_grad_style" = h_grad_style, + "h_grad_offset" = "[h_grad_offset_x],[h_grad_offset_y]", + "h_grad_colour" = h_grad_colour, + "h_grad_alpha" = h_grad_alpha )) if(!query.warn_execute()) @@ -441,6 +462,11 @@ loadout_gear = query.item[51] autohiss_mode = text2num(query.item[52]) + h_grad_style = query.item[54] + h_grad_offset_x = query.item[55] // parsed down below + h_grad_colour = query.item[56] + h_grad_alpha = query.item[57] + //Sanitize var/datum/species/SP = GLOB.all_species[species] metadata = sanitize_text(metadata, initial(metadata)) @@ -507,6 +533,13 @@ socks = sanitize_text(socks, initial(socks)) body_accessory = sanitize_text(body_accessory, initial(body_accessory)) + h_grad_style = sanitize_text(length(h_grad_style) ? h_grad_style : null, "None") + var/list/expl = splittext(h_grad_offset_x, ",") + if(length(expl) == 2) + h_grad_offset_x = text2num(expl[1]) || 0 + h_grad_offset_y = text2num(expl[2]) || 0 + h_grad_colour = sanitize_hexcolor(h_grad_colour) + h_grad_alpha = sanitize_integer(h_grad_alpha, 0, 255, initial(h_grad_alpha)) loadout_gear = sanitize_json(loadout_gear) if(!player_alt_titles) @@ -874,6 +907,18 @@ else if(hair_style.do_colouration) hair_s.Blend(h_colour, ICON_ADD) + var/datum/sprite_accessory/hair_gradient/gradient = GLOB.hair_gradients_list[h_grad_style] + if(gradient) + var/icon/grad_s = new/icon("icon" = gradient.icon, "icon_state" = gradient.icon_state) + if(h_grad_offset_x) + grad_s.Shift(EAST, h_grad_offset_x) + if(h_grad_offset_y) + grad_s.Shift(NORTH, h_grad_offset_y) + grad_s.Blend(hair_s, ICON_ADD) + grad_s.MapColors(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, h_grad_colour) + grad_s.ChangeOpacity(h_grad_alpha / 255) + hair_s.Blend(grad_s, ICON_OVERLAY) + if(hair_style.secondary_theme) var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") if(!hair_style.no_sec_colour && hair_style.do_colouration ) @@ -1781,6 +1826,12 @@ H.f_style = f_style H.alt_head = alt_head + + H.h_grad_style = h_grad_style + H.h_grad_offset_x = h_grad_offset_x + H.h_grad_offset_y = h_grad_offset_y + H.h_grad_colour = h_grad_colour + H.h_grad_alpha = h_grad_alpha //End of head-specific. character.skin_colour = s_colour diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm index fe4f9574128..3d6c0521ed1 100644 --- a/code/modules/client/preference/link_processing.dm +++ b/code/modules/client/preference/link_processing.dm @@ -372,6 +372,29 @@ var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_hairstyles if(new_h_style) active_character.h_style = new_h_style + + if("h_grad_style") + var/result = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list + if(result) + active_character.h_grad_style = result + + if("h_grad_offset") + var/result = input(user, "Enter your character's hair gradient offset as a comma-separated value (x,y). Example:\n0,0 (no offset)\n5,0 (5 pixels to the right)", "Character Preference") as null|text + if(result) + var/list/expl = splittext(result, ",") + if(length(expl) == 2) + active_character.h_grad_offset_x = clamp(text2num(expl[1]) || 0, -16, 16) + active_character.h_grad_offset_y = clamp(text2num(expl[2]) || 0, -16, 16) + + if("h_grad_colour") + var/result = input(user, "Choose your character's hair gradient colour:", "Character Preference", active_character.h_grad_colour) as color|null + if(result) + active_character.h_grad_colour = result + + if("h_grad_alpha") + var/result = input(user, "Choose your character's hair gradient alpha (0-255):", "Character Preference", active_character.h_grad_alpha) as num|null + if(!isnull(result)) + active_character.h_grad_alpha = clamp(result, 0, 255) if("headaccessory") if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories. diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 9b2bb14e554..4a3c01d41ed 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -233,6 +233,14 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts var/datum/sprite_accessory/temp_hair_style = GLOB.hair_styles_public_list[active_character.h_style] if(temp_hair_style && temp_hair_style.secondary_theme && !temp_hair_style.no_sec_colour) dat += " Color #2 [color_square(active_character.h_sec_colour)]" + // Hair gradient + dat += "
" + dat += "- Gradient:" + dat += " [active_character.h_grad_style]" + dat += " Color [color_square(active_character.h_grad_colour)]" + dat += " [active_character.h_grad_alpha]" + dat += "
" + dat += "- Gradient Offset: [active_character.h_grad_offset_x],[active_character.h_grad_offset_y]" dat += "
" dat += "Facial Hair: " diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm index 8d8b16ead1e..923a430f9dc 100644 --- a/code/modules/client/preference/preferences_mysql.dm +++ b/code/modules/client/preference/preferences_mysql.dm @@ -98,7 +98,6 @@ qdel(query) return 1 - /datum/preferences/proc/load_random_character_slot(client/C) var/list/datum/character_save/valid_slots = list() for(var/datum/character_save/CS in character_saves) diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm index 80cfd86079b..3b6378d89dc 100644 --- a/code/modules/mob/living/carbon/human/appearance.dm +++ b/code/modules/mob/living/carbon/human/appearance.dm @@ -153,6 +153,12 @@ else //this shouldn't happen H.h_style = "Bald" + // Gradient + H.h_grad_style = "None" + H.h_grad_offset_x = 0 + H.h_grad_offset_y = 0 + H.h_grad_colour = "#000000" + H.h_grad_alpha = 255 update_hair() @@ -310,6 +316,25 @@ update_body() return 1 +/mob/living/carbon/human/proc/change_hair_gradient(style, offset_raw, color, alpha) + var/obj/item/organ/external/head/H = get_organ("head") + if(!H) + return + + if(!isnull(style)) + H.h_grad_style = style + if(!isnull(offset_raw)) + var/list/expl = splittext(offset_raw, ",") + if(length(expl) == 2) + H.h_grad_offset_x = clamp(text2num(expl[1]) || 0, -16, 16) + H.h_grad_offset_y = clamp(text2num(expl[2]) || 0, -16, 16) + if(!isnull(color)) + H.h_grad_colour = color + if(!isnull(alpha)) + H.h_grad_alpha = clamp(alpha, 0, 255) + + update_hair() + /mob/living/carbon/human/proc/update_dna() check_dna() dna.ready_dna(src) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 79d6c50ebfc..400c1f065c6 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -354,49 +354,54 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) else //warning("Invalid ha_style for [species.name]: [ha_style]") - - -//HAIR OVERLAY +/** + * Generates overlays for the hair layer. + */ /mob/living/carbon/human/proc/update_hair() - //Reset our hair remove_overlay(HAIR_LAYER) - var/obj/item/organ/external/head/head_organ = get_organ("head") - if(!head_organ) + var/obj/item/organ/external/head/O = get_organ("head") + if(!O) return - //masks and helmets can obscure our hair, unless we're a synthetic - if((head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR))) + if((head?.flags & BLOCKHAIR) || (wear_mask?.flags & BLOCKHAIR)) return - //base icons - var/icon/hair_standing = new /icon('icons/mob/human_face.dmi',"bald_s") - if(head_organ.h_style && !(head && (head.flags & BLOCKHEADHAIR) && !(ismachineperson(src)))) - var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] - if(hair_style && hair_style.species_allowed) - if((head_organ.dna.species.name in hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics... - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - if(istype(head_organ.dna.species, /datum/species/slime)) // I am el worstos - hair_s.Blend("[skin_colour]A0", ICON_AND) - else if(hair_style.do_colouration) - hair_s.Blend(head_organ.hair_colour, ICON_ADD) + var/mutable_appearance/MA = new() + MA.appearance_flags = KEEP_TOGETHER + MA.layer = -HAIR_LAYER + if(O.h_style && !(head?.flags & BLOCKHEADHAIR) && !ismachineperson(src)) + var/datum/sprite_accessory/hair/hair = GLOB.hair_styles_full_list[O.h_style] + if(hair?.species_allowed && ((O.dna.species.name in hair.species_allowed) || (O.dna.species.bodyflags & ALL_RPARTS))) + // Base hair + var/mutable_appearance/img_hair = mutable_appearance(hair.icon, "[hair.icon_state]_s") + if(istype(O.dna.species, /datum/species/slime)) + img_hair.color = COLOR_MATRIX_OVERLAY("[skin_colour]A0") + else if(hair.do_colouration) + img_hair.color = COLOR_MATRIX_ADD(O.hair_colour) + MA.overlays += img_hair - if(hair_style.secondary_theme) - var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") - if(!hair_style.no_sec_colour) - hair_secondary_s.Blend(head_organ.sec_hair_colour, ICON_ADD) - hair_s.Blend(hair_secondary_s, ICON_OVERLAY) + // Gradient + var/datum/sprite_accessory/hair_gradient/gradient = GLOB.hair_gradients_list[O.h_grad_style] + if(gradient) + var/mutable_appearance/img_gradient = mutable_appearance(gradient.icon, gradient.icon_state) + img_gradient.alpha = O.h_grad_alpha + img_gradient.color = COLOR_MATRIX_OVERLAY(O.h_grad_colour) + img_gradient.pixel_x = O.h_grad_offset_x + img_gradient.pixel_y = O.h_grad_offset_y + img_gradient.blend_mode = BLEND_INSET_OVERLAY + MA.overlays += img_gradient - hair_standing = hair_s //hair_standing.Blend(hair_s, ICON_OVERLAY) - //Having it this way preserves animations. Useful for IPC screens. - else - //warning("Invalid h_style for [species.name]: [h_style]") - //hair_standing.Blend(debrained_s, ICON_OVERLAY)//how does i overlay for fish? + // Secondary style + if(hair.secondary_theme) + var/mutable_appearance/img_secondary = mutable_appearance(hair.icon, "[hair.icon_state]_[hair.secondary_theme]_s") + if(!hair.no_sec_colour) + img_secondary.color = COLOR_MATRIX_ADD(O.sec_hair_colour) + MA.overlays += img_secondary - overlays_standing[HAIR_LAYER] = mutable_appearance(hair_standing, layer = -HAIR_LAYER) + overlays_standing[HAIR_LAYER] = MA apply_overlay(HAIR_LAYER) - //FACIAL HAIR OVERLAY /mob/living/carbon/human/proc/update_fhair() //Reset our facial hair diff --git a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm index 7d9be34c063..a3ffe70eb39 100644 --- a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm @@ -648,3 +648,48 @@ /datum/sprite_accessory/socks/stockings_blue name = "Stockings (Blue)" icon_state = "stockings_blue" + +/* HAIR GRADIENT */ + +/datum/sprite_accessory/hair_gradient + icon = 'icons/mob/hair_gradients.dmi' + +/datum/sprite_accessory/hair_gradient/none + name = "None" + icon_state = "none" + +/datum/sprite_accessory/hair_gradient/fadeup + name = "Fade Up" + icon_state = "fadeup" + +/datum/sprite_accessory/hair_gradient/fadedown + name = "Fade Down" + icon_state = "fadedown" + +/datum/sprite_accessory/hair_gradient/vertical_split + name = "Vertical Split" + icon_state = "vsplit" + +/datum/sprite_accessory/hair_gradient/_split + name = "Horizontal Split" + icon_state = "bottomflat" + +/datum/sprite_accessory/hair_gradient/reflected + name = "Reflected" + icon_state = "reflected_high" + +/datum/sprite_accessory/hair_gradient/reflected_inverse + name = "Reflected Inverse" + icon_state = "reflected_inverse_high" + +/datum/sprite_accessory/hair_gradient/wavy + name = "Wavy" + icon_state = "wavy" + +/datum/sprite_accessory/hair_gradient/long_fade_up + name = "Long Fade Up" + icon_state = "long_fade_up" + +/datum/sprite_accessory/hair_gradient/long_fade_down + name = "Long Fade Down" + icon_state = "long_fade_down" diff --git a/code/modules/surgery/organs/subtypes/standard.dm b/code/modules/surgery/organs/subtypes/standard.dm index 895bcbefd95..f992c31fd77 100644 --- a/code/modules/surgery/organs/subtypes/standard.dm +++ b/code/modules/surgery/organs/subtypes/standard.dm @@ -213,6 +213,11 @@ var/hair_colour = "#000000" var/sec_hair_colour = "#000000" var/h_style = "Bald" + var/h_grad_style = "None" + var/h_grad_offset_x = 0 + var/h_grad_offset_y = 0 + var/h_grad_colour = "#000000" + var/h_grad_alpha = 255 //Head accessory colour and style var/headacc_colour = "#000000" diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index 6af513e1d08..f9f51cafc83 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -93,6 +93,30 @@ if(new_hair && (!..()) && owner.change_hair_color(new_hair, 1)) update_dna() + if("hair_gradient") + if(can_change(APPEARANCE_HAIR) && length(valid_hairstyles)) + var/new_style = input("Please select gradient style.", "Hair Gradient", head_organ.h_grad_style) as null|anything in GLOB.hair_gradients_list + if(new_style) + owner.change_hair_gradient(style = new_style) + + if("hair_gradient_offset") + if(can_change(APPEARANCE_HAIR) && length(valid_hairstyles)) + var/new_offset = input("Please enter gradient offset as a comma-separated value (x,y). Example:\n0,0 (no offset)\n5,0 (5 pixels to the right)", "Hair Gradient", "[head_organ.h_grad_offset_x],[head_organ.h_grad_offset_y]") as null|text + if(new_offset) + owner.change_hair_gradient(offset_raw = new_offset) + + if("hair_gradient_colour") + if(can_change(APPEARANCE_HAIR) && length(valid_hairstyles)) + var/new_color = input("Please select gradient color.", "Hair Gradient", head_organ.h_grad_colour) as null|color + if(new_color) + owner.change_hair_gradient(color = new_color) + + if("hair_gradient_alpha") + if(can_change(APPEARANCE_HAIR) && length(valid_hairstyles)) + var/new_alpha = input("Please enter gradient alpha (0-255).", "Hair Gradient", head_organ.h_grad_alpha) as null|num + if(!isnull(new_alpha)) + owner.change_hair_gradient(alpha = new_alpha) + if("facial_hair") if(can_change(APPEARANCE_FACIAL_HAIR) && (params["facial_hair"] in valid_facial_hairstyles)) if(owner.change_facial_hair(params["facial_hair"])) @@ -274,6 +298,7 @@ data["change_head_marking_color"] = can_change_markings("head") data["change_body_marking_color"] = can_change_markings("body") data["change_tail_marking_color"] = can_change_markings("tail") + data["change_hair_gradient"] = can_change(APPEARANCE_HAIR) && length(valid_hairstyles) return data diff --git a/config/example/config.toml b/config/example/config.toml index ad90552155e..150e989a2a1 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -141,7 +141,7 @@ ipc_screens = [ # Enable/disable the database on a whole sql_enabled = false # SQL version. If this is a mismatch, round start will be delayed -sql_version = 27 +sql_version = 28 # SQL server address. Can be an IP or DNS name sql_address = "127.0.0.1" # SQL server port diff --git a/icons/mob/hair_gradients.dmi b/icons/mob/hair_gradients.dmi new file mode 100644 index 00000000000..d457ebf7d14 Binary files /dev/null and b/icons/mob/hair_gradients.dmi differ diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger.js b/tgui/packages/tgui/interfaces/AppearanceChanger.js index 56c318bd774..3146a47f4a8 100644 --- a/tgui/packages/tgui/interfaces/AppearanceChanger.js +++ b/tgui/packages/tgui/interfaces/AppearanceChanger.js @@ -28,6 +28,7 @@ export const AppearanceChanger = (props, context) => { change_hair, hair_styles, hair_style, + change_hair_gradient, change_facial_hair, facial_hair_styles, facial_hair_style, @@ -130,6 +131,14 @@ export const AppearanceChanger = (props, context) => { ))} )} + {!!change_hair_gradient && ( + +