mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-26 20:19:50 +01:00
Compare commits
3
Commits
59108d7637
...
b060e65af5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b060e65af5 | ||
|
|
8a3e57637d | ||
|
|
90ad923672 |
No files matched your search
@@ -1,22 +0,0 @@
|
||||
import { fetchTankDefinitions } from '../blitzkit';
|
||||
import type { BlitzStats } from './compositeStats/constants';
|
||||
|
||||
export async function getTierWeights(
|
||||
stats: Record<number, BlitzStats>,
|
||||
order: number[] = Object.keys(stats).map(parseInt),
|
||||
) {
|
||||
const tankDefinitions = await fetchTankDefinitions();
|
||||
const tierWeights: TierWeightsRecord = {};
|
||||
|
||||
order.forEach((id) => {
|
||||
const entry = tankDefinitions.tanks[id];
|
||||
|
||||
if (!entry) return;
|
||||
if (!tierWeights[entry.tier]) tierWeights[entry.tier] = 0;
|
||||
|
||||
tierWeights[entry.tier]! += stats[id].battles;
|
||||
});
|
||||
|
||||
return tierWeights;
|
||||
}
|
||||
export type TierWeightsRecord = Partial<Record<number, number>>;
|
||||
@@ -1,9 +1,8 @@
|
||||
export * from './calculateWN8';
|
||||
export * from './compositeStats';
|
||||
export * from './deltaBkrlBlitzStats';
|
||||
export * from './deltaTankStats';
|
||||
export * from './getBkniPercentile';
|
||||
export * from './getTierWeights';
|
||||
export * from './getWN8Percentile';
|
||||
export * from './percentiles';
|
||||
export * from './sumAllStats';
|
||||
export * from "./calculateWN8";
|
||||
export * from "./compositeStats";
|
||||
export * from "./deltaBkrlBlitzStats";
|
||||
export * from "./deltaTankStats";
|
||||
export * from "./getBkniPercentile";
|
||||
export * from "./getWN8Percentile";
|
||||
export * from "./percentiles";
|
||||
export * from "./sumAllStats";
|
||||
@@ -5,7 +5,6 @@ import { parse as parsePath } from "path";
|
||||
import ProgressBar from "progress";
|
||||
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
||||
import { PATCHES_ROOT, vfs } from "./buildAssets/constants";
|
||||
import { writeDVPL } from "./core/blitz/writeDVPL";
|
||||
|
||||
const versionTextFile = await vfs.text(`Data/version.txt`);
|
||||
const currentVersion = versionTextFile
|
||||
@@ -24,83 +23,80 @@ while (true) {
|
||||
)}/dlc/s${currentVersion}_${patchIndex}.yaml`,
|
||||
);
|
||||
|
||||
console.log(patchIndex, response.status)
|
||||
console.log(patchIndex, response.status);
|
||||
|
||||
if (!response.ok) break;
|
||||
|
||||
console.log(`Applying patch ${patchIndex}...`);
|
||||
|
||||
const data = parseYaml(await response.text());
|
||||
const dvpm = await fetch(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${data.dx11}`,
|
||||
).then((response) => response.arrayBuffer());
|
||||
const dvpd = await fetch(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${data.dx11.replace(
|
||||
".dvpm",
|
||||
".dvpd",
|
||||
)}`,
|
||||
).then((response) => response.arrayBuffer());
|
||||
const files = await dvp(dvpm, dvpd);
|
||||
const bar = new ProgressBar(
|
||||
`Patching ${files.length} files :bar`,
|
||||
files.length,
|
||||
console.log(`Applying patch ${patchIndex}...`);
|
||||
|
||||
const data = parseYaml(await response.text());
|
||||
const dvpm = await fetch(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${data.dx11}`,
|
||||
).then((response) => response.arrayBuffer());
|
||||
const dvpd = await fetch(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${data.dx11.replace(
|
||||
".dvpm",
|
||||
".dvpd",
|
||||
)}`,
|
||||
).then((response) => response.arrayBuffer());
|
||||
const files = await dvp(dvpm, dvpd);
|
||||
const bar = new ProgressBar(
|
||||
`Patching ${files.length} files :bar`,
|
||||
files.length,
|
||||
);
|
||||
|
||||
for (const { path, data } of files) {
|
||||
console.log(`Patching "${path}"...`);
|
||||
|
||||
const { dir } = parsePath(path);
|
||||
|
||||
try {
|
||||
await mkdir(`${PATCHES_ROOT}/Data/${dir}`, { recursive: true });
|
||||
} catch (error) {
|
||||
console.warn(`Failed to make directory "${dir}"`);
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(data);
|
||||
|
||||
await writeFile(`${PATCHES_ROOT}/Data/${path}`, new Uint8Array(buffer));
|
||||
|
||||
bar.tick();
|
||||
}
|
||||
|
||||
console.log("patches done for patch");
|
||||
|
||||
if ("dynamicContentLocalizationsDir" in data) {
|
||||
console.log(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${
|
||||
data.dynamicContentLocalizationsDir
|
||||
}/en.yaml`,
|
||||
);
|
||||
const localizationsResponse = await fetch(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${
|
||||
data.dynamicContentLocalizationsDir
|
||||
}/en.yaml`,
|
||||
);
|
||||
console.log("fetched");
|
||||
const newStrings = parseYaml(await localizationsResponse.text());
|
||||
console.log("got new strings");
|
||||
const oldStrings =
|
||||
await vfs.yaml<Record<string, string>>(`Data/Strings/en.yaml`);
|
||||
console.log("got old strings");
|
||||
const patchedStrings = { ...oldStrings, ...newStrings };
|
||||
const patchedContent = stringifyYaml(patchedStrings);
|
||||
console.log("stringified");
|
||||
|
||||
for (const { path, data } of files) {
|
||||
console.log(`Patching "${path}"...`);
|
||||
await mkdir(`${PATCHES_ROOT}/Data/Strings`, { recursive: true });
|
||||
console.log("mkdir");
|
||||
await writeFile(
|
||||
`${PATCHES_ROOT}/Data/Strings/en.yaml`,
|
||||
new Uint8Array(Buffer.from(patchedContent)),
|
||||
);
|
||||
console.log("file");
|
||||
}
|
||||
|
||||
const { dir } = parsePath(path);
|
||||
|
||||
try {
|
||||
await mkdir(`${PATCHES_ROOT}/Data/${dir}`, { recursive: true });
|
||||
} catch (error) {
|
||||
console.warn(`Failed to make directory "${dir}"`);
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(data);
|
||||
|
||||
await writeFile(
|
||||
`${PATCHES_ROOT}/Data/${path}`,
|
||||
new Uint8Array(buffer),
|
||||
);
|
||||
|
||||
bar.tick();
|
||||
}
|
||||
|
||||
console.log('patches done for patch')
|
||||
|
||||
if ("dynamicContentLocalizationsDir" in data) {
|
||||
console.log(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${
|
||||
data.dynamicContentLocalizationsDir
|
||||
}/en.yaml`,
|
||||
);
|
||||
const localizationsResponse = await fetch(
|
||||
`${assertSecret(import.meta.env.WOTB_DLC_CDN)}/dlc/${
|
||||
data.dynamicContentLocalizationsDir
|
||||
}/en.yaml`,
|
||||
);
|
||||
console.log('fetched')
|
||||
const newStrings = parseYaml(await localizationsResponse.text());
|
||||
console.log('got new strings')
|
||||
const oldStrings =
|
||||
await vfs.yaml<Record<string, string>>(`Data/Strings/en.yaml`);
|
||||
console.log('got old strings')
|
||||
const patchedStrings = { ...oldStrings, ...newStrings };
|
||||
const patchedContent = stringifyYaml(patchedStrings);
|
||||
console.log('stringified')
|
||||
|
||||
await mkdir(`${PATCHES_ROOT}/Data/Strings`, { recursive: true });
|
||||
console.log('mkdir')
|
||||
await writeFile(
|
||||
`${PATCHES_ROOT}/Data/Strings/en.yaml`,
|
||||
new Uint8Array((Buffer.from(patchedContent))),
|
||||
);
|
||||
console.log('file')
|
||||
}
|
||||
|
||||
patchIndex++;
|
||||
patchIndex++;
|
||||
}
|
||||
|
||||
vfs.dispose()
|
||||
vfs.dispose();
|
||||
process.exit(0);
|
||||
@@ -1,225 +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 = /<items path="(.+)\.xml"\/>/g;
|
||||
|
||||
async function extractPackedIcon(
|
||||
texture: sharp.Sharp,
|
||||
sizes: number[],
|
||||
context: string,
|
||||
) {
|
||||
const [left, top, width, height] = sizes;
|
||||
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.clone().extract({ left, top, width, height }).toBuffer();
|
||||
}
|
||||
|
||||
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<Mappings>(`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();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { asset, TankType, type TankDefinition } from "@blitzkit/core";
|
||||
import { TankType, type TankDefinition } from "@blitzkit/core";
|
||||
import { Flex, Text, type TextProps } from "@radix-ui/themes";
|
||||
import { uniq } from "lodash-es";
|
||||
import { forwardRef, type ReactNode } from "react";
|
||||
@@ -60,7 +60,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
||||
className="tank-search-card"
|
||||
data-provide-link={provideLink}
|
||||
style={{
|
||||
backgroundImage: `url(${asset(`flags/scratched/${tank.nation}.webp`)})`,
|
||||
backgroundImage: `url(/api/flags/scratched/${tank.nation}.webp)`,
|
||||
...style,
|
||||
}}
|
||||
{...props}
|
||||
@@ -76,7 +76,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
||||
>
|
||||
<img
|
||||
alt={unwrap(tank.name!)}
|
||||
src={asset(`icons/tanks/big/${tank.id}.webp`)}
|
||||
src={`/api/tanks/${tank.id}/icons/big.webp`}
|
||||
className="image"
|
||||
draggable={false}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import type sharp from "sharp";
|
||||
|
||||
export async function extractPackedIcon(texture: sharp.Sharp, sizes: number[]) {
|
||||
const [left, top, width, height] = sizes;
|
||||
const bounds = [left, top, width, height];
|
||||
|
||||
if (!bounds.every(Number.isInteger)) {
|
||||
throw new Error(`Invalid RIFF sprite bounds: ${bounds.join(" ")}`);
|
||||
}
|
||||
|
||||
const metadata = await texture.metadata();
|
||||
|
||||
if (metadata.width === undefined || metadata.height === undefined) {
|
||||
throw new Error(`Failed to read image dimensions`);
|
||||
}
|
||||
|
||||
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: ${bounds.join(" ")} in ${metadata.width}x${metadata.height}`,
|
||||
);
|
||||
}
|
||||
|
||||
return await texture.clone().extract({ left, top, width, height }).toBuffer();
|
||||
}
|
||||
File renamed without changes.
@@ -1,15 +1,13 @@
|
||||
import {
|
||||
ClientBlitzKitAPI,
|
||||
ServerBlitzKitAPI,
|
||||
type BlitzKitAPI,
|
||||
} from "@blitzkit/core";
|
||||
import { type BlitzKitAPI } from "@blitzkit/core";
|
||||
import { vfs } from "./vfs";
|
||||
|
||||
let _api: BlitzKitAPI;
|
||||
|
||||
if (import.meta.env.SSR) {
|
||||
const { ServerBlitzKitAPI } = await import("@blitzkit/core");
|
||||
_api = await new ServerBlitzKitAPI(vfs).init();
|
||||
} else {
|
||||
const { ClientBlitzKitAPI } = await import("@blitzkit/core");
|
||||
_api = new ClientBlitzKitAPI();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { ConsumablesCommon } 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 = /<items path="(.+)\.xml"\/>/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<Mappings>(`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 path = styleSheet.properties["bg-sprite"]
|
||||
.replace("~res:/", "")
|
||||
.replace(".psd", "")
|
||||
.replace(".txt", "");
|
||||
|
||||
paths.push({
|
||||
props: { path },
|
||||
params: { id: consumable.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 consumablesTexture = sharp(packedBuffer);
|
||||
const sizes = parsePackedSpriteRect(packedBuffer);
|
||||
|
||||
if (sizes) {
|
||||
buffer = await extractPackedIcon(consumablesTexture, sizes);
|
||||
} else {
|
||||
buffer = await consumablesTexture.trim({ threshold: 80 }).toBuffer();
|
||||
}
|
||||
} else {
|
||||
const consumablesTexture = 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 consumablesTexture
|
||||
.clone()
|
||||
.extract({
|
||||
left: sizes[0],
|
||||
top: sizes[1],
|
||||
width: sizes[2],
|
||||
height: sizes[3],
|
||||
})
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
const bytes = new Uint8Array(buffer);
|
||||
|
||||
return new Response(bytes);
|
||||
}
|
||||
@@ -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 = /<items path="(.+)\.xml"\/>/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<Mappings>(`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);
|
||||
}
|
||||
Reference in new issue
Block a user