feat: equipment icons

This commit is contained in:
TrèsAbhi committed 2024-01-16 16:58:00 -06:00
1 parent a741cc305a
commit 5adc69977b
20 files changed
+428 -218

No files matched your search

+2
View File
@@ -26,6 +26,7 @@
"debugflags",
"deburr",
"decalmask",
"defence",
"diacriticless",
"donotremove",
"drei",
@@ -51,6 +52,7 @@
"loddist",
"lods",
"metallicness",
"minitelrose",
"MIPMAPCOUNT",
"modeltransform",
"MURICA",
+2 -2
View File
@@ -10,5 +10,5 @@
- Maddox: game file decompression
- Pyogenics: tank model decompression
- aeson000: game files layout
- ChickenMan7777: 3-caliber rule edge cases
- \_34, HisRoyalFatness, Synx, xkdkw: embed experience
- minitelrose, ChickenMan7777: 3-caliber rule edge cases
- \_34, HisRoyalFatness, Synx, xkdkw: embed UX
+1 -1
View File
@@ -62,7 +62,7 @@
"react-promise-suspense": "^0.3.4",
"react-spinners": "^0.13.8",
"satori": "^0.10.8",
"sharp": "0.33.1",
"sharp": "^0.33.2",
"three": "^0.160.0",
"three-custom-shader-material": "^5.4.0",
"three-stdlib": "^2.29.1",
+3 -3
View File
@@ -3,10 +3,10 @@ import { readBase64DVPL } from '../../src/core/blitz/readBase64DVPL';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
export async function circleFlags() {
const flags = await readdir(`${DATA}/${DOI.flags}`);
const flags = await readdir(`${DATA}/${POI.flags}`);
commitMultipleFiles(
'tresabhi',
@@ -21,7 +21,7 @@ export async function circleFlags() {
!flag.endsWith('@2x.packed.webp.dvpl'),
)
.map(async (flag) => {
const content = await readBase64DVPL(`${DATA}/${DOI.flags}/${flag}`);
const content = await readBase64DVPL(`${DATA}/${POI.flags}/${flag}`);
const name = flag.match(/flag_profile-stat_(.+)\.packed\.webp/)![1];
return {
+9 -1
View File
@@ -1,6 +1,7 @@
export const DATA =
'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data';
export const DOI = {
export const POI = {
vehicleDefinitions: 'XML/item_defs/vehicles',
strings: 'Strings',
tankParameters: '3d/Tanks/Parameters',
@@ -11,4 +12,11 @@ export const DOI = {
bigShellIcons: 'Gfx/Shared/tank-supply/ammunition/big',
moduleIcons: 'Gfx/UI/ModulesTechTree',
collisionMeshes: '3d/Tanks/CollisionMeshes',
optionalDevices: 'XML/item_defs/vehicles/common/optional_devices.xml',
optionalDeviceSlots:
'XML/item_defs/vehicles/common/optional_device_slots.xml',
optionalDeviceImageMappings:
'UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml',
optionalDevicesImage:
'Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp',
} as const;
+54 -8
View File
@@ -17,7 +17,7 @@ import {
TankDefinitions,
Tier,
} from '../../src/core/blitzkrieg/tankDefinitions';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
type BlitzTankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank';
interface Strings {
@@ -49,6 +49,7 @@ type VehicleDefinitionArmor = Record<
>;
interface VehicleDefinitions {
invisibility: { moving: number; still: number; firePenalty: number };
optDevicePreset: string;
hull: {
armor: VehicleDefinitionArmor;
turretPositions: { turret: string };
@@ -151,6 +152,42 @@ interface GunDefinitionsList {
};
}
export interface OptionalDevices {
[key: string]: {
id: number;
userString: string;
description: string;
icon: string;
script: unknown;
display_params: unknown;
};
}
interface OptionalDeviceSlots {
presets: {
[key: string]: {
level0: OptionalDeviceSlotRow;
level1: OptionalDeviceSlotRow;
level2: OptionalDeviceSlotRow;
};
};
}
interface OptionalDeviceSlotRow {
attack: {
device0: string;
device1: string;
};
defence: {
device0: string;
device1: string;
};
support: {
device0: string;
device1: string;
};
}
const blitzTankTypeToBlitzkrieg: Record<BlitzTankType, TankType> = {
'AT-SPG': 'tank_destroyer',
lightTank: 'light',
@@ -170,32 +207,38 @@ export async function buildDefinitions() {
const tankDefinitions: TankDefinitions = {};
const modelDefinitions: ModelDefinitions = {};
const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then(
const nations = await readdir(`${DATA}/${POI.vehicleDefinitions}`).then(
(nations) => nations.filter((nation) => nation !== 'common'),
);
const strings = await readYAMLDVPL<Strings>(
`${DATA}/${DOI.strings}/${LANGUAGE}.yaml.dvpl`,
`${DATA}/${POI.strings}/${LANGUAGE}.yaml.dvpl`,
);
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
`${DATA}/${POI.optionalDevices}`,
);
const optionalDeviceSlots = await readXMLDVPL<{
root: OptionalDeviceSlots;
}>(`${DATA}/${POI.optionalDeviceSlots}`);
await Promise.all(
nations.map(async (nation) => {
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
);
const turretList = await readXMLDVPL<{
root: TurretDefinitionsList;
}>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/components/turrets.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/components/turrets.xml.dvpl`,
);
const gunList = await readXMLDVPL<{
root: GunDefinitionsList;
}>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/components/guns.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/components/guns.xml.dvpl`,
);
const shellList = await readXMLDVPL<{
root: ShellDefinitionsList;
}>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/components/shells.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/components/shells.xml.dvpl`,
);
for (const tankKey in tankList.root) {
@@ -205,7 +248,7 @@ export async function buildDefinitions() {
? { type: 'credits', value: tank.price }
: { type: 'gold', value: tank.price['#text'] };
const tankDefinition = await readXMLDVPL<{ root: VehicleDefinitions }>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/${tankKey}.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/${tankKey}.xml.dvpl`,
);
const turretOrigin = tankDefinition.root.hull.turretPositions.turret
.split(' ')
@@ -215,6 +258,7 @@ export async function buildDefinitions() {
const hullArmor: ModelArmor = { thickness: {} };
const trackArmorRaw = Object.values(tankDefinition.root.chassis).at(-1)!
.armor.leftTrack;
const equipment = tankDefinition.root.optDevicePreset;
Object.keys(tankDefinition.root.hull.armor)
.filter((name) => name.startsWith('armor_'))
@@ -239,6 +283,7 @@ export async function buildDefinitions() {
tankDefinitions[tankId] = {
id: tankId,
equipment,
name:
(tank.shortUserString
? strings[tank.shortUserString]
@@ -492,6 +537,7 @@ export async function buildDefinitions() {
}),
);
return;
console.log('Committing definitions...');
await commitMultipleFiles(
'tresabhi',
+101
View File
@@ -0,0 +1,101 @@
import sharp from 'sharp';
import { readDVPLFile } from '../../src/core/blitz/readDVPLFile';
import { readStringDVPL } from '../../src/core/blitz/readStringDVPL';
import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, POI } 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...');
const changes: FileChange[] = [];
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
`${DATA}/${POI.optionalDevices}.dvpl`,
);
const mappings = await readYAMLDVPL<Mappings>(
`${DATA}/${POI.optionalDeviceImageMappings}.dvpl`,
);
const image = sharp(
await readDVPLFile(`${DATA}/${POI.optionalDevicesImage}.dvpl`),
);
await Promise.all(
Object.entries(optionalDevices.root).map(
async ([optionalDeviceKey, optionalDevice]) => {
if (optionalDeviceKey === 'nextAvailableId') return;
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...`,
);
return;
}
const configPathRaw = mapping.properties['bg-sprite'];
const configPath = configPathRaw.replace('~res:/', '');
if (configPath.startsWith('Gfx/Lobby')) {
const configPathWebp = configPath.replace('.txt', '');
const buffer = await readDVPLFile(
`${DATA}/${configPathWebp}.packed.webp.dvpl`,
);
changes.push({
path: `icons/equipment/${optionalDevice.id}.webp`,
encoding: 'base64',
content: buffer.toString('base64'),
});
} else {
const config = await readStringDVPL(`${DATA}/${configPath}.dvpl`);
const sizes = config.split('\n')[4].split(' ').map(Number);
const content = (
await image
.clone()
.extract({
left: sizes[0],
top: sizes[1],
width: sizes[2],
height: sizes[3],
})
.toBuffer()
).toString('base64');
changes.push({
path: `icons/equipment/${optionalDevice.id}.webp`,
encoding: 'base64',
content,
});
}
},
),
);
console.log('Committing equipment icons...');
commitMultipleFiles(
'tresabhi',
'blitzkrieg-assets',
'main',
'equipment icons',
changes,
true,
);
}
+5
View File
@@ -2,6 +2,7 @@ import { config } from 'dotenv';
import { argv } from 'process';
import { circleFlags } from './circleFlags';
import { buildDefinitions } from './definitions';
import { equipmentIcons } from './equipmentIcons';
import { buildModuleIcons } from './moduleIcons';
import { buildScratchedFlags } from './scratchedFlags';
import { buildShellIcons } from './shellIcons';
@@ -57,3 +58,7 @@ if (allTargets || targets?.includes('moduleIcons')) {
if (allTargets || targets?.includes('tankArmor')) {
await buildTankArmors();
}
if (allTargets || targets?.includes('equipmentIcons')) {
await equipmentIcons();
}
+3 -3
View File
@@ -3,13 +3,13 @@ import { readBase64DVPL } from '../../src/core/blitz/readBase64DVPL';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
export async function buildModuleIcons() {
console.log('Building module icons...');
const changes = await Promise.all(
(await readdir(`${DATA}/${DOI.moduleIcons}`))
(await readdir(`${DATA}/${POI.moduleIcons}`))
.filter(
(file) =>
!file.endsWith('@2x.packed.webp.dvpl') && file.startsWith('vehicle'),
@@ -18,7 +18,7 @@ export async function buildModuleIcons() {
const nameRaw = file.match(/vehicle(.+)\.packed\.webp\.dvpl/)![1];
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
const content = await readBase64DVPL(
`${DATA}/${DOI.moduleIcons}/${file}`,
`${DATA}/${POI.moduleIcons}/${file}`,
);
return {
+3 -3
View File
@@ -3,12 +3,12 @@ import { readBase64DVPL } from '../../src/core/blitz/readBase64DVPL';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
export async function buildScratchedFlags() {
console.log('Building scratched flags...');
const flags = await readdir(`${DATA}/${DOI.flags}`);
const flags = await readdir(`${DATA}/${POI.flags}`);
console.log('Committing scratched flags...');
commitMultipleFiles(
@@ -24,7 +24,7 @@ export async function buildScratchedFlags() {
!flag.endsWith('@2x.packed.webp.dvpl'),
)
.map(async (flag) => {
const content = await readBase64DVPL(`${DATA}/${DOI.flags}/${flag}`);
const content = await readBase64DVPL(`${DATA}/${POI.flags}/${flag}`);
const name = flag.match(/flag_tutor-tank_(.+)\.packed\.webp/)![1];
return {
+4 -4
View File
@@ -5,24 +5,24 @@ import { readStringDVPL } from '../../src/core/blitz/readStringDVPL';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
export async function buildShellIcons() {
console.log('Building shell icons...');
const image = sharp(
await readDVPLFile(
`${DATA}/${DOI.bigShellIcons}/texture0.packed.webp.dvpl`,
`${DATA}/${POI.bigShellIcons}/texture0.packed.webp.dvpl`,
),
);
const changes = await Promise.all(
(await readdir(`${DATA}/${DOI.bigShellIcons}`))
(await readdir(`${DATA}/${POI.bigShellIcons}`))
.filter((file) => file.endsWith('_l.txt.dvpl'))
.map(async (file) => {
const name = file.match(/(.+)_l\.txt\.dvpl/)![1];
const sizes = (
await readStringDVPL(`${DATA}/${DOI.bigShellIcons}/${file}`)
await readStringDVPL(`${DATA}/${POI.bigShellIcons}/${file}`)
)
.split('\n')[4]
.split(' ')
+3 -3
View File
@@ -6,7 +6,7 @@ import { toUniqueId } from '../../src/core/blitz/toUniqueId';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
import { VehicleDefinitionList } from './definitions';
export async function buildTankArmors() {
@@ -14,14 +14,14 @@ export async function buildTankArmors() {
const changes: FileChange[] = [];
const nodeIO = new NodeIO();
const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then(
const nations = await readdir(`${DATA}/${POI.vehicleDefinitions}`).then(
(nations) => nations.filter((nation) => nation !== 'common'),
);
await Promise.all(
nations.map(async (nation) => {
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
);
await Promise.all(
+4 -4
View File
@@ -6,7 +6,7 @@ import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
import { VehicleDefinitionList } from './definitions';
export interface TankParameters {
@@ -21,14 +21,14 @@ export async function buildTankIcons(big?: boolean, small?: boolean) {
console.log('Building tank icons...');
const changes: FileChange[] = [];
const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then(
const nations = await readdir(`${DATA}/${POI.vehicleDefinitions}`).then(
(nations) => nations.filter((nation) => nation !== 'common'),
);
await Promise.all(
nations.map(async (nation) => {
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
);
for (const tankIndex in tanks.root) {
@@ -36,7 +36,7 @@ export async function buildTankIcons(big?: boolean, small?: boolean) {
const nationVehicleId = tank.id;
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
const parameters = await readYAMLDVPL<TankParameters>(
`${DATA}/${DOI.tankParameters}/${nation}/${tankIndex}.yaml.dvpl`,
`${DATA}/${POI.tankParameters}/${nation}/${tankIndex}.yaml.dvpl`,
);
const small = `${DATA}/${parameters.resourcesPath.smallIconPath
.replace(/~res:\//, '')
+5 -5
View File
@@ -8,7 +8,7 @@ import { toUniqueId } from '../../src/core/blitz/toUniqueId';
import commitMultipleFiles, {
FileChange,
} from '../../src/core/blitzkrieg/commitMultipleFiles';
import { DATA, DOI } from './constants';
import { DATA, POI } from './constants';
import { VehicleDefinitionList } from './definitions';
import { TankParameters } from './tankIcons';
@@ -21,17 +21,17 @@ export async function buildTankModels() {
const changes: FileChange[] = [];
const nodeIO = new NodeIO();
const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then(
const nations = await readdir(`${DATA}/${POI.vehicleDefinitions}`).then(
(nations) => nations.filter((nation) => nation !== 'common'),
);
await Promise.all(
nations.map(async (nation) => {
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
);
const customization = await readXMLDVPL<{ root: VehicleCustomization }>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/customization.xml.dvpl`,
`${DATA}/${POI.vehicleDefinitions}/${nation}/customization.xml.dvpl`,
);
const baseColor = customization.root.armorColor
.split(' ')
@@ -48,7 +48,7 @@ export async function buildTankModels() {
console.log(`Building model ${id} @ ${nation}/${tankKey}`);
const parameters = await readYAMLDVPL<TankParameters>(
`${DATA}/${DOI.tankParameters}/${nation}/${tankKey}.yaml.dvpl`,
`${DATA}/${POI.tankParameters}/${nation}/${tankKey}.yaml.dvpl`,
);
const model = await extractModel(
DATA,
@@ -0,0 +1,91 @@
import { Flex, Heading } from '@radix-ui/themes';
import { produce } from 'immer';
import { Dispatch, SetStateAction } from 'react';
import { ModuleButton } from '../../../../../components/ModuleButton';
import { Duel } from '../page';
interface ConfigureProps {
duel: Duel;
setDuel: Dispatch<SetStateAction<Duel>>;
}
export function Configure({ duel, setDuel }: ConfigureProps) {
return (
<Flex gap="6" direction="column">
<Heading>Configure</Heading>
<Flex gap="4" direction="column">
<Heading size="5">Modules</Heading>
<Flex gap="2">
<Flex>
{duel.protagonist.tank.turrets.map((thisTurret, index) => {
return (
<ModuleButton
key={thisTurret.id}
selected={duel.protagonist.turret.id === thisTurret.id}
type="module"
module="turret"
tier={thisTurret.tier}
first={index === 0}
last={index === duel.protagonist.tank.turrets.length - 1}
rowChild
onClick={() => {
setDuel(
produce<Duel>((draft) => {
draft.protagonist.turret = thisTurret;
draft.protagonist.gun = thisTurret.guns.at(-1)!;
draft.protagonist.shell =
thisTurret.guns.at(-1)!.shells[0];
}),
);
}}
/>
);
})}
</Flex>
<Flex>
{duel.protagonist.turret.guns.map((thisGun, index) => {
return (
<ModuleButton
key={thisGun.id}
type="module"
module="gun"
tier={thisGun.tier}
selected={duel.protagonist.gun.id === thisGun.id}
first={index === 0}
last={index === duel.protagonist.turret.guns.length - 1}
rowChild
onClick={() => {
setDuel(
produce<Duel>((draft) => {
draft.protagonist.gun = thisGun;
draft.protagonist.shell = thisGun.shells[0];
}),
);
}}
/>
);
})}
</Flex>
</Flex>
</Flex>
<Flex gap="4" direction="column">
<Heading size="5">Camouflage</Heading>
</Flex>
<Flex gap="4" direction="column">
<Heading size="5">Consumables</Heading>
</Flex>
<Flex gap="4" direction="column">
<Heading size="5">Provisions</Heading>
</Flex>
<Flex gap="4" direction="column">
<Heading size="5">Equipment</Heading>
</Flex>
</Flex>
);
}
@@ -1,67 +0,0 @@
import { Flex } from '@radix-ui/themes';
import { produce } from 'immer';
import { Dispatch, SetStateAction } from 'react';
import { ModuleButton } from '../../../../../components/ModuleButton';
import { Duel } from '../page';
interface ModulesProps {
duel: Duel;
setDuel: Dispatch<SetStateAction<Duel>>;
}
export function Modules({ duel, setDuel }: ModulesProps) {
return (
<Flex gap="2">
<Flex>
{duel.protagonist.tank.turrets.map((thisTurret, index) => {
return (
<ModuleButton
key={thisTurret.id}
selected={duel.protagonist.turret.id === thisTurret.id}
type="module"
module="turret"
tier={thisTurret.tier}
first={index === 0}
last={index === duel.protagonist.tank.turrets.length - 1}
rowChild
onClick={() => {
setDuel(
produce<Duel>((draft) => {
draft.protagonist.turret = thisTurret;
draft.protagonist.gun = thisTurret.guns.at(-1)!;
draft.protagonist.shell = thisTurret.guns.at(-1)!.shells[0];
}),
);
}}
/>
);
})}
</Flex>
<Flex>
{duel.protagonist.turret.guns.map((thisGun, index) => {
return (
<ModuleButton
key={thisGun.id}
type="module"
module="gun"
tier={thisGun.tier}
selected={duel.protagonist.gun.id === thisGun.id}
first={index === 0}
last={index === duel.protagonist.turret.guns.length - 1}
rowChild
onClick={() => {
setDuel(
produce<Duel>((draft) => {
draft.protagonist.gun = thisGun;
draft.protagonist.shell = thisGun.shells[0];
}),
);
}}
/>
);
})}
</Flex>
</Flex>
);
}
+3 -2
View File
@@ -7,8 +7,8 @@ import PageWrapper from '../../../../components/PageWrapper';
import { tankDefinitions } from '../../../../core/blitzkrieg/tankDefinitions';
import mutateTankopedia, { DuelMember } from '../../../../stores/tankopedia';
import { AntagonistBar } from './components/AntagonistBar';
import { Configure } from './components/Configure';
import { TankDisplay } from './components/Model/TankDisplay';
import { Modules } from './components/Modules';
import { Title } from './components/Title';
export interface Duel {
@@ -83,8 +83,9 @@ export default function Page({ params }: { params: { id: string } }) {
<Title duel={duel} />
<TankDisplay duel={duel} />
<AntagonistBar duel={duel} setDuel={setDuel} />
<Modules duel={duel} setDuel={setDuel} />
</Flex>
<Configure duel={duel} setDuel={setDuel} />
</Flex>
</PageWrapper>
);
@@ -0,0 +1,22 @@
import { asset } from './asset';
type EquipmentOptions = [number, number];
type EquipmentRow = [EquipmentOptions, EquipmentOptions, EquipmentOptions];
type EquipmentRows = [EquipmentRow, EquipmentRow, EquipmentRow];
interface EquipmentDefinitions {
presets: {
[key: string]: EquipmentRows;
};
equipments: {
[key: number]: {
name: string;
};
};
}
export const equipmentDefinitions = fetch(asset('definitions/equipment.json'), {
cache: 'no-cache',
}).then(async (response) => response.json() as Promise<EquipmentDefinitions>);
+1
View File
@@ -24,6 +24,7 @@ export interface TankDefinition {
turrets: TurretDefinition[];
price: TankDefinitionPrice;
camouflage: { still: number; moving: number; firing: number };
equipment: string;
}
export type TankDefinitionPrice =
| { type: 'credits'; value: number }
+112 -112
View File
@@ -427,10 +427,10 @@
"@docsearch/css" "3.5.2"
algoliasearch "^4.19.1"
"@emnapi/runtime@^0.44.0":
version "0.44.0"
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-0.44.0.tgz#1ef702f846cfcd559d28eb7673919087ba5b63e3"
integrity sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw==
"@emnapi/runtime@^0.45.0":
version "0.45.0"
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-0.45.0.tgz#e754de04c683263f34fd0c7f32adfe718bbe4ddd"
integrity sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==
dependencies:
tslib "^2.4.0"
@@ -838,118 +838,118 @@
dependencies:
property-graph "^1.3.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.1.tgz#9d3cb0e4899b10b003608a018877b45b6db02861"
integrity sha512-esr2BZ1x0bo+wl7Gx2hjssYhjrhUsD88VQulI0FrG8/otRQUOxLWHMBd1Y1qo2Gfg2KUvXNpT0ASnV9BzJCexw==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz#0a52a82c2169112794dac2c71bfba9e90f7c5bd1"
integrity sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==
optionalDependencies:
"@img/sharp-libvips-darwin-arm64" "1.0.0"
"@img/sharp-libvips-darwin-arm64" "1.0.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.1.tgz#3ad6b275dba0ed9995ce4403fc2c59b0475a0162"
integrity sha512-YrnuB3bXuWdG+hJlXtq7C73lF8ampkhU3tMxg5Hh+E7ikxbUVOU9nlNtVTloDXz6pRHt2y2oKJq7DY/yt+UXYw==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz#982e26bb9d38a81f75915c4032539aed621d1c21"
integrity sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==
optionalDependencies:
"@img/sharp-libvips-darwin-x64" "1.0.0"
"@img/sharp-libvips-darwin-x64" "1.0.1"
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.0.tgz#8baf27b01dabba524e885c30287e1916ab978de7"
integrity sha512-VzYd6OwnUR81sInf3alj1wiokY50DjsHz5bvfnsFpxs5tqQxESoHtJO6xyksDs3RIkyhMWq2FufXo6GNSU9BMw==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz#81e83ffc2c497b3100e2f253766490f8fad479cd"
integrity sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.0.tgz#509113f649d3ea0e99b76d41437fc0049d8ba2f9"
integrity sha512-dD9OznTlHD6aovRswaPNEy8dKtSAmNo4++tO7uuR4o5VxbVAOoEQ1uSmN4iFAdQneTHws1lkTZeiXPrcCkh6IA==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz#fc1fcd9d78a178819eefe2c1a1662067a83ab1d6"
integrity sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.0.tgz#9e131cc95f9f8aa36da9addee89efead21ad9993"
integrity sha512-xTYThiqEZEZc0PRU90yVtM3KE7lw1bKdnDQ9kCTHWbqWyHOe4NpPOtMGy27YnN51q0J5dqRrvicfPbALIOeAZA==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz#26eb8c556a9b0db95f343fc444abc3effb67ebcf"
integrity sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.0.tgz#a529f68a28ab1d219907071c41ace029121d1c4f"
integrity sha512-VwgD2eEikDJUk09Mn9Dzi1OW2OJFRQK+XlBTkUNmAWPrtj8Ly0yq05DFgu1VCMx2/DqCGQVi5A1dM9hTmxf3uw==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz#2a377b959ff7dd6528deee486c25461296a4fa8b"
integrity sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.0.tgz#c26aaa9ee58b47ff901bb5f93f29c4ab3f03caf7"
integrity sha512-o9E46WWBC6JsBlwU4QyU9578G77HBDT1NInd+aERfxeOPbk0qBZHgoDsQmA2v9TbqJRWzoBPx1aLOhprBMgPjw==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz#af28ac9ba929204467ecdf843330d791e9421e10"
integrity sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.0.tgz#7a04795fbf2668b9dd7c74bf8326cb779131d809"
integrity sha512-naldaJy4hSVhWBgEjfdBY85CAa4UO+W1nx6a1sWStHZ7EUfNiuBTTN2KUYT5dH1+p/xij1t2QSXfCiFJoC5S/Q==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz#4273d182aa51912e655e1214ea47983d7c1f7f8d"
integrity sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.0.tgz#600e7322faa5ce124d3ae2cbf63808ea47678591"
integrity sha512-OdorplCyvmSAPsoJLldtLh3nLxRrkAAAOHsGWGDYfN0kh730gifK+UZb3dWORRa6EusNqCTjfXV4GxvgJ/nPDQ==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz#d150c92151cea2e8d120ad168b9c358d09c77ce8"
integrity sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==
"@img/[email protected].0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.0.tgz#af33a04e75f192c5396c6a41df8b7b7bf15a8006"
integrity sha512-FW8iK6rJrg+X2jKD0Ajhjv6y74lToIBEvkZhl42nZt563FfxkCYacrXZtd+q/sRQDypQLzY5WdLkVTbJoPyqNg==
"@img/[email protected].1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz#e297c1a4252c670d93b0f9e51fca40a7a5b6acfd"
integrity sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.1.tgz#3f419eb6f9ace924c511903a4d9034fb847de06b"
integrity sha512-59B5GRO2d5N3tIfeGHAbJps7cLpuWEQv/8ySd9109ohQ3kzyCACENkFVAnGPX00HwPTQcaBNF7HQYEfZyZUFfw==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz#af3409f801a9bee1d11d0c7e971dcd6180f80022"
integrity sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==
optionalDependencies:
"@img/sharp-libvips-linux-arm64" "1.0.0"
"@img/sharp-libvips-linux-arm64" "1.0.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.1.tgz#87e157716c55fa274dd652e71bf9a5b7c260f8ab"
integrity sha512-Ii4X1vnzzI4j0+cucsrYA5ctrzU9ciXERfJR633S2r39CiD8npqH2GMj63uFZRCFt3E687IenAdbwIpQOJ5BNA==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz#181f7466e6ac074042a38bfb679eb82505e17083"
integrity sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==
optionalDependencies:
"@img/sharp-libvips-linux-arm" "1.0.0"
"@img/sharp-libvips-linux-arm" "1.0.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.1.tgz#c554567bb211cb1e916562752e70ef65df0cdcb7"
integrity sha512-tRGrb2pHnFUXpOAj84orYNxHADBDIr0J7rrjwQrTNMQMWA4zy3StKmMvwsI7u3dEZcgwuMMooIIGWEWOjnmG8A==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz#9c171f49211f96fba84410b3e237b301286fa00f"
integrity sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==
optionalDependencies:
"@img/sharp-libvips-linux-s390x" "1.0.0"
"@img/sharp-libvips-linux-s390x" "1.0.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.1.tgz#c8623c743e2a68b460b63bd2e225b1b5a485d144"
integrity sha512-4y8osC0cAc1TRpy02yn5omBeloZZwS62fPZ0WUAYQiLhSFSpWJfY/gMrzKzLcHB9ulUV6ExFiu2elMaixKDbeg==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz#b956dfc092adc58c2bf0fae2077e6f01a8b2d5d7"
integrity sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==
optionalDependencies:
"@img/sharp-libvips-linux-x64" "1.0.0"
"@img/sharp-libvips-linux-x64" "1.0.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.1.tgz#1b17f9950246108cb22cf98f1822fbe4a5b0dc9e"
integrity sha512-D3lV6clkqIKUizNS8K6pkuCKNGmWoKlBGh5p0sLO2jQERzbakhu4bVX1Gz+RS4vTZBprKlWaf+/Rdp3ni2jLfA==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz#10e0ec5a79d1234c6a71df44c9f3b0bef0bc0f15"
integrity sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==
optionalDependencies:
"@img/sharp-libvips-linuxmusl-arm64" "1.0.0"
"@img/sharp-libvips-linuxmusl-arm64" "1.0.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.1.tgz#69e2ab197c6d6e7a09748e0f2d03244d2c2afed7"
integrity sha512-LOGKNu5w8uu1evVqUAUKTix2sQu1XDRIYbsi5Q0c/SrXhvJ4QyOx+GaajxmOg5PZSsSnCYPSmhjHHsRBx06/wQ==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz#29e0030c24aa27c38201b1fc84e3d172899fcbe0"
integrity sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==
optionalDependencies:
"@img/sharp-libvips-linuxmusl-x64" "1.0.0"
"@img/sharp-libvips-linuxmusl-x64" "1.0.1"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.1.tgz#aa6f33a8535e6bd4a66c59aeb569499db9d30043"
integrity sha512-vWI/sA+0p+92DLkpAMb5T6I8dg4z2vzCUnp8yvxHlwBpzN8CIcO3xlSXrLltSvK6iMsVMNswAv+ub77rsf25lA==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz#38d7c740a22de83a60ad1e6bcfce17462b0d4230"
integrity sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==
dependencies:
"@emnapi/runtime" "^0.44.0"
"@emnapi/runtime" "^0.45.0"
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.1.tgz#8df522d79b3c08f59e65fbe773849daa353c4f12"
integrity sha512-/xhYkylsKL05R+NXGJc9xr2Tuw6WIVl2lubFJaFYfW4/MQ4J+dgjIo/T4qjNRizrqs/szF/lC9a5+updmY9jaQ==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz#09456314e223f68e5417c283b45c399635c16202"
integrity sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==
"@img/[email protected].1":
version "0.33.1"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.1.tgz#8b56e97dc9d987b070d7530a357161d0f057c5af"
integrity sha512-XaM69X0n6kTEsp9tVYYLhXdg7Qj32vYJlAKRutxUsm1UlgQNx6BOhHwZPwukCGXBU2+tH87ip2eV1I/E8MQnZg==
"@img/[email protected].2":
version "0.33.2"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz#148e96dfd6e68747da41a311b9ee4559bb1b1471"
integrity sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==
"@isaacs/cliui@^8.0.2":
version "8.0.2"
@@ -5328,34 +5328,34 @@ [email protected]:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
[email protected].1:
version "0.33.1"
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.1.tgz#81e8778b9f5e2b195666cf56b2e5a110c2399d70"
integrity sha512-iAYUnOdTqqZDb3QjMneBKINTllCJDZ3em6WaWy7NPECM4aHncvqHRm0v0bN9nqJxMiwamv5KIdauJ6lUzKDpTQ==
sharp@^0.33.2:
version "0.33.2"
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.2.tgz#fcd52f2c70effa8a02160b1bfd989a3de55f2dfb"
integrity sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==
dependencies:
color "^4.2.3"
detect-libc "^2.0.2"
semver "^7.5.4"
optionalDependencies:
"@img/sharp-darwin-arm64" "0.33.1"
"@img/sharp-darwin-x64" "0.33.1"
"@img/sharp-libvips-darwin-arm64" "1.0.0"
"@img/sharp-libvips-darwin-x64" "1.0.0"
"@img/sharp-libvips-linux-arm" "1.0.0"
"@img/sharp-libvips-linux-arm64" "1.0.0"
"@img/sharp-libvips-linux-s390x" "1.0.0"
"@img/sharp-libvips-linux-x64" "1.0.0"
"@img/sharp-libvips-linuxmusl-arm64" "1.0.0"
"@img/sharp-libvips-linuxmusl-x64" "1.0.0"
"@img/sharp-linux-arm" "0.33.1"
"@img/sharp-linux-arm64" "0.33.1"
"@img/sharp-linux-s390x" "0.33.1"
"@img/sharp-linux-x64" "0.33.1"
"@img/sharp-linuxmusl-arm64" "0.33.1"
"@img/sharp-linuxmusl-x64" "0.33.1"
"@img/sharp-wasm32" "0.33.1"
"@img/sharp-win32-ia32" "0.33.1"
"@img/sharp-win32-x64" "0.33.1"
"@img/sharp-darwin-arm64" "0.33.2"
"@img/sharp-darwin-x64" "0.33.2"
"@img/sharp-libvips-darwin-arm64" "1.0.1"
"@img/sharp-libvips-darwin-x64" "1.0.1"
"@img/sharp-libvips-linux-arm" "1.0.1"
"@img/sharp-libvips-linux-arm64" "1.0.1"
"@img/sharp-libvips-linux-s390x" "1.0.1"
"@img/sharp-libvips-linux-x64" "1.0.1"
"@img/sharp-libvips-linuxmusl-arm64" "1.0.1"
"@img/sharp-libvips-linuxmusl-x64" "1.0.1"
"@img/sharp-linux-arm" "0.33.2"
"@img/sharp-linux-arm64" "0.33.2"
"@img/sharp-linux-s390x" "0.33.2"
"@img/sharp-linux-x64" "0.33.2"
"@img/sharp-linuxmusl-arm64" "0.33.2"
"@img/sharp-linuxmusl-x64" "0.33.2"
"@img/sharp-wasm32" "0.33.2"
"@img/sharp-win32-ia32" "0.33.2"
"@img/sharp-win32-x64" "0.33.2"
shebang-command@^2.0.0:
version "2.0.0"