From 9856c6444310d2a5aa95f295ae4b4e5fd1d46232 Mon Sep 17 00:00:00 2001 From: Cameron Lennox Date: Tue, 19 Aug 2025 20:42:42 -0400 Subject: [PATCH] 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 --- code/_helpers/icons.dm | 86 +++++++++++++++---- code/_onclick/hud/alert.dm | 2 +- code/game/atom/_atom.dm | 13 --- .../suit_storage/suit_cycler_datums.dm | 2 +- code/game/mecha/mecha_appearance.dm | 2 +- code/game/objects/items.dm | 6 +- .../objects/items/weapons/storage/wallets.dm | 2 +- code/game/objects/structures/cliff.dm | 2 +- code/game/turfs/simulated/wall_icon.dm | 4 +- .../modules/clothing/accessories/accessory.dm | 4 +- code/modules/clothing/accessories/lockets.dm | 2 +- code/modules/clothing/clothing.dm | 18 ++-- .../food/drinkingglass/drinkingglass.dm | 26 +++--- code/modules/hydroponics/grown.dm | 2 +- .../mob/living/carbon/human/update_icons.dm | 2 +- code/modules/mob/living/silicon/pai/pai_vr.dm | 4 +- code/modules/unit_tests/clothing_tests.dm | 4 +- code/modules/unit_tests/cosmetic_tests.dm | 6 +- code/modules/unit_tests/poster_tests.dm | 2 +- code/modules/unit_tests/robot_tests.dm | 4 +- 20 files changed, 119 insertions(+), 74 deletions(-) diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index 8579e335b9..f7820de2dd 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -152,9 +152,8 @@ var/render_icon = curicon if (render_icon) - var/curstates = icon_states(curicon) - if(!(curstate in curstates)) - if ("" in curstates) + if(!icon_exists(curicon, curstate)) + if(icon_exists(curicon, "")) curstate = "" else render_icon = FALSE @@ -845,21 +844,12 @@ GLOBAL_LIST_EMPTY(cached_examine_icons) /// If you want a stack trace to be output when the given state/file doesn't exist, use /// `/proc/icon_exists_or_scream()`. /proc/icon_exists(file, state) - var/static/list/icon_states_cache = list() if(isnull(file) || isnull(state)) return FALSE //This is common enough that it shouldn't panic, imo. - if(isnull(icon_states_cache[file])) - icon_states_cache[file] = list() - var/file_string = "[file]" - if(isfile(file) && length(file_string)) // ensure that it's actually a file, and not a runtime icon - for(var/istate in json_decode(rustg_dmi_icon_states(file_string))) - icon_states_cache[file][istate] = TRUE - else // Otherwise, we have to use the slower BYOND proc - for(var/istate in icon_states(file)) - icon_states_cache[file][istate] = TRUE - - return !isnull(icon_states_cache[file][state]) + if(isnull(GLOB.icon_states_cache_lookup[file])) + compile_icon_states_cache(file) + return !isnull(GLOB.icon_states_cache_lookup[file][state]) /// Cached, rustg-based alternative to icon_states() /proc/icon_states_fast(file) @@ -882,3 +872,69 @@ GLOBAL_LIST_EMPTY(cached_examine_icons) for(var/istate in icon_states(file)) GLOB.icon_states_cache[file] += istate GLOB.icon_states_cache_lookup[file][istate] = TRUE + +/// Functions the same as `/proc/icon_exists()`, but with the addition of a stack trace if the +/// specified file or state doesn't exist. +/// +/// Stack traces will only be output once for each file. +/proc/icon_exists_or_scream(file, state) + if(icon_exists(file, state)) + return TRUE + + var/static/list/screams = list() + if(!isnull(screams[file])) + screams[file] = TRUE + stack_trace("State [state] in file [file] does not exist.") + + return FALSE + +/** + * Returns the size of the sprite in tiles. + * Takes the icon size and divides it by the world icon size (default 32). + * This gives the size of the sprite in tiles. + * + * @return size of the sprite in tiles + */ +/proc/get_size_in_tiles(obj/target) + var/icon/size_check = icon(target.icon, target.icon_state) + var/size = size_check.Width() / ICON_SIZE_X + + return size + +/// Returns a list containing the width and height of an icon file +/proc/get_icon_dimensions(icon_path) + if(istype(icon_path, /datum/universal_icon)) + var/datum/universal_icon/u_icon = icon_path + icon_path = u_icon.icon_file + // Icons can be a real file(), a rsc backed file(), a dynamic rsc (dyn.rsc) reference (known as a cache reference in byond docs), or an /icon which is pointing to one of those. + // Runtime generated dynamic icons are an unbounded concept cache identity wise, the same icon can exist millions of ways and holding them in a list as a key can lead to unbounded memory usage if called often by consumers. + // Check distinctly that this is something that has this unspecified concept, and thus that we should not cache. + if (!istext(icon_path) && (!isfile(icon_path) || !length("[icon_path]"))) + var/icon/my_icon = icon(icon_path) + return list("width" = my_icon.Width(), "height" = my_icon.Height()) + if (isnull(GLOB.icon_dimensions[icon_path])) + // Used cached icon metadata + var/list/metadata = icon_metadata(icon_path) + var/list/result = null + if(islist(metadata) && isnum(metadata["width"]) && isnum(metadata["height"])) + result = list("width" = metadata["width"], "height" = metadata["height"]) + // Otherwise, we have to use the slower BYOND proc + else + var/icon/my_icon = icon(icon_path) + result = list("width" = my_icon.Width(), "height" = my_icon.Height()) + GLOB.icon_dimensions[icon_path] = result + + return GLOB.icon_dimensions[icon_path] + +/// Returns a list containing the width and height of an icon file, without using rustg for pure function calls +/proc/get_icon_dimensions_pure(icon_path) + // Icons can be a real file(), a rsc backed file(), a dynamic rsc (dyn.rsc) reference (known as a cache reference in byond docs), or an /icon which is pointing to one of those. + // Runtime generated dynamic icons are an unbounded concept cache identity wise, the same icon can exist millions of ways and holding them in a list as a key can lead to unbounded memory usage if called often by consumers. + // Check distinctly that this is something that has this unspecified concept, and thus that we should not cache. + if (!isfile(icon_path) || !length("[icon_path]")) + var/icon/my_icon = icon(icon_path) + return list("width" = my_icon.Width(), "height" = my_icon.Height()) + if (isnull(GLOB.icon_dimensions[icon_path])) + var/icon/my_icon = icon(icon_path) + GLOB.icon_dimensions[icon_path] = list("width" = my_icon.Width(), "height" = my_icon.Height()) + return GLOB.icon_dimensions[icon_path] diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 9aa0bcf651..33d6441bb1 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -454,7 +454,7 @@ so as to remain in compliance with the most up-to-date laws." for(var/i in 1 to length(alerts)) var/obj/screen/alert/alert = alerts[alerts[i]] - if(alert.icon_state in cached_icon_states(ui_style)) + if(icon_exists(ui_style, alert.icon_state)) alert.icon = ui_style else if(!alert.no_underlay) diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index acdc9fca3b..53590180a9 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -563,19 +563,6 @@ GLOBAL_LIST_EMPTY(icon_dimensions) "y" = icon_height > world.icon_size /*&& pixel_y != 0*/ ? (icon_height - world.icon_size) * 0.5 : 0, // we don't have pixel_y in use ) -/// Returns a list containing the width and height of an icon file -/proc/get_icon_dimensions(icon_path) - // Icons can be a real file(), a rsc backed file(), a dynamic rsc (dyn.rsc) reference (known as a cache reference in byond docs), or an /icon which is pointing to one of those. - // Runtime generated dynamic icons are an unbounded concept cache identity wise, the same icon can exist millions of ways and holding them in a list as a key can lead to unbounded memory usage if called often by consumers. - // Check distinctly that this is something that has this unspecified concept, and thus that we should not cache. - if (!isfile(icon_path) || !length("[icon_path]")) - var/icon/my_icon = icon(icon_path) - return list("width" = my_icon.Width(), "height" = my_icon.Height()) - if (isnull(GLOB.icon_dimensions[icon_path])) - var/icon/my_icon = icon(icon_path) - GLOB.icon_dimensions[icon_path] = list("width" = my_icon.Width(), "height" = my_icon.Height()) - return GLOB.icon_dimensions[icon_path] - /// Returns the src and all recursive contents as a list. /atom/proc/get_all_contents(ignore_flag_1) . = list(src) diff --git a/code/game/machinery/suit_storage/suit_cycler_datums.dm b/code/game/machinery/suit_storage/suit_cycler_datums.dm index 15a90436ad..71411f9111 100644 --- a/code/game/machinery/suit_storage/suit_cycler_datums.dm +++ b/code/game/machinery/suit_storage/suit_cycler_datums.dm @@ -314,7 +314,7 @@ GLOBAL_LIST_EMPTY(suit_cycler_emagged) /datum/suit_cycler_choice/species/proc/can_refit_to(...) for(var/obj/item/clothing/C in args) if(LAZYACCESS(C.sprite_sheets_obj, name)) - if(!(C.icon_state in cached_icon_states(C.sprite_sheets_obj[name]))) + if(!icon_exists(C.sprite_sheets_obj[name], C.icon_state)) return FALSE // Species was in sprite_sheets_obj, but had no sprite for this object in particular return TRUE diff --git a/code/game/mecha/mecha_appearance.dm b/code/game/mecha/mecha_appearance.dm index 4e779409c0..31757be73c 100644 --- a/code/game/mecha/mecha_appearance.dm +++ b/code/game/mecha/mecha_appearance.dm @@ -40,7 +40,7 @@ var/icon/Cutter - if("[initial_icon]_cutter" in cached_icon_states(icon)) + if(icon_exists(icon, "[initial_icon]_cutter")) Cutter = new(src.icon, "[initial_icon]_cutter") if(Cutter) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 89c2e8aa8e..43f545efe3 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -946,13 +946,17 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. //2: species-specific sprite sheets (skipped for inhands) if(LAZYLEN(sprite_sheets) && !inhands) var/sheet = sprite_sheets[body_type] - if(sheet) + if(sheet && icon_exists(sheet, icon_state)) //Checks to make sure our custom sheet actually HAS the icon_state return sheet //3: slot-specific sprite sheets if(LAZYLEN(item_icons)) var/sheet = item_icons[slot_name] if(sheet) + /* //Alerts that we are equipping an item that has no item_state for the slot-specific icon sheet. Commented out because it's not really too important. + if(!icon_exists(sheet, icon_state)) + log_debug("Item [src] is equippable on the [slot_name] but has no sprite for it!") + */ return sheet //4: item's default icon diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index 48b2efe8d6..58d1ffcad6 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -71,7 +71,7 @@ cut_overlays() if(front_id) var/tiny_state = "id-generic" - if("id-[front_id.icon_state]" in cached_icon_states(icon)) + if(icon_exists(icon, "id-[front_id.icon_state]")) tiny_state = "id-"+front_id.icon_state var/image/tiny_image = new/image(icon, icon_state = tiny_state) tiny_image.appearance_flags = RESET_COLOR diff --git a/code/game/objects/structures/cliff.dm b/code/game/objects/structures/cliff.dm index a39aafc61b..82709a375e 100644 --- a/code/game/objects/structures/cliff.dm +++ b/code/game/objects/structures/cliff.dm @@ -129,7 +129,7 @@ two tiles on initialization, and which way a cliff is facing may change during m var/subtraction_icon_state = "[icon_state]-subtract" var/cache_string = "[icon_state]_[T.icon]_[T.icon_state]" - if(T && (subtraction_icon_state in cached_icon_states(icon))) + if(T && icon_exists(icon, subtraction_icon_state)) cut_overlays() // If we've made the same icon before, just recycle it. if(cache_string in GLOB.cliff_icon_cache) diff --git a/code/game/turfs/simulated/wall_icon.dm b/code/game/turfs/simulated/wall_icon.dm index 2f805fbf72..7b75ad3333 100644 --- a/code/game/turfs/simulated/wall_icon.dm +++ b/code/game/turfs/simulated/wall_icon.dm @@ -67,13 +67,13 @@ I.color = reinf_material.icon_colour add_overlay(I) else - if("[reinf_material.icon_reinf]0" in cached_icon_states(wall_masks)) + if(icon_exists(wall_masks, "[reinf_material.icon_reinf]0")) // Directional icon for(var/i = 1 to 4) I = image(wall_masks, "[reinf_material.icon_reinf][wall_connections[i]]", dir = 1<<(i-1)) I.color = reinf_material.icon_colour add_overlay(I) - else if("[reinf_material.icon_reinf]" in cached_icon_states(wall_masks)) + else if(icon_exists(wall_masks, "[reinf_material.icon_reinf]")) I = image(wall_masks, reinf_material.icon_reinf) I.color = reinf_material.icon_colour add_overlay(I) diff --git a/code/modules/clothing/accessories/accessory.dm b/code/modules/clothing/accessories/accessory.dm index c13329aaf1..e69f924ca7 100644 --- a/code/modules/clothing/accessories/accessory.dm +++ b/code/modules/clothing/accessories/accessory.dm @@ -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! diff --git a/code/modules/clothing/accessories/lockets.dm b/code/modules/clothing/accessories/lockets.dm index cff6e310f7..3465f4f030 100644 --- a/code/modules/clothing/accessories/lockets.dm +++ b/code/modules/clothing/accessories/lockets.dm @@ -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 diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 1a11bbf98e..d98de029ad 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -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 diff --git a/code/modules/food/drinkingglass/drinkingglass.dm b/code/modules/food/drinkingglass/drinkingglass.dm index 8e2d003bfc..dde1dfd8ea 100644 --- a/code/modules/food/drinkingglass/drinkingglass.dm +++ b/code/modules/food/drinkingglass/drinkingglass.dm @@ -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) diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 47aa75df78..6c80474ff7 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -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) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 3059d826b9..d993aa1e9f 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -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) diff --git a/code/modules/mob/living/silicon/pai/pai_vr.dm b/code/modules/mob/living/silicon/pai/pai_vr.dm index 7b8148928f..dab51a3595 100644 --- a/code/modules/mob/living/silicon/pai/pai_vr.dm +++ b/code/modules/mob/living/silicon/pai/pai_vr.dm @@ -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" diff --git a/code/modules/unit_tests/clothing_tests.dm b/code/modules/unit_tests/clothing_tests.dm index 258caecb85..d4141027c3 100644 --- a/code/modules/unit_tests/clothing_tests.dm +++ b/code/modules/unit_tests/clothing_tests.dm @@ -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 diff --git a/code/modules/unit_tests/cosmetic_tests.dm b/code/modules/unit_tests/cosmetic_tests.dm index a6f940a92d..f59884cf95 100644 --- a/code/modules/unit_tests/cosmetic_tests.dm +++ b/code/modules/unit_tests/cosmetic_tests.dm @@ -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 diff --git a/code/modules/unit_tests/poster_tests.dm b/code/modules/unit_tests/poster_tests.dm index 64e1ca88d6..f023557fce 100644 --- a/code/modules/unit_tests/poster_tests.dm +++ b/code/modules/unit_tests/poster_tests.dm @@ -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.") diff --git a/code/modules/unit_tests/robot_tests.dm b/code/modules/unit_tests/robot_tests.dm index e8d25ec071..0ba47c32eb 100644 --- a/code/modules/unit_tests/robot_tests.dm +++ b/code/modules/unit_tests/robot_tests.dm @@ -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]\".")