Clothing fallback (#18258)

* Clothing fallback

Makes clothing have a fallback in the event of a custom species file not having the appropriate icon state.

* some logging

* testing

* swap all unit tests to use icon_exists

* Update poster_tests.dm

* Update clothing_tests.dm

* Update cosmetic_tests.dm

* Update robot_tests.dm

* whoop

* upgrades people, upgrades

* port these

* Update _atom.dm

* adjust all these

* Update clothing.dm

* TRUEFALSE
This commit is contained in:
Cameron Lennox
2025-08-19 20:42:42 -04:00
committed by GitHub
parent e5f9c80628
commit 9856c64443
20 changed files with 119 additions and 74 deletions
@@ -30,7 +30,7 @@
if(!inv_overlay)
var/tmp_icon_state = "[overlay_state? "[overlay_state]" : "[icon_state]"]"
if(icon_override)
if("[tmp_icon_state]_tie" in cached_icon_states(icon_override))
if(icon_exists(icon_override, "[tmp_icon_state]_tie"))
tmp_icon_state = "[tmp_icon_state]_tie"
inv_overlay = image(icon = icon_override, icon_state = tmp_icon_state, dir = SOUTH)
else
@@ -61,7 +61,7 @@
tmp_icon_state = on_rolled["rolled"]
if(icon_override)
if("[tmp_icon_state]_mob" in cached_icon_states(icon_override))
if(icon_exists(icon_override, "[tmp_icon_state]_mob"))
tmp_icon_state = "[tmp_icon_state]_mob"
mob_overlay = image("icon" = icon_override, "icon_state" = "[tmp_icon_state]")
else if(H && LAZYACCESS(sprite_sheets, H.species.get_bodytype(H))) //Teshari can finally into webbing, too!
+1 -1
View File
@@ -15,7 +15,7 @@
if(!base_icon)
base_icon = icon_state
if(!("[base_icon]_open" in cached_icon_states(icon)))
if(!icon_exists(icon, "[base_icon]_open"))
to_chat(user, "\The [src] doesn't seem to open.")
return
+9 -9
View File
@@ -1037,7 +1037,7 @@
/obj/item/clothing/suit/proc/taurize(var/mob/living/carbon/human/taur, has_taur_tail = FALSE)
if(has_taur_tail)
var/datum/sprite_accessory/tail/taur/taurtail = taur.tail_style
if(taurtail.suit_sprites && (get_worn_icon_state(slot_wear_suit_str) in cached_icon_states(taurtail.suit_sprites)))
if(taurtail.suit_sprites && (icon_exists(taurtail.suit_sprites, get_worn_icon_state(slot_wear_suit_str))))
icon_override = taurtail.suit_sprites
taurized = TRUE
// means that if a taur puts on an already taurized suit without a taur sprite
@@ -1151,7 +1151,7 @@
//autodetect rollability
if(rolled_down < 0)
if(("[worn_state]_d" in cached_icon_states(icon)) || (worn_state in cached_icon_states(rolled_down_icon)) || ("[worn_state]_d" in cached_icon_states(icon_override)))
if((icon_exists(icon, "[worn_state]_d") || icon_exists(rolled_down_icon, worn_state) || icon_exists(icon_override, "[worn_state]_d")))
rolled_down = 0
if(rolled_down == -1)
@@ -1171,11 +1171,11 @@
under_icon = sprite_sheets[H.species.get_bodytype(H)]
else if(LAZYACCESS(item_icons, slot_w_uniform_str))
under_icon = item_icons[slot_w_uniform_str]
else if (worn_state in cached_icon_states(rolled_down_icon))
else if (icon_exists(rolled_down_icon, worn_state))
under_icon = rolled_down_icon
// The _s is because the icon update procs append it.
if((under_icon == rolled_down_icon && ("[worn_state]" in cached_icon_states(under_icon))) || ("[worn_state]_d" in cached_icon_states(under_icon)))
if((under_icon == rolled_down_icon && (icon_exists(under_icon, worn_state))) || (icon_exists(under_icon, "[worn_state]_d")))
if(rolled_down != 1)
rolled_down = 0
else
@@ -1194,13 +1194,13 @@
under_icon = sprite_sheets[H.species.get_bodytype(H)]
else if(LAZYACCESS(item_icons, slot_w_uniform_str))
under_icon = item_icons[slot_w_uniform_str]
else if (worn_state in cached_icon_states(rolled_down_sleeves_icon))
else if (icon_exists(rolled_down_sleeves_icon, worn_state))
under_icon = rolled_down_sleeves_icon
else
under_icon = new /icon(INV_W_UNIFORM_DEF_ICON)
// The _s is because the icon update procs append it.
if((under_icon == rolled_down_sleeves_icon && ("[worn_state]" in cached_icon_states(under_icon))) || ("[worn_state]_r" in cached_icon_states(under_icon)))
if((under_icon == rolled_down_sleeves_icon && (icon_exists(under_icon, worn_state))) || (icon_exists(under_icon, "[worn_state]_r")))
if(rolled_sleeves != 1)
rolled_sleeves = 0
else
@@ -1284,7 +1284,7 @@
body_parts_covered &= ~(UPPER_TORSO|ARMS)
heat_protection &= ~(UPPER_TORSO|ARMS)
cold_protection &= ~(UPPER_TORSO|ARMS)
if(worn_state in cached_icon_states(rolled_down_icon))
if(icon_exists(rolled_down_icon, worn_state))
icon_override = rolled_down_icon
LAZYSET(item_state_slots, slot_w_uniform_str, worn_state)
else
@@ -1321,7 +1321,7 @@
body_parts_covered &= ~(ARMS)
heat_protection &= ~(ARMS)
cold_protection &= ~(ARMS)
if(worn_state in cached_icon_states(rolled_down_sleeves_icon))
if(icon_exists(rolled_down_sleeves_icon, worn_state))
icon_override = rolled_down_sleeves_icon
LAZYSET(item_state_slots, slot_w_uniform_str, worn_state)
else
@@ -1369,7 +1369,7 @@
// only override icon if a corresponding digitigrade replacement icon_state exists
// otherwise, keep the old non-digi icon_define (or nothing)
if(icon_state && cached_icon_states(update_icon_define_digi):Find(icon_state))
if(icon_state && cached_icon_states(update_icon_define_digi):Find(icon_state)) //Unsure what to do to this seeing as it does :Find()
update_icon_define = update_icon_define_digi
@@ -49,9 +49,9 @@
var/datum/reagent/R = reagents.get_master_reagent()
if(!((R.id == REAGENT_ID_ICE) || (REAGENT_ID_ICE in R.glass_special))) // if it's not a cup of ice, and it's not already supposed to have ice in, see if the bartender's put ice in it
if(reagents.has_reagent(REAGENT_ID_ICE, reagents.total_volume / 10)) // 10% ice by volume
return 1
return TRUE
return 0
return FALSE
/obj/item/reagent_containers/food/drinks/glass2/proc/has_fizz()
if(reagents.reagent_list.len > 0)
@@ -62,8 +62,8 @@
if("fizz" in re.glass_special)
totalfizzy += re.volume
if(totalfizzy >= reagents.total_volume / 5) // 20% fizzy by volume
return 1
return 0
return TRUE
return FALSE
/obj/item/reagent_containers/food/drinks/glass2/Initialize(mapload)
. = ..()
@@ -74,12 +74,10 @@
update_icon()
/obj/item/reagent_containers/food/drinks/glass2/proc/can_add_extra(obj/item/glass_extra/GE)
if(!("[base_icon]_[GE.glass_addition]left" in cached_icon_states(icon))) //VOREStation Edit
return 0
if(!("[base_icon]_[GE.glass_addition]right" in cached_icon_states(icon))) //VOREStation Edit
return 0
if(!icon_exists(icon, "[base_icon]_[GE.glass_addition]left") || !icon_exists(icon, "[base_icon]_[GE.glass_addition]right"))
return FALSE
return 1
return TRUE
/obj/item/reagent_containers/food/drinks/glass2/update_icon()
underlays.Cut()
@@ -106,9 +104,9 @@
over_liquid |= "[base_icon][amnt]_fizz"
for(var/S in R.glass_special)
if("[base_icon]_[S]" in cached_icon_states(icon)) //VOREStation Edit
if(icon_exists(icon, "[base_icon]_[S]"))
under_liquid |= "[base_icon]_[S]"
else if("[base_icon][amnt]_[S]" in cached_icon_states(icon)) //VOREStation Edit
else if(icon_exists(icon, "[base_icon][amnt]_[S]"))
over_liquid |= "[base_icon][amnt]_[S]"
for(var/k in under_liquid)
@@ -150,13 +148,13 @@
/obj/item/reagent_containers/food/drinks/glass2/afterattack(var/obj/target, var/mob/user, var/proximity)
if(user.a_intent == I_HURT) //We only want splashing to be done if they are on harm intent.
if(!is_open_container() || !proximity)
return 1
return TRUE
if(standard_splash_mob(user, target))
return 1
return TRUE
if(reagents && reagents.total_volume) //They are on harm intent, aka wanting to spill it.
to_chat(user, span_notice("You splash the solution onto [target]."))
reagents.splash(target, reagents.total_volume)
return 1
return TRUE
..()
/obj/item/reagent_containers/food/drinks/glass2/standard_feed_mob(var/mob/user, var/mob/target)
+1 -1
View File
@@ -135,7 +135,7 @@
var/image/fruit_base = image('icons/obj/hydroponics_products.dmi',"[seed.get_trait(TRAIT_PRODUCT_ICON)]-product")
fruit_base.color = "[seed.get_trait(TRAIT_PRODUCT_COLOUR)]"
plant_icon.add_overlay(fruit_base)
if("[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf" in cached_icon_states('icons/obj/hydroponics_products.dmi'))
if(icon_exists('icons/obj/hydroponics_products.dmi', "[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf"))
var/image/fruit_leaves = image('icons/obj/hydroponics_products.dmi',"[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf")
fruit_leaves.color = "[seed.get_trait(TRAIT_PLANT_COLOUR)]"
plant_icon.add_overlay(fruit_leaves)
@@ -345,7 +345,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) //see UpdateDamageIcon()
base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3]))
//Handle husk overlay.
if(husk && ("overlay_husk" in cached_icon_states(species.icobase)))
if(husk && icon_exists(species.icobase, "overlay_husk"))
var/icon/mask = new(base_icon)
var/icon/husk_over = new(species.icobase,"overlay_husk")
mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0)
@@ -83,12 +83,12 @@
// Unfortunately not all these states exist, ugh.
else if(vore_fullness && !resting)
if("[chassis]_full[fullness_extension]" in cached_icon_states(icon))
if(icon_exists(icon, "[chassis]_full[fullness_extension]"))
icon_state = "[chassis]_full[fullness_extension]"
else
icon_state = "[chassis]"
else if(vore_fullness && resting)
if("[chassis]_rest_full[fullness_extension]" in cached_icon_states(icon))
if(icon_exists(icon, "[chassis]_rest_full[fullness_extension]"))
icon_state = "[chassis]_rest_full[fullness_extension]"
else
icon_state = "[chassis]_rest"
+2 -2
View File
@@ -54,7 +54,7 @@
TEST_ASSERT(C.name != "", "[C.type]: Clothing - Empty name.")
// Icons
if(!("[C.icon_state]" in cached_icon_states(C.icon)))
if(!icon_exists(C.icon, C.icon_state))
if(C.icon == initial(C.icon) && C.icon_state == initial(C.icon_state))
TEST_NOTICE("[C.type]: Clothing - Icon_state \"[C.icon_state]\" is not present in [C.icon].")
else
@@ -165,7 +165,7 @@
return
// All that matters
if(!("[set_state]" in cached_icon_states(set_icon)))
if(!icon_exists(set_icon, set_state))
TEST_NOTICE("[item_path]: Clothing - Testing \"[species]\" state \"[set_state]\" for slot \"[slot_name]\", but it was not in dmi \"[set_icon]\"")
signal_failed = TRUE
return
+3 -3
View File
@@ -38,12 +38,12 @@
if(istype(accessory, /datum/sprite_accessory/hair))
actual_icon_state += "_s"
TEST_ASSERT(actual_icon_state in cached_icon_states(accessory::icon), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
TEST_ASSERT(icon_exists(accessory::icon, actual_icon_state), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
return
if(istype(accessory, /datum/sprite_accessory/facial_hair))
actual_icon_state += "_s"
TEST_ASSERT(actual_icon_state in cached_icon_states(accessory::icon), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
TEST_ASSERT(icon_exists(accessory::icon, actual_icon_state), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
return
if(istype(accessory, /datum/sprite_accessory/marking))
@@ -52,5 +52,5 @@
TEST_ASSERT(body_part in BP_ALL, "[accessory::name] - [accessory::type]: Cosmetic - Has an illegal bodypart \"[body_part]\". ONLY use parts listed in BP_ALL.")
actual_icon_state = "[accessory::icon_state]-[body_part]"
TEST_ASSERT(actual_icon_state in cached_icon_states(accessory::icon), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
TEST_ASSERT(icon_exists(accessory::icon, actual_icon_state), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
return
+1 -1
View File
@@ -10,4 +10,4 @@
var/icon/I = initial(P.icon)
if(D.icon_override)
I = D.icon_override
TEST_ASSERT(D.icon_state in cached_icon_states(I), "[D.type]: Poster - missing icon_state \"[D.icon_state]\" in \"[I]\", as [D.icon_override ? "override" : "base"] dmi.")
TEST_ASSERT(icon_exists(I, D.icon_state), "[D.type]: Poster - missing icon_state \"[D.icon_state]\" in \"[I]\", as [D.icon_override ? "override" : "base"] dmi.")
+2 -2
View File
@@ -152,7 +152,7 @@
if(robot.has_dead_sprite)
check_state(robot,"-wreck")
if(robot.has_dead_sprite_overlay) // Only one per dmi
TEST_ASSERT("wreck-overlay" in cached_icon_states(robot.sprite_icon), "[robot.type]: Robots - Robot sprite \"[robot.name]\", missing icon_state wreck-overlay, in dmi \"[robot.sprite_icon]\".")
TEST_ASSERT(icon_exists(robot.sprite_icon, "wreck-overlay"), "[robot.type]: Robots - Robot sprite \"[robot.name]\", missing icon_state wreck-overlay, in dmi \"[robot.sprite_icon]\".")
// offset
var/icon/I = new(robot.sprite_icon)
TEST_ASSERT_EQUAL(robot.icon_x, I.Width(), "[robot.type]: Robots - Robot sprite \"[robot.name]\", icon_x \"[robot.icon_x]\" did not match dmi configured width \"[I.Width()]\"")
@@ -167,4 +167,4 @@
/datum/unit_test/all_robot_sprites_must_be_valid/proc/check_state(datum/robot_sprite/robot, append)
var/check_state = "[robot.sprite_icon_state][append]"
TEST_ASSERT(check_state in cached_icon_states(robot.sprite_icon), "[robot.type]: Robots - Robot sprite \"[robot.name]\", enabled but missing icon_state \"[check_state]\", in dmi \"[robot.sprite_icon]\".")
TEST_ASSERT(icon_exists(robot.sprite_icon, check_state), "[robot.type]: Robots - Robot sprite \"[robot.name]\", enabled but missing icon_state \"[check_state]\", in dmi \"[robot.sprite_icon]\".")