Bumps rust_g to 4.0.0 / IconForge Improvements (#92333)
@@ -138,6 +138,36 @@
|
||||
*/
|
||||
#define rustg_dmi_icon_states(fname) RUSTG_CALL(RUST_G, "dmi_icon_states")(fname)
|
||||
|
||||
/**
|
||||
* The below functions involve dmi metadata represented in the following format:
|
||||
* list(
|
||||
* "width": number,
|
||||
* "height": number,
|
||||
* "states": list([STATE_DATA], ...)
|
||||
* )
|
||||
*
|
||||
* STATE_DATA format:
|
||||
* list(
|
||||
* "name": string,
|
||||
* "dirs": 1 | 4 | 8,
|
||||
* "delays"?: list(number, ...),
|
||||
* "rewind"?: TRUE | FALSE,
|
||||
* "movement"?: TRUE | FALSE,
|
||||
* "loop"?: number
|
||||
* )
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the dmi metadata of the file located at `fname`.
|
||||
* Returns a list in the metadata format listed above, or an error message.
|
||||
*/
|
||||
#define rustg_dmi_read_metadata(fname) json_decode(RUSTG_CALL(RUST_G, "dmi_read_metadata")(fname))
|
||||
/**
|
||||
* Inject dmi metadata into a png file located at `path`.
|
||||
* `metadata` must be a json_encode'd list in the metadata format listed above.
|
||||
*/
|
||||
#define rustg_dmi_inject_metadata(path, metadata) RUSTG_CALL(RUST_G, "dmi_inject_metadata")(path, metadata)
|
||||
|
||||
#define rustg_file_read(fname) RUSTG_CALL(RUST_G, "file_read")(fname)
|
||||
#define rustg_file_exists(fname) (RUSTG_CALL(RUST_G, "file_exists")(fname) == "true")
|
||||
#define rustg_file_write(text, fname) RUSTG_CALL(RUST_G, "file_write")(text, fname)
|
||||
@@ -198,13 +228,21 @@
|
||||
#define rustg_http_request_blocking(method, url, body, headers, options) RUSTG_CALL(RUST_G, "http_request_blocking")(method, url, body, headers, options)
|
||||
#define rustg_http_request_async(method, url, body, headers, options) RUSTG_CALL(RUST_G, "http_request_async")(method, url, body, headers, options)
|
||||
#define rustg_http_check_request(req_id) RUSTG_CALL(RUST_G, "http_check_request")(req_id)
|
||||
/// This is basically just `rustg_http_request_async` if you don't care about the response.
|
||||
/// This will either return "ok" or an error, as this does not create a job.
|
||||
#define rustg_http_request_fire_and_forget(method, url, body, headers, options) RUSTG_CALL(RUST_G, "http_request_fire_and_forget")(method, url, body, headers, options)
|
||||
|
||||
/// Generates a spritesheet at: [file_path][spritesheet_name]_[size_id].png
|
||||
/// Generates a spritesheet at: [file_path][spritesheet_name]_[size_id].[png or dmi]
|
||||
/// The resulting spritesheet arranges icons in a random order, with the position being denoted in the "sprites" return value.
|
||||
/// All icons have the same y coordinate, and their x coordinate is equal to `icon_width * position`.
|
||||
///
|
||||
/// hash_icons is a boolean (0 or 1), and determines if the generator will spend time creating hashes for the output field dmi_hashes.
|
||||
/// These hashes can be heplful for 'smart' caching (see rustg_iconforge_cache_valid), but require extra computation.
|
||||
/// These hashes can be helpful for 'smart' caching (see rustg_iconforge_cache_valid), but require extra computation.
|
||||
///
|
||||
/// generate_dmi is a boolean (0 or 1), and determines if the generator will save the sheet as a DMI or stripped PNG file.
|
||||
/// DMI files can be used to replace bulk Insert() operations, PNGs are more useful for asset transport or UIs. DMI generation is slower due to more metadata.
|
||||
/// flatten is a boolean (0 or 1), and determines if the DMI output will be flattened to a single frame/dir if unscoped (null/0 dir or frame values).
|
||||
/// PNGs are always flattened, regardless of argument.
|
||||
///
|
||||
/// Spritesheet will contain all sprites listed within "sprites".
|
||||
/// "sprites" format:
|
||||
@@ -220,9 +258,15 @@
|
||||
/// )
|
||||
/// TRANSFORM_OBJECT format:
|
||||
/// list("type" = RUSTG_ICONFORGE_BLEND_COLOR, "color" = "#ff0000", "blend_mode" = ICON_MULTIPLY)
|
||||
/// list("type" = RUSTG_ICONFORGE_BLEND_ICON, "icon" = [SPRITE_OBJECT], "blend_mode" = ICON_OVERLAY)
|
||||
/// list("type" = RUSTG_ICONFORGE_BLEND_ICON, "icon" = [SPRITE_OBJECT], "blend_mode" = ICON_OVERLAY, "x" = 1, "y" = 1) // offsets optional
|
||||
/// list("type" = RUSTG_ICONFORGE_SCALE, "width" = 32, "height" = 32)
|
||||
/// list("type" = RUSTG_ICONFORGE_CROP, "x1" = 1, "y1" = 1, "x2" = 32, "y2" = 32) // (BYOND icons index from 1,1 to the upper bound, inclusive)
|
||||
/// list("type" = RUSTG_ICONFORGE_MAP_COLORS, "rr" = 0.5, "rg" = 0.5, "rb" = 0.5, "ra" = 1, "gr" = 1, "gg" = 1, "gb" = 1, "ga" = 1, ...) // alpha arguments and rgba0 optional
|
||||
/// list("type" = RUSTG_ICONFORGE_FLIP, "dir" = SOUTH)
|
||||
/// list("type" = RUSTG_ICONFORGE_TURN, "angle" = 90.0)
|
||||
/// list("type" = RUSTG_ICONFORGE_SHIFT, "dir" = EAST, "offset" = 10, "wrap" = FALSE)
|
||||
/// list("type" = RUSTG_ICONFORGE_SWAP_COLOR, "src_color" = "#ff0000", "dst_color" = "#00ff00") // alpha bits supported
|
||||
/// list("type" = RUSTG_ICONFORGE_DRAW_BOX, "color" = "#ff0000", "x1" = 1, "y1" = 1, "x2" = 32, "y2" = 32) // alpha bits supported. color can be null/omitted for transparency. x2 and y2 will default to x1 and y1 if omitted
|
||||
///
|
||||
/// Returns a SpritesheetResult as JSON, containing fields:
|
||||
/// list(
|
||||
@@ -233,9 +277,9 @@
|
||||
/// "error" = "[A string, empty if there were no errors.]"
|
||||
/// )
|
||||
/// In the case of an unrecoverable panic from within Rust, this function ONLY returns a string containing the error.
|
||||
#define rustg_iconforge_generate(file_path, spritesheet_name, sprites, hash_icons) RUSTG_CALL(RUST_G, "iconforge_generate")(file_path, spritesheet_name, sprites, "[hash_icons]")
|
||||
#define rustg_iconforge_generate(file_path, spritesheet_name, sprites, hash_icons, generate_dmi, flatten) RUSTG_CALL(RUST_G, "iconforge_generate")(file_path, spritesheet_name, sprites, "[hash_icons]", "[generate_dmi]", "[flatten]")
|
||||
/// Returns a job_id for use with rustg_iconforge_check()
|
||||
#define rustg_iconforge_generate_async(file_path, spritesheet_name, sprites, hash_icons) RUSTG_CALL(RUST_G, "iconforge_generate_async")(file_path, spritesheet_name, sprites, "[hash_icons]")
|
||||
#define rustg_iconforge_generate_async(file_path, spritesheet_name, sprites, hash_icons, generate_dmi, flatten) RUSTG_CALL(RUST_G, "iconforge_generate_async")(file_path, spritesheet_name, sprites, "[hash_icons]", "[generate_dmi]", "[flatten]")
|
||||
/// Returns the status of an async job_id, or its result if it is completed. See RUSTG_JOB DEFINEs.
|
||||
#define rustg_iconforge_check(job_id) RUSTG_CALL(RUST_G, "iconforge_check")("[job_id]")
|
||||
/// Clears all cached DMIs and images, freeing up memory.
|
||||
@@ -256,7 +300,7 @@
|
||||
/// Provided a /datum/greyscale_config typepath, JSON string containing the greyscale config, and path to a DMI file containing the base icons,
|
||||
/// Loads that config into memory for later use by rustg_iconforge_gags(). The config_path is the unique identifier used later.
|
||||
/// JSON Config schema: https://hackmd.io/@tgstation/GAGS-Layer-Types
|
||||
/// Unsupported features: color_matrix layer type, 'or' blend_mode. May not have BYOND parity with animated icons or varying dirs between layers.
|
||||
/// Adding dirs or frames (via blending larger icons) to icons with more than 1 dir or 1 frame is not supported.
|
||||
/// Returns "OK" if successful, otherwise, returns a string containing the error.
|
||||
#define rustg_iconforge_load_gags_config(config_path, config_json, config_icon_path) RUSTG_CALL(RUST_G, "iconforge_load_gags_config")("[config_path]", config_json, config_icon_path)
|
||||
/// Given a config_path (previously loaded by rustg_iconforge_load_gags_config), and a string of hex colors formatted as "#ff00ff#ffaa00"
|
||||
@@ -272,6 +316,12 @@
|
||||
#define RUSTG_ICONFORGE_BLEND_ICON "BlendIcon"
|
||||
#define RUSTG_ICONFORGE_CROP "Crop"
|
||||
#define RUSTG_ICONFORGE_SCALE "Scale"
|
||||
#define RUSTG_ICONFORGE_MAP_COLORS "MapColors"
|
||||
#define RUSTG_ICONFORGE_FLIP "Flip"
|
||||
#define RUSTG_ICONFORGE_TURN "Turn"
|
||||
#define RUSTG_ICONFORGE_SHIFT "Shift"
|
||||
#define RUSTG_ICONFORGE_SWAP_COLOR "SwapColor"
|
||||
#define RUSTG_ICONFORGE_DRAW_BOX "DrawBox"
|
||||
|
||||
#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
|
||||
#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
|
||||
@@ -298,6 +348,39 @@
|
||||
*/
|
||||
#define rustg_noise_poisson_map(seed, width, length, radius) RUSTG_CALL(RUST_G, "noise_poisson_map")(seed, width, length, radius)
|
||||
|
||||
/**
|
||||
* Register a list of nodes into a rust library. This list of nodes must have been serialized in a json.
|
||||
* Node {// Index of this node in the list of nodes
|
||||
* unique_id: usize,
|
||||
* // Position of the node in byond
|
||||
* x: usize,
|
||||
* y: usize,
|
||||
* z: usize,
|
||||
* // Indexes of nodes connected to this one
|
||||
* connected_nodes_id: Vec<usize>}
|
||||
* It is important that the node with the unique_id 0 is the first in the json, unique_id 1 right after that, etc.
|
||||
* It is also important that all unique ids follow. {0, 1, 2, 4} is not a correct list and the registering will fail
|
||||
* Nodes should not link across z levels.
|
||||
* A node cannot link twice to the same node and shouldn't link itself either
|
||||
*/
|
||||
#define rustg_register_nodes_astar(json) RUSTG_CALL(RUST_G, "register_nodes_astar")(json)
|
||||
|
||||
/**
|
||||
* Add a new node to the static list of nodes. Same rule as registering_nodes applies.
|
||||
* This node unique_id must be equal to the current length of the static list of nodes
|
||||
*/
|
||||
#define rustg_add_node_astar(json) RUSTG_CALL(RUST_G, "add_node_astar")(json)
|
||||
|
||||
/**
|
||||
* Remove every link to the node with unique_id. Replace that node by null
|
||||
*/
|
||||
#define rustg_remove_node_astar(unique_id) RUSTG_CALL(RUST_G, "remove_node_astar")("[unique_id]")
|
||||
|
||||
/**
|
||||
* Compute the shortest path between start_node and goal_node using A*. Heuristic used is simple geometric distance
|
||||
*/
|
||||
#define rustg_generate_path_astar(start_node_id, goal_node_id) RUSTG_CALL(RUST_G, "generate_path_astar")("[start_node_id]", "[goal_node_id]")
|
||||
|
||||
/*
|
||||
* Takes in a string and json_encode()"d lists to produce a sanitized string.
|
||||
* This function operates on whitelists, there is currently no way to blacklist.
|
||||
@@ -362,8 +445,17 @@
|
||||
#define rustg_time_milliseconds(id) text2num(RUSTG_CALL(RUST_G, "time_milliseconds")(id))
|
||||
#define rustg_time_reset(id) RUSTG_CALL(RUST_G, "time_reset")(id)
|
||||
|
||||
/// Returns the current timestamp (in local time), formatted with the given format string.
|
||||
/// See https://docs.rs/chrono/latest/chrono/format/strftime/index.html for documentation on the formatting syntax.
|
||||
#define rustg_formatted_timestamp(format) RUSTG_CALL(RUST_G, "formatted_timestamp")(format)
|
||||
|
||||
/// Returns the current timestamp (with the given UTC offset in hours), formatted with the given format string.
|
||||
/// See https://docs.rs/chrono/latest/chrono/format/strftime/index.html for documentation on the formatting syntax.
|
||||
#define rustg_formatted_timestamp_tz(format, offset) RUSTG_CALL(RUST_G, "formatted_timestamp")(format, offset)
|
||||
|
||||
/// Returns the timestamp as a string
|
||||
#define rustg_unix_timestamp(...) (RUSTG_CALL(RUST_G, "unix_timestamp")())
|
||||
/proc/rustg_unix_timestamp()
|
||||
return RUSTG_CALL(RUST_G, "unix_timestamp")()
|
||||
|
||||
#define rustg_raw_read_toml_file(path) json_decode(RUSTG_CALL(RUST_G, "toml_file_to_json")(path) || "null")
|
||||
|
||||
@@ -391,10 +483,3 @@
|
||||
#define url_decode(text) rustg_url_decode(text)
|
||||
#endif
|
||||
|
||||
/// Returns the current timestamp (in local time), formatted with the given format string.
|
||||
/// See https://docs.rs/chrono/latest/chrono/format/strftime/index.html for documentation on the formatting syntax.
|
||||
#define rustg_formatted_timestamp(format) RUSTG_CALL(RUST_G, "formatted_timestamp")(format)
|
||||
|
||||
/// Returns the current timestamp (with the given UTC offset in hours), formatted with the given format string.
|
||||
/// See https://docs.rs/chrono/latest/chrono/format/strftime/index.html for documentation on the formatting syntax.
|
||||
#define rustg_formatted_timestamp_tz(format, offset) RUSTG_CALL(RUST_G, "formatted_timestamp")(format, offset)
|
||||
|
||||
@@ -1171,27 +1171,60 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
|
||||
animate(pixel_x = initialpixelx + rand(-pixelshiftx,pixelshiftx), pixel_y = initialpixely + rand(-pixelshifty,pixelshifty), time = shake_interval)
|
||||
animate(pixel_x = initialpixelx, pixel_y = initialpixely, time = shake_interval)
|
||||
|
||||
/// Returns rustg-parsed metadata for an icon, universal icon, or DMI file, using cached values where possible
|
||||
/// Returns null if passed object is not a filepath or icon with a valid DMI file
|
||||
/proc/icon_metadata(file)
|
||||
var/static/list/icon_metadata_cache = list()
|
||||
if(istype(file, /datum/universal_icon))
|
||||
var/datum/universal_icon/u_icon = file
|
||||
file = u_icon.icon_file
|
||||
var/file_string = "[file]"
|
||||
if(!istext(file) && !(isfile(file) && length(file_string)))
|
||||
return null
|
||||
var/list/cached_metadata = icon_metadata_cache[file_string]
|
||||
if(islist(cached_metadata))
|
||||
return cached_metadata
|
||||
var/list/metadata_result = rustg_dmi_read_metadata(file_string)
|
||||
if(!islist(metadata_result) || !length(metadata_result))
|
||||
CRASH("Error while reading DMI metadata for path '[file_string]': [metadata_result]")
|
||||
else
|
||||
icon_metadata_cache[file_string] = metadata_result
|
||||
return metadata_result
|
||||
|
||||
/// Checks whether a given icon state exists in a given icon file. If `file` and `state` both exist,
|
||||
/// this will return `TRUE` - otherwise, it will return `FALSE`.
|
||||
///
|
||||
/// If you want a stack trace to be output when the given state/file doesn't exist, use
|
||||
/// `/proc/icon_exists_or_scream()`.
|
||||
/proc/icon_exists(file, state)
|
||||
var/static/list/icon_states_cache = list()
|
||||
if(isnull(file) || isnull(state))
|
||||
return FALSE //This is common enough that it shouldn't panic, imo.
|
||||
|
||||
if(isnull(icon_states_cache[file]))
|
||||
icon_states_cache[file] = list()
|
||||
var/file_string = "[file]"
|
||||
if(isfile(file) && length(file_string)) // ensure that it's actually a file, and not a runtime icon
|
||||
for(var/istate in json_decode(rustg_dmi_icon_states(file_string)))
|
||||
icon_states_cache[file][istate] = TRUE
|
||||
else // Otherwise, we have to use the slower BYOND proc
|
||||
for(var/istate in icon_states(file))
|
||||
icon_states_cache[file][istate] = TRUE
|
||||
if(isnull(GLOB.icon_states_cache_lookup[file]))
|
||||
compile_icon_states_cache(file)
|
||||
return !isnull(GLOB.icon_states_cache_lookup[file][state])
|
||||
|
||||
return !isnull(icon_states_cache[file][state])
|
||||
/// Cached, rustg-based alternative to icon_states()
|
||||
/proc/icon_states_fast(file)
|
||||
if(isnull(file))
|
||||
return null
|
||||
if(isnull(GLOB.icon_states_cache[file]))
|
||||
compile_icon_states_cache(file)
|
||||
return GLOB.icon_states_cache[file]
|
||||
|
||||
/proc/compile_icon_states_cache(file)
|
||||
GLOB.icon_states_cache[file] = list()
|
||||
GLOB.icon_states_cache_lookup[file] = list()
|
||||
// Try to use rustg first
|
||||
var/list/metadata = icon_metadata(file)
|
||||
if(islist(metadata) && islist(metadata["states"]))
|
||||
for(var/list/state_data as anything in metadata["states"])
|
||||
GLOB.icon_states_cache[file] += state_data["name"]
|
||||
GLOB.icon_states_cache_lookup[file][state_data["name"]] = TRUE
|
||||
else // Otherwise, we have to use the slower BYOND proc
|
||||
for(var/istate in icon_states(file))
|
||||
GLOB.icon_states_cache[file] += istate
|
||||
GLOB.icon_states_cache_lookup[file][istate] = TRUE
|
||||
|
||||
/// Functions the same as `/proc/icon_exists()`, but with the addition of a stack trace if the
|
||||
/// specified file or state doesn't exist.
|
||||
@@ -1238,6 +1271,31 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
|
||||
|
||||
/// Returns a list containing the width and height of an icon file
|
||||
/proc/get_icon_dimensions(icon_path)
|
||||
if(istype(icon_path, /datum/universal_icon))
|
||||
var/datum/universal_icon/u_icon = icon_path
|
||||
icon_path = u_icon.icon_file
|
||||
// Icons can be a real file(), a rsc backed file(), a dynamic rsc (dyn.rsc) reference (known as a cache reference in byond docs), or an /icon which is pointing to one of those.
|
||||
// Runtime generated dynamic icons are an unbounded concept cache identity wise, the same icon can exist millions of ways and holding them in a list as a key can lead to unbounded memory usage if called often by consumers.
|
||||
// Check distinctly that this is something that has this unspecified concept, and thus that we should not cache.
|
||||
if (!istext(icon_path) && (!isfile(icon_path) || !length("[icon_path]")))
|
||||
var/icon/my_icon = icon(icon_path)
|
||||
return list("width" = my_icon.Width(), "height" = my_icon.Height())
|
||||
if (isnull(GLOB.icon_dimensions[icon_path]))
|
||||
// Used cached icon metadata
|
||||
var/list/metadata = icon_metadata(icon_path)
|
||||
var/list/result = null
|
||||
if(islist(metadata) && isnum(metadata["width"]) && isnum(metadata["height"]))
|
||||
result = list("width" = metadata["width"], "height" = metadata["height"])
|
||||
// Otherwise, we have to use the slower BYOND proc
|
||||
else
|
||||
var/icon/my_icon = icon(icon_path)
|
||||
result = list("width" = my_icon.Width(), "height" = my_icon.Height())
|
||||
GLOB.icon_dimensions[icon_path] = result
|
||||
|
||||
return GLOB.icon_dimensions[icon_path]
|
||||
|
||||
/// Returns a list containing the width and height of an icon file, without using rustg for pure function calls
|
||||
/proc/get_icon_dimensions_pure(icon_path)
|
||||
// Icons can be a real file(), a rsc backed file(), a dynamic rsc (dyn.rsc) reference (known as a cache reference in byond docs), or an /icon which is pointing to one of those.
|
||||
// Runtime generated dynamic icons are an unbounded concept cache identity wise, the same icon can exist millions of ways and holding them in a list as a key can lead to unbounded memory usage if called often by consumers.
|
||||
// Check distinctly that this is something that has this unspecified concept, and thus that we should not cache.
|
||||
|
||||
@@ -236,7 +236,7 @@ Turf and target are separate in case you want to teleport some distance from a t
|
||||
var/pixel_y_offset = checked_atom.pixel_y + checked_atom.pixel_z + atom_matrix.get_y_shift()
|
||||
|
||||
//Irregular objects
|
||||
var/list/icon_dimensions = get_icon_dimensions(checked_atom.icon)
|
||||
var/list/icon_dimensions = get_icon_dimensions_pure(checked_atom.icon)
|
||||
var/checked_atom_icon_height = icon_dimensions["height"]
|
||||
var/checked_atom_icon_width = icon_dimensions["width"]
|
||||
if(checked_atom_icon_height != ICON_SIZE_Y || checked_atom_icon_width != ICON_SIZE_X)
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
/// Cache of the width and height of icon files, to avoid repeating the same expensive operation
|
||||
GLOBAL_LIST_EMPTY(icon_dimensions)
|
||||
/// Cache of the states of icon files
|
||||
GLOBAL_LIST_EMPTY(icon_states_cache)
|
||||
/// Cache of the states of icon files, stored associatively with TRUE for lookup
|
||||
GLOBAL_LIST_EMPTY(icon_states_cache_lookup)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/// Disable to use builtin DM-based generation.
|
||||
/// IconForge is 250x times faster but requires storing the icons in tmp/ and may result in higher asset transport.
|
||||
/// Note that the builtin GAGS editor still uses the 'legacy' generation to allow for debugging.
|
||||
/// IconForge also does not support the color matrix layer type or the 'or' blend_mode, however both are currently unused.
|
||||
#define USE_RUSTG_ICONFORGE_GAGS
|
||||
|
||||
PROCESSING_SUBSYSTEM_DEF(greyscale)
|
||||
|
||||
@@ -189,10 +189,10 @@
|
||||
var/data_out
|
||||
if(yield || !isnull(job_id))
|
||||
if(isnull(job_id))
|
||||
job_id = rustg_iconforge_generate_async("data/spritesheets/", name, entries_json, do_cache)
|
||||
job_id = rustg_iconforge_generate_async("data/spritesheets/", name, entries_json, do_cache, FALSE, TRUE)
|
||||
UNTIL((data_out = rustg_iconforge_check(job_id)) != RUSTG_JOB_NO_RESULTS_YET)
|
||||
else
|
||||
data_out = rustg_iconforge_generate("data/spritesheets/", name, entries_json, do_cache)
|
||||
data_out = rustg_iconforge_generate("data/spritesheets/", name, entries_json, do_cache, FALSE, TRUE)
|
||||
if (data_out == RUSTG_JOB_ERROR)
|
||||
CRASH("Spritesheet [name] JOB PANIC")
|
||||
else if(!findtext(data_out, "{", 1, 2))
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
var/datum/icon_transformer/transform
|
||||
|
||||
/// Don't instantiate these yourself, use uni_icon.
|
||||
/datum/universal_icon/New(icon/icon_file, icon_state="", dir=SOUTH, frame=1, datum/icon_transformer/transform=null, color=null)
|
||||
/datum/universal_icon/New(icon/icon_file, icon_state="", dir=null, frame=null, datum/icon_transformer/transform=null, color=null)
|
||||
#ifdef UNIT_TESTS
|
||||
// This check is kinda slow and shouldn't fail unless a developer makes a mistake. So it'll get caught in unit tests.
|
||||
if(!isicon(icon_file) || !isfile(icon_file) || "[icon_file]" == "/icon" || !length("[icon_file]"))
|
||||
@@ -44,10 +44,10 @@
|
||||
transform.blend_color(color, blend_mode)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/blend_icon(datum/universal_icon/icon_object, blend_mode)
|
||||
/datum/universal_icon/proc/blend_icon(datum/universal_icon/icon_object, blend_mode, x=1, y=1)
|
||||
if(!transform)
|
||||
transform = new
|
||||
transform.blend_icon(icon_object, blend_mode)
|
||||
transform.blend_icon(icon_object, blend_mode, x, y)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/scale(width, height)
|
||||
@@ -62,14 +62,116 @@
|
||||
transform.crop(x1, y1, x2, y2)
|
||||
return src
|
||||
|
||||
/// Internally performs a crop.
|
||||
/datum/universal_icon/proc/shift(dir, amount, icon_width, icon_height)
|
||||
/datum/universal_icon/proc/flip(dir)
|
||||
if(!transform)
|
||||
transform = new
|
||||
var/list/offsets = dir2offset(dir)
|
||||
var/shift_x = -offsets[1] * amount
|
||||
var/shift_y = -offsets[2] * amount
|
||||
transform.crop(1 + shift_x, 1 + shift_y, icon_width + shift_x, icon_height + shift_y)
|
||||
transform.flip(dir)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/rotate(angle)
|
||||
if(!transform)
|
||||
transform = new
|
||||
transform.rotate(angle)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/shift(dir, offset, wrap=0)
|
||||
if(!transform)
|
||||
transform = new
|
||||
transform.shift(dir, offset, wrap)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/swap_color(src_color, dst_color)
|
||||
if(!transform)
|
||||
transform = new
|
||||
transform.swap_color(src_color, dst_color)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/draw_box(color, x1, y1, x2=x1, y2=y1)
|
||||
if(!transform)
|
||||
transform = new
|
||||
transform.draw_box(color, x1, y1, x2, y2)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/map_colors_inferred(list/color_args)
|
||||
var/num_args = length(color_args)
|
||||
if(num_args <= 20 || num_args >= 16)
|
||||
src.map_colors_rgba(arglist(color_args))
|
||||
else if(num_args <= 12 || num_args >= 9)
|
||||
src.map_colors_rgb(arglist(color_args))
|
||||
else if(num_args == 5)
|
||||
src.map_colors_rgba_hex(arglist(color_args))
|
||||
else if(num_args == 4)
|
||||
// is there alpha in the hex?
|
||||
if(length(color_args[3]) == 7 || length(color_args[3]) == 4)
|
||||
src.map_colors_rgb_hex(arglist(color_args))
|
||||
else
|
||||
src.map_colors_rgba_hex(arglist(color_args))
|
||||
else if(num_args == 3)
|
||||
src.map_colors_rgb_hex(arglist(color_args))
|
||||
|
||||
/datum/universal_icon/proc/map_colors_rgba(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, r0=0, g0=0, b0=0, a0=0)
|
||||
if(!transform)
|
||||
transform = new
|
||||
transform.map_colors(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, r0, g0, b0, a0)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/map_colors_rgb(rr, rg, rb, gr, gg, gb, br, bg, bb, r0=0, g0=0, b0=0)
|
||||
if(!transform)
|
||||
transform = new
|
||||
transform.map_colors(rr, rg, rb, 0, gr, gg, gb, 0, br, bg, bb, 0, 0, 0, 0, 1, r0, g0, b0, 0)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/map_colors_rgb_hex(r_rgb, g_rgb, b_rgb, rgb0=rgb(0,0,0))
|
||||
if(!transform)
|
||||
transform = new
|
||||
var/rr = hex2num(copytext(r_rgb, 2, 4)) / 255
|
||||
var/rg = hex2num(copytext(r_rgb, 4, 6)) / 255
|
||||
var/rb = hex2num(copytext(r_rgb, 6, 8)) / 255
|
||||
|
||||
var/gr = hex2num(copytext(g_rgb, 2, 4)) / 255
|
||||
var/gg = hex2num(copytext(g_rgb, 4, 6)) / 255
|
||||
var/gb = hex2num(copytext(g_rgb, 6, 8)) / 255
|
||||
|
||||
var/br = hex2num(copytext(b_rgb, 2, 4)) / 255
|
||||
var/bg = hex2num(copytext(b_rgb, 4, 6)) / 255
|
||||
var/bb = hex2num(copytext(b_rgb, 6, 8)) / 255
|
||||
|
||||
var/r0 = hex2num(copytext(rgb0, 2, 4)) / 255
|
||||
var/b0 = hex2num(copytext(rgb0, 4, 6)) / 255
|
||||
var/g0 = hex2num(copytext(rgb0, 6, 8)) / 255
|
||||
|
||||
transform.map_colors(rr, rg, rb, 0, gr, gg, gb, 0, br, bg, bb, 0, 0, 0, 0, 1, r0, b0, g0, 0)
|
||||
return src
|
||||
|
||||
/datum/universal_icon/proc/map_colors_rgba_hex(r_rgba, g_rgba, b_rgba, a_rgba, rgba0="#00000000")
|
||||
if(!transform)
|
||||
transform = new
|
||||
var/rr = hex2num(copytext(r_rgba, 2, 4)) / 255
|
||||
var/rg = hex2num(copytext(r_rgba, 4, 6)) / 255
|
||||
var/rb = hex2num(copytext(r_rgba, 6, 8)) / 255
|
||||
var/ra = hex2num(copytext(r_rgba, 8, 10)) / 255
|
||||
|
||||
var/gr = hex2num(copytext(g_rgba, 2, 4)) / 255
|
||||
var/gg = hex2num(copytext(g_rgba, 4, 6)) / 255
|
||||
var/gb = hex2num(copytext(g_rgba, 6, 8)) / 255
|
||||
var/ga = hex2num(copytext(g_rgba, 8, 10)) / 255
|
||||
|
||||
var/br = hex2num(copytext(b_rgba, 2, 4)) / 255
|
||||
var/bg = hex2num(copytext(b_rgba, 4, 6)) / 255
|
||||
var/bb = hex2num(copytext(b_rgba, 6, 8)) / 255
|
||||
var/ba = hex2num(copytext(b_rgba, 8, 10)) / 255
|
||||
|
||||
var/ar = hex2num(copytext(a_rgba, 2, 4)) / 255
|
||||
var/ag = hex2num(copytext(a_rgba, 4, 6)) / 255
|
||||
var/ab = hex2num(copytext(a_rgba, 6, 8)) / 255
|
||||
var/aa = hex2num(copytext(a_rgba, 8, 10)) / 255
|
||||
|
||||
var/r0 = hex2num(copytext(rgba0, 2, 4)) / 255
|
||||
var/b0 = hex2num(copytext(rgba0, 4, 6)) / 255
|
||||
var/g0 = hex2num(copytext(rgba0, 6, 8)) / 255
|
||||
var/a0 = hex2num(copytext(rgba0, 8, 10)) / 255
|
||||
|
||||
transform.map_colors(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, r0, b0, g0, a0)
|
||||
return src
|
||||
|
||||
/// Internally performs a color blend.
|
||||
@@ -118,11 +220,29 @@
|
||||
if(!istype(icon_object))
|
||||
stack_trace("Invalid icon found in icon transformer during apply()! [icon_object]")
|
||||
continue
|
||||
target.Blend(icon_object.to_icon(), transform["blend_mode"])
|
||||
target.Blend(icon_object.to_icon(), transform["blend_mode"], transform["x"], transform["y"])
|
||||
if(RUSTG_ICONFORGE_SCALE)
|
||||
target.Scale(transform["width"], transform["height"])
|
||||
if(RUSTG_ICONFORGE_CROP)
|
||||
target.Crop(transform["x1"], transform["y1"], transform["x2"], transform["y2"])
|
||||
if(RUSTG_ICONFORGE_MAP_COLORS)
|
||||
target.MapColors(
|
||||
transform["rr"], transform["rg"], transform["rb"], transform["ra"],
|
||||
transform["gr"], transform["gg"], transform["gb"], transform["ga"],
|
||||
transform["br"], transform["bg"], transform["bb"], transform["ba"],
|
||||
transform["ar"], transform["ag"], transform["ab"], transform["aa"],
|
||||
transform["r0"], transform["g0"], transform["b0"], transform["a0"],
|
||||
)
|
||||
if(RUSTG_ICONFORGE_FLIP)
|
||||
target.Flip(transform["dir"])
|
||||
if(RUSTG_ICONFORGE_TURN)
|
||||
target.Turn(transform["angle"])
|
||||
if(RUSTG_ICONFORGE_SHIFT)
|
||||
target.Shift(transform["dir"], transform["offset"], transform["wrap"])
|
||||
if(RUSTG_ICONFORGE_SWAP_COLOR)
|
||||
target.SwapColor(transform["src_color"], transform["dst_color"])
|
||||
if(RUSTG_ICONFORGE_DRAW_BOX)
|
||||
target.DrawBox(transform["color"], transform["x1"], transform["y1"], transform["x2"], transform["y2"])
|
||||
return target
|
||||
|
||||
/datum/icon_transformer/proc/copy()
|
||||
@@ -142,13 +262,17 @@
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_BLEND_COLOR, "color" = color, "blend_mode" = blend_mode))
|
||||
|
||||
/datum/icon_transformer/proc/blend_icon(datum/universal_icon/icon_object, blend_mode)
|
||||
/datum/icon_transformer/proc/blend_icon(datum/universal_icon/icon_object, blend_mode, x=1, y=1)
|
||||
#ifdef UNIT_TESTS
|
||||
// icon_object's type is checked later in to_list
|
||||
if(!isnum(blend_mode))
|
||||
CRASH("Invalid blend_mode provided to blend_icon: [blend_mode]")
|
||||
if(!isnum(x))
|
||||
CRASH("Invalid x offset provided to blend_icon: [x]")
|
||||
if(!isnum(y))
|
||||
CRASH("Invalid y offset provided to blend_icon: [y]")
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_BLEND_ICON, "icon" = icon_object, "blend_mode" = blend_mode))
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_BLEND_ICON, "icon" = icon_object, "blend_mode" = blend_mode, "x" = x, "y" = y))
|
||||
|
||||
/datum/icon_transformer/proc/scale(width, height)
|
||||
#ifdef UNIT_TESTS
|
||||
@@ -164,6 +288,51 @@
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_CROP, "x1" = x1, "y1" = y1, "x2" = x2, "y2" = y2))
|
||||
|
||||
/datum/icon_transformer/proc/flip(dir)
|
||||
#ifdef UNIT_TESTS
|
||||
if(!isnum(dir))
|
||||
CRASH("Invalid arguments provided to flip: [dir]")
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_FLIP, "dir" = dir))
|
||||
|
||||
/datum/icon_transformer/proc/rotate(angle)
|
||||
#ifdef UNIT_TESTS
|
||||
if(!isnum(angle))
|
||||
CRASH("Invalid arguments provided to rotate: [angle]")
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_TURN, "angle" = angle))
|
||||
|
||||
/datum/icon_transformer/proc/shift(dir, offset, wrap=FALSE)
|
||||
#ifdef UNIT_TESTS
|
||||
if(!isnum(dir) || !isnum(offset) || (wrap != FALSE && wrap != TRUE))
|
||||
CRASH("Invalid arguments provided to shift: [dir],[offset],[wrap]")
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_SHIFT, "dir" = dir, "offset" = offset, "wrap" = wrap))
|
||||
|
||||
/datum/icon_transformer/proc/swap_color(src_color, dst_color)
|
||||
#ifdef UNIT_TESTS
|
||||
if(!istext(src_color) || !istext(dst_color))
|
||||
CRASH("Invalid arguments provided to swap_color: [src_color],[dst_color]")
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_SWAP_COLOR, "src_color" = src_color, "dst_color" = dst_color))
|
||||
|
||||
/datum/icon_transformer/proc/draw_box(color, x1, y1, x2=x1, y2=y1)
|
||||
#ifdef UNIT_TESTS
|
||||
if(!istext(color) || !isnum(x1) || !isnum(y1) || !isnum(x2) || !isnum(y2))
|
||||
CRASH("Invalid arguments provided to draw_box: [color],[x1],[y1],[x2],[y2]")
|
||||
#endif
|
||||
transforms += list(list("type" = RUSTG_ICONFORGE_DRAW_BOX, "color" = color, "x1" = x1, "y1" = y1, "x2" = x2, "y2" = y2))
|
||||
|
||||
/datum/icon_transformer/proc/map_colors(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, r0=0, g0=0, b0=0, a0=0)
|
||||
transforms += list(list(
|
||||
"type" = RUSTG_ICONFORGE_MAP_COLORS,
|
||||
"rr" = rr, "rg" = rg, "rb" = rb, "ra" = ra,
|
||||
"gr" = gr, "gg" = gg, "gb" = gb, "ga" = ga,
|
||||
"br" = br, "bg" = bg, "bb" = bb, "ba" = ba,
|
||||
"ar" = ar, "ag" = ag, "ab" = ab, "aa" = aa,
|
||||
"r0" = r0, "g0" = g0, "b0" = b0, "a0" = a0,
|
||||
))
|
||||
|
||||
/// Recursively converts all contained [/datum/universal_icon]s and their associated [/datum/icon_transformer]s into list form so the transforms can be JSON encoded.
|
||||
/datum/icon_transformer/proc/to_list()
|
||||
RETURN_TYPE(/list)
|
||||
@@ -218,21 +387,19 @@
|
||||
/proc/get_display_icon_for(atom/atom_path)
|
||||
if (!ispath(atom_path, /atom))
|
||||
return FALSE
|
||||
var/icon_file = initial(atom_path.icon)
|
||||
var/icon_state = initial(atom_path.icon_state)
|
||||
if(initial(atom_path.greyscale_config) && initial(atom_path.greyscale_colors))
|
||||
var/icon_file = atom_path::icon
|
||||
var/icon_state = atom_path::icon_state
|
||||
if(atom_path::greyscale_config && atom_path::greyscale_colors)
|
||||
return gags_to_universal_icon(atom_path)
|
||||
if(ispath(atom_path, /obj))
|
||||
var/obj/obj_path = atom_path
|
||||
if(initial(obj_path.icon_state_preview))
|
||||
icon_state = initial(obj_path.icon_state_preview)
|
||||
return uni_icon(icon_file, icon_state, color=initial(atom_path.color))
|
||||
if(obj_path::icon_state_preview)
|
||||
icon_state = obj_path::icon_state_preview
|
||||
return uni_icon(icon_file, icon_state, color=atom_path::color)
|
||||
|
||||
/// getFlatIcon for [/datum/universal_icon]s
|
||||
/// Only supports 32x32 icons facing south
|
||||
/// Tough luck if you want anything else
|
||||
/// Still fairly slow for complex appearances due to filesystem operations. Try to avoid using it
|
||||
/proc/get_flat_uni_icon(image/appearance, deficon, defstate, defblend, start = TRUE, parentcolor)
|
||||
/proc/get_flat_uni_icon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, parentcolor)
|
||||
// Loop through the underlays, then overlays, sorting them into the layers list
|
||||
#define PROCESS_OVERLAYS_OR_UNDERLAYS(flat, process, base_layer) \
|
||||
for (var/i in 1 to process.len) { \
|
||||
@@ -283,7 +450,7 @@
|
||||
var/curstate = appearance.icon_state || defstate
|
||||
// Filter out 'runtime' icons (server-generated RSC cache icons)
|
||||
// Write the icon to the filesystem so it can be used by iconforge
|
||||
if(!isfile(curicon) || string_curicon == "/icon" || string_curicon == "/image" || !length(string_curicon))
|
||||
if(!isfile(curicon) || !length(string_curicon))
|
||||
var/file_path_tmp = "tmp/uni_icon-tmp-[rand(1, 999)].dmi" // this filename is temporary.
|
||||
fcopy(curicon, file_path_tmp)
|
||||
var/file_hash = rustg_hash_file(RUSTG_HASH_MD5, file_path_tmp)
|
||||
@@ -293,21 +460,51 @@
|
||||
fdel(file_path_tmp) // delete the old one
|
||||
curicon = file(file_path)
|
||||
|
||||
var/curblend = appearance.blend_mode || defblend
|
||||
var/list/curstates = icon_states(curicon)
|
||||
if(!(curstate in curstates))
|
||||
if("" in curstates) // BYOND defaulting functionality
|
||||
if(!icon_exists(curicon, curstate))
|
||||
if("" in icon_states_fast(curicon)) // BYOND defaulting functionality
|
||||
curstate = ""
|
||||
else
|
||||
should_display = FALSE
|
||||
|
||||
var/curdir = (!appearance.dir || appearance.dir == SOUTH) ? defdir : appearance.dir
|
||||
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
|
||||
|
||||
if(should_display)
|
||||
//Determines if there're directionals.
|
||||
if (curdir != SOUTH)
|
||||
// icon states either have 1, 4 or 8 dirs. We only have to check
|
||||
// one of NORTH, EAST or WEST to know that this isn't a 1-dir icon_state since they just have SOUTH.
|
||||
var/list/metadata = icon_metadata(curicon)
|
||||
if(islist(metadata))
|
||||
for(var/list/state_data as anything in metadata["states"])
|
||||
var/name = state_data["name"]
|
||||
if(name != curstate)
|
||||
continue
|
||||
var/dir_count = state_data["dirs"]
|
||||
if(dir_count == 1)
|
||||
base_icon_dir = SOUTH
|
||||
else if(!length(icon_states(icon(curicon, curstate, NORTH))))
|
||||
base_icon_dir = SOUTH
|
||||
|
||||
var/list/icon_dimensions = get_icon_dimensions(curicon)
|
||||
var/icon_width = icon_dimensions["width"]
|
||||
var/icon_height = icon_dimensions["height"]
|
||||
if(icon_width != 32 || icon_height != 32)
|
||||
flat.scale(icon_width, icon_height)
|
||||
|
||||
if(!base_icon_dir)
|
||||
base_icon_dir = curdir
|
||||
|
||||
var/curblend = appearance.blend_mode || defblend
|
||||
|
||||
|
||||
if(appearance.overlays.len || appearance.underlays.len)
|
||||
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
|
||||
var/list/layers = list()
|
||||
var/image/copy
|
||||
if(should_display)
|
||||
// Add the atom's icon itself, without pixel_x/y offsets.
|
||||
copy = image(icon=curicon, icon_state=curstate, layer=appearance.layer, dir=SOUTH)
|
||||
copy = image(icon=curicon, icon_state=curstate, layer=appearance.layer, dir=base_icon_dir)
|
||||
copy.color = appearance.color
|
||||
copy.alpha = appearance.alpha
|
||||
copy.blend_mode = curblend
|
||||
@@ -318,15 +515,26 @@
|
||||
|
||||
var/datum/universal_icon/add // Icon of overlay being added
|
||||
|
||||
var/list/flat_dimensions = get_icon_dimensions(flat)
|
||||
var/flatX1 = 1
|
||||
var/flatX2 = flat_dimensions["width"]
|
||||
var/flatY1 = 1
|
||||
var/flatY2 = flat_dimensions["height"]
|
||||
|
||||
var/addX1 = 0
|
||||
var/addX2 = 0
|
||||
var/addY1 = 0
|
||||
var/addY2 = 0
|
||||
|
||||
if(appearance.color)
|
||||
if(islist(appearance.color))
|
||||
stack_trace("Unsupported color map appearance provided to get_flat_uni_icon, ignoring it.")
|
||||
flat.map_colors_inferred(appearance.color)
|
||||
else
|
||||
flat.blend_color(appearance.color, ICON_MULTIPLY)
|
||||
|
||||
if(parentcolor && !(appearance.appearance_flags & RESET_COLOR))
|
||||
if(islist(parentcolor))
|
||||
stack_trace("Unsupported color map appearance provided to get_flat_uni_icon, ignoring it.")
|
||||
flat.map_colors_inferred(parentcolor)
|
||||
else
|
||||
flat.blend_color(parentcolor, ICON_MULTIPLY)
|
||||
|
||||
@@ -338,19 +546,45 @@
|
||||
|
||||
if(layer_image == copy && length("[layer_image.icon]")) // 'layer_image' is an /image based on the object being flattened, and isn't a 'runtime' icon.
|
||||
curblend = BLEND_OVERLAY
|
||||
add = uni_icon(layer_image.icon, layer_image.icon_state, SOUTH)
|
||||
add = uni_icon(layer_image.icon, layer_image.icon_state, base_icon_dir)
|
||||
if(appearance.color)
|
||||
if(islist(appearance.color))
|
||||
stack_trace("Unsupported color map appearance provided to get_flat_uni_icon, ignoring it.")
|
||||
add.map_colors_inferred(appearance.color)
|
||||
else
|
||||
add.blend_color(appearance.color, ICON_MULTIPLY)
|
||||
else // 'layer_image' is an appearance object.
|
||||
add = get_flat_uni_icon(layer_image, curicon, curstate, curblend, FALSE, next_parentcolor)
|
||||
add = get_flat_uni_icon(layer_image, curdir, curicon, curstate, curblend, FALSE, next_parentcolor)
|
||||
if(!add || !length(add.icon_file))
|
||||
continue
|
||||
|
||||
// Find the new dimensions of the flat icon to fit the added overlay
|
||||
var/list/add_dimensions = get_icon_dimensions(add)
|
||||
addX1 = min(flatX1, layer_image.pixel_x + layer_image.pixel_w + 1)
|
||||
addX2 = max(flatX2, layer_image.pixel_x + layer_image.pixel_w + add_dimensions["width"]) // assuming 32x32
|
||||
addY1 = min(flatY1, layer_image.pixel_y + layer_image.pixel_z + 1)
|
||||
addY2 = max(flatY2, layer_image.pixel_y + layer_image.pixel_z + add_dimensions["height"])
|
||||
|
||||
if (
|
||||
addX1 != flatX1 \
|
||||
&& addX2 != flatX2 \
|
||||
&& addY1 != flatY1 \
|
||||
&& addY2 != flatY2 \
|
||||
)
|
||||
// Resize the flattened icon so the new icon fits
|
||||
flat.crop(
|
||||
addX1 - flatX1 + 1,
|
||||
addY1 - flatY1 + 1,
|
||||
addX2 - flatX1 + 1,
|
||||
addY2 - flatY1 + 1
|
||||
)
|
||||
|
||||
flatX1 = addX1
|
||||
flatX2 = addY1
|
||||
flatY1 = addX2
|
||||
flatY2 = addY2
|
||||
|
||||
// Blend the overlay into the flattened icon
|
||||
flat.blend_icon(add, blendMode2iconMode(curblend))
|
||||
flat.blend_icon(add, blendMode2iconMode(curblend), layer_image.pixel_x + layer_image.pixel_w + 2 - flatX1, layer_image.pixel_y + layer_image.pixel_z + 2 - flatY1)
|
||||
|
||||
if(appearance.alpha < 255)
|
||||
flat.blend_color(rgb(255, 255, 255, appearance.alpha), ICON_MULTIPLY)
|
||||
@@ -358,14 +592,14 @@
|
||||
return flat
|
||||
|
||||
else if(should_display) // There's no overlays.
|
||||
var/datum/universal_icon/final_icon = uni_icon(curicon, curstate, SOUTH)
|
||||
var/datum/universal_icon/final_icon = uni_icon(curicon, curstate, base_icon_dir)
|
||||
|
||||
if (appearance.alpha < 255)
|
||||
final_icon.blend_color(rgb(255,255,255, appearance.alpha), ICON_MULTIPLY)
|
||||
|
||||
if (appearance.color)
|
||||
if (islist(appearance.color))
|
||||
stack_trace("Unsupported color map appearance provided to get_flat_uni_icon, ignoring it.")
|
||||
final_icon.map_colors_inferred(appearance.color)
|
||||
else
|
||||
final_icon.blend_color(appearance.color, ICON_MULTIPLY)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
var/datum/universal_icon/head_accessory_icon = uni_icon(sprite_accessory.icon, sprite_accessory.icon_state)
|
||||
if(y_offset)
|
||||
head_accessory_icon.shift(NORTH, y_offset, ICON_SIZE_X, ICON_SIZE_Y)
|
||||
head_accessory_icon.shift(NORTH, y_offset)
|
||||
head_accessory_icon.blend_color(COLOR_DARK_BROWN, ICON_MULTIPLY)
|
||||
final_icon.blend_icon(head_accessory_icon, ICON_OVERLAY)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export BYOND_MAJOR=516
|
||||
export BYOND_MINOR=1659
|
||||
|
||||
#rust_g git tag
|
||||
export RUST_G_VERSION=3.9.0
|
||||
export RUST_G_VERSION=4.0.0
|
||||
|
||||
# node version
|
||||
export NODE_VERSION_LTS=22.11.0
|
||||
|
||||
|
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 1016 B |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |