small tank icons

This commit is contained in:
tresabhi committed 2026-08-04 19:54:08 -05:00
1 parent be0804ae59
commit 9968b2c83d
4 files changed
+58 -26

No files matched your search

@@ -1,6 +1,5 @@
import { asset } from '@blitzkit/core'; import { GenericTankComponentButton } from "./GenericTankComponentButton";
import { GenericTankComponentButton } from './GenericTankComponentButton'; import type { TankComponentButtonProps } from "./TankComponentButton";
import type { TankComponentButtonProps } from './TankComponentButton';
interface ModuleProps extends TankComponentButtonProps { interface ModuleProps extends TankComponentButtonProps {
module: string; module: string;
@@ -9,11 +8,11 @@ interface ModuleProps extends TankComponentButtonProps {
export function ModuleButton({ module, ...props }: ModuleProps) { export function ModuleButton({ module, ...props }: ModuleProps) {
return ( return (
<GenericTankComponentButton <GenericTankComponentButton
icon={asset(`icons/modules/${module}.webp`)} icon={`/api/icons/modules/${module}.webp`}
iconStyles={{ iconStyles={{
top: '50%', top: "50%",
left: '50%', left: "50%",
transform: 'translate(calc(-50% + 2px), calc(-50% + 2px))', transform: "translate(calc(-50% + 2px), calc(-50% + 2px))",
}} }}
{...props} {...props}
/> />
@@ -1,5 +1,3 @@
import { asset } from '@blitzkit/core';
interface SmallTankIconProps { interface SmallTankIconProps {
id: number; id: number;
size?: number; size?: number;
@@ -13,16 +11,16 @@ export function SmallTankIcon({ id, size = HEIGHT }: SmallTankIconProps) {
<div <div
style={{ style={{
height: size, height: size,
overflow: 'hidden', overflow: "hidden",
width: WIDTH * (size / HEIGHT), width: WIDTH * (size / HEIGHT),
}} }}
> >
<img <img
alt={`Tank ${id}`} alt={`Tank ${id}`}
src={asset(`icons/tanks/small/${id}.webp`)} src={`/tanks/${id}/icons/small.webp`}
style={{ style={{
transform: `scale(${(size / HEIGHT) * 100}%)`, transform: `scale(${(size / HEIGHT) * 100}%)`,
transformOrigin: 'top left', transformOrigin: "top left",
}} }}
/> />
</div> </div>
@@ -36,40 +36,32 @@ export async function GET({ props }: APIContext<{ id: number }>) {
const bigPath = `Data/${parameters.resourcesPath.bigIconPath const bigPath = `Data/${parameters.resourcesPath.bigIconPath
.replace(/~res:\//, "") .replace(/~res:\//, "")
.replace(/\..+/, "")}.packed.webp`; .replace(/\..+/, "")}.packed.webp`;
const big = await extractPackedTankIcon( const big = await extractPackedTankIcon(await vfs.file(bigPath));
await vfs.file(bigPath),
`tank ${tankKey} big`,
);
return new Response(big); return new Response(big);
} }
} }
} }
export async function extractPackedTankIcon( export async function extractPackedTankIcon(bytes: Uint8Array) {
bytes: Uint8Array,
context: string,
) {
const texture = sharp(bytes); const texture = sharp(bytes);
const spriteRect = parsePackedSpriteRect(bytes); const spriteRect = parsePackedSpriteRect(bytes);
if (!spriteRect) { if (!spriteRect) {
throw new Error(`Missing RIFF sprite bounds for ${context}`); throw new Error(`Missing RIFF sprite bounds`);
} }
const [left, top, width, height] = spriteRect; const [left, top, width, height] = spriteRect;
const bounds = [left, top, width, height]; const bounds = [left, top, width, height];
if (!bounds.every(Number.isInteger)) { if (!bounds.every(Number.isInteger)) {
throw new Error( throw new Error(`Invalid RIFF sprite bounds: ${bounds.join(" ")}`);
`Invalid RIFF sprite bounds for ${context}: ${bounds.join(" ")}`,
);
} }
const metadata = await texture.metadata(); const metadata = await texture.metadata();
if (metadata.width === undefined || metadata.height === undefined) { if (metadata.width === undefined || metadata.height === undefined) {
throw new Error(`Failed to read image dimensions for ${context}`); throw new Error(`Failed to read image dimensions`);
} }
if ( if (
@@ -81,7 +73,7 @@ export async function extractPackedTankIcon(
top + height > metadata.height top + height > metadata.height
) { ) {
throw new Error( throw new Error(
`Out-of-bounds RIFF sprite for ${context}: ${bounds.join(" ")} in ${metadata.width}x${metadata.height}`, `Out-of-bounds RIFF sprite: ${bounds.join(" ")} in ${metadata.width}x${metadata.height}`,
); );
} }
@@ -0,0 +1,43 @@
import {
NATION_IDS,
type TankParameters,
type VehicleDefinitionList,
} from "@blitzkit/core";
import type { APIContext } from "astro";
import { vfs } from "../../../../../core/blitzkit/vfs";
import { extractPackedTankIcon } from "./big.webp";
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<TankParameters>(
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
);
const bigPath = `Data/${parameters.resourcesPath.smallIconPath
.replace(/~res:\//, "")
.replace(/\..+/, "")}.packed.webp`;
const big = await extractPackedTankIcon(await vfs.file(bigPath));
return new Response(big);
}
}
}