mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 20:48:56 +01:00
IconForge: Antag and species icons, greyscale previews optimization (#94954)
## About The Pull Request Converts species and antagonist icon generation to the batched spritesheet system using IconForge, thanks to the new `get_flat_uni_icon` implementation. Unfortunately the cost of *building* the sprite is still expensive (GFI is always expensive, even a fancy list-based one), but the generation is SIGNIFICANTLY faster. We will see evidence of parity in the screenshot tests. but here: <img width="892" height="634" alt="image" src="https://github.com/user-attachments/assets/2a17f2e3-c024-41f6-9d1e-c2cb70642a81" /> The main advantage is that species and antag icons can now take advantage of the development-time smart cache which invalidates automatically. On the server this PR does very little except make antag icon generation a little bit more likely to find and announce errors (BYOND has a habit of silently eating weird icon proc calls). Also optimizes the greyscale preview generator from #90940 (~2x speedup) using `rustg_iconforge_generate_headless` instead of `Insert()` to build the resulting sheets. This can be further optimized in the future by implementing a smart cache, like batched spritesheets, and storing it in the repo, but for now it's not important/slow enough to be worth the effort. Also fixes a silent compilation error that would always happen outside unit tests, but for some reason doesn't appear on local? Notice how `map_icon_key` is not a defined variable anywhere. That's because `USE_RUSTG_ICONFORGE_GAGS` is *never* defined at this point, so it was always using the 'slow' generation. I also took the liberty of cleaning up the cultist and heretic icon generation randomly initializing a blade object when it could just use a static access. ## Why It's Good For The Game The subsystem timing may not be much faster, but the interactivity benefits during spritesheet realization are undeniable. Opening the preferences menu during init on local is orders of magnitude faster. **Old** Early Assets: 5.02 seconds Greyscale Previews: 1.38 seconds **Fresh (No Cache)** Early Assets: 4.21 seconds Greyscale Previews: 0.5 seconds **Cache Invalidated** Early Assets: 4.27 seconds **Cache Hit** Early Assets: 4.05~4.2 seconds **Preferences lag:** ~6 sec to open to ~2 sec to open due to caching in dev ## Changelog 🆑 code: Optimized species and antagonist icon loading in the preferences menu on local, speeding up time to open in development. fix: GAGS map preview generation no longer silently errors outside of unit tests due to a compilation error. /🆑
This commit is contained in:
@@ -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)
|
||||
```
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
Reference in New Issue
Block a user