Digitigrade code refactor and shoes + explorer suit (Using GAGS and zero sprite bloat) (#88096)
## About The Pull Request Refactors and extends the existing [digitigrade clothing system](https://github.com/tgstation/tgstation/pull/85406) to also support oversuits and shoes (only enabled for sneakers, specific boots, and shaft miner's explorer suit). All digitigrade-specific clothes are generated by GAGS by color sampling the base sprite and applying it to a greyscale template.  More up to date boots:   Credit to MrMelbert for prototype code and Junkgle for new sprites ## Why It's Good For The Game Looks good, doesn't introduce maintainability issues or sprite bloat ## Changelog 🆑 add: digitigrade lizards can wear certain shoes and suits image: added digitigrade shoes & oversuit templates refactor: improved digi clothing generator code /🆑 --------- Co-authored-by: MrMelbert <kmelbert4@gmail.com> Co-authored-by: Roryl-c <5150427+Roryl-c@users.noreply.github.com>
@@ -163,7 +163,6 @@ DEFINE_BITFIELD(no_equip_flags, list(
|
||||
/// The sprite works fine for digitigrade legs as-is.
|
||||
#define CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON (1<<2)
|
||||
/// Auto-generates the leg portion of the sprite with GAGS
|
||||
/// Suggested that you set [/obj/item/var/digitigrade_greyscale_config_worn] when using this flag
|
||||
#define CLOTHING_DIGITIGRADE_MASK (1<<3)
|
||||
|
||||
/// All variation flags which render "correctly" on a digitigrade leg setup
|
||||
|
||||
@@ -276,11 +276,6 @@
|
||||
icon_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
|
||||
json_config = 'code/datums/greyscale/json_configs/jumpsuit_prison_inhand.json'
|
||||
|
||||
/datum/greyscale_config/jumpsuit/worn_digi
|
||||
name = "Jumpsuit Worn (Digitigrate)"
|
||||
icon_file = 'icons/mob/clothing/under/digi_template.dmi'
|
||||
json_config = 'code/datums/greyscale/json_configs/jumpsuit_worn_digilegs.json'
|
||||
|
||||
/datum/greyscale_config/eth_tunic
|
||||
name = "Ethereal Tunic"
|
||||
icon_file = 'icons/obj/clothing/under/ethereal.dmi'
|
||||
@@ -627,6 +622,14 @@
|
||||
name = "Waistcoat (Worn)"
|
||||
icon_file = 'icons/mob/clothing/accessories.dmi'
|
||||
|
||||
// Digi Stuff
|
||||
|
||||
/datum/greyscale_config/digitigrade
|
||||
name = "Digitigrade Clothes"
|
||||
icon_file = 'icons/mob/clothing/digi_template.dmi'
|
||||
json_config = 'code/datums/greyscale/json_configs/digitigrade.json'
|
||||
|
||||
|
||||
//
|
||||
// SUIT + HEAD
|
||||
// (Specifically for toggleable suits with hats, i.e. winter coats)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"jumpsuit_worn": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "jumpsuit",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
}
|
||||
],
|
||||
"oversuit_worn": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "oversuit",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
}
|
||||
],
|
||||
"sneakers_worn": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "shoes_colored",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "shoes_uncolored",
|
||||
"blend_mode": "overlay"
|
||||
}
|
||||
],
|
||||
"boots_worn": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "boots",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "jumpsuit",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -39,12 +39,6 @@
|
||||
///The config type to use for greyscaled belt overlays. Both this and greyscale_colors must be assigned to work.
|
||||
var/greyscale_config_belt
|
||||
|
||||
/// Greyscale config used when generating digitigrade versions of the sprite.
|
||||
var/digitigrade_greyscale_config_worn
|
||||
/// Greyscale colors used when generating digitigrade versions of the sprite.
|
||||
/// Optional - If not set it will default to normal greyscale colors, or approximate them if those are unset as well
|
||||
var/digitigrade_greyscale_colors
|
||||
|
||||
/* !!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!
|
||||
|
||||
IF YOU ADD MORE ICON CRAP TO THIS
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
var/mob/M = loc
|
||||
M.update_worn_shoes()
|
||||
|
||||
/obj/item/clothing/shoes/generate_digitigrade_icons(icon/base_icon, greyscale_colors)
|
||||
return icon(SSgreyscale.GetColoredIconByType(/datum/greyscale_config/digitigrade, greyscale_colors), "boots_worn")
|
||||
|
||||
/**
|
||||
* adjust_laces adjusts whether our shoes (assuming they can_be_tied) and tied, untied, or knotted
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
desc = "High speed, low drag combat boots."
|
||||
icon_state = "jackboots"
|
||||
inhand_icon_state = "jackboots"
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
body_parts_covered = FEET|LEGS
|
||||
armor_type = /datum/armor/shoes_combat
|
||||
strip_delay = 40
|
||||
@@ -45,6 +46,7 @@
|
||||
desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time."
|
||||
icon_state = "jackboots"
|
||||
inhand_icon_state = "jackboots"
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
strip_delay = 30
|
||||
equip_delay_other = 50
|
||||
resistance_flags = NONE
|
||||
@@ -71,6 +73,7 @@
|
||||
desc = "Is it just me or is there a pair of jackboots on the floor?"
|
||||
icon_state = "ftc_boots"
|
||||
inhand_icon_state = null
|
||||
supports_variations_flags = NONE
|
||||
|
||||
/obj/item/clothing/shoes/jackboots/floortile/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -81,6 +84,7 @@
|
||||
desc = "Boots lined with 'synthetic' animal fur."
|
||||
icon_state = "winterboots"
|
||||
inhand_icon_state = null
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
armor_type = /datum/armor/shoes_winterboots
|
||||
cold_protection = FEET|LEGS
|
||||
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
|
||||
@@ -131,6 +135,7 @@
|
||||
icon_state = "workboots"
|
||||
inhand_icon_state = "jackboots"
|
||||
armor_type = /datum/armor/shoes_workboots
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
strip_delay = 20
|
||||
equip_delay_other = 40
|
||||
lace_time = 8 SECONDS
|
||||
@@ -155,6 +160,7 @@
|
||||
icon_state = "rus_shoes"
|
||||
inhand_icon_state = null
|
||||
lace_time = 8 SECONDS
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
|
||||
/obj/item/clothing/shoes/russian/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
@@ -9,9 +9,17 @@
|
||||
greyscale_config_worn = /datum/greyscale_config/sneakers/worn
|
||||
greyscale_config_inhand_left = /datum/greyscale_config/sneakers/inhand_left
|
||||
greyscale_config_inhand_right = /datum/greyscale_config/sneakers/inhand_right
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
flags_1 = IS_PLAYER_COLORABLE_1
|
||||
interaction_flags_mouse_drop = NEED_HANDS
|
||||
|
||||
/obj/item/clothing/shoes/sneakers/get_general_color(icon/base_icon)
|
||||
var/colors = SSgreyscale.ParseColorString(greyscale_colors)
|
||||
return colors ? colors[1] : ..()
|
||||
|
||||
/obj/item/clothing/shoes/sneakers/generate_digitigrade_icons(icon/base_icon, greyscale_colors)
|
||||
return icon(SSgreyscale.GetColoredIconByType(/datum/greyscale_config/digitigrade, greyscale_colors), "sneakers_worn")
|
||||
|
||||
/obj/item/clothing/shoes/sneakers/random/Initialize(mapload)
|
||||
. = ..()
|
||||
greyscale_colors = "#" + random_color() + "#" + random_color()
|
||||
|
||||
@@ -43,3 +43,7 @@
|
||||
if(ismob(loc))
|
||||
var/mob/M = loc
|
||||
M.update_worn_oversuit()
|
||||
|
||||
/obj/item/clothing/suit/generate_digitigrade_icons(icon/base_icon, greyscale_colors)
|
||||
var/icon/legs = icon(SSgreyscale.GetColoredIconByType(/datum/greyscale_config/digitigrade, greyscale_colors), "oversuit_worn")
|
||||
return replace_icon_legs(base_icon, legs)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
interaction_flags_click = NEED_DEXTERITY
|
||||
armor_type = /datum/armor/clothing_under
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
digitigrade_greyscale_config_worn = /datum/greyscale_config/jumpsuit/worn_digi
|
||||
equip_sound = 'sound/items/equip/jumpsuit_equip.ogg'
|
||||
drop_sound = 'sound/items/handling/cloth_drop.ogg'
|
||||
pickup_sound = 'sound/items/handling/cloth_pickup.ogg'
|
||||
@@ -142,6 +141,10 @@
|
||||
adjusted = DIGITIGRADE_STYLE
|
||||
update_appearance()
|
||||
|
||||
/obj/item/clothing/under/generate_digitigrade_icons(icon/base_icon, greyscale_colors)
|
||||
var/icon/legs = icon(SSgreyscale.GetColoredIconByType(/datum/greyscale_config/digitigrade, greyscale_colors), "jumpsuit_worn")
|
||||
return replace_icon_legs(base_icon, legs)
|
||||
|
||||
/obj/item/clothing/under/equipped(mob/living/user, slot)
|
||||
..()
|
||||
if((slot & ITEM_SLOT_ICLOTHING) && freshly_laundered)
|
||||
|
||||
@@ -224,10 +224,12 @@
|
||||
greyscale_config_inhand_left = null
|
||||
greyscale_config_inhand_right = null
|
||||
greyscale_config_worn = null
|
||||
digitigrade_greyscale_colors = "#3f3f3f"
|
||||
can_adjust = FALSE
|
||||
flags_1 = NONE
|
||||
|
||||
/obj/item/clothing/under/color/rainbow/get_general_color(icon/base_icon)
|
||||
return "#3f3f3f"
|
||||
|
||||
/obj/item/clothing/under/color/jumpskirt/rainbow
|
||||
name = "rainbow jumpskirt"
|
||||
desc = "A multi-colored jumpskirt!"
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
desc = "Groovy!"
|
||||
icon_state = "psyche"
|
||||
inhand_icon_state = "p_suit"
|
||||
digitigrade_greyscale_colors = "#3f3f3f"
|
||||
|
||||
/obj/item/clothing/under/misc/psyche/get_general_color(icon/base_icon)
|
||||
return "#3f3f3f"
|
||||
|
||||
/obj/item/clothing/under/misc/vice_officer
|
||||
name = "vice officer's jumpsuit"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
icon = 'icons/obj/clothing/suits/utility.dmi'
|
||||
worn_icon = 'icons/mob/clothing/suits/utility.dmi'
|
||||
inhand_icon_state = null
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
cold_protection = CHEST|GROIN|LEGS|ARMS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
@@ -15,6 +16,9 @@
|
||||
armor_type = /datum/armor/hooded_explorer
|
||||
resistance_flags = FIRE_PROOF
|
||||
|
||||
/obj/item/clothing/suit/hooded/explorer/get_general_color(icon/base_icon)
|
||||
return "#796755"
|
||||
|
||||
/datum/armor/hooded_explorer
|
||||
melee = 30
|
||||
bullet = 10
|
||||
|
||||
@@ -573,20 +573,70 @@ There are several things that need to be remembered:
|
||||
|
||||
return icon(female_clothing_icon)
|
||||
|
||||
// These coordonates point to roughly somewhere in the middle of the left leg
|
||||
// Used in approximating what color the pants of clothing should be
|
||||
/// Modifies a sprite to conform to digitigrade body shapes
|
||||
/proc/wear_digi_version(icon/base_icon, obj/item/item, key, greyscale_colors)
|
||||
ASSERT(istype(item), "wear_digi_version: no item passed")
|
||||
ASSERT(istext(key), "wear_digi_version: no key passed")
|
||||
if(isnull(greyscale_colors) || length(SSgreyscale.ParseColorString(greyscale_colors)) > 1)
|
||||
greyscale_colors = item.get_general_color(base_icon)
|
||||
|
||||
var/index = "[key]-[item.type]-[greyscale_colors]"
|
||||
var/static/list/digitigrade_clothing_cache = list()
|
||||
var/icon/resulting_icon = digitigrade_clothing_cache[index]
|
||||
if(!resulting_icon)
|
||||
resulting_icon = item.generate_digitigrade_icons(base_icon, greyscale_colors)
|
||||
if(!resulting_icon)
|
||||
stack_trace("[item.type] is set to generate a masked digitigrade icon, but generate_digitigrade_icons was not implemented (or error'd).")
|
||||
return base_icon
|
||||
digitigrade_clothing_cache[index] = fcopy_rsc(resulting_icon)
|
||||
|
||||
return icon(resulting_icon)
|
||||
|
||||
/// Modifies a sprite to replace the legs with a new version
|
||||
/proc/replace_icon_legs(icon/base_icon, icon/new_legs)
|
||||
var/static/icon/leg_mask
|
||||
if(!leg_mask)
|
||||
leg_mask = icon('icons/mob/clothing/under/masking_helpers.dmi', "digi_leg_mask")
|
||||
|
||||
// cuts the legs off
|
||||
base_icon.Blend(leg_mask, ICON_SUBTRACT)
|
||||
// staples the new legs on
|
||||
base_icon.Blend(new_legs, ICON_OVERLAY)
|
||||
return base_icon
|
||||
|
||||
/**
|
||||
* Generates a digitigrade version of this item's worn icon
|
||||
*
|
||||
* Arguments:
|
||||
* * base_icon: The icon to generate the digitigrade icon from
|
||||
* * greyscale_colors: The greyscale colors to use for the digitigrade icon
|
||||
*
|
||||
* Returns an icon that is the digitigrade version of the item's worn icon
|
||||
* Returns null if the item has no support for digitigrade variations via this method
|
||||
*/
|
||||
/obj/item/proc/generate_digitigrade_icons(icon/base_icon, greyscale_colors)
|
||||
return null
|
||||
|
||||
/**
|
||||
* Get what color the item is on "average"
|
||||
* Can be used to approximate what color this item is/should be
|
||||
*
|
||||
* Arguments:
|
||||
* * base_icon: The icon to get the color from
|
||||
*/
|
||||
/obj/item/proc/get_general_color(icon/base_icon)
|
||||
if(greyscale_colors && length(SSgreyscale.ParseColorString(greyscale_colors)) == 1)
|
||||
return greyscale_colors
|
||||
return color
|
||||
|
||||
// These coordinates point to the middle of the left leg
|
||||
#define LEG_SAMPLE_X_LOWER 13
|
||||
#define LEG_SAMPLE_X_UPPER 14
|
||||
|
||||
#define LEG_SAMPLE_Y_LOWER 8
|
||||
#define LEG_SAMPLE_Y_UPPER 9
|
||||
|
||||
/// Modifies a sprite to conform to digitigrade body shapes
|
||||
/proc/wear_digi_version(icon/base_icon, key, greyscale_config = /datum/greyscale_config/jumpsuit/worn_digi, greyscale_colors)
|
||||
ASSERT(key, "wear_digi_version: no key passed")
|
||||
ASSERT(ispath(greyscale_config, /datum/greyscale_config), "wear_digi_version: greyscale_config is not a valid path (got: [greyscale_config])")
|
||||
// items with greyscale colors containing multiple colors are invalid
|
||||
if(isnull(greyscale_colors) || length(SSgreyscale.ParseColorString(greyscale_colors)) > 1)
|
||||
/obj/item/clothing/get_general_color(icon/base_icon)
|
||||
if(slot_flags & (ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING))
|
||||
var/pant_color
|
||||
// approximates the color of the pants by sampling a few pixels in the middle of the left leg
|
||||
for(var/x in LEG_SAMPLE_X_LOWER to LEG_SAMPLE_X_UPPER)
|
||||
@@ -594,37 +644,26 @@ There are several things that need to be remembered:
|
||||
var/xy_color = base_icon.GetPixel(x, y)
|
||||
pant_color = pant_color ? BlendRGB(pant_color, xy_color, 0.5) : xy_color
|
||||
|
||||
greyscale_colors = pant_color || "#1d1d1d" // black pants always look good
|
||||
return pant_color || "#1d1d1d" // black pants always look good
|
||||
|
||||
var/index = "[key]-[greyscale_config]-[greyscale_colors]"
|
||||
var/static/list/digitigrade_clothing_icons = list()
|
||||
var/icon/digitigrade_clothing_icon = digitigrade_clothing_icons[index]
|
||||
if(!digitigrade_clothing_icon)
|
||||
var/static/icon/torso_mask
|
||||
if(!torso_mask)
|
||||
torso_mask = icon('icons/mob/clothing/under/masking_helpers.dmi', "digi_torso_mask")
|
||||
var/static/icon/leg_mask
|
||||
if(!leg_mask)
|
||||
leg_mask = icon('icons/mob/clothing/under/masking_helpers.dmi', "digi_leg_mask")
|
||||
|
||||
base_icon.Blend(leg_mask, ICON_SUBTRACT) // cuts the legs off
|
||||
|
||||
var/icon/leg_icon = SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)
|
||||
leg_icon.Blend(torso_mask, ICON_SUBTRACT) // cuts the torso off
|
||||
|
||||
base_icon.Blend(leg_icon, ICON_OVERLAY) // puts the new legs on
|
||||
|
||||
digitigrade_clothing_icon = fcopy_rsc(base_icon)
|
||||
digitigrade_clothing_icons[index] = digitigrade_clothing_icon
|
||||
|
||||
return icon(digitigrade_clothing_icon)
|
||||
return ..()
|
||||
|
||||
#undef LEG_SAMPLE_X_LOWER
|
||||
#undef LEG_SAMPLE_X_UPPER
|
||||
|
||||
#undef LEG_SAMPLE_Y_LOWER
|
||||
#undef LEG_SAMPLE_Y_UPPER
|
||||
|
||||
// Points to the tip of the left foot
|
||||
#define SHOE_SAMPLE_X 11
|
||||
#define SHOE_SAMPLE_Y 2
|
||||
|
||||
/obj/item/clothing/shoes/get_general_color(icon/base_icon)
|
||||
// just grabs the color of the middle of the left foot
|
||||
return base_icon.GetPixel(SHOE_SAMPLE_X, SHOE_SAMPLE_Y) || "#1d1d1d"
|
||||
|
||||
#undef SHOE_SAMPLE_X
|
||||
#undef SHOE_SAMPLE_Y
|
||||
|
||||
/mob/living/carbon/human/proc/get_overlays_copy(list/unwantedLayers)
|
||||
var/list/out = new
|
||||
for(var/i in 1 to TOTAL_LAYERS)
|
||||
@@ -774,9 +813,9 @@ generate/load female uniform sprites matching all previously decided variables
|
||||
if(!isinhands && is_digi && (supports_variations_flags & CLOTHING_DIGITIGRADE_MASK))
|
||||
building_icon = wear_digi_version(
|
||||
base_icon = building_icon || icon(file2use, t_state),
|
||||
item = src,
|
||||
key = "[t_state]-[file2use]-[female_uniform]",
|
||||
greyscale_config = digitigrade_greyscale_config_worn || greyscale_config_worn,
|
||||
greyscale_colors = digitigrade_greyscale_colors || greyscale_colors || color,
|
||||
greyscale_colors = greyscale_colors,
|
||||
)
|
||||
if(building_icon)
|
||||
standing = mutable_appearance(building_icon, layer = -layer2use)
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 706 B |
|
Before Width: | Height: | Size: 586 B |
|
Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 433 B |