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)