Adds icon state configuration to GAS (#58487)

Relatively simple change that allows you to specify icon states in greyscale config so as to allow generating icon files with multiple states. This is a requisite for GAS to work with icon smoothing.

Also a couple bits of additional warnings and documentation.
This commit is contained in:
Emmett Gaines
2021-04-18 11:34:10 +12:00
committed by GitHub
parent 3c8f0aa50f
commit b630f46019
12 changed files with 56 additions and 41 deletions
+4 -2
View File
@@ -29,7 +29,7 @@ The json is made up of some metadata and a list of layers used while creating th
```json
{
"layers": [
"icon_state_name": [
{
"type": "reference",
"reference_type": "/datum/greyscale_config/some_other_config",
@@ -61,6 +61,8 @@ The last layer is another reference type. Note that you don't need to give color
"blend_mode" and "color_ids" are special, all layer types have them. The blend mode is what controls how that layer's finished product gets merged together with the rest of the sprite. The color ids control what colors are passed in to the layer.
Once it is done generating it will be placed in an icon file with the icon state of "icon_state_name". You can use any name you like here.
## Dmi File
There are no special requirements from the dmi file to work with this system. You just need to specify the icon file in code and the icon_state in the json configuration.
@@ -88,4 +90,4 @@ More configurations can be found in [code/datums/greyscale/greyscale_configs.dm]
## Debugging
If you're making a new greyscale sprite you sometimes want to be able to see how layers got generated or maybe you're just tweaking some colors. Rather than rebooting the server with every change there is a greyscale modification menu that can be found in the vv dropdown menu for the greyscale object. Here you can change colors and preview the results.
If you're making a new greyscale sprite you sometimes want to be able to see how layers got generated or maybe you're just tweaking some colors. Rather than rebooting the server with every change there is a greyscale modification menu that can be found in the vv dropdown menu for the greyscale object. Here you can change colors, preview the results, and reload everything from their files.
+37 -23
View File
@@ -14,8 +14,8 @@
/// String path to the icon file, used for reloading
var/string_icon_file
/// Layer objects that the sprite is made up of
var/list/layers
/// A list of icon states and their layers
var/list/icon_states
/// How many colors are expected to be given when building the sprite
var/expected_colors = 0
@@ -40,10 +40,13 @@
json_config = file(string_json_config)
icon_file = file(string_icon_file)
icon_states = list()
var/list/raw = json_decode(file2text(json_config))
layers = ReadLayersFromJson(raw["layers"])
if(!length(layers))
CRASH("The json configuration [DebugName()] is missing any layers.")
ReadIconStateConfiguration(raw)
if(!length(icon_states))
CRASH("The json configuration [DebugName()] is missing any icon_states.")
icon_cache = list()
@@ -53,6 +56,17 @@
/datum/greyscale_config/proc/DebugName()
return "([icon_file]|[json_config])"
/// Takes the json icon state configuration and puts it into a more processed format
/datum/greyscale_config/proc/ReadIconStateConfiguration(list/data)
for(var/state in data)
var/list/raw_layers = data[state]
if(!length(raw_layers))
stack_trace("The json configuration [DebugName()] for icon state '[state]' is missing any layers.")
continue
if(icon_states[state])
stack_trace("The json configuration [DebugName()] has a duplicate icon state '[state]' and is being overriden.")
icon_states[state] = ReadLayersFromJson(raw_layers)
/// Takes the json layers configuration and puts it into a more processed format
/datum/greyscale_config/proc/ReadLayersFromJson(list/data)
var/list/output = ReadLayerGroup(data)
@@ -74,7 +88,9 @@
/// Reads layer configurations to take out some useful overall information
/datum/greyscale_config/proc/ReadMetadata()
var/list/datum/greyscale_layer/all_layers = list()
var/list/to_process = list(layers)
var/list/to_process = list()
for(var/state in icon_states)
to_process += icon_states[state]
while(length(to_process))
var/current = to_process[length(to_process)]
to_process.len--
@@ -91,21 +107,24 @@
expected_colors = length(color_groups)
/// Actually create the icon and color it in, handles caching
/datum/greyscale_config/proc/Generate(color_string)
/datum/greyscale_config/proc/Generate(color_string, list/render_steps)
var/key = color_string
var/icon/new_icon = icon_cache[key]
if(new_icon)
if(new_icon && !render_steps)
return icon(new_icon)
var/list/colors = ParseColorString(color_string)
if(length(colors) != expected_colors)
CRASH("[DebugName()] expected [expected_colors] color arguments but only received [length(colors)]")
new_icon = GenerateLayerGroup(colors, layers)
// We read a pixel to force the icon to be fully generated before we let it loose into the world
// I hate this
new_icon.GetPixel(1, 1)
new_icon = fcopy_rsc(new_icon)
icon_cache[key] = new_icon
return icon(new_icon)
var/icon/icon_bundle = new
for(var/icon_state in icon_states)
var/icon/generated_icon = GenerateLayerGroup(colors, icon_states[icon_state], render_steps)
// We read a pixel to force the icon to be fully generated before we let it loose into the world
// I hate this
generated_icon.GetPixel(1, 1)
icon_bundle.Insert(generated_icon, icon_state)
icon_bundle = fcopy_rsc(icon_bundle)
icon_cache[key] = icon_bundle
return icon(icon_bundle)
/// Internal recursive proc to handle nested layer groups
/datum/greyscale_config/proc/GenerateLayerGroup(list/colors, list/group, list/render_steps)
@@ -125,20 +144,15 @@
// These are so we can see the result of every step of the process in the preview ui
if(render_steps)
var/icon/new_icon_copy = new(new_icon)
var/icon/layer_icon_copy = new(layer_icon)
render_steps[layer_icon_copy] = icon(new_icon_copy)
render_steps[image(new_icon)] = image(layer_icon)
return new_icon
/datum/greyscale_config/proc/GenerateDebug(list/colors)
if(length(colors) != expected_colors)
CRASH("[DebugName()] expected [expected_colors] color arguments but only received [length(colors)]")
/datum/greyscale_config/proc/GenerateDebug(colors)
var/list/output = list()
var/list/debug_steps = list()
output["steps"] = debug_steps
output["icon"] = GenerateLayerGroup(colors, layers, debug_steps)
output["icon"] = Generate(colors, debug_steps)
return output
/datum/greyscale_config/proc/ParseColorString(color_string)
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "icon_state",
"icon_state": "base",
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "reference",
"reference_type": "/datum/greyscale_config/canister/base",
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "reference",
"reference_type": "/datum/greyscale_config/canister/base",
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "reference",
"reference_type": "/datum/greyscale_config/canister/base",
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "icon_state",
"icon_state": "outline",
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "icon_state",
"icon_state": "can_base",
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "reference",
"reference_type": "/datum/greyscale_config/canister/base",
@@ -1,5 +1,5 @@
{
"layers": [
"": [
{
"type": "icon_state",
"icon_state": "cutters",
+5 -7
View File
@@ -14,6 +14,9 @@
/datum/greyscale_layer/New(icon_file, list/json_data)
color_ids = json_data["color_ids"]
for(var/i in color_ids)
if(!isnum(i))
CRASH("Color ids must be a positive integer starting from 1, '[i]' is not valid. Make sure it is not quoted in the json configuration.")
blend_mode = blend_modes[lowertext(json_data["blend_mode"])]
if(isnull(blend_mode))
CRASH("Greyscale config for [icon_file] is missing a blend mode on a layer.")
@@ -26,7 +29,7 @@
processed_colors += colors[i]
return InternalGenerate(processed_colors, render_steps)
/datum/greyscale_layer/proc/InternalGenerate(list/colors)
/datum/greyscale_layer/proc/InternalGenerate(list/colors, list/render_steps)
////////////////////////////////////////////////////////
// Subtypes
@@ -66,9 +69,4 @@
CRASH("An unknown greyscale configuration was given to a reference layer: [json_data["reference_type"]]")
/datum/greyscale_layer/reference/InternalGenerate(list/colors, list/render_steps)
if(render_steps)
// We're debugging
var/list/debug_data = reference_config.GenerateDebug(colors)
render_steps += debug_data["steps"]
return debug_data["icon"]
return reference_config.Generate(colors.Join())
return reference_config.Generate(colors.Join(), render_steps)
+2 -1
View File
@@ -83,10 +83,11 @@
split_colors += "#[raw_colors[i]]"
/datum/greyscale_modify_menu/proc/refresh_preview()
var/list/data = SSgreyscale.configurations["[target.greyscale_config]"].GenerateDebug(split_colors)
var/list/data = SSgreyscale.configurations["[target.greyscale_config]"].GenerateDebug(split_colors.Join())
sprite_data = list()
var/list/steps = list()
sprite_data["steps"] = steps
for(var/step in data["steps"])
CHECK_TICK
steps += list(list("layer"=icon2html(data["steps"][step], user, sourceonly=TRUE), "result"=icon2html(step, user, sourceonly=TRUE)))