diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index ed6205e25d8..9350ce35f37 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -203,3 +203,8 @@
#ifdef MAP_TEST
#warn Compiling in MAP_TEST mode. Certain game mechanics will be disabled.
#endif
+
+/// 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.
+#define USE_RUSTG_ICONFORGE_GAGS
diff --git a/code/controllers/subsystem/greyscale_previews.dm b/code/controllers/subsystem/greyscale_previews.dm
index d74f16ff7a3..9a63cb6b2de 100644
--- a/code/controllers/subsystem/greyscale_previews.dm
+++ b/code/controllers/subsystem/greyscale_previews.dm
@@ -1,8 +1,3 @@
-/// If we are in unit tests OR if we are not using iconforge, then we should make sure the icons we are using are valid.
-#if !defined(USE_RUSTG_ICONFORGE_GAGS) || defined(UNIT_TESTS)
- #define CHECK_SPRITESHEET_ICON_VALIDITY
-#endif
-
SUBSYSTEM_DEF(greyscale_previews)
name = "Greyscale Previews"
flags = SS_NO_FIRE
@@ -135,6 +130,11 @@ SUBSYSTEM_DEF(greyscale_previews)
/datum/controller/subsystem/greyscale_previews/proc/ExportMapPreviewsForType(filename, list/entries)
var/list/icons = list()
+ #ifdef USE_RUSTG_ICONFORGE_GAGS
+ // make sure the error icon exists as a default
+ var/datum/universal_icon/empty_icon = uni_icon('icons/testing/greyscale_error.dmi', "")
+ icons[""] = empty_icon.to_list()
+ #endif
for (var/entry in entries)
var/atom/typepath
@@ -162,25 +162,46 @@ SUBSYSTEM_DEF(greyscale_previews)
// This is what the actual icon state will be in the map_icon .dmi
var/key = reskin_icon_state ? "[typepath]--[icon_state]" : "[typepath]"
- #ifdef CHECK_SPRITESHEET_ICON_VALIDITY
+ #ifdef USE_RUSTG_ICONFORGE_GAGS
+ // No large icons, use icon_preview and icon_preview_state instead.
+ var/datum/universal_icon/map_icon = SSgreyscale.GetColoredIconByTypeUniversalIcon(greyscale_config, greyscale_colors, icon_state)
+ icons[key] = map_icon.to_list()
+ #else
var/icon/map_icon = icon(SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)) // No large icons, use icon_preview and icon_preview_state instead.
+ #ifdef UNIT_TESTS // this is much slower using legacy generation, so limit it to unit tests.
if (map_icon.Width() > 32 || map_icon.Height() > 32)
stack_trace("GAGS configuration is trying to generate a map preview graphic for '[typepath]' (icon state: [icon_state]), which has a large icon. This is not suppoorted; implement icon_preview instead.")
continue
if (!(icon_state in map_icon.IconStates()))
stack_trace("GAGS configuration missing icon state ([icon_state]) needed to generate map preview graphic for '[typepath]'. Make sure the right greyscale_config is set up.")
continue
+ #endif
map_icon = icon(map_icon, icon_state)
icons[key] = map_icon
- #else // will be updated to use iconforge's new .dmi spritesheet generation instead
- var/icon/map_icon = icon(SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors))
- map_icon = icon(map_icon, icon_state)
- icons[map_icon_key] = map_icon
#endif
+ #ifdef USE_RUSTG_ICONFORGE_GAGS
+ // generate all previews in parallel
+ var/holder = "tmp/greyscale_previews.dmi"
+ var/list/headless_result = rustg_iconforge_generate_headless(holder, json_encode(icons), FALSE)
+ if(!istype(headless_result))
+ stack_trace("Could not generate greyscale previews using rustg_iconforge_generate_headless! The output format was invalid (JSON did not decode to a list). Got: [headless_result]")
+ if(!length(headless_result))
+ stack_trace("Could not generate greyscale previews using rustg_iconforge_generate_headless! The output list was empty. Got: [json_encode(headless_result)]")
+ if(!isnull(headless_result["error"]))
+ stack_trace("Could not generate greyscale previews using rustg_iconforge_generate_headless! There was an error: [headless_result["error"]]")
+ if(headless_result["file_path"] != holder)
+ stack_trace("Could not generate greyscale previews using rustg_iconforge_generate_headless! The output file_path differed from the input. Got: [json_encode(headless_result)]")
+ // testing size is cheap with rustg, so no need to limit to unit tests.
+ if(headless_result["width"] != 32 || headless_result["height"] != 32)
+ stack_trace("Could not generate greyscale previews using rustg_iconforge_generate_headless! The output size was not 32x32. Got: [json_encode(headless_result)]")
+ if(!fexists(holder))
+ stack_trace("Could not generate greyscale previews using rustg_iconforge_generate_headless! The output file does not exist on the filesystem! Got: [json_encode(headless_result)]")
+ #else
var/icon/holder = icon('icons/testing/greyscale_error.dmi')
for(var/state in icons)
holder.Insert(icons[state], state)
+ #endif
var/filepath = "icons/map_icons/[filename].dmi"
#ifdef UNIT_TESTS
@@ -192,7 +213,3 @@ SUBSYSTEM_DEF(greyscale_previews)
if(old_md5 != new_md5)
stack_trace("Generated map icons were different than what is currently saved. If you see this in a CI run it means you need to run the game once through initialization and commit the resulting files in 'icons/map_icons/'")
#endif
-
-#ifdef CHECK_SPRITESHEET_ICON_VALIDITY
- #undef CHECK_SPRITESHEET_ICON_VALIDITY
-#endif
diff --git a/code/controllers/subsystem/processing/greyscale.dm b/code/controllers/subsystem/processing/greyscale.dm
index 2fe8444bd7c..03b19ac2eff 100644
--- a/code/controllers/subsystem/processing/greyscale.dm
+++ b/code/controllers/subsystem/processing/greyscale.dm
@@ -1,8 +1,3 @@
-/// 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.
-#define USE_RUSTG_ICONFORGE_GAGS
-
PROCESSING_SUBSYSTEM_DEF(greyscale)
name = "Greyscale"
flags = SS_BACKGROUND
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index ac45ba08242..eaca9403857 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -475,7 +475,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/get_admin_commands()
. = list()
-/// Creates an icon from the preview outfit.
+/// Creates a /datum/universal_icon from the preview outfit.
/// Custom implementors of `get_preview_icon` should use this, as the
/// result of `get_preview_icon` is expected to be the completed version.
/datum/antagonist/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy)
@@ -483,34 +483,35 @@ GLOBAL_LIST_EMPTY(antagonists)
dummy.equipOutfit(outfit, visuals_only = TRUE)
dummy.wear_suit?.update_greyscale()
dummy.set_combat_mode(TRUE)
- var/icon = getFlatIcon(dummy)
+ var/datum/universal_icon/antag_icon = get_flat_uni_icon(dummy)
// We don't want to qdel the dummy right away, since its items haven't initialized yet.
SSatoms.prepare_deletion(dummy)
- return icon
+ return antag_icon
-/// Given an icon, will crop it to be consistent of those in the preferences menu.
+/// Given a /datum/universal_icon, will crop it to be consistent of those in the preferences menu.
/// Not necessary, and in fact will look bad if it's anything other than a human.
-/datum/antagonist/proc/finish_preview_icon(icon/icon)
+/datum/antagonist/proc/finish_preview_icon(datum/universal_icon/antag_icon)
// Zoom in on the top of the head and the chest
// I have no idea how to do this dynamically.
- icon.Scale(115, 115)
+ antag_icon.scale(115, 115)
// This is probably better as a Crop, but I cannot figure it out.
- icon.Shift(WEST, 8)
- icon.Shift(SOUTH, 30)
+ antag_icon.shift(WEST, 8)
+ antag_icon.shift(SOUTH, 30)
- icon.Crop(1, 1, ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
+ antag_icon.crop(1, 1, ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
- return icon
+ return antag_icon
-/// Returns the icon to show on the preferences menu.
+/// Returns the /datum/universal_icon to shown on the preferences menu.
/datum/antagonist/proc/get_preview_icon()
if (isnull(preview_outfit))
return null
- return finish_preview_icon(render_preview_outfit(preview_outfit))
+ var/datum/universal_icon/preview_icon = render_preview_outfit(preview_outfit)
+ return finish_preview_icon(preview_icon)
/datum/antagonist/proc/edit_memory(mob/user)
var/new_memo = tgui_input_text(user, "Write a new memory", "Antag Memory", antag_memory, multiline = TRUE)
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index a1cf2f26502..380eea90b96 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -23,14 +23,14 @@
scientist.set_species(/datum/species/abductor)
agent.set_species(/datum/species/abductor)
- var/icon/scientist_icon = render_preview_outfit(/datum/outfit/abductor/scientist, scientist)
- scientist_icon.Shift(WEST, 8)
+ var/datum/universal_icon/scientist_icon = render_preview_outfit(/datum/outfit/abductor/scientist, scientist)
+ scientist_icon.shift(WEST, 8)
- var/icon/agent_icon = render_preview_outfit(/datum/outfit/abductor/agent, agent)
- agent_icon.Shift(EAST, 8)
+ var/datum/universal_icon/agent_icon = render_preview_outfit(/datum/outfit/abductor/agent, agent)
+ agent_icon.shift(EAST, 8)
- var/icon/final_icon = scientist_icon
- final_icon.Blend(agent_icon, ICON_OVERLAY)
+ var/datum/universal_icon/final_icon = scientist_icon
+ final_icon.blend_icon(agent_icon, ICON_OVERLAY)
qdel(scientist)
qdel(agent)
diff --git a/code/modules/antagonists/blob/blob_antag.dm b/code/modules/antagonists/blob/blob_antag.dm
index a2e07edaa2e..51d9acdc891 100644
--- a/code/modules/antagonists/blob/blob_antag.dm
+++ b/code/modules/antagonists/blob/blob_antag.dm
@@ -44,10 +44,10 @@
/datum/antagonist/blob/get_preview_icon()
var/datum/blobstrain/reagent/reactive_spines/reactive_spines = /datum/blobstrain/reagent/reactive_spines
- var/icon/icon = icon('icons/mob/nonhuman-player/blob.dmi', "blob_core")
- icon.Blend(initial(reactive_spines.color), ICON_MULTIPLY)
- icon.Blend(icon('icons/mob/nonhuman-player/blob.dmi', "blob_core_overlay"), ICON_OVERLAY)
- icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
+ var/datum/universal_icon/icon = uni_icon('icons/mob/nonhuman-player/blob.dmi', "blob_core")
+ icon.blend_color(initial(reactive_spines.color), ICON_MULTIPLY)
+ icon.blend_icon(uni_icon('icons/mob/nonhuman-player/blob.dmi', "blob_core_overlay"), ICON_OVERLAY)
+ icon.scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
return icon
@@ -159,17 +159,17 @@
pref_flag = ROLE_BLOB_INFECTION
/datum/antagonist/blob/infection/get_preview_icon()
- var/icon/blob_icon = ..()
+ var/datum/universal_icon/blob_icon = ..()
var/datum/blobstrain/reagent/reactive_spines/reactive_spines = /datum/blobstrain/reagent/reactive_spines
- var/icon/blob_head = icon('icons/mob/nonhuman-player/blob.dmi', "blob_head")
- blob_head.Blend(initial(reactive_spines.complementary_color), ICON_MULTIPLY)
+ var/datum/universal_icon/blob_head = uni_icon('icons/mob/nonhuman-player/blob.dmi', "blob_head")
+ blob_head.blend_color(initial(reactive_spines.complementary_color), ICON_MULTIPLY)
- var/icon/human_icon = render_preview_outfit(/datum/outfit/job/miner)
- human_icon.Blend(blob_head, ICON_OVERLAY)
- human_icon.ChangeOpacity(0.7)
+ var/datum/universal_icon/human_icon = render_preview_outfit(/datum/outfit/job/miner)
+ human_icon.blend_icon(blob_head, ICON_OVERLAY)
+ human_icon.change_opacity(0.7)
- blob_icon.Blend(finish_preview_icon(human_icon), ICON_OVERLAY)
+ blob_icon.blend_icon(finish_preview_icon(human_icon), ICON_OVERLAY)
return blob_icon
diff --git a/code/modules/antagonists/blood_worm/blood_worm_antag.dm b/code/modules/antagonists/blood_worm/blood_worm_antag.dm
index 16b4a56fdd7..8804d60c7b4 100644
--- a/code/modules/antagonists/blood_worm/blood_worm_antag.dm
+++ b/code/modules/antagonists/blood_worm/blood_worm_antag.dm
@@ -81,11 +81,11 @@
return ..()
/datum/antagonist/blood_worm/get_preview_icon()
- var/icon/icon = icon('icons/mob/nonhuman-player/blood_worm_32x32.dmi', "juvenile")
+ var/datum/universal_icon/icon = uni_icon('icons/mob/nonhuman-player/blood_worm_32x32.dmi', "juvenile")
- icon.Crop(1, 1, 32, 32)
- icon.Shift(SOUTH, 4)
- icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
+ icon.crop(1, 1, 32, 32)
+ icon.shift(SOUTH, 4)
+ icon.scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
return icon
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 6cf17e5efab..4120c8de238 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -141,20 +141,20 @@
brother2.dna.features[FEATURE_MOTH_WINGS] = "Plain"
brother2.set_species(/datum/species/moth)
- var/icon/brother1_icon = render_preview_outfit(/datum/outfit/job/quartermaster, brother1)
- var/icon/brother1_blood_icon = icon('icons/effects/blood.dmi', "maskblood")
- brother1_blood_icon.Blend(BLOOD_COLOR_RED, ICON_MULTIPLY)
- brother1_icon.Blend(brother1_blood_icon, ICON_OVERLAY)
- brother1_icon.Shift(WEST, 8)
+ var/datum/universal_icon/brother1_icon = render_preview_outfit(/datum/outfit/job/quartermaster, brother1)
+ var/datum/universal_icon/brother1_blood_icon = uni_icon('icons/effects/blood.dmi', "maskblood")
+ brother1_blood_icon.blend_color(BLOOD_COLOR_RED, ICON_MULTIPLY)
+ brother1_icon.blend_icon(brother1_blood_icon, ICON_OVERLAY)
+ brother1_icon.shift(WEST, 8)
- var/icon/brother2_icon = render_preview_outfit(/datum/outfit/job/scientist/consistent, brother2)
- var/icon/brother2_blood_icon = icon('icons/effects/blood.dmi', "uniformblood")
- brother2_blood_icon.Blend(BLOOD_COLOR_RED, ICON_MULTIPLY)
- brother2_icon.Blend(brother2_blood_icon, ICON_OVERLAY)
- brother2_icon.Shift(EAST, 8)
+ var/datum/universal_icon/brother2_icon = render_preview_outfit(/datum/outfit/job/scientist/consistent, brother2)
+ var/datum/universal_icon/brother2_blood_icon = uni_icon('icons/effects/blood.dmi', "uniformblood")
+ brother2_blood_icon.blend_color(BLOOD_COLOR_RED, ICON_MULTIPLY)
+ brother2_icon.blend_icon(brother2_blood_icon, ICON_OVERLAY)
+ brother2_icon.shift(EAST, 8)
- var/icon/final_icon = brother1_icon
- final_icon.Blend(brother2_icon, ICON_OVERLAY)
+ var/datum/universal_icon/final_icon = brother1_icon
+ final_icon.blend_icon(brother2_icon, ICON_OVERLAY)
qdel(brother1)
qdel(brother2)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 4f6e0385719..b841ec26626 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -1015,16 +1015,16 @@
return parts.Join("
")
/datum/antagonist/changeling/get_preview_icon()
- var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling)
- var/icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer)
+ var/datum/universal_icon/final_icon = render_preview_outfit(/datum/outfit/changeling)
+ var/datum/universal_icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer)
- final_icon.Shift(WEST, ICON_SIZE_X / 2)
- final_icon.Shift(EAST, ICON_SIZE_X / 2)
+ final_icon.shift(WEST, ICON_SIZE_X / 2)
+ final_icon.shift(EAST, ICON_SIZE_X / 2)
- split_icon.Shift(EAST, ICON_SIZE_X / 2)
- split_icon.Shift(WEST, ICON_SIZE_X / 2)
+ split_icon.shift(EAST, ICON_SIZE_X / 2)
+ split_icon.shift(WEST, ICON_SIZE_X / 2)
- final_icon.Blend(split_icon, ICON_OVERLAY)
+ final_icon.blend_icon(split_icon, ICON_OVERLAY)
return finish_preview_icon(final_icon)
@@ -1064,7 +1064,7 @@
name = "\improper Space Changeling"
/datum/antagonist/changeling/space/get_preview_icon()
- var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling_space)
+ var/datum/universal_icon/final_icon = render_preview_outfit(/datum/outfit/changeling_space)
return finish_preview_icon(final_icon)
/datum/antagonist/changeling/space/greet()
diff --git a/code/modules/antagonists/cult/datums/cultist.dm b/code/modules/antagonists/cult/datums/cultist.dm
index c637b2b45cb..de1d5ad2978 100644
--- a/code/modules/antagonists/cult/datums/cultist.dm
+++ b/code/modules/antagonists/cult/datums/cultist.dm
@@ -255,21 +255,21 @@
to_chat(cult_mind, span_cult_large("The Cult's Master, [owner.current.name], has fallen in \the [current_area]!"))
/datum/antagonist/cult/get_preview_icon()
- var/icon/icon = render_preview_outfit(preview_outfit)
+ var/datum/universal_icon/icon = render_preview_outfit(preview_outfit)
// The longsword is 64x64, but getFlatIcon crunches to 32x32.
// So I'm just going to add it in post, screw it.
// Center the dude, because item icon states start from the center.
// This makes the image 64x64.
- icon.Crop(-15, -15, 48, 48)
+ icon.crop(-15, -15, 48, 48)
- var/obj/item/melee/cultblade/longsword = new
- icon.Blend(icon(longsword.lefthand_file, longsword.inhand_icon_state), ICON_OVERLAY)
- qdel(longsword)
+ var/obj/item/melee/cultblade/blade_type = /obj/item/melee/cultblade
+ var/datum/universal_icon/blade_icon = uni_icon(blade_type::lefthand_file, blade_type::inhand_icon_state)
+ icon.blend_icon(blade_icon, ICON_OVERLAY)
// Move the guy back to the bottom left, 32x32.
- icon.Crop(17, 17, 48, 48)
+ icon.crop(17, 17, 48, 48)
return finish_preview_icon(icon)
diff --git a/code/modules/antagonists/fugitive/fugitive.dm b/code/modules/antagonists/fugitive/fugitive.dm
index e42782bbf74..8f667a9a901 100644
--- a/code/modules/antagonists/fugitive/fugitive.dm
+++ b/code/modules/antagonists/fugitive/fugitive.dm
@@ -16,22 +16,22 @@
/datum/antagonist/fugitive/get_preview_icon()
//start with prisoner at the front
- var/icon/final_icon = render_preview_outfit(preview_outfit)
+ var/datum/universal_icon/final_icon = render_preview_outfit(preview_outfit)
//then to the left add cultists of yalp elor
- final_icon.Blend(make_background_fugitive_icon(/datum/outfit/yalp_cultist), ICON_UNDERLAY, -8, 0)
+ final_icon.blend_icon(make_background_fugitive_icon(/datum/outfit/yalp_cultist), ICON_UNDERLAY, -8, 0)
//to the right add waldo (we just had to, okay?)
- final_icon.Blend(make_background_fugitive_icon(/datum/outfit/waldo), ICON_UNDERLAY, 8, 0)
+ final_icon.blend_icon(make_background_fugitive_icon(/datum/outfit/waldo), ICON_UNDERLAY, 8, 0)
- final_icon.Scale(64, 64)
+ final_icon.scale(64, 64)
return finish_preview_icon(final_icon)
/datum/antagonist/fugitive/proc/make_background_fugitive_icon(datum/outfit/fugitive_fit)
var/mob/living/carbon/human/dummy/consistent/fugitive = new
- var/icon/fugitive_icon = render_preview_outfit(fugitive_fit, fugitive)
- fugitive_icon.ChangeOpacity(0.5)
+ var/datum/universal_icon/fugitive_icon = render_preview_outfit(fugitive_fit, fugitive)
+ fugitive_icon.change_opacity(0.5)
qdel(fugitive)
return fugitive_icon
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index a34ef8418dd..ae1a5dfb932 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -315,7 +315,7 @@
return ..()
/datum/antagonist/heretic/get_preview_icon()
- var/icon/icon = render_preview_outfit(preview_outfit)
+ var/datum/universal_icon/icon = render_preview_outfit(preview_outfit)
// MOTHBLOCKS TODO: Copied and pasted from cult, make this its own proc
@@ -324,14 +324,14 @@
// Center the dude, because item icon states start from the center.
// This makes the image 64x64.
- icon.Crop(-15, -15, 48, 48)
+ icon.crop(-15, -15, 48, 48)
- var/obj/item/melee/sickly_blade/blade = new
- icon.Blend(icon(blade.lefthand_file, blade.inhand_icon_state), ICON_OVERLAY)
- qdel(blade)
+ var/obj/item/melee/sickly_blade/blade_type = /obj/item/melee/sickly_blade
+ var/datum/universal_icon/blade_icon = uni_icon(blade_type::lefthand_file, blade_type::inhand_icon_state)
+ icon.blend_icon(blade_icon, ICON_OVERLAY)
// Move the guy back to the bottom left, 32x32.
- icon.Crop(17, 17, 48, 48)
+ icon.crop(17, 17, 48, 48)
return finish_preview_icon(icon)
diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm
index d35a00f8b7c..83b84c2e7b9 100644
--- a/code/modules/antagonists/malf_ai/malf_ai.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai.dm
@@ -260,12 +260,12 @@
return result.Join("
")
/datum/antagonist/malf_ai/get_preview_icon()
- var/icon/malf_ai_icon = icon('icons/mob/silicon/ai.dmi', "ai-red")
+ var/datum/universal_icon/malf_ai_icon = uni_icon('icons/mob/silicon/ai.dmi', "ai-red")
// Crop out the borders of the AI, just the face
- malf_ai_icon.Crop(5, 27, 28, 6)
+ malf_ai_icon.crop(5, 6, 28, 27)
- malf_ai_icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
+ malf_ai_icon.scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
return malf_ai_icon
diff --git a/code/modules/antagonists/nukeop/datums/operative.dm b/code/modules/antagonists/nukeop/datums/operative.dm
index abf38593990..dccd7450f97 100644
--- a/code/modules/antagonists/nukeop/datums/operative.dm
+++ b/code/modules/antagonists/nukeop/datums/operative.dm
@@ -111,19 +111,19 @@
if (!preview_outfit)
return null
- var/icon/final_icon = render_preview_outfit(preview_outfit)
+ var/datum/universal_icon/final_icon = render_preview_outfit(preview_outfit)
if (!isnull(preview_outfit_behind))
- var/icon/teammate = render_preview_outfit(preview_outfit_behind)
- teammate.Blend(rgb(128, 128, 128, 128), ICON_MULTIPLY)
+ var/datum/universal_icon/teammate = render_preview_outfit(preview_outfit_behind)
+ teammate.blend_color("#80808080", ICON_MULTIPLY)
- final_icon.Blend(teammate, ICON_UNDERLAY, -ICON_SIZE_X / 4, 0)
- final_icon.Blend(teammate, ICON_UNDERLAY, ICON_SIZE_X / 4, 0)
+ final_icon.blend_icon(teammate, ICON_UNDERLAY, -ICON_SIZE_X / 4, 0)
+ final_icon.blend_icon(teammate, ICON_UNDERLAY, ICON_SIZE_X / 4, 0)
if (!isnull(nuke_icon_state))
- var/icon/nuke = icon('icons/obj/machines/nuke.dmi', nuke_icon_state)
- nuke.Shift(SOUTH, 6)
- final_icon.Blend(nuke, ICON_OVERLAY)
+ var/datum/universal_icon/nuke = uni_icon('icons/obj/machines/nuke.dmi', nuke_icon_state)
+ nuke.shift(SOUTH, 6)
+ final_icon.blend_icon(nuke, ICON_OVERLAY)
return finish_preview_icon(final_icon)
diff --git a/code/modules/antagonists/obsessed/obsessed.dm b/code/modules/antagonists/obsessed/obsessed.dm
index 0296c3c53ee..1cd433c055e 100644
--- a/code/modules/antagonists/obsessed/obsessed.dm
+++ b/code/modules/antagonists/obsessed/obsessed.dm
@@ -58,19 +58,15 @@
. = ..()
/datum/antagonist/obsessed/get_preview_icon()
- var/mob/living/carbon/human/dummy/consistent/victim_dummy = new
- victim_dummy.set_haircolor("#bb9966", update = FALSE)
- victim_dummy.set_hairstyle("Messy", update = TRUE)
+ var/datum/universal_icon/obsessed_icon = render_preview_outfit(preview_outfit)
+ var/datum/universal_icon/blood_icon = uni_icon('icons/effects/blood.dmi', "uniformblood")
+ blood_icon.blend_color(BLOOD_COLOR_RED, ICON_MULTIPLY)
+ obsessed_icon.blend_icon(blood_icon, ICON_OVERLAY)
- var/icon/obsessed_icon = render_preview_outfit(preview_outfit)
- var/icon/blood_icon = icon('icons/effects/blood.dmi', "uniformblood")
- blood_icon.Blend(BLOOD_COLOR_RED, ICON_MULTIPLY)
- obsessed_icon.Blend(blood_icon, ICON_OVERLAY)
+ var/datum/universal_icon/final_icon = finish_preview_icon(obsessed_icon)
- var/icon/final_icon = finish_preview_icon(obsessed_icon)
-
- final_icon.Blend(
- icon('icons/ui/antags/obsessed.dmi', "obsession"),
+ final_icon.blend_icon(
+ uni_icon('icons/ui/antags/obsessed.dmi', "obsession"),
ICON_OVERLAY,
ANTAGONIST_PREVIEW_ICON_SIZE - 30,
20,
diff --git a/code/modules/antagonists/paradox_clone/paradox_clone.dm b/code/modules/antagonists/paradox_clone/paradox_clone.dm
index 18631c51237..fe76e891a4f 100644
--- a/code/modules/antagonists/paradox_clone/paradox_clone.dm
+++ b/code/modules/antagonists/paradox_clone/paradox_clone.dm
@@ -9,18 +9,18 @@
preview_outfit = /datum/outfit/paradox_clone
/datum/antagonist/paradox_clone/get_preview_icon()
- var/icon/final_icon = render_preview_outfit(preview_outfit)
+ var/datum/universal_icon/final_icon = render_preview_outfit(preview_outfit)
- final_icon.Blend(make_background_clone_icon(preview_outfit), ICON_UNDERLAY, -8, 0)
- final_icon.Scale(64, 64)
+ final_icon.blend_icon(make_background_clone_icon(preview_outfit), ICON_UNDERLAY, -8, 0)
+ final_icon.scale(64, 64)
return finish_preview_icon(final_icon)
/datum/antagonist/paradox_clone/proc/make_background_clone_icon(datum/outfit/clone_fit)
var/mob/living/carbon/human/dummy/consistent/clone = new
- var/icon/clone_icon = render_preview_outfit(clone_fit, clone)
- clone_icon.ChangeOpacity(0.5)
+ var/datum/universal_icon/clone_icon = render_preview_outfit(clone_fit, clone)
+ clone_icon.change_opacity(0.5)
qdel(clone)
return clone_icon
diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm
index 8f15508989c..3227377779a 100644
--- a/code/modules/antagonists/revenant/revenant_antag.dm
+++ b/code/modules/antagonists/revenant/revenant_antag.dm
@@ -14,7 +14,7 @@
. = ..()
/datum/antagonist/revenant/get_preview_icon()
- return finish_preview_icon(icon('icons/mob/simple/mob.dmi', "revenant_idle"))
+ return finish_preview_icon(uni_icon('icons/mob/simple/mob.dmi', "revenant_idle"))
/datum/antagonist/revenant/forge_objectives()
var/datum/objective/revenant/objective = new
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index 94e5f0c0484..4c4de17add0 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -222,21 +222,21 @@
return ..() + "(Leader)"
/datum/antagonist/rev/head/get_preview_icon()
- var/icon/final_icon = render_preview_outfit(preview_outfit)
+ var/datum/universal_icon/final_icon = render_preview_outfit(preview_outfit)
- final_icon.Blend(make_assistant_icon("Business Hair"), ICON_UNDERLAY, -8, 0)
- final_icon.Blend(make_assistant_icon("CIA"), ICON_UNDERLAY, 8, 0)
+ final_icon.blend_icon(make_assistant_icon("Business Hair"), ICON_UNDERLAY, -8, 0)
+ final_icon.blend_icon(make_assistant_icon("CIA"), ICON_UNDERLAY, 8, 0)
// Apply the rev head HUD, but scale up the preview icon a bit beforehand.
// Otherwise, the R gets cut off.
- final_icon.Scale(64, 64)
+ final_icon.scale(64, 64)
- var/icon/rev_head_icon = icon('icons/mob/huds/antag_hud.dmi', "rev_head")
- rev_head_icon.Scale(48, 48)
- rev_head_icon.Crop(1, 1, 64, 64)
- rev_head_icon.Shift(EAST, 10)
- rev_head_icon.Shift(NORTH, 16)
- final_icon.Blend(rev_head_icon, ICON_OVERLAY)
+ var/datum/universal_icon/rev_head_icon = uni_icon('icons/mob/huds/antag_hud.dmi', "rev_head")
+ rev_head_icon.scale(48, 48)
+ rev_head_icon.crop(1, 1, 64, 64)
+ rev_head_icon.shift(EAST, 10)
+ rev_head_icon.shift(NORTH, 16)
+ final_icon.blend_icon(rev_head_icon, ICON_OVERLAY)
return finish_preview_icon(final_icon)
@@ -244,8 +244,8 @@
var/mob/living/carbon/human/dummy/consistent/assistant = new
assistant.set_hairstyle(hairstyle, update = TRUE)
- var/icon/assistant_icon = render_preview_outfit(/datum/outfit/job/assistant/consistent, assistant)
- assistant_icon.ChangeOpacity(0.5)
+ var/datum/universal_icon/assistant_icon = render_preview_outfit(/datum/outfit/job/assistant/consistent, assistant)
+ assistant_icon.change_opacity(0.5)
qdel(assistant)
diff --git a/code/modules/antagonists/sentient_creature/sentient_creature.dm b/code/modules/antagonists/sentient_creature/sentient_creature.dm
index b2035471c46..ef87cff49e6 100644
--- a/code/modules/antagonists/sentient_creature/sentient_creature.dm
+++ b/code/modules/antagonists/sentient_creature/sentient_creature.dm
@@ -6,17 +6,17 @@
ui_name = "AntagInfoSentient"
/datum/antagonist/sentient_creature/get_preview_icon()
- var/icon/final_icon = icon('icons/mob/simple/pets.dmi', "corgi")
+ var/datum/universal_icon/final_icon = uni_icon('icons/mob/simple/pets.dmi', "corgi")
- var/icon/pandora = icon('icons/mob/simple/lavaland/lavaland_elites.dmi', "pandora")
- pandora.Blend(rgb(128, 128, 128, 128), ICON_MULTIPLY)
- final_icon.Blend(pandora, ICON_UNDERLAY, -ICON_SIZE_X / 4, 0)
+ var/datum/universal_icon/pandora = uni_icon('icons/mob/simple/lavaland/lavaland_elites.dmi', "pandora")
+ pandora.blend_color("#80808080", ICON_MULTIPLY)
+ final_icon.blend_icon(pandora, ICON_UNDERLAY, -ICON_SIZE_X / 4, 0)
- var/icon/rat = icon('icons/mob/simple/animal.dmi', "regalrat")
- rat.Blend(rgb(128, 128, 128, 128), ICON_MULTIPLY)
- final_icon.Blend(rat, ICON_UNDERLAY, ICON_SIZE_X / 4, 0)
+ var/datum/universal_icon/rat = uni_icon('icons/mob/simple/animal.dmi', "regalrat")
+ rat.blend_color("#80808080", ICON_MULTIPLY)
+ final_icon.blend_icon(rat, ICON_UNDERLAY, ICON_SIZE_X / 4, 0)
- final_icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
+ final_icon.scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
return final_icon
/datum/antagonist/sentient_creature/on_gain()
diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm
index 320015c34bc..c71b5302b6e 100644
--- a/code/modules/antagonists/space_dragon/space_dragon.dm
+++ b/code/modules/antagonists/space_dragon/space_dragon.dm
@@ -107,13 +107,13 @@
return ..()
/datum/antagonist/space_dragon/get_preview_icon()
- var/icon/icon = icon('icons/mob/nonhuman-player/spacedragon.dmi', "spacedragon")
+ var/datum/universal_icon/icon = uni_icon('icons/mob/nonhuman-player/spacedragon.dmi', "spacedragon")
- icon.Blend(COLOR_STRONG_VIOLET, ICON_MULTIPLY)
- icon.Blend(icon('icons/mob/nonhuman-player/spacedragon.dmi', "spacedragon_overlay_base"), ICON_OVERLAY)
+ icon.blend_color(COLOR_STRONG_VIOLET, ICON_MULTIPLY)
+ icon.blend_icon(uni_icon('icons/mob/nonhuman-player/spacedragon.dmi', "spacedragon_overlay_base"), ICON_OVERLAY)
- icon.Crop(10, 9, 54, 53)
- icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
+ icon.crop(10, 9, 54, 53)
+ icon.scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
return icon
diff --git a/code/modules/antagonists/spy/spy.dm b/code/modules/antagonists/spy/spy.dm
index d0297b9842c..3685800dcf1 100644
--- a/code/modules/antagonists/spy/spy.dm
+++ b/code/modules/antagonists/spy/spy.dm
@@ -196,7 +196,9 @@
var/mob/living/carbon/human/dummy/consistent/dummy = new()
dummy.set_haircolor(COLOR_SILVER, update = FALSE)
dummy.set_hairstyle("CIA", update = FALSE)
- return finish_preview_icon(render_preview_outfit(preview_outfit, dummy))
+ var/datum/universal_icon/dummy_icon = render_preview_outfit(preview_outfit, dummy)
+ qdel(dummy)
+ return finish_preview_icon(dummy_icon)
/datum/outfit/spy
name = "Spy (Preview only)"
diff --git a/code/modules/antagonists/voidwalker/voidwalker.dm b/code/modules/antagonists/voidwalker/voidwalker.dm
index 932582b4bd1..dc73f7aa9a8 100644
--- a/code/modules/antagonists/voidwalker/voidwalker.dm
+++ b/code/modules/antagonists/voidwalker/voidwalker.dm
@@ -22,8 +22,8 @@
/datum/antagonist/voidwalker/get_preview_icon()
var/mob/living/basic/voidwalker/walker_type = /mob/living/basic/voidwalker
- var/icon/icon = icon(walker_type::icon, walker_type::icon_state)
- icon.Crop(5, 18, 30, 44)
+ var/datum/universal_icon/icon = uni_icon(walker_type::icon, walker_type::icon_state)
+ icon.crop(5, 18, 30, 44)
return finish_preview_icon(icon)
/datum/antagonist/voidwalker/forge_objectives()
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index 3f045a87ec5..2504f6b2ba0 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -44,7 +44,7 @@
return xeno_team
/datum/antagonist/xeno/get_preview_icon()
- return finish_preview_icon(icon('icons/mob/nonhuman-player/alien.dmi', "alienh"))
+ return finish_preview_icon(uni_icon('icons/mob/nonhuman-player/alien.dmi', "alienh"))
/datum/antagonist/xeno/forge_objectives()
var/datum/objective/advance_hive/objective = new
diff --git a/code/modules/asset_cache/spritesheet/batched/universal_icon.dm b/code/modules/asset_cache/spritesheet/batched/universal_icon.dm
index 0ecdd1ea349..efd120ac782 100644
--- a/code/modules/asset_cache/spritesheet/batched/universal_icon.dm
+++ b/code/modules/asset_cache/spritesheet/batched/universal_icon.dm
@@ -13,7 +13,7 @@
/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]"))
+ if((!isicon(icon_file) && !isfile(icon_file)) || "[icon_file]" == "/icon" || !length("[icon_file]"))
// bad! use 'icons/path_to_dmi.dmi' format only
CRASH("FATAL: universal_icon was provided icon_file: [icon_file] - icons provided to batched spritesheets MUST be DMI files, they cannot be /image, /icon, or other runtime generated icons.")
#endif
diff --git a/code/modules/client/preferences/README.md b/code/modules/client/preferences/README.md
index d6a96791e3b..e30198eafbd 100644
--- a/code/modules/client/preferences/README.md
+++ b/code/modules/client/preferences/README.md
@@ -397,24 +397,24 @@ Space Ninja, for example, looks like:
preview_outift = /datum/outfit/ninja
```
-However, if you want to get creative, you can override `/get_preview_icon()`. This proc should return an icon of size `ANTAGONIST_PREVIEW_ICON_SIZE`x`ANTAGONIST_PREVIEW_ICON_SIZE`.
+However, if you want to get creative, you can override `/get_preview_icon()`. This proc should return a /datum/universal_icon of size `ANTAGONIST_PREVIEW_ICON_SIZE`x`ANTAGONIST_PREVIEW_ICON_SIZE`.
-There are some helper procs you can use as well. `render_preview_outfit(outfit_type)` will take an outfit and give you an icon of someone wearing those clothes. `finish_preview_outfit` will, given an icon, resize it appropriately and zoom in on the head. Note that this will look bad on anything that isn't a human, so if you have a non-human antagonist (such as sentient disease), just run `icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)`.
+There are some helper procs you can use as well. `render_preview_outfit(outfit_type)` will take an outfit and give you a /datum/universal_icon of someone wearing those clothes (using get_flat_uni_icon). `finish_preview_outfit` will, given an icon, resize it appropriately and zoom in on the head. Note that this will look bad on anything that isn't a human, so if you have a non-human antagonist (such as sentient disease), just run `icon.scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)`.
For inspiration, here is changeling's:
```dm
/datum/antagonist/changeling/get_preview_icon()
- var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling)
- var/icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer)
+ var/datum/universal_icon/final_icon = render_preview_outfit(/datum/outfit/changeling)
+ var/datum/universal_icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer)
- final_icon.Shift(WEST, ICON_SIZE_X / 2)
- final_icon.Shift(EAST, ICON_SIZE_X / 2)
+ final_icon.shift(WEST, ICON_SIZE_X / 2)
+ final_icon.shift(EAST, ICON_SIZE_X / 2)
- split_icon.Shift(EAST, ICON_SIZE_X / 2)
- split_icon.Shift(WEST, ICON_SIZE_X / 2)
+ split_icon.shift(EAST, ICON_SIZE_X / 2)
+ split_icon.shift(WEST, ICON_SIZE_X / 2)
- final_icon.Blend(split_icon, ICON_OVERLAY)
+ final_icon.blend_icon(split_icon, ICON_OVERLAY)
return finish_preview_icon(final_icon)
```
diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm
index 39948078b11..1d3a3c1aeed 100644
--- a/code/modules/client/preferences/middleware/antags.dm
+++ b/code/modules/client/preferences/middleware/antags.dm
@@ -36,7 +36,7 @@ GLOBAL_LIST_INIT(non_ruleset_antagonists, list(
/datum/preference_middleware/antags/get_ui_assets()
return list(
- get_asset_datum(/datum/asset/spritesheet/antagonists),
+ get_asset_datum(/datum/asset/spritesheet_batched/antagonists),
)
/datum/preference_middleware/antags/proc/set_antags(list/params, mob/user)
@@ -136,14 +136,14 @@ GLOBAL_LIST_INIT(non_ruleset_antagonists, list(
return get_remaining_days(antag_time_limits[checked_antag_flag] || 0)
/// Sprites generated for the antagonists panel
-/datum/asset/spritesheet/antagonists
+/datum/asset/spritesheet_batched/antagonists
name = "antagonists"
early = TRUE
- /// Mapping of spritesheet keys -> icons
+ /// List of keys -> universal icon datums
var/list/antag_icons = list()
-/datum/asset/spritesheet/antagonists/create_spritesheets()
+/datum/asset/spritesheet_batched/antagonists/create_spritesheets()
var/list/antagonists = GLOB.non_ruleset_antagonists.Copy()
for (var/datum/dynamic_ruleset/ruleset as anything in subtypesof(/datum/dynamic_ruleset))
@@ -166,10 +166,13 @@ GLOBAL_LIST_INIT(non_ruleset_antagonists, list(
if (!isnull(generated_icons[antagonist_type]))
antag_icons[spritesheet_key] = generated_icons[antagonist_type]
+ // make sure IconForge also knows to generate this duplicate for the given key
+ // internal caching will prevent double generation of the same icon
+ insert_icon(spritesheet_key, generated_icons[antagonist_type])
continue
var/datum/antagonist/antagonist = new antagonist_type
- var/icon/preview_icon = antagonist.get_preview_icon()
+ var/datum/universal_icon/preview_icon = antagonist.get_preview_icon()
if (isnull(preview_icon))
continue
@@ -179,11 +182,12 @@ GLOBAL_LIST_INIT(non_ruleset_antagonists, list(
// preview_icons are not scaled at this stage INTENTIONALLY.
// If an icon is not prepared to be scaled to that size, it looks really ugly, and this
// makes it harder to figure out what size it *actually* is.
- generated_icons[antagonist_type] = preview_icon
- antag_icons[spritesheet_key] = preview_icon
- for (var/spritesheet_key in antag_icons)
- Insert(spritesheet_key, antag_icons[spritesheet_key])
+ // make sure no antagonists are rebuilt
+ generated_icons[antagonist_type] = preview_icon
+ // store for screenshot tests
+ antag_icons[spritesheet_key] = preview_icon
+ insert_icon(spritesheet_key, preview_icon)
/// Serializes an antag name to be used for preferences UI
/proc/serialize_antag_name(antag_name)
diff --git a/code/modules/client/preferences/middleware/species.dm b/code/modules/client/preferences/middleware/species.dm
index c7fc4265390..f45482a5069 100644
--- a/code/modules/client/preferences/middleware/species.dm
+++ b/code/modules/client/preferences/middleware/species.dm
@@ -3,16 +3,14 @@
/datum/preference_middleware/species/get_ui_assets()
return list(
- get_asset_datum(/datum/asset/spritesheet/species),
+ get_asset_datum(/datum/asset/spritesheet_batched/species),
)
-/datum/asset/spritesheet/species
+/datum/asset/spritesheet_batched/species
name = "species"
early = TRUE
-/datum/asset/spritesheet/species/create_spritesheets()
- var/list/to_insert = list()
-
+/datum/asset/spritesheet_batched/species/create_spritesheets()
for (var/species_id in get_selectable_species())
var/datum/species/species_type = GLOB.species_list[species_id]
@@ -21,13 +19,10 @@
dummy.equipOutfit(/datum/outfit/job/assistant/consistent, visuals_only = TRUE)
dummy.dna.species.prepare_human_for_preview(dummy)
- var/icon/dummy_icon = getFlatIcon(dummy)
- dummy_icon.Scale(64, 64)
- dummy_icon.Crop(15, 64 - 31, 15 + 31, 64)
- dummy_icon.Scale(64, 64)
- to_insert[sanitize_css_class_name(initial(species_type.name))] = dummy_icon
+ var/datum/universal_icon/dummy_icon = get_flat_uni_icon(dummy)
+ dummy_icon.scale(64, 64)
+ dummy_icon.crop(15, 64 - 31, 15 + 31, 64)
+ dummy_icon.scale(64, 64)
+ insert_icon(sanitize_css_class_name(initial(species_type.name)), dummy_icon)
SSatoms.prepare_deletion(dummy)
-
- for (var/spritesheet_key in to_insert)
- Insert(spritesheet_key, to_insert[spritesheet_key])
diff --git a/code/modules/unit_tests/screenshot_antag_icons.dm b/code/modules/unit_tests/screenshot_antag_icons.dm
index 0a210ae31ed..a3f3f62af65 100644
--- a/code/modules/unit_tests/screenshot_antag_icons.dm
+++ b/code/modules/unit_tests/screenshot_antag_icons.dm
@@ -2,11 +2,26 @@
/datum/unit_test/screenshot_antag_icons
/datum/unit_test/screenshot_antag_icons/Run()
- var/datum/asset/spritesheet/antagonists/antagonists = get_asset_datum(/datum/asset/spritesheet/antagonists)
+ var/datum/asset/spritesheet_batched/antagonists/antagonists = get_asset_datum(/datum/asset/spritesheet_batched/antagonists)
- for (var/antag_icon_key in antagonists.antag_icons)
- var/icon/reference_icon = antagonists.antag_icons[antag_icon_key]
+ // Generates the spritesheet in /tmp, while also ensuring that ALL icons output to the same size (will error otherwise)
+ // Left here in case caching changes somehow and we want a fresh generator
+ var/test_icon_filepath = "tmp/antag_icon_screenshot_test.dmi"
+ var/list/headless_result = rustg_iconforge_generate_headless(test_icon_filepath, antagonists.entries_json, TRUE)
+ if(!istype(headless_result))
+ TEST_FAIL("Could not generate antagonist icons using rustg_iconforge_generate_headless! The output format was invalid (JSON did not decode to a list). Got: [headless_result]")
+ if(!length(headless_result))
+ TEST_FAIL("Could not generate antagonist icons using rustg_iconforge_generate_headless! The output list was empty. Got: [json_encode(headless_result)]")
+ if(!isnull(headless_result["error"]))
+ TEST_FAIL("Could not generate antagonist icons using rustg_iconforge_generate_headless! There was an error: [headless_result["error"]]")
+ if(headless_result["file_path"] != test_icon_filepath)
+ TEST_FAIL("Could not generate antagonist icons using rustg_iconforge_generate_headless! The output file_path differed from the input. Got: [json_encode(headless_result)]")
+ if(headless_result["width"] != ANTAGONIST_PREVIEW_ICON_SIZE || headless_result["height"] != ANTAGONIST_PREVIEW_ICON_SIZE)
+ TEST_FAIL("Could not generate antagonist icons using rustg_iconforge_generate_headless! The output size was not ANTAGONIST_PREVIEW_ICON_SIZE. Got: [json_encode(headless_result)]")
+ if(!fexists(test_icon_filepath))
+ TEST_FAIL("Could not generate antagonist icons using rustg_iconforge_generate_headless! The output file does not exist on the filesystem! Got: [json_encode(headless_result)]")
+ var/icon/test_icon = icon(file(test_icon_filepath))
- var/icon/icon = new()
- icon.Insert(reference_icon, null, SOUTH, 1)
+ for (var/antag_icon_key in antagonists.entries)
+ var/icon/icon = icon(test_icon, antag_icon_key, SOUTH, 1)
test_screenshot(antag_icon_key, icon)
diff --git a/icons/map_icons/clothing/_clothing.dmi b/icons/map_icons/clothing/_clothing.dmi
index 6c781a70ad1..6a9956844f4 100644
Binary files a/icons/map_icons/clothing/_clothing.dmi and b/icons/map_icons/clothing/_clothing.dmi differ
diff --git a/icons/map_icons/clothing/accessory.dmi b/icons/map_icons/clothing/accessory.dmi
index 1719b8a73bb..4c4244f716a 100644
Binary files a/icons/map_icons/clothing/accessory.dmi and b/icons/map_icons/clothing/accessory.dmi differ
diff --git a/icons/map_icons/clothing/head/_head.dmi b/icons/map_icons/clothing/head/_head.dmi
index 567646e1af5..67ffccf2c2a 100644
Binary files a/icons/map_icons/clothing/head/_head.dmi and b/icons/map_icons/clothing/head/_head.dmi differ
diff --git a/icons/map_icons/clothing/head/beret.dmi b/icons/map_icons/clothing/head/beret.dmi
index 975bbf80cf2..fb920ce8327 100644
Binary files a/icons/map_icons/clothing/head/beret.dmi and b/icons/map_icons/clothing/head/beret.dmi differ
diff --git a/icons/map_icons/clothing/mask.dmi b/icons/map_icons/clothing/mask.dmi
index e8fa3c8594c..2841cb9effc 100644
Binary files a/icons/map_icons/clothing/mask.dmi and b/icons/map_icons/clothing/mask.dmi differ
diff --git a/icons/map_icons/clothing/neck.dmi b/icons/map_icons/clothing/neck.dmi
index 9adba4c1e50..3a5094318ef 100644
Binary files a/icons/map_icons/clothing/neck.dmi and b/icons/map_icons/clothing/neck.dmi differ
diff --git a/icons/map_icons/clothing/shoes.dmi b/icons/map_icons/clothing/shoes.dmi
index 97712fd9724..d6c336245fb 100644
Binary files a/icons/map_icons/clothing/shoes.dmi and b/icons/map_icons/clothing/shoes.dmi differ
diff --git a/icons/map_icons/clothing/suit/_suit.dmi b/icons/map_icons/clothing/suit/_suit.dmi
index ef633374f89..82feabad49e 100644
Binary files a/icons/map_icons/clothing/suit/_suit.dmi and b/icons/map_icons/clothing/suit/_suit.dmi differ
diff --git a/icons/map_icons/clothing/suit/costume.dmi b/icons/map_icons/clothing/suit/costume.dmi
index 3db98406230..b47fc0c68ab 100644
Binary files a/icons/map_icons/clothing/suit/costume.dmi and b/icons/map_icons/clothing/suit/costume.dmi differ
diff --git a/icons/map_icons/clothing/under/_under.dmi b/icons/map_icons/clothing/under/_under.dmi
index 82a09867c53..5d9b9146e86 100644
Binary files a/icons/map_icons/clothing/under/_under.dmi and b/icons/map_icons/clothing/under/_under.dmi differ
diff --git a/icons/map_icons/clothing/under/color.dmi b/icons/map_icons/clothing/under/color.dmi
index aa09a947f9d..87189da7548 100644
Binary files a/icons/map_icons/clothing/under/color.dmi and b/icons/map_icons/clothing/under/color.dmi differ
diff --git a/icons/map_icons/clothing/under/costume.dmi b/icons/map_icons/clothing/under/costume.dmi
index 4f3d049fe17..17e56c8eaa1 100644
Binary files a/icons/map_icons/clothing/under/costume.dmi and b/icons/map_icons/clothing/under/costume.dmi differ
diff --git a/icons/map_icons/clothing/under/dress.dmi b/icons/map_icons/clothing/under/dress.dmi
index fde8dfbebaa..14e11b72a7f 100644
Binary files a/icons/map_icons/clothing/under/dress.dmi and b/icons/map_icons/clothing/under/dress.dmi differ
diff --git a/icons/map_icons/items/_item.dmi b/icons/map_icons/items/_item.dmi
index ce998dbb391..9988f49c6fa 100644
Binary files a/icons/map_icons/items/_item.dmi and b/icons/map_icons/items/_item.dmi differ
diff --git a/icons/map_icons/items/encryptionkey.dmi b/icons/map_icons/items/encryptionkey.dmi
index be7514ad789..360180773f3 100644
Binary files a/icons/map_icons/items/encryptionkey.dmi and b/icons/map_icons/items/encryptionkey.dmi differ
diff --git a/icons/map_icons/items/pda.dmi b/icons/map_icons/items/pda.dmi
index 488e0ea2e7a..c28a9b7043f 100644
Binary files a/icons/map_icons/items/pda.dmi and b/icons/map_icons/items/pda.dmi differ
diff --git a/icons/map_icons/mobs.dmi b/icons/map_icons/mobs.dmi
index 6c781a70ad1..6a9956844f4 100644
Binary files a/icons/map_icons/mobs.dmi and b/icons/map_icons/mobs.dmi differ
diff --git a/icons/map_icons/objects.dmi b/icons/map_icons/objects.dmi
index faefc9cb0d6..dd3a926e2a7 100644
Binary files a/icons/map_icons/objects.dmi and b/icons/map_icons/objects.dmi differ
diff --git a/icons/map_icons/turfs.dmi b/icons/map_icons/turfs.dmi
index 6c781a70ad1..6a9956844f4 100644
Binary files a/icons/map_icons/turfs.dmi and b/icons/map_icons/turfs.dmi differ
diff --git a/icons/map_icons/unsorted.dmi b/icons/map_icons/unsorted.dmi
index 6c781a70ad1..6a9956844f4 100644
Binary files a/icons/map_icons/unsorted.dmi and b/icons/map_icons/unsorted.dmi differ