mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-13 16:13:19 +01:00
9fa5fbb3c6
* Made turf contents listing more performant. * Made images resend and reload if they don't arrive. * Runtime fix. * Remove redundant code. * Lint. * Apply suggestions from code review Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> Signed-off-by: Charlie Nolan <funnyman3595@gmail.com> * Safety. --------- Signed-off-by: Charlie Nolan <funnyman3595@gmail.com> Co-authored-by: FunnyMan3595 (Charlie Nolan) <funnyman@google.com> Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com>
531 lines
21 KiB
Plaintext
531 lines
21 KiB
Plaintext
/*
|
|
IconProcs README
|
|
|
|
A BYOND library for manipulating icons and colors
|
|
|
|
by Lummox JR
|
|
|
|
version 1.0
|
|
|
|
============================================================
|
|
|
|
AA EDIT - Removed a bunch of procs we have natively now
|
|
|
|
============================================================
|
|
|
|
The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation
|
|
routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners.
|
|
|
|
CHANGING ICONS
|
|
|
|
Several new procs have been added to the /icon datum to simplify working with icons. To use them,
|
|
remember you first need to setup an /icon var like so:
|
|
|
|
var/icon/my_icon = new('iconfile.dmi')
|
|
|
|
icon/ChangeOpacity(amount = 1)
|
|
A very common operation in DM is to try to make an icon more or less transparent. Making an icon more
|
|
transparent is usually much easier than making it less so, however. This proc basically is a frontend
|
|
for MapColors() which can change opacity any way you like, in much the same way that SetIntensity()
|
|
can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half.
|
|
If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque.
|
|
icon/GrayScale()
|
|
Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact.
|
|
icon/ColorTone(tone)
|
|
Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an
|
|
RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect.
|
|
See also the global ColorTone() proc.
|
|
icon/MinColors(icon)
|
|
The icon is blended with a second icon where the minimum of each RGB pixel is the result.
|
|
Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon.
|
|
icon/MaxColors(icon)
|
|
The icon is blended with a second icon where the maximum of each RGB pixel is the result.
|
|
Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon.
|
|
icon/Opaque(background = "#000000")
|
|
All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify.
|
|
icon/BecomeAlphaMask()
|
|
You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc.
|
|
The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white.
|
|
icon/AddAlphaMask(mask)
|
|
The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque,
|
|
the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent.
|
|
Where the mask is translucent, the current icon becomes more transparent.
|
|
icon/UseAlphaMask(mask, mode)
|
|
Sometimes you may want to take the alpha values from one icon and use them on a different icon.
|
|
This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change
|
|
so it has the same colors as before but uses the mask for opacity.
|
|
|
|
COLOR MANAGEMENT AND HSV
|
|
|
|
RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value.
|
|
|
|
* The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to
|
|
cyan to blue to magenta and back to red.
|
|
* The saturation of a color is how much color is in it. A color with low saturation will be more gray,
|
|
and with no saturation at all it is a shade of gray.
|
|
* The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark,
|
|
and no value at all is black.
|
|
|
|
Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three
|
|
hex digits because it ranges from 0 to 0x5FF.
|
|
|
|
* 0 to 0xFF - red to yellow
|
|
* 0x100 to 0x1FF - yellow to green
|
|
* 0x200 to 0x2FF - green to cyan
|
|
* 0x300 to 0x3FF - cyan to blue
|
|
* 0x400 to 0x4FF - blue to magenta
|
|
* 0x500 to 0x5FF - magenta to red
|
|
|
|
Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible),
|
|
value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff".
|
|
|
|
More than one HSV color can match the same RGB color.
|
|
|
|
Here are some procs you can use for color management:
|
|
|
|
ReadRGB(rgb)
|
|
Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used
|
|
that includes alpha, the list will have a fourth item for the alpha value.
|
|
hsv(hue, sat, val, apha)
|
|
Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa"
|
|
format. Alpha is not included in the result if null.
|
|
ReadHSV(rgb)
|
|
Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that
|
|
includes alpha, the list will have a fourth item for the alpha value.
|
|
RGBtoHSV(rgb)
|
|
Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff".
|
|
HSVtoRGB(hsv)
|
|
Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa".
|
|
BlendRGB(rgb1, rgb2, amount)
|
|
Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result;
|
|
if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
|
|
The returned value is an RGB or RGBA color.
|
|
BlendHSV(hsv1, hsv2, amount)
|
|
Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB
|
|
blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1,
|
|
the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
|
|
The returned value is an HSV or HSVA color.
|
|
BlendRGBasHSV(rgb1, rgb2, amount)
|
|
Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form.
|
|
HueToAngle(hue)
|
|
Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue.
|
|
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
|
|
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.
|
|
ColorTone(rgb, tone)
|
|
Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of
|
|
using strict shades of gray. The tone value is an RGB color; any alpha value is ignored.
|
|
*/
|
|
|
|
/*
|
|
Get Flat Icon DEMO by DarkCampainger
|
|
|
|
This is a test for the get flat icon proc, modified approprietly for icons and their states.
|
|
Probably not a good idea to run this unless you want to see how the proc works in detail.
|
|
mob
|
|
icon = 'old_or_unused.dmi'
|
|
icon_state = "green"
|
|
|
|
Login()
|
|
// Testing image underlays
|
|
underlays += image(icon='old_or_unused.dmi',icon_state="red")
|
|
underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32)
|
|
underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32)
|
|
|
|
// Testing image overlays
|
|
overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32)
|
|
overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32)
|
|
overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32)
|
|
|
|
// Testing icon file overlays (defaults to mob's state)
|
|
overlays += '_flat_demoIcons2.dmi'
|
|
|
|
// Testing icon_state overlays (defaults to mob's icon)
|
|
overlays += "white"
|
|
|
|
// Testing dynamic icon overlays
|
|
var/icon/I = icon('old_or_unused.dmi', icon_state="aqua")
|
|
I.Shift(NORTH,16,1)
|
|
overlays+=I
|
|
|
|
// Testing dynamic image overlays
|
|
I=image(icon=I,pixel_x = -32, pixel_y = 32)
|
|
overlays+=I
|
|
|
|
// Testing object types (and layers)
|
|
overlays+=/obj/effect/overlayTest
|
|
|
|
loc = locate (10,10,1)
|
|
verb
|
|
Browse_Icon()
|
|
set name = "1. Browse Icon"
|
|
// Give it a name for the cache
|
|
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
|
// Send the icon to src's local cache
|
|
src<<browse_rsc(getFlatIcon(src), iconName)
|
|
// Display the icon in their browser
|
|
src<<browse("<body bgcolor='#000000'><p><img src='[iconName]'></p></body>")
|
|
|
|
Output_Icon()
|
|
set name = "2. Output Icon"
|
|
to_chat(src, "Icon is: [bicon(getFlatIcon(src))]")
|
|
|
|
Label_Icon()
|
|
set name = "3. Label Icon"
|
|
// Give it a name for the cache
|
|
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
|
// Copy the file to the rsc manually
|
|
var/icon/I = fcopy_rsc(getFlatIcon(src))
|
|
// Send the icon to src's local cache
|
|
src<<browse_rsc(I, iconName)
|
|
// Update the label to show it
|
|
winset(src,"imageLabel","image='\ref[I]'");
|
|
|
|
Add_Overlay()
|
|
set name = "4. Add Overlay"
|
|
overlays += image(icon='old_or_unused.dmi',icon_state="yellow",pixel_x = rand(-64,32), pixel_y = rand(-64,32))
|
|
|
|
Stress_Test()
|
|
set name = "5. Stress Test"
|
|
for(var/i = 0 to 1000)
|
|
// The third parameter forces it to generate a new one, even if it's already cached
|
|
getFlatIcon(src,0,2)
|
|
if(prob(5))
|
|
Add_Overlay()
|
|
Browse_Icon()
|
|
|
|
Cache_Test()
|
|
set name = "6. Cache Test"
|
|
for(var/i = 0 to 1000)
|
|
getFlatIcon(src)
|
|
Browse_Icon()
|
|
|
|
obj/effect/overlayTest
|
|
icon = 'old_or_unused.dmi'
|
|
icon_state = "blue"
|
|
pixel_x = -24
|
|
pixel_y = 24
|
|
layer = TURF_LAYER // Should appear below the rest of the overlays
|
|
|
|
world
|
|
view = "7x7"
|
|
maxx = 20
|
|
maxy = 20
|
|
maxz = 1
|
|
*/
|
|
|
|
#define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87))
|
|
|
|
/icon/proc/MakeLying()
|
|
var/icon/I = new(src,dir=SOUTH)
|
|
I.BecomeLying()
|
|
return I
|
|
|
|
/icon/proc/BecomeLying()
|
|
Turn(90)
|
|
Shift(SOUTH,6)
|
|
Shift(EAST,1)
|
|
|
|
// Multiply all alpha values by this float
|
|
/icon/proc/ChangeOpacity(opacity = 1.0)
|
|
MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,opacity, 0,0,0,0)
|
|
|
|
// Convert to grayscale
|
|
/icon/proc/GrayScale()
|
|
MapColors(0.3,0.3,0.3, 0.59,0.59,0.59, 0.11,0.11,0.11, 0,0,0)
|
|
|
|
/icon/proc/ColorTone(tone)
|
|
GrayScale()
|
|
|
|
var/list/TONE = rgb2num(tone)
|
|
var/gray = round(TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11, 1)
|
|
|
|
var/icon/upper = (255-gray) ? new(src) : null
|
|
|
|
if(gray)
|
|
MapColors(255/gray,0,0, 0,255/gray,0, 0,0,255/gray, 0,0,0)
|
|
Blend(tone, ICON_MULTIPLY)
|
|
else SetIntensity(0)
|
|
if(255-gray)
|
|
upper.Blend(rgb(gray,gray,gray), ICON_SUBTRACT)
|
|
upper.MapColors((255-TONE[1])/(255-gray),0,0,0, 0,(255-TONE[2])/(255-gray),0,0, 0,0,(255-TONE[3])/(255-gray),0, 0,0,0,0, 0,0,0,1)
|
|
Blend(upper, ICON_ADD)
|
|
|
|
// Take the minimum color of two icons; combine transparency as if blending with ICON_ADD
|
|
/icon/proc/MinColors(icon)
|
|
var/icon/I = new(src)
|
|
I.Opaque()
|
|
I.Blend(icon, ICON_SUBTRACT)
|
|
Blend(I, ICON_SUBTRACT)
|
|
|
|
// Take the maximum color of two icons; combine opacity as if blending with ICON_OR
|
|
/icon/proc/MaxColors(icon)
|
|
var/icon/I
|
|
if(isicon(icon))
|
|
I = new(icon)
|
|
else
|
|
// solid color
|
|
I = new(src)
|
|
I.Blend("#000000", ICON_OVERLAY)
|
|
I.SwapColor("#000000", null)
|
|
I.Blend(icon, ICON_OVERLAY)
|
|
var/icon/J = new(src)
|
|
J.Opaque()
|
|
I.Blend(J, ICON_SUBTRACT)
|
|
Blend(I, ICON_OR)
|
|
|
|
// make this icon fully opaque--transparent pixels become black
|
|
/icon/proc/Opaque(background = "#000000")
|
|
SwapColor(null, background)
|
|
MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0, 0,0,0,1)
|
|
|
|
// Change a grayscale icon into a white icon where the original color becomes the alpha
|
|
// I.e., black -> transparent, gray -> translucent white, white -> solid white
|
|
/icon/proc/BecomeAlphaMask()
|
|
SwapColor(null, "#000000ff") // don't let transparent become gray
|
|
MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0)
|
|
|
|
/icon/proc/UseAlphaMask(mask)
|
|
Opaque()
|
|
AddAlphaMask(mask)
|
|
|
|
/icon/proc/AddAlphaMask(mask)
|
|
var/icon/M = new(mask)
|
|
M.Blend("#ffffff", ICON_SUBTRACT)
|
|
// apply mask
|
|
Blend(M, ICON_ADD)
|
|
|
|
/// Generates a filename for a given asset.
|
|
/// Like generate_asset_name(), except returns the rsc reference and the rsc file hash as well as the asset name (sans extension)
|
|
/// Used so that certain asset files dont have to be hashed twice
|
|
/proc/generate_and_hash_rsc_file(file, dmi_file_path)
|
|
var/rsc_ref = fcopy_rsc(file)
|
|
var/hash
|
|
// If we have a valid dmi file path we can trust md5'ing the rsc file because we know it doesnt have the bug described in http://www.byond.com/forum/post/2611357
|
|
if(dmi_file_path)
|
|
hash = md5(rsc_ref)
|
|
else // Otherwise, we need to do the expensive fcopy() workaround
|
|
hash = md5asfile(rsc_ref)
|
|
|
|
return list(rsc_ref, hash, "asset.[hash]")
|
|
|
|
GLOBAL_LIST_EMPTY(bicon_cache)
|
|
|
|
/// Gets a dummy savefile for usage in icon generation.
|
|
/// Savefiles generated from this proc will be empty.
|
|
/proc/get_dummy_savefile(from_failure = FALSE)
|
|
var/static/next_id = 0
|
|
if(next_id++ > 9)
|
|
next_id = 0
|
|
var/savefile_path = "tmp/dummy-save-[next_id].sav"
|
|
try
|
|
if(fexists(savefile_path))
|
|
fdel(savefile_path)
|
|
return new /savefile(savefile_path)
|
|
catch(var/exception/error)
|
|
// if we failed to create a dummy once, try again; maybe someone slept somewhere they shouldnt have
|
|
if(from_failure) // this *is* the retry, something fucked up
|
|
CRASH("get_dummy_savefile failed to create a dummy savefile: '[error]'")
|
|
return get_dummy_savefile(from_failure = TRUE)
|
|
|
|
/**
|
|
* Converts an icon to base64. Operates by putting the icon in the iconCache savefile,
|
|
* exporting it as text, and then parsing the base64 from that.
|
|
* (This relies on byond automatically storing icons in savefiles as base64)
|
|
*/
|
|
/proc/icon2base64(icon/icon)
|
|
if(!isicon(icon))
|
|
return FALSE
|
|
var/savefile/dummySave = get_dummy_savefile()
|
|
WRITE_FILE(dummySave["dummy"], icon)
|
|
var/iconData = dummySave.ExportText("dummy")
|
|
var/list/partial = splittext(iconData, "{")
|
|
return replacetext(copytext_char(partial[2], 3, -5), "\n", "") //if cleanup fails we want to still return the correct base64
|
|
|
|
/proc/bicon(obj, use_class = 1)
|
|
var/class = use_class ? "class='icon misc'" : null
|
|
if(!obj)
|
|
return
|
|
|
|
if(isicon(obj))
|
|
if(!GLOB.bicon_cache["\ref[obj]"]) // Doesn't exist yet, make it.
|
|
GLOB.bicon_cache["\ref[obj]"] = icon2base64(obj)
|
|
|
|
return "<img [class] src='data:image/png;base64,[GLOB.bicon_cache["\ref[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(!GLOB.bicon_cache[key]) // Doesn't exist, make it.
|
|
var/icon/I = icon(A.icon, A.icon_state, SOUTH, 1)
|
|
if(ishuman(obj)) // Shitty workaround for a BYOND issue.
|
|
var/icon/temp = I
|
|
I = icon()
|
|
I.Insert(temp, dir = SOUTH)
|
|
GLOB.bicon_cache[key] = icon2base64(I, key)
|
|
if(use_class)
|
|
class = "class='icon [A.icon_state]'"
|
|
|
|
return "<img [class] src='data:image/png;base64,[GLOB.bicon_cache[key]]'>"
|
|
|
|
///given a text string, returns whether it is a valid dmi icons folder path
|
|
/proc/is_valid_dmi_file(icon_path)
|
|
if(!istext(icon_path) || !length(icon_path))
|
|
return FALSE
|
|
|
|
var/is_in_icon_folder = findtextEx(icon_path, "icons/")
|
|
var/is_dmi_file = findtextEx(icon_path, ".dmi")
|
|
|
|
if(is_in_icon_folder && is_dmi_file)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/// Given an icon object, dmi file path, or atom/image/mutable_appearance, attempts to find and return an associated dmi file path.
|
|
/// A weird quirk about dm is that /icon objects represent both compile-time or dynamic icons in the rsc,
|
|
/// But stringifying rsc references returns a dmi file path
|
|
/// ONLY if that icon represents a completely unchanged dmi file from when the game was compiled.
|
|
/// So if the given object is associated with an icon that was in the rsc when the game was compiled, this returns a path. otherwise it returns ""
|
|
/proc/get_icon_dmi_path(icon/icon)
|
|
/// The dmi file path we attempt to return if the given object argument is associated with a stringifiable icon
|
|
/// If successful, this looks like "icons/path/to/dmi_file.dmi"
|
|
var/icon_path = ""
|
|
|
|
if(isatom(icon) || istype(icon, /image) || istype(icon, /mutable_appearance))
|
|
var/atom/atom_icon = icon
|
|
icon = atom_icon.icon
|
|
// Atom icons compiled in from 'icons/path/to/dmi_file.dmi' are weird and not really icon objects that you generate with icon().
|
|
// If theyre unchanged dmi's then they're stringifiable to "icons/path/to/dmi_file.dmi"
|
|
|
|
if(isicon(icon) && isfile(icon))
|
|
// Icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and arent really /icon objects,
|
|
// But they pass both isicon() and isfile() checks. theyre the easiest case since stringifying them gives us the path we want
|
|
var/icon_ref = "\ref[icon]"
|
|
var/locate_icon_string = "[locate(icon_ref)]"
|
|
|
|
icon_path = locate_icon_string
|
|
|
|
else if(isicon(icon) && "[icon]" == "/icon")
|
|
// Icon objects generated from icon() at runtime are icons, but they ARENT files themselves, they represent icon files.
|
|
// If the files they represent are compile time dmi files in the rsc, then
|
|
// The rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi"
|
|
var/rsc_ref = fcopy_rsc(icon)
|
|
|
|
var/icon_ref = "\ref[rsc_ref]"
|
|
|
|
var/icon_path_string = "[locate(icon_ref)]"
|
|
|
|
icon_path = icon_path_string
|
|
|
|
else if(istext(icon))
|
|
var/rsc_ref = fcopy_rsc(icon)
|
|
// If its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path
|
|
|
|
var/rsc_ref_ref = "\ref[rsc_ref]"
|
|
var/rsc_ref_string = "[locate(rsc_ref_ref)]"
|
|
|
|
icon_path = rsc_ref_string
|
|
|
|
if(is_valid_dmi_file(icon_path))
|
|
return icon_path
|
|
|
|
return FALSE
|
|
|
|
/**
|
|
* generate an asset for the given icon or the icon of the given appearance for [thing], and send it to any clients in target.
|
|
* Arguments:
|
|
* * thing - either a /icon object, or an object that has an appearance (atom, image, mutable_appearance).
|
|
* * target - either a reference to or a list of references to /client's or mobs with clients
|
|
* * icon_state - string to force a particular icon_state for the icon to be used
|
|
* * dir - dir number to force a particular direction for the icon to be used
|
|
* * frame - what frame of the icon_state's animation for the icon being used
|
|
* * moving - whether or not to use a moving state for the given icon
|
|
* * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players
|
|
* * extra_classes - string of extra css classes to use when returning the icon string
|
|
*/
|
|
/proc/icon2asset(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null)
|
|
if(!thing)
|
|
return
|
|
|
|
var/key
|
|
var/icon/icon2collapse = thing
|
|
if(!target)
|
|
return
|
|
if(target == world)
|
|
target = GLOB.clients
|
|
var/list/targets
|
|
if(!islist(target))
|
|
targets = list(target)
|
|
else
|
|
targets = target
|
|
if(!length(targets))
|
|
return
|
|
|
|
// Check if the given object is associated with a dmi file in the icons folder. if it is then we dont need to do a lot of work
|
|
// For asset generation to get around byond limitations
|
|
var/icon_path = get_icon_dmi_path(thing)
|
|
|
|
if(!isicon(icon2collapse))
|
|
if(isfile(thing)) //special snowflake
|
|
var/name = SANITIZE_FILENAME("[GENERATE_ASSET_NAME(thing)].png")
|
|
if(!SSassets.cache[name])
|
|
SSassets.transport.register_asset(name, thing)
|
|
for(var/thing2 in targets)
|
|
SSassets.transport.send_assets(thing2, name)
|
|
if(sourceonly)
|
|
return SSassets.transport.get_asset_url(name)
|
|
return "<img class='icon icon-misc' src='[SSassets.transport.get_asset_url(name)]'>"
|
|
|
|
// Its either an atom, image, or mutable_appearance, we want its icon var
|
|
icon2collapse = thing.icon
|
|
if(isnull(icon_state))
|
|
icon_state = thing.icon_state
|
|
// Despite casting to atom, this code path supports mutable appearances, so let's be nice to them
|
|
if(isnull(icon_state) || (isatom(thing)))
|
|
icon_state = initial(thing.icon_state)
|
|
if(isnull(dir))
|
|
dir = initial(thing.dir)
|
|
|
|
if(isnull(dir))
|
|
dir = thing.dir
|
|
|
|
if(ishuman(thing)) // Shitty workaround for a BYOND issue.
|
|
var/icon/temp = icon2collapse
|
|
icon2collapse = icon()
|
|
icon2collapse.Insert(temp, dir = SOUTH)
|
|
dir = SOUTH
|
|
|
|
else
|
|
if(isnull(dir))
|
|
dir = SOUTH
|
|
if(isnull(icon_state))
|
|
icon_state = ""
|
|
|
|
icon2collapse = icon(icon2collapse, icon_state, dir, frame, moving)
|
|
|
|
var/list/name_and_ref = generate_and_hash_rsc_file(icon2collapse, icon_path) // Pretend that tuples exist
|
|
|
|
var/rsc_ref = name_and_ref[1] // Weird object thats not even readable to the debugger, represents a reference to the icons rsc entry
|
|
var/file_hash = name_and_ref[2]
|
|
key = "[name_and_ref[3]].png"
|
|
if(!SSassets.cache[key])
|
|
SSassets.transport.register_asset(key, rsc_ref, file_hash, icon_path)
|
|
for(var/client_target in targets)
|
|
SSassets.transport.send_assets(client_target, key)
|
|
return key
|
|
|
|
/// Costlier version of icon2asset() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs.
|
|
/proc/costly_icon2asset(thing, target, sourceonly = FALSE)
|
|
if(!thing)
|
|
return
|
|
|
|
if(isicon(thing))
|
|
return icon2asset(thing, target)
|
|
|
|
var/icon/I = getFlatIcon(thing)
|
|
return icon2asset(I, target)
|