feat: consumable definitions

This commit is contained in:
TrèsAbhi committed 2024-01-16 21:49:15 -06:00
1 parent 3e3ed35fb0
commit d4e8ec125f
6 files changed
+247 -90

No files matched your search

+3
View File
@@ -11,11 +11,14 @@
"bepaint",
"BINORMAL",
"Binormals",
"bkwd",
"blitzstars",
"Camo",
"classname",
"cleandist",
"cockroackdb",
"Coef",
"cooldown",
"corsproxy",
"CUBEMAP",
"CUBETEXCOORD",
+1
View File
@@ -19,4 +19,5 @@ export const POI = {
'UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml',
optionalDevicesImage:
'Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp',
consumables: 'XML/item_defs/vehicles/common/consumables/common.xml',
} as const;
+112
View File
@@ -6,6 +6,7 @@ import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
import commitMultipleFiles from '../../src/core/blitzkrieg/commitMultipleFiles';
import { ConsumableDefinitions } from '../../src/core/blitzkrieg/consumablesDefinitions';
import {
EquipmentDefinitions,
EquipmentRow,
@@ -182,6 +183,41 @@ interface OptionalDeviceSlotRow {
[key: string]: { device0: string; device1: string };
}
interface Consumables {
[key: string]: {
id: number;
userString: string;
description: string;
icon: string;
category: string;
tags: string;
vehicleFilter: {
include: { vehicle: ConsumablesFilter };
exclude?: { vehicle: ConsumablesFilter };
};
script: {
'#text': string;
automatic?: boolean;
duration?: number;
cooldown?: number;
shotEffect?: string;
bonusValues?: { [key: string]: number };
} & Record<string, string>;
};
}
type ConsumablesFilter =
| { minLevel: number; maxLevel: number }
| { name: string }
| { extendedTags: string };
const consumableEffectSuffixes = [
'Increase',
'Factor',
'Bias',
'Coef',
'Percent',
];
const blitzTankTypeToBlitzkrieg: Record<BlitzTankType, TankType> = {
'AT-SPG': 'tank_destroyer',
lightTank: 'light',
@@ -196,6 +232,10 @@ const blitzShellKindToBLitzkrieg: Record<ShellKind, ShellType> = {
};
const LANGUAGE = 'en';
const missingStrings: Record<string, string> = {
'#artefacts:tungstentip/name': 'Tungsten Shells',
};
export async function buildDefinitions() {
console.log('Building tank definitions...');
@@ -205,9 +245,11 @@ export async function buildDefinitions() {
presets: {},
equipments: {},
};
const consumableDefinitions: ConsumableDefinitions = {};
const nations = await readdir(`${DATA}/${POI.vehicleDefinitions}`).then(
(nations) => nations.filter((nation) => nation !== 'common'),
);
const tankStringIdMap: Record<string, number> = {};
const strings = await readYAMLDVPL<Strings>(
`${DATA}/${POI.strings}/${LANGUAGE}.yaml.dvpl`,
);
@@ -217,6 +259,9 @@ export async function buildDefinitions() {
const optionalDeviceSlots = await readXMLDVPL<{
root: OptionalDeviceSlots;
}>(`${DATA}/${POI.optionalDeviceSlots}.dvpl`);
const consumables = await readXMLDVPL<{ root: Consumables }>(
`${DATA}/${POI.consumables}.dvpl`,
);
await Promise.all(
nations.map(async (nation) => {
@@ -257,6 +302,7 @@ export async function buildDefinitions() {
const trackArmorRaw = Object.values(tankDefinition.root.chassis).at(-1)!
.armor.leftTrack;
const equipment = tankDefinition.root.optDevicePreset;
tankStringIdMap[`${nation}:${tankKey}`] = tankId;
Object.keys(tankDefinition.root.hull.armor)
.filter((name) => name.startsWith('armor_'))
@@ -563,6 +609,67 @@ export async function buildDefinitions() {
},
);
Object.values(consumables.root).forEach((consumable) => {
consumableDefinitions[consumable.id] = {
id: consumable.id,
name:
strings[consumable.userString] ??
missingStrings[consumable.userString] ??
`Unknown ${consumable.id}`,
include: [],
};
const includeRaw = consumable.vehicleFilter.include.vehicle;
const excludeRaw = consumable.vehicleFilter.exclude?.vehicle;
if ('minLevel' in includeRaw) {
consumableDefinitions[consumable.id].include.push({
type: 'tier',
min: includeRaw.minLevel,
max: includeRaw.maxLevel,
});
} else if ('name' in includeRaw) {
consumableDefinitions[consumable.id].include.push({
type: 'ids',
ids: includeRaw.name.split(' ').map((key) => tankStringIdMap[key]),
});
} else throw new SyntaxError('Unhandled include type');
if (excludeRaw) {
consumableDefinitions[consumable.id].exclude = [];
if ('name' in excludeRaw) {
consumableDefinitions[consumable.id].exclude!.push({
type: 'ids',
ids: excludeRaw.name.split(' ').map((key) => tankStringIdMap[key]),
});
} else if ('extendedTags' in excludeRaw) {
consumableDefinitions[consumable.id].exclude!.push({
type: 'category',
categories: excludeRaw.extendedTags.split(' '),
});
} else throw new SyntaxError('Unhandled exclude type');
}
Object.entries({
...consumable.script,
...consumable.script.bonusValues,
}).forEach(([effectName, effect]) => {
if (
!consumableEffectSuffixes.some((suffix) => effectName.endsWith(suffix))
) {
return;
}
if (!consumableDefinitions[consumable.id].effects) {
consumableDefinitions[consumable.id].effects = {};
}
consumableDefinitions[consumable.id].effects![effectName] =
effect as number;
});
});
console.log('Committing definitions...');
await commitMultipleFiles(
'tresabhi',
@@ -585,6 +692,11 @@ export async function buildDefinitions() {
encoding: 'utf-8',
path: 'definitions/equipment.json',
},
{
content: JSON.stringify(consumableDefinitions),
encoding: 'utf-8',
path: 'definitions/consumables.json',
},
],
true,
);
@@ -23,103 +23,110 @@ export function Configure({ duel, setDuel }: ConfigureProps) {
<Flex gap="4" direction="column">
<Heading>Configure</Heading>
<Heading size="4">Modules</Heading>
<Flex gap="4" justify="between" wrap="wrap">
<Flex gap="2" direction="column">
<Heading size="4">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>
<Heading size="4">Equipment</Heading>
<Flex direction="column" gap="2">
{equipmentRows.map((equipmentRow, rowIndex) => (
<Flex gap="2">
{equipmentRow.map((equipment, columnIndex) => (
<Flex>
<ModuleButton
type="equipment"
equipment={equipment[0]}
first
rowChild
selected={equipmentMatrix[rowIndex][columnIndex] === 0}
onClick={() => {
mutateTankopedia((draft) => {
draft.equipmentMatrix[rowIndex][columnIndex] = 0;
});
}}
/>
<ModuleButton
type="equipment"
equipment={equipment[1]}
last
rowChild
selected={equipmentMatrix[rowIndex][columnIndex] === 1}
onClick={() => {
mutateTankopedia((draft) => {
draft.equipmentMatrix[rowIndex][columnIndex] = 1;
});
}}
/>
<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="2" direction="column">
<Heading size="4">Equipment</Heading>
<Flex direction="column" gap="2">
{equipmentRows.map((equipmentRow, rowIndex) => (
<Flex gap="2" key={rowIndex}>
{equipmentRow.map((equipment, columnIndex) => (
<Flex key={columnIndex}>
<ModuleButton
type="equipment"
equipment={equipment[0]}
first
rowChild
selected={equipmentMatrix[rowIndex][columnIndex] === 0}
onClick={() => {
mutateTankopedia((draft) => {
draft.equipmentMatrix[rowIndex][columnIndex] = 0;
});
}}
/>
<ModuleButton
type="equipment"
equipment={equipment[1]}
last
rowChild
selected={equipmentMatrix[rowIndex][columnIndex] === 1}
onClick={() => {
mutateTankopedia((draft) => {
draft.equipmentMatrix[rowIndex][columnIndex] = 1;
});
}}
/>
</Flex>
))}
</Flex>
))}
</Flex>
))}
</Flex>
</Flex>
{/* <Flex gap="4" direction="column">
<Heading size="5">Modules</Heading>
<Flex gap="2" direction="column">
<Heading size="4">Consumables</Heading>
</Flex>
</Flex>
{/*
<Flex gap="4" direction="column">
<Heading size="5">Camouflage</Heading>
</Flex>
+2 -1
View File
@@ -36,7 +36,8 @@ export function ModuleButton({
<Button
radius="medium"
color={selected ? undefined : 'gray'}
variant={selected ? 'solid' : 'soft'}
variant={selected ? 'surface' : 'soft'}
// variant="soft"
style={{
padding: 0,
width: 48,
@@ -0,0 +1,33 @@
import { asset } from './asset';
type ConsumableDefinitionFilter =
| {
type: 'tier';
min: number;
max: number;
}
| {
type: 'ids';
ids: number[];
}
| {
type: 'category';
categories: string[];
};
export interface ConsumableDefinitions {
[key: string]: {
id: number;
name: string;
effects?: Record<string, number>;
include: ConsumableDefinitionFilter[];
exclude?: ConsumableDefinitionFilter[];
};
}
export const consumableDefinitions = fetch(
asset('definitions/consumables.json'),
{
cache: 'no-cache',
},
).then(async (response) => response.json() as Promise<ConsumableDefinitions>);