diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm
index 6559f28f26..6bd32c762c 100644
--- a/code/ATMOSPHERICS/atmospherics.dm
+++ b/code/ATMOSPHERICS/atmospherics.dm
@@ -48,6 +48,9 @@ Pipelines + Other Objects -> Pipe network
pipe_color = null
init_dir()
+/obj/machinery/atmospherics/examine_icon()
+ return icon(icon=initial(icon),icon_state=initial(icon_state))
+
// This is used to set up what directions pipes will connect to. Should be called inside New() and whenever a dir changes.
/obj/machinery/atmospherics/proc/init_dir()
return
diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm
index 97e66ac93d..8bec734441 100644
--- a/code/_helpers/icons.dm
+++ b/code/_helpers/icons.dm
@@ -679,7 +679,7 @@ proc/ColorTone(rgb, tone)
var/curstate = A.icon_state || defstate
if(!((noIcon = (!curicon))))
- var/curstates = icon_states(curicon)
+ var/curstates = cached_icon_states(curicon)
if(!(curstate in curstates))
if("" in curstates)
curstate = ""
@@ -689,19 +689,16 @@ proc/ColorTone(rgb, tone)
var/curdir
var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have
- //These should use the parent's direction (most likely)
- if(!A.dir || A.dir == SOUTH)
- curdir = defdir
- else
- curdir = A.dir
+ // Use the requested dir or the atom's current dir
+ curdir = defdir || A.dir
- //Try to remove/optimize this section ASAP, CPU hog.
+ //Try to remove/optimize this section ASAP, CPU hog. //Slightly mitigated by implementing caching using cached_icon_states
//Determines if there's directionals.
if(!noIcon && curdir != SOUTH)
var/exist = FALSE
var/static/list/checkdirs = list(NORTH, EAST, WEST)
for(var/i in checkdirs) //Not using GLOB for a reason.
- if(length(icon_states(icon(curicon, curstate, i))))
+ if(length(cached_icon_states(icon(curicon, curstate, i))))
exist = TRUE
break
if(!exist)
@@ -739,8 +736,8 @@ proc/ColorTone(rgb, tone)
continue
var/current_layer = current.layer
if(current_layer < 0)
- if(current_layer <= -1000)
- return flat
+ //if(current_layer <= -1000)
+ //return flat
current_layer = process_set + A.layer + current_layer / 1000
for(var/p in 1 to layers.len)
@@ -768,7 +765,7 @@ proc/ColorTone(rgb, tone)
curblend = BLEND_OVERLAY
add = icon(I.icon, I.icon_state, base_icon_dir)
else // 'I' is an appearance object.
- add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim)
+ add = getFlatIcon(image(I), I.dir||curdir, curicon, curstate, curblend, FALSE, no_anim)
if(!add)
continue
// Find the new dimensions of the flat icon to fit the added overlay
@@ -903,13 +900,12 @@ GLOBAL_LIST_EMPTY(icon_state_lists)
/proc/cached_icon_states(var/icon/I)
if(!I)
return list()
- var/key = "\ref[I]"
+ var/key = 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)
+ returnlist = icon_states(I)
+ if(isfile(I)) // It's something that will stick around
+ GLOB.icon_state_lists[key] = returnlist
return returnlist
/proc/expire_states_cache(var/key)
@@ -918,6 +914,19 @@ GLOBAL_LIST_EMPTY(icon_state_lists)
return TRUE
return FALSE
+GLOBAL_LIST_EMPTY(cached_examine_icons)
+/proc/set_cached_examine_icon(var/atom/A, var/icon/I, var/expiry = 12000)
+ GLOB.cached_examine_icons[weakref(A)] = I
+ if(expiry)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/uncache_examine_icon, weakref(A)), expiry, TIMER_UNIQUE)
+
+/proc/get_cached_examine_icon(var/atom/A)
+ var/weakref/WR = weakref(A)
+ return GLOB.cached_examine_icons[WR]
+
+/proc/uncache_examine_icon(var/weakref/WR)
+ GLOB.cached_examine_icons -= WR
+
proc/adjust_brightness(var/color, var/value)
if (!color) return "#FFFFFF"
if (!value) return color
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 5f8ccb3391..7d39676d51 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -192,6 +192,10 @@
return output
+// Don't make these call bicon or anything, these are what bicon uses. They need to return an icon.
+/atom/proc/examine_icon()
+ return icon(icon=src.icon, icon_state=src.icon_state, dir=SOUTH, frame=1, moving=0)
+
// called by mobs when e.g. having the atom as their machine, pulledby, loc (AKA mob being inside the atom) or buckled var set.
// see code/modules/mob/mob_movement.dm for more.
/atom/proc/relaymove()
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 3df62610c9..2434d33e89 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -47,6 +47,9 @@
else
. += "There is a thick layer of silicate covering it."
+/obj/structure/window/examine_icon()
+ return icon(icon=initial(icon),icon_state=initial(icon_state))
+
/obj/structure/window/take_damage(var/damage = 0, var/sound_effect = 1)
var/initialhealth = health
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index add0b3b11d..1fc49d58a9 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -46,6 +46,9 @@
dismantle_wall(null,null,1)
..()
+/turf/simulated/wall/examine_icon()
+ return icon(icon=initial(icon), icon_state=initial(icon_state))
+
/turf/simulated/wall/process()
// Calling parent will kill processing
if(!radiate())
diff --git a/code/modules/examine/examine.dm b/code/modules/examine/examine.dm
index 6e2df0b1fa..f49c672bca 100644
--- a/code/modules/examine/examine.dm
+++ b/code/modules/examine/examine.dm
@@ -5,7 +5,7 @@
This means that this file can be unchecked, along with the other examine files, and can be removed entirely with no effort.
*/
-#define EXAMINE_PANEL_PADDING " "
+#define EXAMINE_PANEL_PADDING " "
/atom/
var/description_info = null //Helpful blue text.
@@ -56,7 +56,7 @@
description_holders["interactions"] = A.get_description_interaction()
description_holders["name"] = "[A.name]"
- description_holders["icon"] = "\icon[A]"
+ description_holders["icon"] = "\icon[A.examine_icon()]"
description_holders["desc"] = A.desc
/mob/Stat()
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index e20d3740c9..349859029f 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -152,6 +152,13 @@
if(new_stat != DEAD)
CRASH("It is best if observers stay dead, thank you.")
+/mob/observer/dead/examine_icon()
+ var/icon/I = get_cached_examine_icon(src)
+ if(!I)
+ I = getFlatIcon(src, defdir = SOUTH, no_anim = TRUE)
+ set_cached_examine_icon(src, I, 200 SECONDS)
+ return I
+
/*
Transfer_mind is there to check if mob is being deleted/not going to have a body.
Works together with spawning an observer, noted above.
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index f7ee2d9520..9687bde18f 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1596,6 +1596,13 @@
else
layer = HIDING_LAYER
+/mob/living/carbon/human/examine_icon()
+ var/icon/I = get_cached_examine_icon(src)
+ if(!I)
+ I = getFlatIcon(src, defdir = SOUTH, no_anim = TRUE)
+ set_cached_examine_icon(src, I, 50 SECONDS)
+ return I
+
/mob/living/carbon/human/proc/get_display_species()
//Shows species in tooltip
if(src.custom_species) //VOREStation Add
diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm
index f853e224fc..249c84be73 100644
--- a/code/modules/tables/tables.dm
+++ b/code/modules/tables/tables.dm
@@ -30,6 +30,9 @@ var/list/table_icon_cache = list()
var/item_place = 1 //allows items to be placed on the table, but not on benches.
+/obj/structure/table/examine_icon()
+ return icon(icon=initial(icon), icon_state=initial(icon_state)) //Basically the map preview version
+
/obj/structure/table/proc/update_material()
var/old_maxhealth = maxhealth
if(!material)
diff --git a/code/modules/vchat/vchat_client.dm b/code/modules/vchat/vchat_client.dm
index 6e0ac509b0..8456c5225f 100644
--- a/code/modules/vchat/vchat_client.dm
+++ b/code/modules/vchat/vchat_client.dm
@@ -266,31 +266,45 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
var/list/partial = splittext(iconData, "{")
return replacetext(copytext(partial[2], 3, -5), "\n", "")
+/proc/expire_bicon_cache(key)
+ if(GLOB.bicon_cache[key])
+ GLOB.bicon_cache -= key
+ return TRUE
+ return FALSE
+
+GLOBAL_LIST_EMPTY(bicon_cache) // Cache of the
tag results, not the icons
/proc/bicon(var/obj, var/use_class = 1, var/custom_classes = "")
var/class = use_class ? "class='icon misc [custom_classes]'" : null
- if (!obj)
+ if(!obj)
return
- var/static/list/bicon_cache = list()
- if (isicon(obj))
- //Icon refs get reused all the time especially on temporarily made ones like chat tags, too difficult to cache.
- //if (!bicon_cache["\ref[obj]"]) // Doesn't exist yet, make it.
- //bicon_cache["\ref[obj]"] = icon2base64(obj)
-
+ // Try to avoid passing bicon an /icon directly. It is better to pass it an atom so it can cache.
+ if(isicon(obj)) // Passed an icon directly, nothing to cache-key on, as icon refs get reused *often*
return "
"
// Either an atom or somebody fucked up and is gonna get a runtime, which I'm fine with.
var/atom/A = obj
- var/key = "[istype(A.icon, /icon) ? "\ref[A.icon]" : A.icon]:[A.icon_state]"
- if (!bicon_cache[key]) // Doesn't exist, make it.
- var/icon/I = icon(A.icon, A.icon_state, SOUTH, 1)
- if (ishuman(obj))
- I = getFlatIcon(obj) //Ugly
- bicon_cache[key] = icon2base64(I, key)
+ var/key
+ var/changes_often = ishuman(A) || isobserver(A) // If this ends up with more, move it into a proc or var on atom.
+
+ if(changes_often)
+ key = "\ref[A]"
+ else
+ key = "[istype(A.icon, /icon) ? "\ref[A.icon]" : A.icon]:[A.icon_state]"
+
+ var/base64 = GLOB.bicon_cache[key]
+ // Non-human atom, no cache
+ if(!base64) // Doesn't exist, make it.
+ base64 = icon2base64(A.examine_icon(), key)
+ GLOB.bicon_cache[key] = base64
+ if(changes_often)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/expire_bicon_cache, key), 50 SECONDS, TIMER_UNIQUE)
+
+ // May add a class to the img tag created by bicon
if(use_class)
class = "class='icon [A.icon_state] [custom_classes]'"
- return "
"
+ return "
"
//Checks if the message content is a valid to_chat message
/proc/is_valid_tochat_message(message)