Files
2020-02-16 19:40:21 -08:00

232 lines
9.1 KiB
Plaintext

/*
!! IMPORTANT NOTE !!
If this is used to handle overlays on an atom then all overlay updates on that atom *MUST* be carried out through this proc.
Failure to do so will cause the list the proc uses to keep track of the overlays to desynchronize with the actual overlays, leading to runtime errors and strange behaviour.
(Ex. Removing the head of a human instead of the glasses they are wearing)
!! UNIMPORTANT NOTE !!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage: UpdateOverlays(var/image/I, var/key, var/force=0, var/retain_cache = 0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Handles updating existing overlays, removing existing overlays and adding new overlays
I = image that you want to add to the atom's overlays (null to clear)
key = which "slot" do you want the image to go in
force = Don't care if there is an existing image with the same details, update anyway. Can be useful if the image to be added is a composite with several overlays of its own.
retain_cache = Does not clear the overlay entry at the same time as clearing the overlay - this retains the image in a retrievable form
Returns 1 on updating an overlay, 0 otherwise
------------------------------------------------------
//Ex.
var/atom/A = new
var/image/ass = image('butt.dmi', "posterior")
A.UpdateOverlays(ass, "hat") //Puts the 'ass' image in the slot defined as "hat"
A.UpdateOverlays(ass, "hat") //This will check the existing overlay in the "hat" slot and reject the update
ass.icon_state = "hindquarters" //Icon state change
A.UpdateOverlays(ass, "hat") //This will detect the change and update the overlay accordingly
A.UpdateOverlays(null, "hat") //Removes the overlay assigned to the "hat" slot, along with the cached image
//Alt.
A.UpdateOverlays(null, "hat",0,1) //Removes the overlay in the "hat" slot, but retains the cached image for retrieval with GetOverlayImage
-------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage: ClearAllOverlays(var/retain_cache=0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Removes all overlays on an atom
retain_cache = Does not clear the overlay entry at the same time as clearing the overlay - this retains the image in a retrievable form
Returns 1 if overlays were cleared, null otherwise
------------------------------------------------------
//Ex.
var/atom/A = new
A.ClearOverlays() //Removes all overlays on A
A.ClearOverlays(1) //Removes all overlays on A but retains the cached images
------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage: GetOverlayImage(var/key)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Gets the image stored in cache for the specified overlay slot
key = The slot in which the desired image is stored
Returns the specified image, if one exists, null otherwise
------------------------------------------------------
//Ex.
//We have an atom A, having an overlay named "hat"
var/image/I = A.GetOverlayImage("hat") //Retrieve the cached image in the hat slot
if(!I) I = image('file', "chapeau") //Not in-scope, but as GetOverlayImage can return null it's good practice to ensure you got an image before doing things to the returned value
I.icon_state = "chapeau" //Change the icon state of it
A.UpdateOverlays(I, "hat") //Update the overlay with the changes
------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage: SafeGetOverlayImage(var/key, var/image_file as file, var/icon_state as text, var/layer as num|null)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Gets the image stored in cache for the specified overlay slot and sets it's icon_state to the desired one, or creates a new one from the image_file and icon_state
key = The slot in which the desired image is stored
image_file = The file to create the new image
icon_state = The desired icon_state
layer = The layer the image should be in
Returns the specified image with modifications according to the input, if one exists, otherwise it creates a new one and returns that
------------------------------------------------------
//Ex.
//We have an atom A, that may or may not have an overlay named "hat"
var/image/I = A.GetOverlayImage("hat", 'obj/item/hats.dmi', "ushanka")
//Retrieve the cached image in the hat slot and sets the icon_state to ushanka or creates a new one using image('obj/item/hats.dmi', "ushanka")
------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage: ClearSpecificOverlays(var/retain_cache=0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Takes a parameter list of overlay keys, then clears the target overlays. If the first parameter is a number it is used to set the cache.
retain_cache = Does not clear the overlay entry at the same time as clearing the overlay - this retains the image in a retrievable form (optional)
Returns a tally of the cleared overlays
------------------------------------------------------
//Ex.
ClearSpecificOverlays("key0", "key1", "key2") //Clears the overlays in slots key0, key1 and key2. Does not retain cached images.
ClearSpecificOverlays(0, "key0", "key1", "key2") //Same as above
ClearSpecificOverlays(1, "key0", "key1", "key2") //Same as above but retains cached images
------------------------------------------------------
*/
#define P_INDEX 1
#define P_IMAGE 2
#define P_ISTATE 3
/atom/var/list/overlay_refs = null
/atom/proc/UpdateOverlays(var/image/I, var/key, var/force=0, var/retain_cache = 0)
if(!key)
CRASH("UpdateOverlays called without a key.")
if (!src.overlay_refs)
src.overlay_refs = list()
var/list/prev_data
//List to store info about the last state of the icon
prev_data = overlay_refs[key]
if(!prev_data && I) //Ok, we don't have previous data, but we will add an overlay
prev_data = list()
prev_data.len = 3
else if(!prev_data) //We don't have data and we won't add an overlay
return 0
//Wire: Temp debugging to narrow down on a runtime
if (!I && key == 1 && force == 0 && retain_cache == 0)
world.log << "\[[time2text(world.timeofday,"hh:mm:ss")]] (Temp Overlay Debug) prev_data is: [json_encode(prev_data)]"
var/hash = hash_image(I)
var/image/prev_overlay = prev_data[P_IMAGE] //overlay_refs[key]
if(!force && (prev_overlay == I) && hash == prev_data[P_ISTATE] ) //If it's the same image as the other one and the hashes match then do not update
return 0
var/index = prev_data[P_INDEX]
if(index > 0) //There is an existing overlay in place in this slot, remove it
src.overlays.Cut(index, index+1) //Fuck yoooou byond (this gotta be by index or it'll fail if the same thing's in overlays several times)
prev_data[P_INDEX] = 0
for(var/ikey in overlay_refs) //Because we're storing the position of each overlay in the list we need to shift our indices down to stay synched
var/list/L = overlay_refs[ikey]
if(!isnull(L) && L.len > 0 && L[P_INDEX] >= index) L[P_INDEX]--
if(I)
src.overlays += I
index = src.overlays.len
prev_data[P_INDEX] = index
prev_data[P_IMAGE] = I
prev_data[P_ISTATE] = hash//I.icon_state
overlay_refs[key] = prev_data
else
if(retain_cache) //Keep the cached image available?
prev_data[P_INDEX] = 0 //Clear the index
prev_data[P_ISTATE] = 0 //Clear the hash
//overlay_refs[key] = prev_data //Update our list <- Pointers, dumbass /Spy
else
overlay_refs -= key
return 1
/atom/proc/ClearAllOverlays(var/retain_cache=0) //Some men just want to watch the world burn
if(src.overlays.len)
if (!src.overlay_refs)
src.overlay_refs = list()
src.overlays.Cut()
if(retain_cache)
for(var/key in src.overlay_refs)
var/list/pd = overlay_refs[key]
pd[P_INDEX] = 0
pd[P_ISTATE] = 0
overlay_refs[key] = pd
else
src.overlay_refs.Cut()
return 1
/atom/proc/ClearSpecificOverlays(var/retain_cache=0)
var/tally = 0
var/keep_cache = isnum(retain_cache) && retain_cache //Maybe someone forgets to include this argument and goes straight for the list, let's handle that case
for(var/key in args)
if(istext(key)) //The retain_cache value will be here as well, so skip it
tally += src.UpdateOverlays(null, key, 0, keep_cache)
return tally
/atom/proc/GetOverlayImage(var/key)
if (!src.overlay_refs)
src.overlay_refs = list()
//Never rely on this proc returning an image.
var/list/ov_data = overlay_refs[key]
if(ov_data)
. = ov_data[P_IMAGE]
else
. = null
/atom/proc/SafeGetOverlayImage(var/key, var/image_file as file, var/icon_state as text, var/layer as num|null)
var/image/I = GetOverlayImage(key)
if(!I)
I = image(image_file, icon_state, layer)
else
//Ok, apparently modifying anything pertaining to the image appearance causes a hubbub, thanks byand
if(I.icon != image_file)
I.icon = image_file
if(icon_state != I.icon_state)
I.icon_state = icon_state
if(layer && layer != I.layer)
I.layer = layer
return I
/////////////////////////////////////////////
//helper procs
/////////////////////////////////////////////
/proc/hash_image(var/image/I)
if(I)
. = md5("\ref[I][I.icon_state][I.overlays ? I.overlays.len : 0][I.color][I.alpha]")
else
. = null
#undef P_INDEX
#undef P_IMAGE
#undef P_ISTATE