diff --git a/packages/scripts/src/buildAssets/tankIcons.ts b/packages/scripts/src/buildAssets/tankIcons.ts index 3e8995f8d..1534e800b 100644 --- a/packages/scripts/src/buildAssets/tankIcons.ts +++ b/packages/scripts/src/buildAssets/tankIcons.ts @@ -5,44 +5,6 @@ import { AssetUploader } from "../core/github/assetUploader"; import { vfs } from "./constants"; import type { VehicleDefinitionList } from "./definitions"; -async function extractPackedTankIcon(packedBuffer: Buffer, context: string) { - const texture = sharp(packedBuffer); - const spriteRect = parsePackedSpriteRect(packedBuffer); - - if (!spriteRect) { - throw new Error(`Missing RIFF sprite bounds for ${context}`); - } - - const [left, top, width, height] = spriteRect; - const bounds = [left, top, width, height]; - - if (!bounds.every(Number.isInteger)) { - throw new Error( - `Invalid RIFF sprite bounds for ${context}: ${bounds.join(" ")}`, - ); - } - - const metadata = await texture.metadata(); - - if (metadata.width === undefined || metadata.height === undefined) { - throw new Error(`Failed to read image dimensions for ${context}`); - } - - if ( - left < 0 || - top < 0 || - width <= 0 || - height <= 0 || - left + width > metadata.width || - top + height > metadata.height - ) { - throw new Error( - `Out-of-bounds RIFF sprite for ${context}: ${bounds.join(" ")} in ${metadata.width}x${metadata.height}`, - ); - } - - return await texture.extract({ left, top, width, height }).toBuffer(); -} export async function tankIcons() { console.log("Building tank icons..."); diff --git a/packages/website/src/pages/[...chunk_api]/tanks/[id]/_index.ts b/packages/website/src/pages/[...chunk_api]/tanks/[id]/_index.ts new file mode 100644 index 000000000..8ea202dcb --- /dev/null +++ b/packages/website/src/pages/[...chunk_api]/tanks/[id]/_index.ts @@ -0,0 +1,12 @@ +import { api } from "../../../../core/blitzkit/api"; +import { mixStaticPaths } from "../../../../core/blitzkit/mixStaticPaths"; +import { getStaticPaths as _getStaticPaths } from "../../_index"; + +export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => { + const tanks = await api.tankDefinitions(); + + return Object.values(tanks.tanks).map((tank) => ({ + params: { id: tank.id }, + props: { id: tank.id }, + })); +}); diff --git a/packages/website/src/pages/[...chunk_api]/tanks/[id]/icons/big.webp.ts b/packages/website/src/pages/[...chunk_api]/tanks/[id]/icons/big.webp.ts new file mode 100644 index 000000000..53863090c --- /dev/null +++ b/packages/website/src/pages/[...chunk_api]/tanks/[id]/icons/big.webp.ts @@ -0,0 +1,92 @@ +import { + NATION_IDS, + type TankParameters, + type VehicleDefinitionList, +} from "@blitzkit/core"; +import type { APIContext } from "astro"; +import sharp from "sharp"; +import { parsePackedSpriteRect } from "../../../../../core/blitz/parsePackedSpriteRect"; +import { vfs } from "../../../../../core/blitzkit/vfs"; + +export { getStaticPaths } from "../_index"; + +export async function GET({ props }: APIContext<{ id: number }>) { + const nations = await vfs + .dir(`Data/XML/item_defs/vehicles`) + .then((files) => files.filter((nation) => nation !== "common")); + + for (const nation of nations) { + const tanks = await vfs.xml<{ root: VehicleDefinitionList }>( + `Data/XML/item_defs/vehicles/${nation}/list.xml`, + ); + + for (const tankKey in tanks.root) { + const tank = tanks.root[tankKey]; + + if (tankKey.includes("tutorial_bot")) continue; + + const nationVehicleId = tank.id; + const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; + + if (id !== props.id) continue; + + const parameters = await vfs.yaml( + `Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`, + ); + const bigPath = `Data/${parameters.resourcesPath.bigIconPath + .replace(/~res:\//, "") + .replace(/\..+/, "")}.packed.webp`; + const big = await extractPackedTankIcon( + await vfs.file(bigPath), + `tank ${tankKey} big`, + ); + + return new Response(big); + } + } +} + +export async function extractPackedTankIcon( + bytes: Uint8Array, + context: string, +) { + const texture = sharp(bytes); + const spriteRect = parsePackedSpriteRect(bytes); + + if (!spriteRect) { + throw new Error(`Missing RIFF sprite bounds for ${context}`); + } + + const [left, top, width, height] = spriteRect; + const bounds = [left, top, width, height]; + + if (!bounds.every(Number.isInteger)) { + throw new Error( + `Invalid RIFF sprite bounds for ${context}: ${bounds.join(" ")}`, + ); + } + + const metadata = await texture.metadata(); + + if (metadata.width === undefined || metadata.height === undefined) { + throw new Error(`Failed to read image dimensions for ${context}`); + } + + if ( + left < 0 || + top < 0 || + width <= 0 || + height <= 0 || + left + width > metadata.width || + top + height > metadata.height + ) { + throw new Error( + `Out-of-bounds RIFF sprite for ${context}: ${bounds.join(" ")} in ${metadata.width}x${metadata.height}`, + ); + } + + const buffer = await texture.extract({ left, top, width, height }).toBuffer(); + const array = new Uint8Array(buffer); + + return array; +}