Merge pull request #7676 from VOREStation/aro-bicon2

Refactor examining and some of getFlatIcon
This commit is contained in:
Aronai Sieyes
2020-05-06 23:21:41 -04:00
committed by GitHub
28 changed files with 130 additions and 58 deletions

View File

@@ -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

View File

@@ -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<EFBFBD> produces yellow.) The result is another HSV or HSVA color with the same saturation and value
(Rotating red by 60deg 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.
@@ -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
@@ -899,6 +896,37 @@ 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 = I
var/returnlist = GLOB.icon_state_lists[key]
if(!returnlist)
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)
if(GLOB.icon_state_lists[key])
GLOB.icon_state_lists -= key
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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -47,6 +47,9 @@
else
. += "<span class='notice'>There is a thick layer of silicate covering it.</span>"
/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

View File

@@ -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))

View File

@@ -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())

View File

@@ -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

View File

@@ -841,7 +841,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
dat += "<td width = 400>[current_species.blurb]</td>"
//vorestation edit end
dat += "<td width = 200 align='center'>"
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 += "<img src='species_preview_[current_species.name].png' width='64px' height='64px'><br/><br/>"
dat += "<b>Language:</b> [current_species.species_language]<br/>"

View File

@@ -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

View File

@@ -134,7 +134,7 @@
var/mob/living/carbon/human/H = user
if(isTaurTail(H.tail_style))
var/datum/sprite_accessory/tail/taur/taurtail = H.tail_style
if(taurtail.suit_sprites && (get_worn_icon_state(slot_wear_suit_str) in icon_states(taurtail.suit_sprites)))
if(taurtail.suit_sprites && (get_worn_icon_state(slot_wear_suit_str) in cached_icon_states(taurtail.suit_sprites)))
icon_override = taurtail.suit_sprites
normalize = FALSE
taurized = TRUE

View File

@@ -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!

View File

@@ -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

View File

@@ -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.

View File

@@ -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()

View File

@@ -74,9 +74,9 @@
update_icon()
/obj/item/weapon/reagent_containers/food/drinks/glass2/proc/can_add_extra(obj/item/weapon/glass_extra/GE)
if(!("[base_icon]_[GE.glass_addition]left" in icon_states(icon))) //VOREStation Edit
if(!("[base_icon]_[GE.glass_addition]left" in cached_icon_states(icon))) //VOREStation Edit
return 0
if(!("[base_icon]_[GE.glass_addition]right" in icon_states(icon))) //VOREStation Edit
if(!("[base_icon]_[GE.glass_addition]right" in cached_icon_states(icon))) //VOREStation Edit
return 0
return 1
@@ -106,9 +106,9 @@
over_liquid |= "[base_icon][amnt]_fizz"
for(var/S in R.glass_special)
if("[base_icon]_[S]" in icon_states(icon)) //VOREStation Edit
if("[base_icon]_[S]" in cached_icon_states(icon)) //VOREStation Edit
under_liquid |= "[base_icon]_[S]"
else if("[base_icon][amnt]_[S]" in icon_states(icon)) //VOREStation Edit
else if("[base_icon][amnt]_[S]" in cached_icon_states(icon)) //VOREStation Edit
over_liquid |= "[base_icon][amnt]_[S]"
for(var/k in under_liquid)

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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].")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 <img> 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 "<img [class] src='data:image/png;base64,[icon2base64(obj)]'>"
// 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 "<img [class] src='data:image/png;base64,[bicon_cache[key]]'>"
return "<img [class] src='data:image/png;base64,[base64]'>"
//Checks if the message content is a valid to_chat message
/proc/is_valid_tochat_message(message)