Spritebloat final boss (#5704)
## About The Pull Request SO! This pr does a few things: One, it makes a new system for having properly dynamic uniforms. You can select the core components (for most things, a shirt and pants), accessories, etc. I've gone ahead and made a dynamic security uniform via this. I also made a dynamic security cap! Two, it adds a few black reskins. All the reskins do is remove some small red accent. The reskins are available for the following: Sec hud sunglasses Sec gloves Sec belt Tackle gloves and gorrila gloves both can also now be reskinned to the black variant that already existed. Before anyone screams, I am adding NO blue. (Though, you could recolor the uniform in the loadout) (video was too big for github so here it is on catbox) https://files.catbox.moe/8wtgkv.mp4 ## Why It's Good For The Game I kept feeling unsatisfied with my options as security, and as a whole. Hopefully this is a better uniform system, even if we don't end up using it that much. ## Proof Of Testing <details> <summary>Screenshots/Videos</summary> </details> ## Changelog 🆑 UvvU add: Added dynamic uniform support add: Added black varients for the sec huds, belt, and gloves /🆑
@@ -31,6 +31,8 @@
|
||||
|
||||
/// Whether the menu is in the middle of refreshing the preview
|
||||
var/refreshing = TRUE
|
||||
///BUBBER VAR: Optional display labels for color groups, keyed by color index.
|
||||
var/list/color_labels
|
||||
|
||||
/**
|
||||
* Whether the menu is currently locked down to prevent abuse from players.
|
||||
@@ -102,12 +104,14 @@
|
||||
/datum/greyscale_modify_menu/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["greyscale_config"] = "[config.name]"
|
||||
data["full_color_string"] = split_colors?.Join("") //BUBBER ADDITION
|
||||
|
||||
var/list/color_data = list()
|
||||
data["colors"] = color_data
|
||||
for(var/i in 1 to config.expected_colors)
|
||||
color_data += list(list(
|
||||
"index" = i,
|
||||
"label" = color_labels?["[i]"] || color_labels?[i], //BUBBER ADDITION
|
||||
"value" = split_colors[i]
|
||||
))
|
||||
|
||||
|
||||
@@ -407,7 +407,11 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
|
||||
formatted_item["reskins"] = get_reskin_options()
|
||||
formatted_item["icon"] = ui_icon
|
||||
formatted_item["icon_state"] = ui_icon_state
|
||||
formatted_item["ckey_whitelist"] = ckeywhitelist // BUBBER EDIT ADDITION: Filter ckey-locked items
|
||||
//BUBBER EDIT ADDITION START - Dynamic Uniforms + ckey lock filter
|
||||
if(initial(item_path.greyscale_component_style_type))
|
||||
add_component_style_preview_to_ui_data(formatted_item)
|
||||
formatted_item["ckey_whitelist"] = ckeywhitelist
|
||||
//BUBBER EDIT ADDITION END
|
||||
|
||||
return formatted_item
|
||||
|
||||
|
||||
@@ -204,6 +204,11 @@
|
||||
|
||||
if(greyscale_colors)
|
||||
vended_item.set_greyscale(colors=greyscale_colors)
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
var/list/component_style_details = greyscale_component_style_vend_details?[REF(user)]
|
||||
if(component_style_details && vended_item.greyscale_component_style_type)
|
||||
vended_item.apply_greyscale_component_style(component_style_details)
|
||||
//BUBBER ADDITION END
|
||||
if(IsReachableBy(user) && user.put_in_hands(vended_item))
|
||||
to_chat(user, span_notice("You take [item_record.name] out of the slot."))
|
||||
vended_item.do_pickup_animation(user, src)
|
||||
|
||||
@@ -44,11 +44,22 @@
|
||||
)
|
||||
|
||||
var/atom/printed = record.product_path
|
||||
//BUBBER ADDITION START - Dynamic uniforms
|
||||
var/component_preview_applied = FALSE
|
||||
if(ispath(printed, /obj/item))
|
||||
var/obj/item/printed_item = printed
|
||||
if(initial(printed_item.greyscale_component_style_type))
|
||||
static_record["image"] = get_component_style_vending_preview_image(printed_item)
|
||||
component_preview_applied = TRUE
|
||||
//BUBBER ADDITION END
|
||||
// If it's not GAGS and has no innate colors we have to care about, we use DMIcon
|
||||
if(ispath(printed, /atom) \
|
||||
//BUBBER EDIT START - dynamic uniform
|
||||
if(!component_preview_applied \
|
||||
&& ispath(printed, /atom) \
|
||||
&& (!initial(printed.greyscale_config) || !initial(printed.greyscale_colors)) \
|
||||
&& !initial(printed.color) \
|
||||
)
|
||||
//BUBBER EDIT END
|
||||
static_record["icon"] = initial(printed.icon)
|
||||
static_record["icon_state"] = initial(printed.icon_state)
|
||||
|
||||
@@ -147,6 +158,12 @@
|
||||
if(!istype(product))
|
||||
return FALSE
|
||||
var/atom/fake_atom = product.product_path
|
||||
//BUBBER ADDITION START - Dynamic uniforms
|
||||
if(ispath(fake_atom, /obj/item))
|
||||
var/obj/item/fake_item = fake_atom
|
||||
if(initial(fake_item.greyscale_component_style_type))
|
||||
return open_component_style_vending_menu(product, params, ui.user)
|
||||
//BUBBER ADDITION END
|
||||
var/config = initial(fake_atom.greyscale_config)
|
||||
if(!config)
|
||||
return FALSE
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,13 @@
|
||||
/// Tracks selected core components for component-style GAGS loadout items.
|
||||
#define INFO_GREYSCALE_COMPONENT_CORES "greyscale_component_cores"
|
||||
/// Tracks selected optional components for component-style GAGS loadout items.
|
||||
#define INFO_GREYSCALE_COMPONENT_ACCESSORIES "greyscale_component_accessories"
|
||||
|
||||
/// Accessory applies to every option in a core component slot.
|
||||
#define COMPONENT_CORE_ALL "all"
|
||||
/// Accessory applies only to the named core option.
|
||||
#define COMPONENT_CORE_EXACT "exact"
|
||||
/// Accessory applies to the named core option and any option descended from it.
|
||||
#define COMPONENT_CORE_BRANCH "branch"
|
||||
/// Accessory applies to a named group defined on the core component.
|
||||
#define COMPONENT_CORE_GROUP "group"
|
||||
@@ -8,6 +8,47 @@
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/captain.dmi'
|
||||
json_config = 'modular_zubbers/code/datums/greyscale/json_configs/cso.json'
|
||||
|
||||
/datum/greyscale_config/security_uniform
|
||||
name = "Security Uniform"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_uniform
|
||||
greyscale_component_config_states = list("recolorable_security_uniform", "rsecurity")
|
||||
|
||||
/datum/greyscale_config/security_uniform/New()
|
||||
InitializeGreyscaleComponentStyleConfig()
|
||||
|
||||
/datum/greyscale_config/security_uniform/Refresh(loadFromDisk = FALSE)
|
||||
return RefreshGreyscaleComponentStyleConfig(loadFromDisk)
|
||||
|
||||
/datum/greyscale_config/security_uniform/worn
|
||||
name = "Security Uniform (Worn)"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
|
||||
/datum/greyscale_config/security_uniform/worn/digi
|
||||
name = "Security Uniform (Worn, Digi)"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
|
||||
/datum/greyscale_config/security_cap
|
||||
name = "Security Cap"
|
||||
icon_file = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_cap
|
||||
greyscale_component_config_states = list("security_cap")
|
||||
|
||||
/datum/greyscale_config/security_cap/New()
|
||||
InitializeGreyscaleComponentStyleConfig()
|
||||
|
||||
/datum/greyscale_config/security_cap/Refresh(loadFromDisk = FALSE)
|
||||
return RefreshGreyscaleComponentStyleConfig(loadFromDisk)
|
||||
|
||||
/datum/greyscale_config/security_cap/worn
|
||||
name = "Security Cap (Worn)"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/head/hats.dmi'
|
||||
greyscale_component_config_state_overrides = list(
|
||||
"security_cap" = "security_capg",
|
||||
"security_cap_accs_front" = "security_capg_accs_backing",
|
||||
"security_cap_accs_logo" = "security_capg_accs_logo",
|
||||
)
|
||||
|
||||
/datum/greyscale_config/gi/worn/digi
|
||||
name = "Gi (Worn, Digi)"
|
||||
icon_file = 'modular_skyrat/master_files/icons/mob/clothing/under/costume_digi.dmi'
|
||||
|
||||
@@ -46,6 +46,75 @@
|
||||
icon_state = "secsoft_v"
|
||||
armor_type = /datum/armor/cosmetic_sec
|
||||
|
||||
/datum/greyscale_component_style/security_cap
|
||||
output_icon_state = "security_cap"
|
||||
fallback_icon_state = "security_cap"
|
||||
default_accessories = list("security_cap_accs_front", "security_cap_accs_logo")
|
||||
core_components = list(
|
||||
list(
|
||||
"name" = "Cap",
|
||||
"key" = "cap",
|
||||
"default" = "security",
|
||||
"options" = list(
|
||||
"security" = list(
|
||||
"name" = "Security cap",
|
||||
"state" = "security_cap",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Main",
|
||||
"default_color" = "#A53429",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
accessories = list(
|
||||
"Backing" = list(
|
||||
"state" = "security_cap_accs_front",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Backing",
|
||||
"default_color" = "#3F6E9E",
|
||||
),
|
||||
"Logo" = list(
|
||||
"state" = "security_cap_accs_logo",
|
||||
"color_id" = 3,
|
||||
"color_label" = "Logo",
|
||||
"default_color" = "#FFFFFF",
|
||||
),
|
||||
)
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable
|
||||
name = "security cap"
|
||||
desc = "It's a robust baseball hat, this one seems to have been custom ordered."
|
||||
icon = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
worn_icon = 'modular_zubbers/icons/mob/clothing/head/hats.dmi'
|
||||
icon_state = "security_cap"
|
||||
post_init_icon_state = "security_cap"
|
||||
soft_type = "security_cap"
|
||||
soft_suffix = ""
|
||||
greyscale_config = /datum/greyscale_config/security_cap
|
||||
greyscale_config_worn = /datum/greyscale_config/security_cap/worn
|
||||
greyscale_colors = "#A53429#3F6E9E#FFFFFF"
|
||||
flags_1 = parent_type::flags_1 | IS_PLAYER_COLORABLE_1
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_cap
|
||||
greyscale_component_icon_file = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
greyscale_component_worn_icon_file = 'modular_zubbers/icons/mob/clothing/head/hats.dmi'
|
||||
greyscale_component_worn_state_overrides = list(
|
||||
"security_cap" = "security_capg",
|
||||
"security_cap_accs_front" = "security_capg_accs_backing",
|
||||
"security_cap_accs_logo" = "security_capg_accs_logo",
|
||||
)
|
||||
greyscale_component_accessories = list("security_cap_accs_front", "security_cap_accs_logo")
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable/Initialize(mapload)
|
||||
initialize_greyscale_component_style()
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable/update_greyscale()
|
||||
. = ..()
|
||||
update_greyscale_component_icons()
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable/flip(mob/user)
|
||||
balloon_alert(user, "can't be flipped!")
|
||||
|
||||
/obj/item/clothing/head/sec/viro/beanie
|
||||
name = "security beanie"
|
||||
desc = "A beanie for security purposes"
|
||||
|
||||
@@ -92,6 +92,196 @@
|
||||
can_adjust = TRUE
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
|
||||
|
||||
/datum/greyscale_component_style/security_uniform
|
||||
output_icon_state = "rsecurity"
|
||||
fallback_icon_state = "security_shirt"
|
||||
default_accessories = list("security_shirt_accs_tie")
|
||||
core_components = list(
|
||||
list(
|
||||
"name" = "Shirt",
|
||||
"key" = "shirt",
|
||||
"default" = "security",
|
||||
"groups" = list(
|
||||
"tie_compatible" = list("security", "viro", "suit"),
|
||||
"arm_trim_compatible" = list("security", "viro", "turtleneck"),
|
||||
),
|
||||
"options" = list(
|
||||
"security" = list(
|
||||
"name" = "Security shirt",
|
||||
"state" = "security_shirt",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
"viro" = list(
|
||||
"name" = "Virosec shirt",
|
||||
"state" = "security_shirt_viro",
|
||||
"parent" = "security",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
"suit" = list(
|
||||
"name" = "Security suit",
|
||||
"state" = "security_shirt_suit",
|
||||
"parent" = "security",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
"turtleneck" = list(
|
||||
"name" = "Security turtleneck",
|
||||
"state" = "security_shirt_turtleneck",
|
||||
"parent" = "security",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
),
|
||||
),
|
||||
list(
|
||||
"name" = "Pants",
|
||||
"key" = "pants",
|
||||
"default" = "security",
|
||||
"options" = list(
|
||||
"security" = list(
|
||||
"name" = "Security pants",
|
||||
"state" = "security_pants",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Pants",
|
||||
"default_color" = "#4B4C51",
|
||||
),
|
||||
"skirt" = list(
|
||||
"name" = "Security skirt",
|
||||
"state" = "security_pants_skirt",
|
||||
"parent" = "security",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Pants",
|
||||
"default_color" = "#4B4C51",
|
||||
),
|
||||
"viro" = list(
|
||||
"name" = "Virosec pants",
|
||||
"state" = "security_pants_viro",
|
||||
"parent" = "security",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Pants",
|
||||
"default_color" = "#4B4C51",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
accessories = list(
|
||||
"Tie" = list(
|
||||
"state" = "security_shirt_accs_tie",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_GROUP = "tie_compatible")),
|
||||
"color_id" = 3,
|
||||
"color_label" = "Tie",
|
||||
"default_color" = "#2D2D33",
|
||||
),
|
||||
"Arm trim one" = list(
|
||||
"state" = "security_shirt_accs_arm_trim_one",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_EXACT = list("security", "viro"))),
|
||||
"color_id" = 4,
|
||||
"color_label" = "Arm trim one",
|
||||
"default_color" = "#39393F",
|
||||
),
|
||||
"Arm trim two" = list(
|
||||
"state" = "security_shirt_accs_arm_trim_two",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_GROUP = "arm_trim_compatible")),
|
||||
"color_id" = 13,
|
||||
"color_label" = "Arm trim two",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Arm trim three" = list(
|
||||
"state" = "security_shirt_accs_arm_trim_three",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_GROUP = "arm_trim_compatible")),
|
||||
"color_id" = 12,
|
||||
"color_label" = "Arm trim three",
|
||||
"default_color" = "#D0D0D0",
|
||||
),
|
||||
"Epaulettes" = list(
|
||||
"state" = "security_shirt_accss_epaulettes",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_BRANCH = "security")),
|
||||
"color_id" = 11,
|
||||
"color_label" = "Epaulettes",
|
||||
"default_color" = "#39393F",
|
||||
),
|
||||
"Viro shirt trim" = list(
|
||||
"state" = "security_shirt_accs_trim_viro",
|
||||
"applies_to" = list("shirt" = list("viro" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 8,
|
||||
"color_label" = "Viro shirt trim",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Suit inner shirt" = list(
|
||||
"state" = "security_shirt_suit_accs_inner",
|
||||
"applies_to" = list("shirt" = list("suit" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 9,
|
||||
"color_label" = "Suit inner shirt",
|
||||
"default_color" = "#EBEBEB",
|
||||
),
|
||||
"Buckle" = list(
|
||||
"state" = "security_pants_accs_buckle",
|
||||
"applies_to" = list("pants" = COMPONENT_CORE_ALL),
|
||||
"color_id" = 10,
|
||||
"color_label" = "Buckle",
|
||||
"default_color" = "#B5BBCF",
|
||||
),
|
||||
"Skirt outer trim" = list(
|
||||
"state" = "security_pants_skirt_accs_outer_trim",
|
||||
"applies_to" = list("pants" = list("skirt" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 5,
|
||||
"color_label" = "Skirt outer trim",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Skirt inner trim" = list(
|
||||
"state" = "security_pants_skirt_accs_inner_trim",
|
||||
"applies_to" = list("pants" = list("skirt" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 6,
|
||||
"color_label" = "Skirt inner trim",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Viro pants trim" = list(
|
||||
"state" = "security_pants_viro_accs_trim",
|
||||
"applies_to" = list("pants" = list("viro" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 7,
|
||||
"color_label" = "Viro pants trim",
|
||||
"default_color" = "#7D2A25",
|
||||
),
|
||||
)
|
||||
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable
|
||||
name = "security uniform"
|
||||
desc = "A tactical security jumpsuit for officers. This one seems to have a few custom modifications."
|
||||
icon = 'icons/map_icons/clothing/under/_under.dmi'
|
||||
worn_icon = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
worn_icon_digi = 'modular_zubbers/icons/mob/clothing/under/security_digi.dmi'
|
||||
icon_state = "/obj/item/clothing/under/rank/security/officer/recolorable"
|
||||
post_init_icon_state = "recolorable_security_uniform"
|
||||
greyscale_config = /datum/greyscale_config/security_uniform
|
||||
greyscale_config_worn = /datum/greyscale_config/security_uniform/worn
|
||||
greyscale_config_worn_digi = /datum/greyscale_config/security_uniform/worn/digi
|
||||
greyscale_colors = "#BA3B2E#4B4C51#2D2D33#39393F#46464D#46464D#7D2A25#46464D#EBEBEB#B5BBCF#39393F#D0D0D0#46464D"
|
||||
flags_1 = parent_type::flags_1 | IS_PLAYER_COLORABLE_1
|
||||
can_adjust = TRUE
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_uniform
|
||||
greyscale_component_worn_icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
greyscale_component_worn_digi_icon_file = 'modular_zubbers/icons/mob/clothing/under/security_digi.dmi'
|
||||
greyscale_component_digi_fallback_icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
greyscale_component_accessories = list("security_shirt_accs_tie")
|
||||
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable/Initialize(mapload)
|
||||
initialize_greyscale_component_style()
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable/update_greyscale()
|
||||
. = ..()
|
||||
update_greyscale_component_icons()
|
||||
icon = 'icons/obj/clothing/under/security.dmi'
|
||||
icon_state = "rsecurity"
|
||||
inhand_icon_state = "r_suit"
|
||||
|
||||
// Virosec, sprites by axietheaxolotl
|
||||
|
||||
/obj/item/clothing/under/rank/security/viro/officer/
|
||||
|
||||
@@ -0,0 +1,866 @@
|
||||
/datum/greyscale_component_style
|
||||
/// Marker used when loadout storage needs to preserve an intentionally empty accessory selection.
|
||||
var/static/no_accessories_marker = "__component_style_no_accessories"
|
||||
|
||||
/datum/greyscale_component_style
|
||||
/// Icon state inserted into generated icon files.
|
||||
var/output_icon_state
|
||||
/// Fallback component state to use if every selected component is missing.
|
||||
var/fallback_icon_state
|
||||
/// Suffix checked first for digitigrade component states.
|
||||
var/digi_suffix = "_digi"
|
||||
/// Ordered list of core component definitions.
|
||||
var/list/core_components = list()
|
||||
/// Optional component definitions, keyed by display name.
|
||||
var/list/accessories = list()
|
||||
/// Optional accessory component states enabled by default.
|
||||
var/list/default_accessories = list()
|
||||
/// Suffix checked first for adjusted/casual component states.
|
||||
var/adjusted_suffix = "_d"
|
||||
|
||||
/datum/greyscale_component_style/proc/default_core_selection() as /list
|
||||
var/list/selection = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
selection[core_component["key"]] = core_component["default"]
|
||||
return selection
|
||||
|
||||
/datum/greyscale_component_style/proc/sanitize_core_selection(list/selected_cores) as /list
|
||||
var/list/sanitized = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/core_key = core_component["key"]
|
||||
var/selected_state = selected_cores?[core_key] || core_component["default"]
|
||||
if(!core_option_exists(core_component, selected_state))
|
||||
selected_state = core_component["default"]
|
||||
sanitized[core_key] = selected_state
|
||||
return sanitized
|
||||
|
||||
/datum/greyscale_component_style/proc/sanitize_accessories(list/selected_accessories, list/selected_cores) as /list
|
||||
var/list/sanitized = list()
|
||||
if(!islist(selected_accessories) || (no_accessories_marker in selected_accessories))
|
||||
return sanitized
|
||||
for(var/accessory in selected_accessories)
|
||||
if(!accessory_allowed(accessory, selected_cores))
|
||||
continue
|
||||
sanitized += accessory
|
||||
return sanitized
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_exists(list/core_component, option_state)
|
||||
var/list/options = core_component["options"]
|
||||
return islist(options?[option_state])
|
||||
|
||||
/datum/greyscale_component_style/proc/core_component_for_key(core_key) as /list
|
||||
for(var/list/core_component as anything in core_components)
|
||||
if(core_component["key"] == core_key)
|
||||
return core_component
|
||||
return null
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_data(list/core_component, option_id) as /list
|
||||
var/list/options = core_component?["options"]
|
||||
return options?[option_id]
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_state(list/core_component, option_id)
|
||||
var/list/option_data = core_option_data(core_component, option_id)
|
||||
return option_data?["state"]
|
||||
|
||||
/datum/greyscale_component_style/proc/core_group_options(list/core_component, group_id) as /list
|
||||
var/list/groups = core_component?["groups"]
|
||||
var/list/group_options = groups?[group_id]
|
||||
return islist(group_options) ? group_options : null
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_matches(list/core_component, selected_option, target_option, match_mode)
|
||||
if(match_mode == COMPONENT_CORE_ALL)
|
||||
return TRUE
|
||||
if(match_mode == COMPONENT_CORE_EXACT)
|
||||
return selected_option == target_option
|
||||
if(match_mode != COMPONENT_CORE_BRANCH)
|
||||
return FALSE
|
||||
|
||||
var/current_option = selected_option
|
||||
while(current_option)
|
||||
if(current_option == target_option)
|
||||
return TRUE
|
||||
var/list/option_data = core_option_data(core_component, current_option)
|
||||
current_option = option_data?["parent"]
|
||||
return FALSE
|
||||
|
||||
/datum/greyscale_component_style/proc/core_rule_targets(list/core_component, rule_value, use_groups = FALSE) as /list
|
||||
if(islist(rule_value))
|
||||
return rule_value
|
||||
if(use_groups)
|
||||
return core_group_options(core_component, rule_value) || list()
|
||||
return list(rule_value)
|
||||
|
||||
/datum/greyscale_component_style/proc/core_apply_rule_matches(list/core_component, selected_option, rule_mode, rule_value)
|
||||
var/list/targets = core_rule_targets(core_component, rule_value, rule_mode == COMPONENT_CORE_GROUP)
|
||||
var/match_mode = (rule_mode == COMPONENT_CORE_GROUP) ? COMPONENT_CORE_EXACT : rule_mode
|
||||
for(var/target_option in targets)
|
||||
if(core_option_matches(core_component, selected_option, target_option, match_mode))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/greyscale_component_style/proc/core_ui_data(list/selected_cores) as /list
|
||||
var/list/core_data = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/core_key = core_component["key"]
|
||||
core_data += list(list(
|
||||
"name" = core_component["name"],
|
||||
"key" = core_key,
|
||||
"options" = option_ui_data(core_component["options"], selected_cores[core_key]),
|
||||
))
|
||||
return core_data
|
||||
|
||||
/datum/greyscale_component_style/proc/option_ui_data(list/options, selected_value) as /list
|
||||
var/list/style_options = list()
|
||||
for(var/option_id in options)
|
||||
var/list/option_data = options[option_id]
|
||||
style_options += list(list(
|
||||
"name" = option_data["name"] || option_id,
|
||||
"value" = option_id,
|
||||
"selected" = (option_id == selected_value),
|
||||
))
|
||||
return style_options
|
||||
|
||||
/datum/greyscale_component_style/proc/accessory_ui_data(list/selected_accessories, list/selected_cores) as /list
|
||||
var/list/style_options = list()
|
||||
for(var/accessory_name in accessories)
|
||||
var/list/accessory_rules = accessories[accessory_name]
|
||||
var/accessory_state = accessory_rules["state"]
|
||||
if(!accessory_allowed(accessory_state, selected_cores))
|
||||
continue
|
||||
style_options += list(list(
|
||||
"name" = accessory_name,
|
||||
"value" = accessory_state,
|
||||
"selected" = (accessory_state in selected_accessories),
|
||||
))
|
||||
return style_options
|
||||
|
||||
/datum/greyscale_component_style/proc/accessory_allowed(accessory_state, list/selected_cores)
|
||||
var/list/accessory_rules = accessory_rules_for_state(accessory_state)
|
||||
if(isnull(accessory_rules))
|
||||
return FALSE
|
||||
|
||||
var/list/apply_rules = accessory_rules["applies_to"]
|
||||
if(!length(apply_rules))
|
||||
return TRUE
|
||||
|
||||
for(var/core_key in apply_rules)
|
||||
var/list/core_component = core_component_for_key(core_key)
|
||||
if(!core_component)
|
||||
return FALSE
|
||||
|
||||
var/selected_option = selected_cores?[core_key]
|
||||
if(!selected_option)
|
||||
return FALSE
|
||||
|
||||
var/apply_rule = apply_rules[core_key]
|
||||
if(apply_rule == COMPONENT_CORE_ALL)
|
||||
continue
|
||||
|
||||
var/matched_core = FALSE
|
||||
if(islist(apply_rule))
|
||||
for(var/rule_key in apply_rule)
|
||||
if(rule_key in list(COMPONENT_CORE_EXACT, COMPONENT_CORE_BRANCH, COMPONENT_CORE_GROUP))
|
||||
if(core_apply_rule_matches(core_component, selected_option, rule_key, apply_rule[rule_key]))
|
||||
matched_core = TRUE
|
||||
break
|
||||
continue
|
||||
|
||||
if(core_apply_rule_matches(core_component, selected_option, apply_rule[rule_key] || COMPONENT_CORE_EXACT, rule_key))
|
||||
matched_core = TRUE
|
||||
break
|
||||
else
|
||||
matched_core = core_option_matches(core_component, selected_option, apply_rule, COMPONENT_CORE_EXACT)
|
||||
|
||||
if(!matched_core)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/greyscale_component_style/proc/accessory_rules_for_state(accessory_state) as /list
|
||||
for(var/accessory_name in accessories)
|
||||
var/list/accessory_rules = accessories[accessory_name]
|
||||
if(accessory_rules["state"] == accessory_state)
|
||||
return accessory_rules
|
||||
return null
|
||||
|
||||
/datum/greyscale_component_style/proc/active_components(list/selected_cores, list/selected_accessories) as /list
|
||||
var/list/components = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/component_state = core_option_state(core_component, selected_cores?[core_component["key"]])
|
||||
if(component_state)
|
||||
components += component_state
|
||||
if(islist(selected_accessories))
|
||||
for(var/accessory_state in selected_accessories)
|
||||
if(!accessory_state)
|
||||
continue
|
||||
components += accessory_state
|
||||
return components
|
||||
|
||||
/datum/greyscale_component_style/proc/color_id_for_component(component_state)
|
||||
var/list/component_rules = component_rules_for_state(component_state)
|
||||
return component_rules?["color_id"]
|
||||
|
||||
/datum/greyscale_component_style/proc/component_rules_for_state(component_state) as /list
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/list/options = core_component["options"]
|
||||
for(var/option_id in options)
|
||||
var/list/option_data = options[option_id]
|
||||
if(option_data["state"] == component_state)
|
||||
return option_data
|
||||
for(var/accessory_name in accessories)
|
||||
var/list/accessory_rules = accessories[accessory_name]
|
||||
if(accessory_rules["state"] == component_state)
|
||||
return accessory_rules
|
||||
return null
|
||||
|
||||
/datum/greyscale_component_style/proc/active_color_ids(list/selected_cores, list/selected_accessories) as /list
|
||||
var/list/active_color_ids = list()
|
||||
for(var/component in active_components(selected_cores, selected_accessories))
|
||||
var/color_id = color_id_for_component(component)
|
||||
if(!color_id || (color_id in active_color_ids))
|
||||
continue
|
||||
active_color_ids += color_id
|
||||
return active_color_ids
|
||||
|
||||
/datum/greyscale_component_style/proc/active_color_labels(list/selected_cores, list/selected_accessories) as /list
|
||||
var/list/labels = list()
|
||||
for(var/component_state in active_components(selected_cores, selected_accessories))
|
||||
var/list/component_rules = component_rules_for_state(component_state)
|
||||
var/color_id = component_rules?["color_id"]
|
||||
var/color_label = component_rules?["color_label"]
|
||||
if(!color_id || !color_label)
|
||||
continue
|
||||
labels["[color_id]"] = color_label
|
||||
return labels
|
||||
|
||||
/datum/greyscale_component_style/proc/default_color_list(list/fallback_colors, expected_colors = 0) as /list
|
||||
var/list/colors = islist(fallback_colors) ? fallback_colors.Copy() : list()
|
||||
while(length(colors) < expected_colors)
|
||||
colors += null
|
||||
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/list/options = core_component["options"]
|
||||
for(var/option_id in options)
|
||||
apply_component_default_color(colors, options[option_id])
|
||||
for(var/accessory_name in accessories)
|
||||
apply_component_default_color(colors, accessories[accessory_name])
|
||||
|
||||
for(var/i in 1 to length(colors))
|
||||
if(!colors[i])
|
||||
colors[i] = rgb(100, 100, 100)
|
||||
return colors
|
||||
|
||||
/datum/greyscale_component_style/proc/apply_component_default_color(list/colors, list/component_rules)
|
||||
var/color_id = component_rules?["color_id"]
|
||||
var/default_color = component_rules?["default_color"]
|
||||
if(!color_id || !default_color)
|
||||
return
|
||||
while(length(colors) < color_id)
|
||||
colors += null
|
||||
colors[color_id] = default_color
|
||||
|
||||
/datum/greyscale_component_style/proc/greyscale_config_data(list/output_states, list/state_overrides) as /list
|
||||
var/list/layers = greyscale_config_layers(state_overrides)
|
||||
var/list/config_data = list()
|
||||
if(!length(output_states))
|
||||
output_states = list(output_icon_state)
|
||||
for(var/output_state in output_states)
|
||||
if(isnull(output_state))
|
||||
continue
|
||||
config_data[output_state] = layers
|
||||
return config_data
|
||||
|
||||
/datum/greyscale_component_style/proc/greyscale_config_layers(list/state_overrides) as /list
|
||||
var/list/layers = list()
|
||||
var/list/seen_states = list()
|
||||
for(var/component_state in greyscale_config_component_states())
|
||||
if(!component_state || seen_states[component_state])
|
||||
continue
|
||||
seen_states[component_state] = TRUE
|
||||
|
||||
var/list/component_rules = component_rules_for_state(component_state)
|
||||
var/layer_state = islist(state_overrides) ? (state_overrides[component_state] || component_state) : component_state
|
||||
var/list/layer_data = list(
|
||||
"type" = "icon_state",
|
||||
"icon_state" = layer_state,
|
||||
"blend_mode" = "overlay",
|
||||
)
|
||||
var/color_id = component_rules?["color_id"]
|
||||
if(color_id)
|
||||
layer_data["color_ids"] = list(color_id)
|
||||
layers += list(layer_data)
|
||||
return layers
|
||||
|
||||
/datum/greyscale_component_style/proc/greyscale_config_component_states() as /list
|
||||
var/list/component_states = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/list/options = core_component["options"]
|
||||
for(var/option_id in options)
|
||||
component_states += options[option_id]["state"]
|
||||
for(var/accessory_name in accessories)
|
||||
component_states += accessories[accessory_name]["state"]
|
||||
return component_states
|
||||
|
||||
/datum/greyscale_config
|
||||
/// Optional dynamic component-style definition used to generate this config without a hand-written JSON file.
|
||||
var/greyscale_component_style_type
|
||||
/// Icon states this generated config should expose in its output bundle.
|
||||
var/list/greyscale_component_config_states
|
||||
/// Optional logical component state to real icon state mapping for this config's icon file.
|
||||
var/list/greyscale_component_config_state_overrides
|
||||
|
||||
/datum/greyscale_config/proc/InitializeGreyscaleComponentStyleConfig()
|
||||
if(!icon_file)
|
||||
stack_trace("Greyscale component-style config object [DebugName()] is missing an icon file, make sure `icon_file` has been assigned a value.")
|
||||
string_icon_file = "[icon_file]"
|
||||
if(!name)
|
||||
stack_trace("Greyscale component-style config object [DebugName()] is missing a name, make sure `name` has been assigned a value.")
|
||||
if(!greyscale_component_style_type)
|
||||
stack_trace("Greyscale component-style config object [DebugName()] is missing a component style type.")
|
||||
|
||||
/datum/greyscale_config/proc/RefreshGreyscaleComponentStyleConfig(loadFromDisk = FALSE)
|
||||
if(loadFromDisk)
|
||||
var/changed = FALSE
|
||||
|
||||
icon_file = file(string_icon_file)
|
||||
var/icon_hash = rustg_hash_file("md5", string_icon_file)
|
||||
if(icon_file_hash != icon_hash)
|
||||
icon_file_hash = icon_hash
|
||||
changed = TRUE
|
||||
|
||||
for(var/datum/greyscale_layer/layer as anything in flat_all_layers)
|
||||
if(layer.DiskRefresh())
|
||||
changed = TRUE
|
||||
|
||||
if(!changed)
|
||||
return FALSE
|
||||
|
||||
var/style_type = greyscale_component_style_type
|
||||
var/datum/greyscale_component_style/component_style = new style_type
|
||||
var/list/raw = component_style.greyscale_config_data(greyscale_component_config_states, greyscale_component_config_state_overrides)
|
||||
raw_json_string = json_encode(raw)
|
||||
qdel(component_style)
|
||||
|
||||
ReadIconStateConfiguration(raw)
|
||||
if(!length(icon_states))
|
||||
CRASH("The component-style greyscale configuration [DebugName()] doesn't have any icon states.")
|
||||
|
||||
icon_cache = list()
|
||||
ReadMetadata()
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_GREYSCALE_CONFIG_REFRESHED)
|
||||
return TRUE
|
||||
|
||||
/datum/greyscale_component_style/proc/build_icon(icon_file, list/colors, list/selected_cores, list/selected_accessories, use_digi = FALSE, digi_fallback_icon_file = null, only_dir = null, list/state_overrides = null, use_adjusted = FALSE)
|
||||
var/icon/final_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
final_icon.Scale(32, 32)
|
||||
if(!icon_file)
|
||||
return final_icon
|
||||
|
||||
var/icon/built_icon
|
||||
for(var/component in active_components(selected_cores, selected_accessories))
|
||||
if(!component)
|
||||
continue
|
||||
var/layer_file = icon_file
|
||||
var/state_to_use = component
|
||||
if(islist(state_overrides))
|
||||
state_to_use = state_overrides[component] || component
|
||||
|
||||
var/list/candidate_states = list()
|
||||
if(use_digi && use_adjusted)
|
||||
candidate_states += "[state_to_use][digi_suffix][adjusted_suffix]"
|
||||
if(use_adjusted)
|
||||
candidate_states += "[state_to_use][adjusted_suffix]"
|
||||
if(use_digi)
|
||||
candidate_states += "[state_to_use][digi_suffix]"
|
||||
candidate_states += state_to_use
|
||||
|
||||
var/list/candidate_files = list(layer_file)
|
||||
if(use_digi && digi_fallback_icon_file)
|
||||
candidate_files += digi_fallback_icon_file
|
||||
|
||||
var/found_component_state = FALSE
|
||||
for(var/candidate_state in candidate_states)
|
||||
for(var/candidate_file in candidate_files)
|
||||
if(!icon_exists(candidate_file, candidate_state))
|
||||
continue
|
||||
layer_file = candidate_file
|
||||
state_to_use = candidate_state
|
||||
found_component_state = TRUE
|
||||
break
|
||||
if(found_component_state)
|
||||
break
|
||||
|
||||
if(!found_component_state)
|
||||
continue
|
||||
|
||||
var/icon/component_icon = isnull(only_dir) ? icon(layer_file, state_to_use) : icon(layer_file, state_to_use, only_dir)
|
||||
var/color_id = color_id_for_component(component)
|
||||
if(color_id && length(colors) >= color_id && colors[color_id])
|
||||
component_icon.Blend(colors[color_id], ICON_MULTIPLY)
|
||||
|
||||
if(isnull(built_icon))
|
||||
built_icon = component_icon
|
||||
else
|
||||
built_icon.Blend(component_icon, ICON_OVERLAY)
|
||||
|
||||
if(isnull(built_icon) && fallback_icon_state)
|
||||
built_icon = isnull(only_dir) ? icon(icon_file, fallback_icon_state) : icon(icon_file, fallback_icon_state, only_dir)
|
||||
|
||||
if(!isnull(built_icon))
|
||||
var/generated_icon_state = use_adjusted ? "[output_icon_state][adjusted_suffix]" : output_icon_state
|
||||
final_icon.Insert(built_icon, generated_icon_state)
|
||||
return final_icon
|
||||
|
||||
/obj/item
|
||||
/// Optional component-style GAGS definition used for layered, selectable components.
|
||||
var/greyscale_component_style_type
|
||||
/// Selected core option ids, keyed by core component id.
|
||||
var/list/greyscale_component_cores
|
||||
/// Selected optional component states.
|
||||
var/list/greyscale_component_accessories
|
||||
/// Object icon file used for component-style GAGS composition.
|
||||
var/greyscale_component_icon_file
|
||||
/// Worn icon file used for component-style GAGS composition.
|
||||
var/greyscale_component_worn_icon_file
|
||||
/// Digitigrade worn icon file used for component-style GAGS composition.
|
||||
var/greyscale_component_worn_digi_icon_file
|
||||
/// Optional non-digi worn icon fallback for missing digitigrade component states.
|
||||
var/greyscale_component_digi_fallback_icon_file
|
||||
/// Whether component-style worn icons should use adjusted/casual component states.
|
||||
var/greyscale_component_use_adjusted = FALSE
|
||||
/// Optional logical component state to worn icon state mapping.
|
||||
var/list/greyscale_component_worn_state_overrides
|
||||
/// Optional logical component state to digitigrade worn icon state mapping.
|
||||
var/list/greyscale_component_worn_digi_state_overrides
|
||||
|
||||
/obj/item/proc/get_greyscale_component_style() as /datum/greyscale_component_style
|
||||
if(!greyscale_component_style_type)
|
||||
return null
|
||||
return new greyscale_component_style_type
|
||||
|
||||
/obj/item/proc/initialize_greyscale_component_style()
|
||||
var/datum/greyscale_component_style/component_style = get_greyscale_component_style()
|
||||
if(!component_style)
|
||||
return
|
||||
greyscale_component_cores = component_style.sanitize_core_selection(greyscale_component_cores)
|
||||
var/list/accessories_to_apply = isnull(greyscale_component_accessories) ? component_style.default_accessories : greyscale_component_accessories
|
||||
greyscale_component_accessories = component_style.sanitize_accessories(accessories_to_apply, greyscale_component_cores)
|
||||
qdel(component_style)
|
||||
|
||||
/obj/item/proc/apply_greyscale_component_style(list/item_details)
|
||||
var/datum/greyscale_component_style/component_style = get_greyscale_component_style()
|
||||
if(!component_style)
|
||||
return
|
||||
greyscale_component_cores = component_style.sanitize_core_selection(item_details?[INFO_GREYSCALE_COMPONENT_CORES] || greyscale_component_cores)
|
||||
var/list/accessories_to_apply = item_details?[INFO_GREYSCALE_COMPONENT_ACCESSORIES]
|
||||
if(isnull(accessories_to_apply))
|
||||
accessories_to_apply = isnull(greyscale_component_accessories) ? component_style.default_accessories : greyscale_component_accessories
|
||||
greyscale_component_accessories = component_style.sanitize_accessories(accessories_to_apply, greyscale_component_cores)
|
||||
qdel(component_style)
|
||||
update_greyscale()
|
||||
|
||||
/obj/item/proc/update_greyscale_component_icons()
|
||||
var/datum/greyscale_component_style/component_style = get_greyscale_component_style()
|
||||
if(!component_style || !greyscale_colors)
|
||||
qdel(component_style)
|
||||
return
|
||||
|
||||
var/list/colors = SSgreyscale.ParseColorString(greyscale_colors)
|
||||
icon_state = component_style.output_icon_state
|
||||
worn_icon_state = greyscale_component_use_adjusted ? "[component_style.output_icon_state][component_style.adjusted_suffix]" : component_style.output_icon_state
|
||||
greyscale_config_worn = null
|
||||
greyscale_config_worn_digi = null
|
||||
if(greyscale_component_icon_file)
|
||||
icon = component_style.build_icon(greyscale_component_icon_file, colors, greyscale_component_cores, greyscale_component_accessories)
|
||||
if(greyscale_component_worn_icon_file)
|
||||
worn_icon = component_style.build_icon(greyscale_component_worn_icon_file, colors, greyscale_component_cores, greyscale_component_accessories, state_overrides = greyscale_component_worn_state_overrides, use_adjusted = greyscale_component_use_adjusted)
|
||||
if(greyscale_component_worn_digi_icon_file)
|
||||
worn_icon_digi = component_style.build_icon(greyscale_component_worn_digi_icon_file, colors, greyscale_component_cores, greyscale_component_accessories, use_digi = TRUE, digi_fallback_icon_file = greyscale_component_digi_fallback_icon_file, state_overrides = greyscale_component_worn_digi_state_overrides || greyscale_component_worn_state_overrides, use_adjusted = greyscale_component_use_adjusted)
|
||||
qdel(component_style)
|
||||
|
||||
var/obj/item/clothing/worn_clothing = src
|
||||
if(!istype(worn_clothing))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/wearer = worn_clothing.loc
|
||||
if(!istype(wearer))
|
||||
return
|
||||
wearer.update_clothing(worn_clothing.slot_flags)
|
||||
|
||||
/obj/item/clothing/under/adjust_to_normal()
|
||||
. = ..()
|
||||
if(!greyscale_component_style_type)
|
||||
return
|
||||
greyscale_component_use_adjusted = FALSE
|
||||
update_greyscale_component_icons()
|
||||
|
||||
/obj/item/clothing/under/adjust_to_alt()
|
||||
. = ..()
|
||||
if(!greyscale_component_style_type)
|
||||
return
|
||||
greyscale_component_use_adjusted = TRUE
|
||||
update_greyscale_component_icons()
|
||||
|
||||
/datum/loadout_item/proc/uses_greyscale_component_style()
|
||||
return !!initial(item_path.greyscale_component_style_type)
|
||||
|
||||
/datum/loadout_item
|
||||
/// Optional icon file used for component-style color previews instead of the item's worn preview.
|
||||
var/greyscale_component_preview_icon_file
|
||||
/// Optional logical component state to preview icon state mapping.
|
||||
var/list/greyscale_component_preview_state_overrides
|
||||
|
||||
/// Opens a component-style GAGS editing menu when the loadout item supports it.
|
||||
/datum/loadout_item/proc/set_component_style_item_color(datum/preference_middleware/loadout/manager, mob/user)
|
||||
if(manager.menu)
|
||||
return FALSE
|
||||
|
||||
var/list/loadout = manager.get_current_loadout()
|
||||
var/list/allowed_configs = list()
|
||||
if(initial(item_path.greyscale_config_worn))
|
||||
allowed_configs += "[initial(item_path.greyscale_config_worn)]"
|
||||
else if(initial(item_path.greyscale_config))
|
||||
allowed_configs += "[initial(item_path.greyscale_config)]"
|
||||
|
||||
var/list/selected_cores = loadout?[item_path]?[INFO_GREYSCALE_COMPONENT_CORES]
|
||||
if(isnull(selected_cores))
|
||||
selected_cores = initial(item_path.greyscale_component_cores)
|
||||
|
||||
var/list/selected_accessories = loadout?[item_path]?[INFO_GREYSCALE_COMPONENT_ACCESSORIES]
|
||||
if(islist(selected_accessories) && (/datum/greyscale_component_style::no_accessories_marker in selected_accessories))
|
||||
selected_accessories = list()
|
||||
|
||||
var/starting_config = initial(item_path.greyscale_config_worn) || initial(item_path.greyscale_config)
|
||||
var/starting_colors = loadout?[item_path]?[INFO_GREYSCALE] || initial(item_path.greyscale_colors)
|
||||
var/datum/greyscale_config/selected_config = SSgreyscale.configurations["[starting_config]"]
|
||||
if(selected_config && length(SSgreyscale.ParseColorString(starting_colors)) != selected_config.expected_colors)
|
||||
starting_colors = initial(item_path.greyscale_colors)
|
||||
|
||||
var/preview_icon_file = greyscale_component_preview_icon_file || initial(item_path.greyscale_component_worn_icon_file) || initial(item_path.greyscale_component_icon_file)
|
||||
var/list/preview_state_overrides = greyscale_component_preview_state_overrides
|
||||
if(!preview_state_overrides && preview_icon_file == initial(item_path.greyscale_component_worn_icon_file))
|
||||
preview_state_overrides = initial(item_path.greyscale_component_worn_state_overrides)
|
||||
|
||||
var/datum/greyscale_modify_menu/component_style/menu = new(
|
||||
manager,
|
||||
user,
|
||||
allowed_configs,
|
||||
CALLBACK(src, PROC_REF(set_slot_greyscale), manager),
|
||||
starting_icon_state = initial(item_path.icon_state),
|
||||
starting_config = starting_config,
|
||||
starting_colors = starting_colors,
|
||||
component_style_type = initial(item_path.greyscale_component_style_type),
|
||||
selected_cores = selected_cores,
|
||||
selected_accessories = selected_accessories,
|
||||
preview_icon_file = preview_icon_file,
|
||||
preview_digi_fallback_icon_file = initial(item_path.greyscale_component_digi_fallback_icon_file),
|
||||
preview_state_overrides = preview_state_overrides,
|
||||
default_colors = initial(item_path.greyscale_colors),
|
||||
default_cores = initial(item_path.greyscale_component_cores),
|
||||
default_accessories = null,
|
||||
)
|
||||
|
||||
manager.register_greyscale_menu(menu)
|
||||
menu.ui_interact(user)
|
||||
return TRUE
|
||||
|
||||
/// Saves component-style GAGS color and component state.
|
||||
/datum/loadout_item/proc/set_component_style_slot_greyscale(datum/preference_middleware/loadout/manager, datum/greyscale_modify_menu/open_menu)
|
||||
var/datum/greyscale_modify_menu/component_style/component_menu = open_menu
|
||||
if(!istype(component_menu))
|
||||
return FALSE
|
||||
|
||||
var/list/loadout = manager.get_current_loadout()
|
||||
if(!loadout?[item_path])
|
||||
return FALSE
|
||||
|
||||
var/list/colors = component_menu.split_colors
|
||||
if(!colors)
|
||||
return FALSE
|
||||
|
||||
loadout[item_path][INFO_GREYSCALE] = colors.Join("")
|
||||
loadout[item_path][INFO_GREYSCALE_COMPONENT_CORES] = component_menu.selected_cores.Copy()
|
||||
loadout[item_path][INFO_GREYSCALE_COMPONENT_ACCESSORIES] = length(component_menu.selected_accessories) ? component_menu.selected_accessories.Copy() : list(/datum/greyscale_component_style::no_accessories_marker)
|
||||
manager.save_current_loadout(loadout)
|
||||
return TRUE
|
||||
|
||||
/datum/loadout_item/proc/apply_component_style_to_equipped_item(obj/item/equipped_item, list/item_details)
|
||||
if(equipped_item?.greyscale_component_style_type)
|
||||
equipped_item.apply_greyscale_component_style(item_details)
|
||||
|
||||
/proc/get_component_style_item_preview_image(obj/item/item_path)
|
||||
if(item_path == /obj/item/clothing/head/soft/sec/recolorable)
|
||||
var/icon/preview_icon = icon('icons/obj/clothing/head/hats.dmi', "secsoft")
|
||||
return icon2base64(preview_icon)
|
||||
|
||||
var/component_style_type = initial(item_path.greyscale_component_style_type)
|
||||
if(!component_style_type)
|
||||
return null
|
||||
var/datum/greyscale_component_style/component_style = new component_style_type
|
||||
var/list/default_cores = component_style.sanitize_core_selection(initial(item_path.greyscale_component_cores))
|
||||
var/list/default_accessories = component_style.default_accessories
|
||||
default_accessories = component_style.sanitize_accessories(default_accessories, default_cores)
|
||||
var/list/default_colors = component_style.default_color_list(SSgreyscale.ParseColorString(initial(item_path.greyscale_colors)))
|
||||
var/preview_icon_file = initial(item_path.greyscale_component_worn_icon_file) || initial(item_path.greyscale_component_icon_file)
|
||||
var/list/preview_state_overrides
|
||||
if(preview_icon_file == initial(item_path.greyscale_component_worn_icon_file))
|
||||
preview_state_overrides = initial(item_path.greyscale_component_worn_state_overrides)
|
||||
var/icon/south_preview_icon = component_style.build_icon(preview_icon_file, default_colors, default_cores, default_accessories, only_dir = SOUTH, state_overrides = preview_state_overrides)
|
||||
var/icon/single_frame_preview = icon(south_preview_icon, component_style.output_icon_state, SOUTH, 1)
|
||||
qdel(component_style)
|
||||
return icon2base64(single_frame_preview)
|
||||
|
||||
/datum/loadout_item/proc/get_component_style_preview_image()
|
||||
return get_component_style_item_preview_image(item_path)
|
||||
|
||||
/datum/loadout_item/proc/add_component_style_preview_to_ui_data(list/formatted_item)
|
||||
if(!uses_greyscale_component_style())
|
||||
return
|
||||
formatted_item["image"] = get_component_style_preview_image()
|
||||
|
||||
/obj/machinery/vending/proc/get_component_style_vending_preview_image(obj/item/item_path)
|
||||
return get_component_style_item_preview_image(item_path)
|
||||
|
||||
/obj/machinery/vending
|
||||
/// Temporary component-style selections keyed by vending user ref while the normal vend path creates the item.
|
||||
var/list/greyscale_component_style_vend_details
|
||||
|
||||
/obj/machinery/vending/proc/open_component_style_vending_menu(datum/data/vending_product/product, list/params, mob/user)
|
||||
var/obj/item/item_path = product.product_path
|
||||
var/list/allowed_configs = list()
|
||||
if(initial(item_path.greyscale_config_worn))
|
||||
allowed_configs += "[initial(item_path.greyscale_config_worn)]"
|
||||
else if(initial(item_path.greyscale_config))
|
||||
allowed_configs += "[initial(item_path.greyscale_config)]"
|
||||
|
||||
var/preview_icon_file = initial(item_path.greyscale_component_worn_icon_file) || initial(item_path.greyscale_component_icon_file)
|
||||
var/list/preview_state_overrides
|
||||
if(preview_icon_file == initial(item_path.greyscale_component_worn_icon_file))
|
||||
preview_state_overrides = initial(item_path.greyscale_component_worn_state_overrides)
|
||||
|
||||
var/datum/greyscale_modify_menu/component_style/menu = new(
|
||||
src,
|
||||
user,
|
||||
allowed_configs,
|
||||
CALLBACK(src, PROC_REF(_vend_component_style), params, user),
|
||||
starting_icon_state = initial(item_path.icon_state),
|
||||
starting_config = initial(item_path.greyscale_config_worn) || initial(item_path.greyscale_config),
|
||||
starting_colors = initial(item_path.greyscale_colors),
|
||||
component_style_type = initial(item_path.greyscale_component_style_type),
|
||||
selected_cores = initial(item_path.greyscale_component_cores),
|
||||
selected_accessories = initial(item_path.greyscale_component_accessories),
|
||||
preview_icon_file = preview_icon_file,
|
||||
preview_digi_fallback_icon_file = initial(item_path.greyscale_component_digi_fallback_icon_file),
|
||||
preview_state_overrides = preview_state_overrides,
|
||||
default_colors = initial(item_path.greyscale_colors),
|
||||
default_cores = initial(item_path.greyscale_component_cores),
|
||||
default_accessories = initial(item_path.greyscale_component_accessories),
|
||||
)
|
||||
menu.ui_interact(user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/vending/proc/_vend_component_style(list/params, mob/user, datum/greyscale_modify_menu/menu)
|
||||
PRIVATE_PROC(TRUE)
|
||||
|
||||
var/datum/greyscale_modify_menu/component_style/component_menu = menu
|
||||
if(user != menu.user || !istype(component_menu))
|
||||
return
|
||||
|
||||
var/list/component_details = list(
|
||||
INFO_GREYSCALE_COMPONENT_CORES = component_menu.selected_cores.Copy(),
|
||||
INFO_GREYSCALE_COMPONENT_ACCESSORIES = length(component_menu.selected_accessories) ? component_menu.selected_accessories.Copy() : list(/datum/greyscale_component_style::no_accessories_marker),
|
||||
)
|
||||
if(!greyscale_component_style_vend_details)
|
||||
greyscale_component_style_vend_details = list()
|
||||
var/user_ref = REF(user)
|
||||
greyscale_component_style_vend_details[user_ref] = component_details
|
||||
vend(params, user, component_menu.split_colors)
|
||||
greyscale_component_style_vend_details -= user_ref
|
||||
if(!length(greyscale_component_style_vend_details))
|
||||
greyscale_component_style_vend_details = null
|
||||
|
||||
/datum/greyscale_modify_menu/component_style
|
||||
var/datum/greyscale_component_style/component_style
|
||||
var/component_style_type
|
||||
var/list/selected_cores
|
||||
var/list/selected_accessories
|
||||
var/preview_icon_file
|
||||
var/preview_use_digi = FALSE
|
||||
var/preview_digi_fallback_icon_file
|
||||
var/list/preview_state_overrides
|
||||
var/list/default_colors
|
||||
var/list/default_cores
|
||||
var/list/default_accessories
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/New(datum/target, client/user, list/allowed_configs, datum/callback/apply_callback, starting_icon_state = "", starting_config, starting_colors, unlocked = FALSE, component_style_type = null, list/selected_cores = null, list/selected_accessories = null, preview_icon_file = null, preview_use_digi = FALSE, preview_digi_fallback_icon_file = null, list/preview_state_overrides = null, default_colors = null, list/default_cores = null, list/default_accessories = null)
|
||||
src.component_style_type = component_style_type || /datum/greyscale_component_style
|
||||
var/style_type = src.component_style_type
|
||||
src.component_style = new style_type
|
||||
src.selected_cores = component_style.sanitize_core_selection(selected_cores)
|
||||
if(isnull(selected_accessories))
|
||||
selected_accessories = component_style.default_accessories
|
||||
src.selected_accessories = component_style.sanitize_accessories(selected_accessories, src.selected_cores)
|
||||
src.preview_icon_file = preview_icon_file
|
||||
src.preview_use_digi = preview_use_digi
|
||||
src.preview_digi_fallback_icon_file = preview_digi_fallback_icon_file
|
||||
src.preview_state_overrides = preview_state_overrides
|
||||
src.default_colors = component_style.default_color_list(SSgreyscale.ParseColorString(default_colors || starting_colors))
|
||||
src.default_cores = component_style.sanitize_core_selection(default_cores)
|
||||
if(isnull(default_accessories))
|
||||
default_accessories = component_style.default_accessories
|
||||
src.default_accessories = component_style.sanitize_accessories(default_accessories, src.default_cores)
|
||||
sprite_data = list(
|
||||
"icon_states" = list(component_style.output_icon_state),
|
||||
"finished" = null,
|
||||
"steps" = list(),
|
||||
"time_spent" = 0,
|
||||
)
|
||||
update_component_color_labels()
|
||||
return ..()
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/Destroy()
|
||||
QDEL_NULL(component_style)
|
||||
selected_cores = null
|
||||
selected_accessories = null
|
||||
preview_state_overrides = null
|
||||
default_colors = null
|
||||
default_cores = null
|
||||
default_accessories = null
|
||||
return ..()
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/ReadColorsFromString(colorString)
|
||||
if(..())
|
||||
return TRUE
|
||||
normalize_split_colors()
|
||||
return TRUE
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/ui_data(mob/user)
|
||||
normalize_split_colors()
|
||||
var/list/data = list()
|
||||
data["greyscale_config"] = "[config.name]"
|
||||
data["full_color_string"] = split_colors.Join("")
|
||||
|
||||
var/list/active_color_ids = component_style.active_color_ids(selected_cores, selected_accessories)
|
||||
var/list/active_colors = list()
|
||||
for(var/i in 1 to config.expected_colors)
|
||||
if(!(i in active_color_ids))
|
||||
continue
|
||||
active_colors += list(list(
|
||||
"index" = i,
|
||||
"label" = color_labels?["[i]"],
|
||||
"value" = (length(split_colors) >= i) ? split_colors[i] : rgb(100, 100, 100),
|
||||
))
|
||||
data["colors"] = active_colors
|
||||
|
||||
data["generate_full_preview"] = generate_full_preview
|
||||
data["unlocked"] = unlocked
|
||||
data["refreshing"] = refreshing
|
||||
data["monitoring_files"] = !!(config.datum_flags & DF_ISPROCESSING)
|
||||
data["sprites_dir"] = dir2text(sprite_dir)
|
||||
data["icon_state"] = icon_state
|
||||
data["sprites"] = sprite_data || list(
|
||||
"icon_states" = list(component_style.output_icon_state),
|
||||
"finished" = null,
|
||||
"steps" = list(),
|
||||
"time_spent" = 0,
|
||||
)
|
||||
data["hide_full_color_string"] = FALSE
|
||||
data["component_style"] = list(
|
||||
"core_components" = component_style.core_ui_data(selected_cores),
|
||||
"accessories" = component_style.accessory_ui_data(selected_accessories, selected_cores),
|
||||
)
|
||||
return data
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
switch(action)
|
||||
if("component_style_set_core")
|
||||
var/core_component = params["component"]
|
||||
var/new_style = params["value"]
|
||||
if(!set_core_component(core_component, new_style))
|
||||
return TRUE
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("component_style_toggle_accessory")
|
||||
var/accessory = params["accessory"]
|
||||
if(!component_style.accessory_allowed(accessory, selected_cores))
|
||||
return TRUE
|
||||
if(accessory in selected_accessories)
|
||||
selected_accessories -= accessory
|
||||
else
|
||||
selected_accessories += accessory
|
||||
update_component_color_labels()
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("random_all_colors")
|
||||
for(var/color_id in component_style.active_color_ids(selected_cores, selected_accessories))
|
||||
randomize_color(color_id)
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("component_style_reset_color")
|
||||
var/color_id = text2num(params["color_index"])
|
||||
if(color_id && length(default_colors) >= color_id)
|
||||
normalize_split_colors()
|
||||
split_colors[color_id] = default_colors[color_id]
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("component_style_reset_all")
|
||||
selected_cores = default_cores.Copy()
|
||||
selected_accessories = default_accessories.Copy()
|
||||
split_colors = islist(default_colors) ? default_colors.Copy() : list()
|
||||
normalize_split_colors()
|
||||
update_component_color_labels()
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/set_core_component(core_key, new_style)
|
||||
for(var/list/core_component as anything in component_style.core_components)
|
||||
if(core_component["key"] != core_key)
|
||||
continue
|
||||
if(!component_style.core_option_exists(core_component, new_style))
|
||||
return FALSE
|
||||
selected_cores[core_key] = new_style
|
||||
prune_invalid_accessories()
|
||||
update_component_color_labels()
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/prune_invalid_accessories()
|
||||
selected_accessories = component_style.sanitize_accessories(selected_accessories, selected_cores)
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/update_component_color_labels()
|
||||
color_labels = component_style.active_color_labels(selected_cores, selected_accessories)
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/normalize_split_colors()
|
||||
if(!config)
|
||||
return
|
||||
if(!islist(split_colors))
|
||||
split_colors = list()
|
||||
if(length(split_colors) > config.expected_colors)
|
||||
split_colors.Cut(config.expected_colors + 1)
|
||||
while(length(split_colors) < config.expected_colors)
|
||||
var/next_color_index = length(split_colors) + 1
|
||||
split_colors += (islist(default_colors) && length(default_colors) >= next_color_index) ? default_colors[next_color_index] : rgb(100, 100, 100)
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/refresh_preview()
|
||||
normalize_split_colors()
|
||||
var/list/used_colors = split_colors.Copy(1, config.expected_colors + 1)
|
||||
|
||||
sprite_data = list()
|
||||
sprite_data["icon_states"] = list(component_style.output_icon_state)
|
||||
icon_state = component_style.output_icon_state
|
||||
|
||||
var/time_spent = TICK_USAGE
|
||||
var/icon/preview_icon = component_style.build_icon(preview_icon_file, used_colors, selected_cores, selected_accessories, preview_use_digi, preview_digi_fallback_icon_file, only_dir = sprite_dir, state_overrides = preview_state_overrides)
|
||||
var/image/finished = image(preview_icon, icon_state = icon_state)
|
||||
time_spent = TICK_USAGE - time_spent
|
||||
|
||||
sprite_data["time_spent"] = TICK_DELTA_TO_MS(time_spent)
|
||||
sprite_data["finished"] = icon2html(finished, user, dir = sprite_dir, sourceonly = TRUE)
|
||||
sprite_data["steps"] = list()
|
||||
refreshing = FALSE
|
||||
@@ -76,6 +76,22 @@
|
||||
item_path = /obj/item/clothing/head/soft/sec
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable
|
||||
name = "Recolorable Security Cap"
|
||||
item_path = /obj/item/clothing/head/soft/sec/recolorable
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
greyscale_component_preview_icon_file = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable/set_item_color(datum/preference_middleware/loadout/manager, mob/user)
|
||||
return set_component_style_item_color(manager, user)
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable/set_slot_greyscale(datum/preference_middleware/loadout/manager, datum/greyscale_modify_menu/open_menu)
|
||||
return set_component_style_slot_greyscale(manager, open_menu)
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable/on_equip_item(obj/item/equipped_item, list/item_details, mob/living/carbon/human/equipper, datum/outfit/outfit, visuals_only = FALSE)
|
||||
. = ..()
|
||||
apply_component_style_to_equipped_item(equipped_item, item_details)
|
||||
|
||||
/datum/loadout_item/head/security_cap/veteran
|
||||
name = "Veteran's Soft Security Cap"
|
||||
item_path = /obj/item/clothing/head/soft/veteran
|
||||
|
||||
@@ -80,6 +80,21 @@
|
||||
item_path = /obj/item/clothing/under/rank/security/officer/redsec
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable
|
||||
name = "Recolorable Security Uniform"
|
||||
item_path = /obj/item/clothing/under/rank/security/officer/recolorable
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable/set_item_color(datum/preference_middleware/loadout/manager, mob/user)
|
||||
return set_component_style_item_color(manager, user)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable/set_slot_greyscale(datum/preference_middleware/loadout/manager, datum/greyscale_modify_menu/open_menu)
|
||||
return set_component_style_slot_greyscale(manager, open_menu)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable/on_equip_item(obj/item/equipped_item, list/item_details, mob/living/carbon/human/equipper, datum/outfit/outfit, visuals_only = FALSE)
|
||||
. = ..()
|
||||
apply_component_style_to_equipped_item(equipped_item, item_details)
|
||||
|
||||
/datum/loadout_item/uniform/security/peacekeeper/armadyne
|
||||
name = "Armadyne Corporate Uniform"
|
||||
item_path =/obj/item/clothing/under/rank/security/peacekeeper/armadyne
|
||||
|
||||
@@ -13,6 +13,21 @@
|
||||
icon_state = "security"
|
||||
worn_icon_state = "security"
|
||||
|
||||
/obj/item/storage/belt/security/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_belt, infinite = FALSE)
|
||||
|
||||
/datum/atom_skin/sec_belt
|
||||
abstract_type = /datum/atom_skin/sec_belt
|
||||
|
||||
/datum/atom_skin/sec_belt/standard
|
||||
preview_name = "Standard"
|
||||
|
||||
/datum/atom_skin/sec_belt/black
|
||||
preview_name = "Black"
|
||||
new_icon = 'modular_zubbers/icons/obj/clothing/belts/belts.dmi'
|
||||
new_worn_icon = 'modular_zubbers/icons/mob/clothing/belt.dmi'
|
||||
new_icon_state = "security_black"
|
||||
|
||||
///Enables you to quickdraw weapons from security holsters
|
||||
/datum/storage/security/open_storage(datum/source, mob/user)
|
||||
var/atom/resolve_parent = parent
|
||||
@@ -51,6 +66,21 @@
|
||||
icon_state = "sunhudsec"
|
||||
glass_colour_type = /datum/client_colour/glass_colour/red
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/sunglasses/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_hud_sunglasses, infinite = FALSE)
|
||||
|
||||
/datum/atom_skin/sec_hud_sunglasses
|
||||
abstract_type = /datum/atom_skin/sec_hud_sunglasses
|
||||
|
||||
/datum/atom_skin/sec_hud_sunglasses/standard
|
||||
preview_name = "Standard"
|
||||
|
||||
/datum/atom_skin/sec_hud_sunglasses/sunglasses
|
||||
preview_name = "Sunglasses"
|
||||
new_icon = 'icons/obj/clothing/glasses.dmi'
|
||||
new_worn_icon = 'icons/mob/clothing/eyes.dmi'
|
||||
new_icon_state = "sun"
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/sunglasses/eyepatch
|
||||
icon_state = "hudpatch"
|
||||
base_icon_state = "hudpatch"
|
||||
@@ -203,16 +233,37 @@
|
||||
worn_icon = 'icons/mob/clothing/hands.dmi'
|
||||
icon_state = "sec"
|
||||
|
||||
/obj/item/clothing/gloves/color/black/security/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_gloves, infinite = FALSE)
|
||||
|
||||
/obj/item/clothing/gloves/tackler/security //Can't just overwrite tackler, as there's a ton of subtypes that we'd then need to account for. This is easier. MUCH easier.
|
||||
icon = 'icons/obj/clothing/gloves.dmi'
|
||||
worn_icon = 'icons/mob/clothing/hands.dmi'
|
||||
icon_state = "gorilla"
|
||||
|
||||
/obj/item/clothing/gloves/tackler/security/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_gloves, infinite = FALSE)
|
||||
|
||||
/obj/item/clothing/gloves/tackler/combat
|
||||
icon = 'icons/obj/clothing/gloves.dmi'
|
||||
worn_icon = 'icons/mob/clothing/hands.dmi'
|
||||
icon_state = "gorilla"
|
||||
|
||||
/obj/item/clothing/gloves/tackler/combat/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_gloves, infinite = FALSE)
|
||||
|
||||
/datum/atom_skin/sec_gloves
|
||||
abstract_type = /datum/atom_skin/sec_gloves
|
||||
|
||||
/datum/atom_skin/sec_gloves/standard
|
||||
preview_name = "Standard"
|
||||
|
||||
/datum/atom_skin/sec_gloves/plain_black
|
||||
preview_name = "Plain Black"
|
||||
new_icon = 'modular_skyrat/master_files/icons/obj/clothing/gloves.dmi'
|
||||
new_worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/hands.dmi'
|
||||
new_icon_state = "combat"
|
||||
|
||||
/obj/item/clothing/gloves/kaza_ruk/sec
|
||||
icon = 'modular_skyrat/master_files/icons/obj/clothing/gloves.dmi'
|
||||
worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/hands.dmi'
|
||||
@@ -646,12 +697,6 @@
|
||||
inhand_icon_state = "security"
|
||||
worn_icon_state = "security"
|
||||
|
||||
/obj/item/storage/belt/security/redsec/Initialize(mapload)
|
||||
. = ..()
|
||||
var/list/reskin_components = GetComponents(/datum/component/reskinable_item)
|
||||
for(var/datum/component/reskinable_item/reskin_component as anything in reskin_components)
|
||||
qdel(reskin_component)
|
||||
|
||||
/obj/item/storage/belt/holster
|
||||
desc = "A rather plain but still cool looking holster that can hold a handgun, and some ammo."
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
/obj/item/clothing/head/security_kepi = 6,
|
||||
/obj/item/clothing/head/beret/sec/peacekeeper/armadyne = 6,
|
||||
/obj/item/clothing/head/soft/sec = 6,
|
||||
/obj/item/clothing/head/soft/sec/recolorable = 6,
|
||||
/obj/item/clothing/head/security_beanie = 6,
|
||||
/obj/item/clothing/head/costume/ushanka/sec = 10,
|
||||
/obj/item/clothing/head/playbunnyears/security = 6,
|
||||
@@ -59,6 +60,7 @@
|
||||
/obj/item/clothing/neck/security_cape = 6,
|
||||
/obj/item/clothing/neck/security_cape/armplate = 6,
|
||||
/obj/item/clothing/under/rank/security/officer = 6,
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable = 6,
|
||||
/obj/item/clothing/under/rank/security/peacekeeper/skirt = 6,
|
||||
/obj/item/clothing/under/rank/security/peacekeeper/miniskirt = 6,
|
||||
/obj/item/clothing/under/rank/security/officer/blueshirt = 6,
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
@@ -9022,6 +9022,7 @@
|
||||
#include "modular_skyrat\modules\xenos_skyrat_redo\code\xeno_types\warrior.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\bloodsucker_defines.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\guardian_defines.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\loadout.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\maps.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\moonstation_defines.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\projectiles.dm"
|
||||
@@ -9759,6 +9760,7 @@
|
||||
#include "modular_zubbers\code\modules\general_sprite_overrides\turfs\walls.dm"
|
||||
#include "modular_zubbers\code\modules\ghost_wavedefence\wave_def.dm"
|
||||
#include "modular_zubbers\code\modules\gladiator\code\game\objects\items\gladiator_items.dm"
|
||||
#include "modular_zubbers\code\modules\greyscale\component_style.dm"
|
||||
#include "modular_zubbers\code\modules\hydroponics\gene_modder.dm"
|
||||
#include "modular_zubbers\code\modules\hydroponics\plant_genes.dm"
|
||||
#include "modular_zubbers\code\modules\hydroponics\grown\rocks.dm"
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Window } from '../layouts';
|
||||
|
||||
type ColorEntry = {
|
||||
index: number;
|
||||
label?: string; //BUBBER ADDITION - dynamic uniforms
|
||||
value: string;
|
||||
};
|
||||
|
||||
@@ -37,6 +38,11 @@ type SpriteEntry = {
|
||||
type GreyscaleMenuData = {
|
||||
greyscale_config: string;
|
||||
colors: ColorEntry[];
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
full_color_string?: string;
|
||||
hide_full_color_string?: boolean;
|
||||
component_style?: ComponentStyleData;
|
||||
//BUBBER ADDITION END - dynamic uniforms
|
||||
sprites: SpriteData;
|
||||
generate_full_preview: boolean;
|
||||
unlocked: boolean;
|
||||
@@ -46,6 +52,25 @@ type GreyscaleMenuData = {
|
||||
refreshing: boolean;
|
||||
};
|
||||
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
type ComponentStyleOption = {
|
||||
name: string;
|
||||
value: string;
|
||||
selected: boolean;
|
||||
};
|
||||
|
||||
type ComponentStyleCore = {
|
||||
name: string;
|
||||
key: string;
|
||||
options: ComponentStyleOption[];
|
||||
};
|
||||
|
||||
type ComponentStyleData = {
|
||||
core_components: ComponentStyleCore[];
|
||||
accessories: ComponentStyleOption[];
|
||||
};
|
||||
//BUBBER ADDITION END - dynamic uniforms
|
||||
|
||||
enum Direction {
|
||||
North = 'north',
|
||||
NorthEast = 'northeast',
|
||||
@@ -86,6 +111,68 @@ const ConfigDisplay = (props) => {
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
const ComponentStyleDisplay = (props) => {
|
||||
const { act, data } = useBackend<GreyscaleMenuData>();
|
||||
const style = data.component_style;
|
||||
|
||||
if (!style) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section title="Style">
|
||||
<LabeledList>
|
||||
{style.core_components.map((component) => (
|
||||
<LabeledList.Item key={component.key} label={component.name}>
|
||||
<Flex wrap>
|
||||
{component.options.map((item) => (
|
||||
<Flex.Item
|
||||
key={`${component.key}-${item.value}`}
|
||||
mr={0.5}
|
||||
mb={0.5}
|
||||
>
|
||||
<Button.Checkbox
|
||||
checked={item.selected}
|
||||
onClick={() =>
|
||||
act('component_style_set_core', {
|
||||
component: component.key,
|
||||
value: item.value,
|
||||
})
|
||||
}
|
||||
>
|
||||
{item.name}
|
||||
</Button.Checkbox>
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
{!!style.accessories.length && (
|
||||
<LabeledList.Item label="Accessories">
|
||||
<Flex wrap>
|
||||
{style.accessories.map((item) => (
|
||||
<Flex.Item key={item.value} mr={0.5} mb={0.5}>
|
||||
<Button.Checkbox
|
||||
checked={item.selected}
|
||||
onClick={() =>
|
||||
act('component_style_toggle_accessory', {
|
||||
accessory: item.value,
|
||||
})
|
||||
}
|
||||
>
|
||||
{item.name}
|
||||
</Button.Checkbox>
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
//BUBBER ADDITION END - dynamic uniforms
|
||||
|
||||
const ColorDisplay = (props) => {
|
||||
const { act, data } = useBackend<GreyscaleMenuData>();
|
||||
@@ -93,23 +180,37 @@ const ColorDisplay = (props) => {
|
||||
return (
|
||||
<Section title="Colors">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Full Color String">
|
||||
<Button
|
||||
icon="dice"
|
||||
onClick={() => act('random_all_colors')}
|
||||
tooltip="Randomizes all color groups."
|
||||
/>
|
||||
<Input
|
||||
value={colors.map((item) => item.value).join('')}
|
||||
onBlur={(value) =>
|
||||
act('recolor_from_string', { color_string: value })
|
||||
}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
{/* BUBBER EDIT START - dynamic uniforms */}
|
||||
{!data.hide_full_color_string && (
|
||||
<LabeledList.Item label="Full Color String">
|
||||
<Button
|
||||
icon="dice"
|
||||
onClick={() => act('random_all_colors')}
|
||||
tooltip="Randomizes all color groups."
|
||||
/>
|
||||
{!!data.component_style && (
|
||||
<Button
|
||||
icon="undo"
|
||||
onClick={() => act('component_style_reset_all')}
|
||||
tooltip="Resets colors and style options to their defaults."
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
value={
|
||||
data.full_color_string ||
|
||||
colors.map((item) => item.value).join('')
|
||||
}
|
||||
onBlur={(value) =>
|
||||
act('recolor_from_string', { color_string: value })
|
||||
}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{/* BUBBER EDIT END - dynamic uniforms */}
|
||||
{colors.map((item) => (
|
||||
<LabeledList.Item
|
||||
key={`colorgroup${item.index}${item.value}`}
|
||||
label={`Color Group ${item.index}`}
|
||||
label={item.label || `Color Group ${item.index}`} //BUBBER EDIT
|
||||
color={item.value}
|
||||
>
|
||||
<ColorBox color={item.value} />{' '}
|
||||
@@ -123,6 +224,19 @@ const ColorDisplay = (props) => {
|
||||
onClick={() => act('random_color', { color_index: item.index })}
|
||||
tooltip="Randomizes the color for this color group."
|
||||
/>
|
||||
{/* BUBBER EDIT START - dynamic uniforms */}
|
||||
{!!data.component_style && (
|
||||
<Button
|
||||
icon="undo"
|
||||
onClick={() =>
|
||||
act('component_style_reset_color', {
|
||||
color_index: item.index,
|
||||
})
|
||||
}
|
||||
tooltip="Resets this color group to its default."
|
||||
/>
|
||||
)}
|
||||
{/* BUBBER EDIT END - dynamic uniforms */}
|
||||
<Input
|
||||
value={item.value}
|
||||
width={7}
|
||||
@@ -284,6 +398,9 @@ export const GreyscaleModifyMenu = (props) => {
|
||||
<Window title="Color Configuration" width={325} height={800}>
|
||||
<Window.Content scrollable>
|
||||
<ConfigDisplay />
|
||||
{/* BUBBER ADDITION START - dynamic uniforms */}
|
||||
<ComponentStyleDisplay />
|
||||
{/* BUBBER ADDITION END - dynamic uniforms */}
|
||||
<ColorDisplay />
|
||||
<IconStatesDisplay />
|
||||
<Flex direction="column">
|
||||
|
||||
@@ -21,6 +21,20 @@ export function ItemIcon(props: Props) {
|
||||
const { item, scale = 3 } = props;
|
||||
const icon_to_use = item.icon;
|
||||
const icon_state_to_use = item.icon_state;
|
||||
//BUBBER EDIT START - dyamic uniforms
|
||||
if (item.image) {
|
||||
return (
|
||||
<img
|
||||
src={`data:image/png;base64,${item.image}`}
|
||||
style={{
|
||||
width: `${32 * scale}px`,
|
||||
height: `${32 * scale}px`,
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
//BUBBER EDIT END - dynamic uniforms
|
||||
|
||||
if (!icon_to_use || !icon_state_to_use) {
|
||||
return (
|
||||
@@ -67,6 +81,7 @@ export function ItemDisplay(props: DisplayProps) {
|
||||
style={{ textTransform: 'capitalize', zIndex: '1' }}
|
||||
tooltip={item.name}
|
||||
tooltipPosition={'bottom'}
|
||||
base64={item.image} //BUBBER EDIT ADDITION: Dynamic uniform icons
|
||||
dmIcon={item.icon}
|
||||
dmIconState={item.icon_state}
|
||||
onClick={() =>
|
||||
|
||||
@@ -36,6 +36,7 @@ export type LoadoutItem = {
|
||||
path: typePath;
|
||||
icon: DmIconFile | null;
|
||||
icon_state: DmIconState | null;
|
||||
image?: string;
|
||||
buttons: LoadoutButton[];
|
||||
reskins: ReskinOption[] | null;
|
||||
information: LoadoutTooltip[];
|
||||
|
||||