From 8a3e57637d7b2e31d6c2d5b7af3ffe5ed4f94201 Mon Sep 17 00:00:00 2001
From: tresabhi <43380238+tresabhi@users.noreply.github.com>
Date: Sun, 2 Aug 2026 20:24:39 -0500
Subject: [PATCH] provision icons
---
.../buildAssets/consumableProvisionIcons.ts | 189 ------------------
.../icons/provisions/[id].webp.ts | 112 +++++++++++
2 files changed, 112 insertions(+), 189 deletions(-)
delete mode 100644 packages/scripts/src/buildAssets/consumableProvisionIcons.ts
create mode 100644 packages/website/src/pages/[...chunk_api]/icons/provisions/[id].webp.ts
diff --git a/packages/scripts/src/buildAssets/consumableProvisionIcons.ts b/packages/scripts/src/buildAssets/consumableProvisionIcons.ts
deleted file mode 100644
index a934a5ad8..000000000
--- a/packages/scripts/src/buildAssets/consumableProvisionIcons.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-import sharp from "sharp";
-import { parsePackedSpriteRect } from "../core/blitz/parsePackedSpriteRect";
-import { AssetUploader } from "../core/github/assetUploader";
-import { vfs } from "./constants";
-import { ConsumablesCommon, ProvisionsCommon } from "./definitions";
-
-interface Mappings {
- Header: { version: number };
- StyleSheets: {
- selectors: string[];
- properties: { "bg-sprite": string };
- }[];
-}
-
-const listItemsPattern = //g;
-
-export async function consumableProvisionIcons() {
- console.log("Building consumable and provision icons...");
-
- using uploader = new AssetUploader("consumable and provision icons");
- const styles = [
- "UI/Styles/Lobby/Inventory/InventoryNormalStyles.yaml",
- "UI/Styles/Lobby/Inventory/Event/InventoryBigStyles.yaml",
- "UI/Styles/Lobby/Inventory/Event/InventoryNormalStyles.yaml",
- "UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml",
- "UI/Screens/Battle/Styles/BattleEquipmentStyles.yaml",
- ];
- const styleSheets = await Promise.all(
- styles.map(async (path) =>
- Object.values((await vfs.yaml(`Data/${path}`)).StyleSheets),
- ),
- ).then((array) => array.flat());
-
- for (const match of (
- await vfs.text(`Data/XML/item_defs/vehicles/common/consumables/list.xml`)
- ).matchAll(listItemsPattern)) {
- const consumablesCommon = await vfs.xml<{
- root: ConsumablesCommon;
- }>(`Data/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`);
-
- for (const consumable of Object.values(consumablesCommon.root)) {
- const styleSheet = styleSheets.find((styleSheet) =>
- styleSheet.selectors.some((selector) =>
- selector.includes(`${consumable.icon} `),
- ),
- );
-
- if (!styleSheet) {
- console.warn(
- `No style sheet found for consumable ${consumable.icon}; skipping...`,
- );
- continue;
- }
-
- const configPath = styleSheet.properties["bg-sprite"]
- .replace("~res:/", "")
- .replace(".psd", "")
- .replace(".txt", "");
-
- if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
- const packedBuffer = await vfs.file(`Data/${configPath}.packed.webp`);
- const consumablesTexture = sharp(packedBuffer);
- const sizes = parsePackedSpriteRect(packedBuffer);
- let content: Buffer;
-
- if (sizes) {
- content = await extractPackedIcon(
- consumablesTexture,
- sizes,
- `consumable ${consumable.icon}`,
- );
- } else {
- content = await consumablesTexture.trim({ threshold: 80 }).toBuffer();
- }
-
- await uploader.add({
- path: `icons/consumables/${consumable.id}.webp`,
- content,
- });
- } else {
- const consumablesTexture = sharp(
- await vfs.file(
- `${`Data/${configPath}`
- .split("/")
- .slice(0, -1)
- .join("/")}/texture0.packed.webp`,
- ),
- );
- const sizes = (await vfs.text(`Data/${configPath}.txt`))
- .split("\n")[4]
- .split(" ")
- .map(Number);
- const content = await consumablesTexture
- .clone()
- .extract({
- left: sizes[0],
- top: sizes[1],
- width: sizes[2],
- height: sizes[3],
- })
- .toBuffer();
-
- await uploader.add({
- path: `icons/consumables/${consumable.id}.webp`,
- content,
- });
- }
- }
- }
-
- for (const match of (
- await vfs.text(`Data/XML/item_defs/vehicles/common/provisions/list.xml`)
- ).matchAll(listItemsPattern)) {
- const provisionsCommon = await vfs.xml<{
- root: ProvisionsCommon;
- }>(`Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`);
-
- for (const provision of Object.values(provisionsCommon.root)) {
- const styleSheet = styleSheets.find((styleSheet) =>
- styleSheet.selectors.some((selector) =>
- selector.includes(`${provision.icon} `),
- ),
- );
-
- if (!styleSheet) {
- console.warn(
- `No style sheet found for provision ${provision.icon}; skipping...`,
- );
- continue;
- }
-
- const configPath = styleSheet.properties["bg-sprite"]
- .replace("~res:/", "")
- .replace(".psd", "")
- .replace(".txt", "");
-
- if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
- const packedBuffer = await vfs.file(`Data/${configPath}.packed.webp`);
- const provisionsTexture = sharp(packedBuffer);
- const sizes = parsePackedSpriteRect(packedBuffer);
- let content: Buffer;
-
- if (sizes) {
- content = await extractPackedIcon(
- provisionsTexture,
- sizes,
- `provision ${provision.icon}`,
- );
- } else {
- content = await provisionsTexture.trim({ threshold: 80 }).toBuffer();
- }
-
- await uploader.add({
- path: `icons/provisions/${provision.id}.webp`,
- content,
- });
- } else {
- const provisionsTexture = sharp(
- await vfs.file(
- `${`Data/${configPath}`
- .split("/")
- .slice(0, -1)
- .join("/")}/texture0.packed.webp`,
- ),
- );
- const sizes = (await vfs.text(`Data/${configPath}.txt`))
- .split("\n")[4]
- .split(" ")
- .map(Number);
- const content = await provisionsTexture
- .clone()
- .extract({
- left: sizes[0],
- top: sizes[1],
- width: sizes[2],
- height: sizes[3],
- })
- .toBuffer();
-
- await uploader.add({
- path: `icons/provisions/${provision.id}.webp`,
- content,
- });
- }
- }
- }
-
- await uploader.flush();
-}
diff --git a/packages/website/src/pages/[...chunk_api]/icons/provisions/[id].webp.ts b/packages/website/src/pages/[...chunk_api]/icons/provisions/[id].webp.ts
new file mode 100644
index 000000000..db38f389a
--- /dev/null
+++ b/packages/website/src/pages/[...chunk_api]/icons/provisions/[id].webp.ts
@@ -0,0 +1,112 @@
+import type { ProvisionsCommon } from "@blitzkit/core";
+import type { APIContext, GetStaticPathsItem } from "astro";
+import sharp from "sharp";
+import { extractPackedIcon } from "../../../../core/blitz/extractPackedIcon";
+import { parsePackedSpriteRect } from "../../../../core/blitz/parsePackedSpriteRect";
+import { mixStaticPaths } from "../../../../core/blitzkit/mixStaticPaths";
+import { vfs } from "../../../../core/blitzkit/vfs";
+import { getStaticPaths as _getStaticPaths } from "../../_index";
+
+interface Mappings {
+ Header: { version: number };
+ StyleSheets: {
+ selectors: string[];
+ properties: { "bg-sprite": string };
+ }[];
+}
+
+const listItemsPattern = //g;
+
+export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => {
+ const paths: GetStaticPathsItem[] = [];
+
+ const styles = [
+ "UI/Styles/Lobby/Inventory/InventoryNormalStyles.yaml",
+ "UI/Styles/Lobby/Inventory/Event/InventoryBigStyles.yaml",
+ "UI/Styles/Lobby/Inventory/Event/InventoryNormalStyles.yaml",
+ "UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml",
+ "UI/Screens/Battle/Styles/BattleEquipmentStyles.yaml",
+ ];
+ const styleSheets = await Promise.all(
+ styles.map(async (path) =>
+ Object.values((await vfs.yaml(`Data/${path}`)).StyleSheets),
+ ),
+ ).then((array) => array.flat());
+
+ for (const match of (
+ await vfs.text(`Data/XML/item_defs/vehicles/common/provisions/list.xml`)
+ ).matchAll(listItemsPattern)) {
+ const provisionsCommon = await vfs.xml<{
+ root: ProvisionsCommon;
+ }>(`Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`);
+
+ for (const provision of Object.values(provisionsCommon.root)) {
+ const styleSheet = styleSheets.find((styleSheet) =>
+ styleSheet.selectors.some((selector) =>
+ selector.includes(`${provision.icon} `),
+ ),
+ );
+
+ if (!styleSheet) {
+ console.warn(
+ `No style sheet found for provision ${provision.icon}; skipping...`,
+ );
+ continue;
+ }
+
+ const path = styleSheet.properties["bg-sprite"]
+ .replace("~res:/", "")
+ .replace(".psd", "")
+ .replace(".txt", "");
+
+ paths.push({
+ props: { path },
+ params: { id: provision.id },
+ });
+ }
+ }
+
+ return paths;
+});
+
+export async function GET({ props }: APIContext<{ path: string }>) {
+ let buffer: Buffer;
+
+ if (await vfs.resolve(`Data/${props.path}.packed.webp`)) {
+ const packedBuffer = await vfs.file(`Data/${props.path}.packed.webp`);
+ const provisionsTexture = sharp(packedBuffer);
+ const sizes = parsePackedSpriteRect(packedBuffer);
+
+ if (sizes) {
+ buffer = await extractPackedIcon(provisionsTexture, sizes);
+ } else {
+ buffer = await provisionsTexture.trim({ threshold: 80 }).toBuffer();
+ }
+ } else {
+ const provisionsTexture = sharp(
+ await vfs.file(
+ `${`Data/${props.path}`
+ .split("/")
+ .slice(0, -1)
+ .join("/")}/texture0.packed.webp`,
+ ),
+ );
+ const sizes = (await vfs.text(`Data/${props.path}.txt`))
+ .split("\n")[4]
+ .split(" ")
+ .map(Number);
+ buffer = await provisionsTexture
+ .clone()
+ .extract({
+ left: sizes[0],
+ top: sizes[1],
+ width: sizes[2],
+ height: sizes[3],
+ })
+ .toBuffer();
+ }
+
+ const bytes = new Uint8Array(buffer);
+
+ return new Response(bytes);
+}