From d4e8ec125ff3ef831e7f3908f1535336c2df198c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Tue, 16 Jan 2024 21:49:15 -0600 Subject: [PATCH] feat: consumable definitions --- .vscode/settings.json | 3 + scripts/buildAssets/constants.ts | 1 + scripts/buildAssets/definitions.ts | 112 +++++++++++ .../tankopedia/[id]/components/Configure.tsx | 185 +++++++++--------- src/components/ModuleButton.tsx | 3 +- src/core/blitzkrieg/consumablesDefinitions.ts | 33 ++++ 6 files changed, 247 insertions(+), 90 deletions(-) create mode 100644 src/core/blitzkrieg/consumablesDefinitions.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ea995f98..37a356287 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,11 +11,14 @@ "bepaint", "BINORMAL", "Binormals", + "bkwd", "blitzstars", "Camo", "classname", "cleandist", "cockroackdb", + "Coef", + "cooldown", "corsproxy", "CUBEMAP", "CUBETEXCOORD", diff --git a/scripts/buildAssets/constants.ts b/scripts/buildAssets/constants.ts index cad2c6f76..64d41be23 100644 --- a/scripts/buildAssets/constants.ts +++ b/scripts/buildAssets/constants.ts @@ -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; diff --git a/scripts/buildAssets/definitions.ts b/scripts/buildAssets/definitions.ts index f8bc1455a..9d2285001 100644 --- a/scripts/buildAssets/definitions.ts +++ b/scripts/buildAssets/definitions.ts @@ -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; + }; +} + +type ConsumablesFilter = + | { minLevel: number; maxLevel: number } + | { name: string } + | { extendedTags: string }; + +const consumableEffectSuffixes = [ + 'Increase', + 'Factor', + 'Bias', + 'Coef', + 'Percent', +]; const blitzTankTypeToBlitzkrieg: Record = { 'AT-SPG': 'tank_destroyer', lightTank: 'light', @@ -196,6 +232,10 @@ const blitzShellKindToBLitzkrieg: Record = { }; const LANGUAGE = 'en'; +const missingStrings: Record = { + '#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 = {}; const strings = await readYAMLDVPL( `${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, ); diff --git a/src/app/tools/tankopedia/[id]/components/Configure.tsx b/src/app/tools/tankopedia/[id]/components/Configure.tsx index 4df99b767..55a2aacaa 100644 --- a/src/app/tools/tankopedia/[id]/components/Configure.tsx +++ b/src/app/tools/tankopedia/[id]/components/Configure.tsx @@ -23,103 +23,110 @@ export function Configure({ duel, setDuel }: ConfigureProps) { Configure - Modules + + + Modules - - - {duel.protagonist.tank.turrets.map((thisTurret, index) => { - return ( - { - setDuel( - produce((draft) => { - draft.protagonist.turret = thisTurret; - draft.protagonist.gun = thisTurret.guns.at(-1)!; - draft.protagonist.shell = - thisTurret.guns.at(-1)!.shells[0]; - }), - ); - }} - /> - ); - })} - - - - {duel.protagonist.turret.guns.map((thisGun, index) => { - return ( - { - setDuel( - produce((draft) => { - draft.protagonist.gun = thisGun; - draft.protagonist.shell = thisGun.shells[0]; - }), - ); - }} - /> - ); - })} - - - - Equipment - - - {equipmentRows.map((equipmentRow, rowIndex) => ( - {equipmentRow.map((equipment, columnIndex) => ( - - { - mutateTankopedia((draft) => { - draft.equipmentMatrix[rowIndex][columnIndex] = 0; - }); - }} - /> - { - mutateTankopedia((draft) => { - draft.equipmentMatrix[rowIndex][columnIndex] = 1; - }); - }} - /> + + {duel.protagonist.tank.turrets.map((thisTurret, index) => { + return ( + { + setDuel( + produce((draft) => { + draft.protagonist.turret = thisTurret; + draft.protagonist.gun = thisTurret.guns.at(-1)!; + draft.protagonist.shell = + thisTurret.guns.at(-1)!.shells[0]; + }), + ); + }} + /> + ); + })} + + + + {duel.protagonist.turret.guns.map((thisGun, index) => { + return ( + { + setDuel( + produce((draft) => { + draft.protagonist.gun = thisGun; + draft.protagonist.shell = thisGun.shells[0]; + }), + ); + }} + /> + ); + })} + + + + + + Equipment + + + {equipmentRows.map((equipmentRow, rowIndex) => ( + + {equipmentRow.map((equipment, columnIndex) => ( + + { + mutateTankopedia((draft) => { + draft.equipmentMatrix[rowIndex][columnIndex] = 0; + }); + }} + /> + { + mutateTankopedia((draft) => { + draft.equipmentMatrix[rowIndex][columnIndex] = 1; + }); + }} + /> + + ))} ))} - ))} - + - {/* - Modules + + Consumables + + {/* Camouflage diff --git a/src/components/ModuleButton.tsx b/src/components/ModuleButton.tsx index 486e7aaeb..085580f84 100644 --- a/src/components/ModuleButton.tsx +++ b/src/components/ModuleButton.tsx @@ -36,7 +36,8 @@ export function ModuleButton({