diff --git a/packages/scripts/src/buildAssets/equipmentIcons.ts b/packages/scripts/src/buildAssets/equipmentIcons.ts index aebe2c847..280d9ec87 100644 --- a/packages/scripts/src/buildAssets/equipmentIcons.ts +++ b/packages/scripts/src/buildAssets/equipmentIcons.ts @@ -3,14 +3,7 @@ import { AssetUploader } from "../core/github/assetUploader"; import { vfs } from "./constants"; import { OptionalDevices } from "./definitions"; -interface Mappings { - Header: { version: number }; - StyleSheets: { - selectors: string[]; - properties: { "bg-sprite": string }; - }[]; -} export async function equipmentIcons() { console.log("Building equipment icons..."); diff --git a/packages/website/src/hooks/useModel.ts b/packages/website/src/hooks/useModel.ts index c8d532bd5..9b9e78dd3 100644 --- a/packages/website/src/hooks/useModel.ts +++ b/packages/website/src/hooks/useModel.ts @@ -1,11 +1,10 @@ -import { asset } from "@blitzkit/core"; import { useLoader } from "@react-three/fiber"; import { Mesh, MeshStandardMaterial } from "three"; import { GLTFLoader } from "three-stdlib"; import { useDispose } from "./useDispose"; export function useModel(id: number) { - const path = asset(`3d/tanks/models/${id}.glb`); + const path = `/api/tanks/${id}/model.glb`; const gltf = useLoader(GLTFLoader, path); useDispose(gltf, path); diff --git a/packages/website/src/pages/[...chunk_api]/icons/equipment/[id].webp.ts b/packages/website/src/pages/[...chunk_api]/icons/equipment/[id].webp.ts new file mode 100644 index 000000000..f47fc889b --- /dev/null +++ b/packages/website/src/pages/[...chunk_api]/icons/equipment/[id].webp.ts @@ -0,0 +1,99 @@ +import type { OptionalDevices } from "@blitzkit/core"; +import type { APIContext, GetStaticPathsItem } from "astro"; +import sharp from "sharp"; +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 }; + }[]; +} + +export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => { + const paths: GetStaticPathsItem[] = []; + + console.log("Building equipment icons..."); + + const optionalDevices = await vfs.xml<{ root: OptionalDevices }>( + `Data/XML/item_defs/vehicles/common/optional_devices.xml`, + ); + const mappings = await vfs.yaml( + `Data/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`, + ); + + for (const optionalDeviceKey in optionalDevices.root) { + const optionalDevice = optionalDevices.root[optionalDeviceKey]; + + if (optionalDeviceKey === "nextAvailableId") continue; + + const mapping = mappings.StyleSheets.find((mapping) => + mapping.selectors.includes( + `.optional_device_item.${optionalDevice.icon} #Img`, + ), + ); + + if (!mapping) { + console.warn(`No mapping found for ${optionalDevice.icon}; skipping...`); + continue; + } + + const configPathRaw = mapping.properties["bg-sprite"]; + const configPath = configPathRaw.replace("~res:/", ""); + + if (configPath.startsWith("Gfx/Lobby")) { + const configPathWebp = configPath.replace(".txt", ""); + + paths.push({ + params: { id: optionalDevice.id }, + props: { + sizes: undefined, + source: `Data/${configPathWebp}.packed.webp`, + }, + }); + } else { + const config = await vfs.text(`Data/${configPath}`); + const sizes = config.split("\n")[4].split(" ").map(Number); + + paths.push({ + params: { id: optionalDevice.id }, + props: { + sizes, + source: `Data/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`, + }, + }); + } + } + + console.log(paths); + + return paths; +}); + +export async function GET({ + props, +}: APIContext<{ sizes?: number[]; source: string }>) { + const image = sharp(await vfs.file(props.source)); + let buffer: Buffer; + + if (props.sizes) { + buffer = await image + .extract({ + left: props.sizes[0], + top: props.sizes[1], + width: props.sizes[2], + height: props.sizes[3], + }) + .toBuffer(); + } else { + buffer = await image.trim().toBuffer(); + } + + const bytes = new Uint8Array(buffer); + + return new Response(bytes); +}