mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-10 15:45:05 +01:00
21b4095dfd
Upstream 04/17/2026 fixes https://github.com/Bubberstation/Bubberstation/issues/5549 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com> Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com> Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com> Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com> Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com> Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com> Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com> Co-authored-by: loganuk <fakeemail123@aol.com> Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com> Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com> Co-authored-by: Lucy <lucy@absolucy.moe> Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com> Co-authored-by: Isratosh <Isratosh@hotmail.com> Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com> Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com> Co-authored-by: Alexander V. <volas@ya.ru> Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com> Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com> Co-authored-by: Cameron Lennox <killer65311@gmail.com> Co-authored-by: Tim <timothymtorres@gmail.com> Co-authored-by: Iamgoofball <iamgoofball@gmail.com> Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com> Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com> Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com> Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com> Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com> Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com> Co-authored-by: Josh <josh.adam.powell@gmail.com> Co-authored-by: Josh Powell <josh.powell@softwire.com> Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com> Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com> Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com> Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
216 lines
9.3 KiB
Plaintext
216 lines
9.3 KiB
Plaintext
SUBSYSTEM_DEF(greyscale_previews)
|
|
name = "Greyscale Previews"
|
|
ss_flags = SS_NO_FIRE
|
|
init_stage = INITSTAGE_EARLY
|
|
dependencies = list(
|
|
/datum/controller/subsystem/processing/greyscale,
|
|
)
|
|
|
|
/datum/controller/subsystem/greyscale_previews/Initialize()
|
|
#ifndef UNIT_TESTS // We want this to run during unit tests regardless of the config
|
|
if(!CONFIG_GET(flag/generate_assets_in_init))
|
|
return SS_INIT_SUCCESS
|
|
#endif
|
|
ExportMapPreviews()
|
|
return SS_INIT_SUCCESS
|
|
|
|
/// Sets up the list of types to process for organizing icons into their respective .dmi.
|
|
/datum/controller/subsystem/greyscale_previews/proc/build_type_category_map(list/types_that_get_their_own_file)
|
|
var/list/type_to_filename = list()
|
|
|
|
for (var/filename in types_that_get_their_own_file)
|
|
var/root = types_that_get_their_own_file[filename]
|
|
|
|
for (var/atom/atom_type as anything in typesof(root))
|
|
// First match wins (prevents /obj from overwriting clothing buckets, etc.)
|
|
if (isnull(type_to_filename[atom_type]))
|
|
type_to_filename[atom_type] = filename
|
|
|
|
return type_to_filename
|
|
|
|
/// Builds a worklist of all the item types to try making a GAGS preview icon for.
|
|
/datum/controller/subsystem/greyscale_previews/proc/build_preview_worklists(list/types_that_get_their_own_file)
|
|
var/list/type_to_filename = build_type_category_map(types_that_get_their_own_file)
|
|
|
|
/// filename => list(entries)
|
|
var/list/worklists = list()
|
|
|
|
for (var/filename in types_that_get_their_own_file)
|
|
worklists[filename] = list()
|
|
|
|
worklists["unsorted"] = list()
|
|
|
|
/// ---- atom skins ----
|
|
for (var/skin_path, atom_skin in get_atom_skins())
|
|
var/datum/atom_skin/skin = atom_skin
|
|
var/atom/typepath = skin.greyscale_item_path
|
|
if (!GLOB.all_loadout_datums[typepath]) // We don't need reskin previews for non-loadout items
|
|
continue
|
|
if (isnull(skin.new_icon_state)) // This is the same as the default icon, which we will be generating below.
|
|
continue
|
|
if (!typepath::greyscale_config || !typepath::greyscale_colors)
|
|
continue
|
|
if (typepath::flags_1 & NO_NEW_GAGS_PREVIEW_1)
|
|
continue
|
|
|
|
var/filename = type_to_filename[typepath] || "unsorted"
|
|
worklists[filename] += skin
|
|
|
|
var/list/seen_typepaths = list()
|
|
/// ---- base atom types ----
|
|
for (var/filename, path in types_that_get_their_own_file)
|
|
var/atom/root = path
|
|
var/list/filename_worklist = worklists[filename]
|
|
|
|
for (var/atom/typepath as anything in valid_typesof(root))
|
|
if (seen_typepaths[typepath])
|
|
continue
|
|
|
|
seen_typepaths[typepath] = TRUE
|
|
|
|
if (!typepath::greyscale_config || !typepath::greyscale_colors)
|
|
continue
|
|
if (typepath::flags_1 & NO_NEW_GAGS_PREVIEW_1)
|
|
continue
|
|
|
|
filename_worklist += typepath
|
|
|
|
return worklists
|
|
|
|
/// Goes through all the valid GAGS item types in subtypes that fall under the types specified in types_that_get_their_own_file, creating a .dmi for each.
|
|
/datum/controller/subsystem/greyscale_previews/proc/ExportMapPreviews()
|
|
// Put subtypes before their parent or the parent file will take all the generated icons
|
|
var/list/types_that_get_their_own_file = list(
|
|
"turfs" = /turf, // None of these yet but it's harmless to be prepared
|
|
"mobs" = /mob, // Ditto
|
|
"clothing/accessory" = /obj/item/clothing/accessory,
|
|
"clothing/head/beret" = /obj/item/clothing/head/beret,
|
|
"clothing/head/_head" = /obj/item/clothing/head,
|
|
"clothing/mask" = /obj/item/clothing/mask,
|
|
"clothing/neck" = /obj/item/clothing/neck,
|
|
"clothing/shoes" = /obj/item/clothing/shoes,
|
|
"clothing/suit/costume" = /obj/item/clothing/suit/costume,
|
|
"clothing/suit/_suit" = /obj/item/clothing/suit,
|
|
"clothing/under/color" = /obj/item/clothing/under/color,
|
|
"clothing/under/costume" = /obj/item/clothing/under/costume,
|
|
"clothing/under/dress" = /obj/item/clothing/under/dress,
|
|
"clothing/under/_under" = /obj/item/clothing/under,
|
|
"clothing/_clothing" = /obj/item/clothing,
|
|
"items/encryptionkey" = /obj/item/encryptionkey,
|
|
"items/pda" = /obj/item/modular_computer/pda,
|
|
"items/_item" = /obj/item,
|
|
"objects" = /obj,
|
|
)
|
|
|
|
#ifdef UNIT_TESTS
|
|
if (!check_map_previews_filepath_order(types_that_get_their_own_file))
|
|
CRASH("The list 'types_that_get_their_own_file', used by ExportMapPreviews, is invalid. Please ensure that subtypes come BEFORE parent types in the list order.")
|
|
#endif
|
|
var/list/worklists = build_preview_worklists(types_that_get_their_own_file)
|
|
for (var/filename in worklists)
|
|
ExportMapPreviewsForType(filename, worklists[filename])
|
|
|
|
/// Checks that we do not have any parent types coming before subtypes in the types_that_get_their_own_file list (which is an assoc list (filepath, typepath))
|
|
/datum/controller/subsystem/greyscale_previews/proc/check_map_previews_filepath_order(list/our_list)
|
|
var/list/type_paths_to_check = list()
|
|
for (var/filepath in our_list)
|
|
type_paths_to_check += our_list[filepath]
|
|
|
|
if (!length(type_paths_to_check))
|
|
return TRUE
|
|
|
|
for (var/i = 1 to length(type_paths_to_check))
|
|
var/path_i = type_paths_to_check[i]
|
|
for(var/j = i+1 to length(type_paths_to_check))
|
|
var/path_j = type_paths_to_check[j]
|
|
if(ispath(path_j, path_i))
|
|
stack_trace("Error: [path_j] (index [j]) is a subtype of [path_i] (index [i]) but appears after it.")
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/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
|
|
var/icon_state
|
|
var/reskin_icon_state
|
|
|
|
if (istype(entry, /datum/atom_skin))
|
|
var/datum/atom_skin/skin = entry
|
|
typepath = skin.greyscale_item_path
|
|
icon_state = skin.new_icon_state
|
|
reskin_icon_state = TRUE
|
|
else
|
|
typepath = entry
|
|
icon_state = typepath::post_init_icon_state
|
|
|
|
if (!typepath)
|
|
continue
|
|
|
|
var/greyscale_config = typepath::greyscale_config
|
|
var/greyscale_colors = typepath::greyscale_colors
|
|
|
|
if (!greyscale_config || !greyscale_colors || (typepath::flags_1 & NO_NEW_GAGS_PREVIEW_1))
|
|
continue
|
|
|
|
// This is what the actual icon state will be in the map_icon .dmi
|
|
var/key = reskin_icon_state ? "[typepath]--[icon_state]" : "[typepath]"
|
|
|
|
#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
|
|
#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
|
|
var/old_md5 = rustg_hash_file(RUSTG_HASH_MD5, filepath)
|
|
#endif
|
|
fcopy(holder, filepath)
|
|
#ifdef UNIT_TESTS
|
|
var/new_md5 = rustg_hash_file(RUSTG_HASH_MD5, filepath)
|
|
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
|