diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm
index 5abedfe9ac..97e66ac93d 100644
--- a/code/_helpers/icons.dm
+++ b/code/_helpers/icons.dm
@@ -107,7 +107,7 @@ AngleToHue(hue)
Converts an angle to a hue in the valid range.
RotateHue(hsv, angle)
Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360.
- (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value
+ (Rotating red by 60� produces yellow.) The result is another HSV or HSVA color with the same saturation and value
as the original, but a different hue.
GrayScale(rgb)
Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string.
@@ -899,6 +899,25 @@ proc/ColorTone(rgb, tone)
composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY)
return composite
+GLOBAL_LIST_EMPTY(icon_state_lists)
+/proc/cached_icon_states(var/icon/I)
+ if(!I)
+ return list()
+ var/key = "\ref[I]"
+ var/returnlist = GLOB.icon_state_lists[key]
+ if(!returnlist)
+ returnlist = icon_state_lists(I)
+ GLOB.icon_state_lists[key] = returnlist
+ if((returnlist?.len == 1) && (returnlist[1] == "")) //It's some icon_state that was generated in-round probably, very likely to be reused \ref soon.
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/expire_states_cache, key), 600, TIMER_UNIQUE)
+ return returnlist
+
+/proc/expire_states_cache(var/key)
+ if(GLOB.icon_state_lists[key])
+ GLOB.icon_state_lists -= key
+ return TRUE
+ return FALSE
+
proc/adjust_brightness(var/color, var/value)
if (!color) return "#FFFFFF"
if (!value) return color
diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm
index 73ae811193..d4400da8df 100644
--- a/code/_helpers/mobs.dm
+++ b/code/_helpers/mobs.dm
@@ -94,7 +94,7 @@ proc/age2agedescription(age)
else return "unknown"
/proc/RoundHealth(health)
- var/list/icon_states = icon_states(ingame_hud_med)
+ var/list/icon_states = cached_icon_states(ingame_hud_med)
for(var/icon_state in icon_states)
if(health >= text2num(icon_state))
return icon_state
diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm
index 95db86460e..a668075068 100644
--- a/code/game/objects/items/weapons/storage/wallets.dm
+++ b/code/game/objects/items/weapons/storage/wallets.dm
@@ -63,7 +63,7 @@
overlays.Cut()
if(front_id)
var/tiny_state = "id-generic"
- if("id-"+front_id.icon_state in icon_states(icon))
+ if("id-"+front_id.icon_state in cached_icon_states(icon))
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/barsign.dm b/code/game/objects/structures/barsign.dm
index cd1c448b2f..e54c75eebb 100644
--- a/code/game/objects/structures/barsign.dm
+++ b/code/game/objects/structures/barsign.dm
@@ -6,7 +6,7 @@
var/cult = 0
/obj/structure/sign/double/barsign/proc/get_valid_states(initial=1)
- . = icon_states(icon)
+ . = cached_icon_states(icon)
. -= "on"
. -= "narsiebistro"
. -= "empty"
diff --git a/code/game/objects/structures/cliff.dm b/code/game/objects/structures/cliff.dm
index c4caf4b7ab..66fb945988 100644
--- a/code/game/objects/structures/cliff.dm
+++ b/code/game/objects/structures/cliff.dm
@@ -114,7 +114,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 icon_states(icon))
+ if(T && subtraction_icon_state in cached_icon_states(icon))
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 5bf41c4a5f..dd49744a8f 100644
--- a/code/game/turfs/simulated/wall_icon.dm
+++ b/code/game/turfs/simulated/wall_icon.dm
@@ -67,7 +67,7 @@
I.color = reinf_material.icon_colour
add_overlay(I)
else
- if("[reinf_material.icon_reinf]0" in icon_states('icons/turf/wall_masks.dmi'))
+ if("[reinf_material.icon_reinf]0" in cached_icon_states('icons/turf/wall_masks.dmi'))
// Directional icon
for(var/i = 1 to 4)
I = image('icons/turf/wall_masks.dmi', "[reinf_material.icon_reinf][wall_connections[i]]", dir = 1<<(i-1))
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index 5580c7fd98..ed6439d3cd 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -204,12 +204,12 @@ You can set verify to TRUE if you want send() to sleep until the client has the
directions = list(SOUTH)
var/sprites = list()
- for (var/icon_state_name in icon_states(I))
+ for (var/icon_state_name in cached_icon_states(I))
for (var/direction in directions)
var/suffix = (directions.len > 1) ? "-[dir2text(direction)]" : ""
var/sprite_name = "[prefix][icon_state_name][suffix]"
var/icon/sprite = icon(I, icon_state=icon_state_name, dir=direction, frame=1, moving=FALSE)
- if (!sprite || !length(icon_states(sprite))) // that direction or state doesn't exist
+ if (!sprite || !length(cached_icon_states(sprite))) // that direction or state doesn't exist
continue
sprites[sprite_name] = sprite
return sprites
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index adb52397ff..3208179b91 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -841,7 +841,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
dat += "
[current_species.blurb] | "
//vorestation edit end
dat += ""
- if("preview" in icon_states(current_species.icobase))
+ if("preview" in cached_icon_states(current_species.icobase))
usr << browse_rsc(icon(current_species.icobase,"preview"), "species_preview_[current_species.name].png")
dat += "
"
dat += "Language: [current_species.species_language] "
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 283406d6ba..eff875ee8d 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -805,7 +805,7 @@
//autodetect rollability
if(rolled_down < 0)
- if(("[worn_state]_d_s" in icon_states(icon)) || ("[worn_state]_s" in icon_states(rolled_down_icon)) || ("[worn_state]_d_s" in icon_states(icon_override)))
+ if(("[worn_state]_d_s" in cached_icon_states(icon)) || ("[worn_state]_s" in cached_icon_states(rolled_down_icon)) || ("[worn_state]_d_s" in cached_icon_states(icon_override)))
rolled_down = 0
if(rolled_down == -1)
@@ -842,11 +842,11 @@
under_icon = sprite_sheets[H.species.get_bodytype(H)]
else if(item_icons && item_icons[slot_w_uniform_str])
under_icon = item_icons[slot_w_uniform_str]
- else if ("[worn_state]_s" in icon_states(rolled_down_icon))
+ else if ("[worn_state]_s" in cached_icon_states(rolled_down_icon))
under_icon = rolled_down_icon
// The _s is because the icon update procs append it.
- if((under_icon == rolled_down_icon && "[worn_state]_s" in icon_states(under_icon)) || ("[worn_state]_d_s" in icon_states(under_icon)))
+ if((under_icon == rolled_down_icon && "[worn_state]_s" in cached_icon_states(under_icon)) || ("[worn_state]_d_s" in cached_icon_states(under_icon)))
if(rolled_down != 1)
rolled_down = 0
else
@@ -865,13 +865,13 @@
under_icon = sprite_sheets[H.species.get_bodytype(H)]
else if(item_icons && item_icons[slot_w_uniform_str])
under_icon = item_icons[slot_w_uniform_str]
- else if ("[worn_state]_s" in icon_states(rolled_down_sleeves_icon))
+ else if ("[worn_state]_s" in cached_icon_states(rolled_down_sleeves_icon))
under_icon = rolled_down_sleeves_icon
else if(index)
under_icon = new /icon("[INV_W_UNIFORM_DEF_ICON]_[index].dmi")
// The _s is because the icon update procs append it.
- if((under_icon == rolled_down_sleeves_icon && "[worn_state]_s" in icon_states(under_icon)) || ("[worn_state]_r_s" in icon_states(under_icon)))
+ if((under_icon == rolled_down_sleeves_icon && "[worn_state]_s" in cached_icon_states(under_icon)) || ("[worn_state]_r_s" in cached_icon_states(under_icon)))
if(rolled_sleeves != 1)
rolled_sleeves = 0
else
@@ -955,7 +955,7 @@
if(rolled_down)
body_parts_covered = initial(body_parts_covered)
body_parts_covered &= ~(UPPER_TORSO|ARMS)
- if("[worn_state]_s" in icon_states(rolled_down_icon))
+ if("[worn_state]_s" in cached_icon_states(rolled_down_icon))
icon_override = rolled_down_icon
item_state_slots[slot_w_uniform_str] = "[worn_state]"
else
@@ -988,7 +988,7 @@
rolled_sleeves = !rolled_sleeves
if(rolled_sleeves)
body_parts_covered &= ~(ARMS)
- if("[worn_state]_s" in icon_states(rolled_down_sleeves_icon))
+ if("[worn_state]_s" in cached_icon_states(rolled_down_sleeves_icon))
icon_override = rolled_down_sleeves_icon
item_state_slots[slot_w_uniform_str] = "[worn_state]"
else
diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm
index 57fe80cd76..6aed5daf39 100644
--- a/code/modules/clothing/under/accessories/accessory.dm
+++ b/code/modules/clothing/under/accessories/accessory.dm
@@ -24,7 +24,7 @@
if(!inv_overlay)
var/tmp_icon_state = "[overlay_state? "[overlay_state]" : "[icon_state]"]"
if(icon_override)
- if("[tmp_icon_state]_tie" in icon_states(icon_override))
+ if("[tmp_icon_state]_tie" in cached_icon_states(icon_override))
tmp_icon_state = "[tmp_icon_state]_tie"
inv_overlay = image(icon = icon_override, icon_state = tmp_icon_state, dir = SOUTH)
else
@@ -48,7 +48,7 @@
tmp_icon_state = on_rolled["rolled"]
if(icon_override)
- if("[tmp_icon_state]_mob" in icon_states(icon_override))
+ if("[tmp_icon_state]_mob" in cached_icon_states(icon_override))
tmp_icon_state = "[tmp_icon_state]_mob"
mob_overlay = image("icon" = icon_override, "icon_state" = "[tmp_icon_state]")
else if(wearer && sprite_sheets[wearer.species.get_bodytype(wearer)]) //Teshari can finally into webbing, too!
diff --git a/code/modules/clothing/under/accessories/lockets.dm b/code/modules/clothing/under/accessories/lockets.dm
index ca859addb5..153df0d584 100644
--- a/code/modules/clothing/under/accessories/lockets.dm
+++ b/code/modules/clothing/under/accessories/lockets.dm
@@ -13,7 +13,7 @@
if(!base_icon)
base_icon = icon_state
- if(!("[base_icon]_open" in icon_states(icon)))
+ if(!("[base_icon]_open" in cached_icon_states(icon)))
to_chat(user, "\The [src] doesn't seem to open.")
return
diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm
index b87183c385..5e85c0123c 100644
--- a/code/modules/customitems/item_spawning.dm
+++ b/code/modules/customitems/item_spawning.dm
@@ -78,7 +78,7 @@
var/list/new_item_icons = list()
var/list/new_item_state_slots = list()
- var/list/available_states = icon_states(CUSTOM_ITEM_MOB)
+ var/list/available_states = cached_icon_states(CUSTOM_ITEM_MOB)
//If l_hand or r_hand are not present, preserve them using item_icons/item_state_slots
//Then use icon_override to make every other slot use the custom sprites by default.
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index 982f44e794..90afa7f416 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -141,7 +141,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.overlays |= fruit_base
- if("[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf" in icon_states('icons/obj/hydroponics_products.dmi'))
+ if("[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf" in cached_icon_states('icons/obj/hydroponics_products.dmi'))
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.overlays |= fruit_leaves
diff --git a/code/modules/hydroponics/seed_controller.dm b/code/modules/hydroponics/seed_controller.dm
index 1ca42f4c60..28315b361a 100644
--- a/code/modules/hydroponics/seed_controller.dm
+++ b/code/modules/hydroponics/seed_controller.dm
@@ -53,7 +53,7 @@ var/global/datum/controller/plants/plant_controller // Set in New().
/datum/controller/plants/proc/setup()
// Build the icon lists.
- for(var/icostate in icon_states('icons/obj/hydroponics_growing.dmi'))
+ for(var/icostate in cached_icon_states('icons/obj/hydroponics_growing.dmi'))
var/split = findtext(icostate,"-")
if(!split)
// invalid icon_state
@@ -71,7 +71,7 @@ var/global/datum/controller/plants/plant_controller // Set in New().
if(!(base in GLOB.forbidden_plant_growth_sprites))
accessible_plant_sprites[base] = ikey
- for(var/icostate in icon_states('icons/obj/hydroponics_products.dmi'))
+ for(var/icostate in cached_icon_states('icons/obj/hydroponics_products.dmi'))
var/split = findtext(icostate,"-")
var/base = copytext(icostate,1,split)
if(split)
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 963ad4cd38..2903bf604f 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -321,7 +321,7 @@ var/global/list/damage_icon_parts = list() //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 icon_states(species.icobase)))
+ if(husk && ("overlay_husk" in cached_icon_states(species.icobase)))
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/new_player/login.dm b/code/modules/mob/new_player/login.dm
index 57a9574f11..fb7f28d112 100644
--- a/code/modules/mob/new_player/login.dm
+++ b/code/modules/mob/new_player/login.dm
@@ -11,7 +11,7 @@ var/obj/effect/lobby_image = new /obj/effect/lobby_image
/obj/effect/lobby_image/Initialize()
icon = using_map.lobby_icon
- var/known_icon_states = icon_states(icon)
+ var/known_icon_states = cached_icon_states(icon)
for(var/lobby_screen in using_map.lobby_screens)
if(!(lobby_screen in known_icon_states))
error("Lobby screen '[lobby_screen]' did not exist in the icon set [icon].")
diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm
index b07cacd92b..01f4e6003a 100644
--- a/code/modules/projectiles/ammunition.dm
+++ b/code/modules/projectiles/ammunition.dm
@@ -201,7 +201,7 @@
/proc/magazine_icondata_cache_add(var/obj/item/ammo_magazine/M)
var/list/icon_keys = list()
var/list/ammo_states = list()
- var/list/states = icon_states(M.icon)
+ var/list/states = cached_icon_states(M.icon)
for(var/i = 0, i <= M.max_ammo, i++)
var/ammo_state = "[M.icon_state]-[i]"
if(ammo_state in states)
|