diff --git a/scripts/buildAssets/currencies.ts b/scripts/buildAssets/currencies.ts new file mode 100644 index 000000000..8764410e8 --- /dev/null +++ b/scripts/buildAssets/currencies.ts @@ -0,0 +1,39 @@ +import sharp from 'sharp'; +import { readDVPLFile } from '../../src/core/blitz/readDVPLFile'; +import { commitAssets } from '../../src/core/blitzkrieg/commitAssets'; +import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles'; +import { DATA } from './constants'; + +const ICONS = [ + ['currency_silver_m.packed.webp', 'silver'], + ['currency_premium_xl.packed.webp', 'premium'], + ['currency_gold_m.packed.webp', 'gold'], + ['currency_free-xp_xl.packed.webp', 'free-xp'], + ['currency_elite-xp_xl.packed.webp', 'elite-xp'], + ['currency_crew-xp_xl.packed.webp', 'crew-xp'], + ['currency_battle-xp_xl.packed.webp', 'xp'], +]; + +export async function currencies(production: boolean) { + console.log('Building currency icons...'); + + const changes = await Promise.all( + ICONS.map(async ([file, name]) => { + const content = ( + await sharp( + await readDVPLFile(`${DATA}/Gfx/Lobby/currency/${file}.dvpl`), + ) + .trim() + .toBuffer() + ).toString('base64'); + + return { + content, + encoding: 'base64', + path: `icons/currencies/${name}.webp`, + } satisfies FileChange; + }), + ); + + await commitAssets('currency icons', changes, production); +} diff --git a/scripts/buildAssets/definitions.ts b/scripts/buildAssets/definitions.ts index 8ff6eb0b9..d6dd7b58f 100644 --- a/scripts/buildAssets/definitions.ts +++ b/scripts/buildAssets/definitions.ts @@ -1,4 +1,4 @@ -import { readdir } from 'fs/promises'; +import { readdir, writeFile } from 'fs/promises'; import { parse as parsePath } from 'path'; import { Vector3Tuple } from 'three'; import { parse as parseYaml } from 'yaml'; @@ -369,10 +369,6 @@ export async function definitions(production: boolean) { ); const tankXps = new Map(); - const gunXps = new Map(); - const turretXps = new Map(); - const engineXps = new Map(); - const trackXps = new Map(); await Promise.all( nations.map(async (nation) => { @@ -406,6 +402,10 @@ export async function definitions(production: boolean) { ); for (const tankKey in tankList.root) { + const gunXps = new Map(); + const turretXps = new Map(); + const engineXps = new Map(); + const trackXps = new Map(); const tank = tankList.root[tankKey]; const tankPrice: TankDefinitionPrice = typeof tank.price === 'number' @@ -891,28 +891,28 @@ export async function definitions(production: boolean) { }); }); }); + + Object.values(tankDefinitions[tankId].turrets).forEach((turret) => { + turret.xp = turretXps.get(turret.id); + + Object.values(turret.guns).forEach((gun) => { + gun.xp = gunXps.get(gun.id); + }); + }); + + Object.values(tankDefinitions[tankId].engines).forEach((engine) => { + engine.xp = engineXps.get(engine.id); + }); + + Object.values(tankDefinitions[tankId].tracks).forEach((track) => { + track.xp = trackXps.get(track.id); + }); } }), ); Object.values(tankDefinitions).forEach((tank) => { tank.xp = tankXps.get(tank.id); - - Object.values(tank.turrets).forEach((turret) => { - turret.xp = turretXps.get(turret.id); - - Object.values(turret.guns).forEach((gun) => { - gun.xp = gunXps.get(gun.id); - }); - }); - - Object.values(tank.engines).forEach((engine) => { - engine.xp = engineXps.get(engine.id); - }); - - Object.values(tank.tracks).forEach((track) => { - track.xp = trackXps.get(track.id); - }); }); Object.values(tankDefinitions).forEach((tank) => { @@ -1084,6 +1084,10 @@ export async function definitions(production: boolean) { } }); + writeFile( + 'test.tanks.cdon.lz4', + Buffer.from(superCompress(tankDefinitions), 'base64'), + ); await commitAssets( 'definitions', [ diff --git a/scripts/buildAssets/index.ts b/scripts/buildAssets/index.ts index b864c754f..643ee2b6b 100644 --- a/scripts/buildAssets/index.ts +++ b/scripts/buildAssets/index.ts @@ -14,6 +14,7 @@ import { boosterIcons } from './boosterIcons'; import { camouflageIcons } from './camouflageIcons'; import { circleFlags } from './circleFlags'; import { consumableProvisionIcons } from './consumableProvisionIcons'; +import { currencies } from './currencies'; import { definitions } from './definitions'; import { equipmentIcons } from './equipmentIcons'; import { moduleIcons } from './moduleIcons'; @@ -38,6 +39,7 @@ const methods = [ boosterIcons, tankArmors, tankModels, + currencies, ]; for (const method of methods) { diff --git a/src/commands/research.tsx b/src/commands/research.tsx index ef66a62ca..cc479c280 100644 --- a/src/commands/research.tsx +++ b/src/commands/research.tsx @@ -1,12 +1,15 @@ import { escapeMarkdown } from 'discord.js'; import CommandWrapper from '../components/CommandWrapper'; import TitleBar from '../components/TitleBar'; +import { equipmentPriceMatrix } from '../constants/equipmentPrice'; import { getAccountInfo } from '../core/blitz/getAccountInfo'; import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; import getTankStats from '../core/blitz/getTankStats'; import resolveTankId from '../core/blitz/resolveTankId'; +import { asset } from '../core/blitzkrieg/asset'; import { buildTechTreeLine } from '../core/blitzkrieg/buildTechTreeLine'; import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL'; +import { iconPng } from '../core/blitzkrieg/iconPng'; import { resolveAncestry } from '../core/blitzkrieg/resolveAncestry'; import { tankDefinitions } from '../core/blitzkrieg/tankDefinitions'; import { @@ -54,7 +57,7 @@ export const researchCommand = new Promise((resolve) => { ); if (firstOwnedTechTree === undefined) { - return `idk what to call this error lol`; + throw new Error('No first owned tech tree found.'); } const line = ( @@ -82,6 +85,48 @@ export const researchCommand = new Promise((resolve) => { {await Promise.all( line.map(async (id) => { const tank = awaitedTankDefinitions[id]; + const turretXps = new Map(); + const gunXps = new Map(); + const engineXps = new Map(); + const trackXps = new Map(); + + tank.turrets.forEach((turret) => { + if (turret.xp !== undefined) { + turretXps.set(turret.id, turret.xp); + } + + turret.guns.forEach((gun) => { + if (gun.xp !== undefined) { + gunXps.set(gun.id, gun.xp); + } + }); + }); + + tank.engines.forEach((engine) => { + if (engine.xp !== undefined) { + engineXps.set(engine.id, engine.xp); + } + }); + + tank.tracks.forEach((track) => { + if (track.xp !== undefined) { + trackXps.set(track.id, track.xp); + } + }); + + let moduleXp = 0; + turretXps.forEach((value) => { + moduleXp += value; + }); + gunXps.forEach((value) => { + moduleXp += value; + }); + engineXps.forEach((value) => { + moduleXp += value; + }); + trackXps.forEach((value) => { + moduleXp += value; + }); return (
((resolve) => { flex: 1, display: 'flex', flexDirection: 'column', + justifyContent: 'center', gap: 8, padding: '16px 0', }} > - Research: 144,000 - Upgrades: 62,000 - Purchase: 1,500,000 - Equipment: 450,000 +
+ + + Research:{' '} + {tank.xp?.toLocaleString(interaction.locale)} + +
+ + {moduleXp > 0 && ( +
+ + + Upgrades: {moduleXp.toLocaleString()} + +
+ )} + +
+ + + Purchase:{' '} + {tank.price.value.toLocaleString(interaction.locale)} + +
+ +
+ + + Equipment:{' '} + {equipmentPriceMatrix[tank.tier] + .reduce((a, b) => a + 3 * b, 0) + .toLocaleString(interaction.locale)} + +
); diff --git a/src/constants/equipmentPrice.ts b/src/constants/equipmentPrice.ts new file mode 100644 index 000000000..115d05fd8 --- /dev/null +++ b/src/constants/equipmentPrice.ts @@ -0,0 +1,15 @@ +import { Vector3Tuple } from 'three'; +import { Tier } from '../core/blitzkrieg/tankDefinitions'; + +export const equipmentPriceMatrix: Record = { + 1: [100, 200, 300], + 2: [200, 300, 400], + 3: [400, 500, 600], + 4: [4000, 5000, 6000], + 5: [40_000, 50_000, 60_000], + 6: [100_000, 120_000, 140_000], + 7: [150_000, 170_000, 190_000], + 8: [200_000, 225_000, 250_000], + 9: [250_000, 275_000, 300_000], + 10: [300_000, 350_000, 400_000], +}; diff --git a/src/core/blitzkrieg/iconPng.ts b/src/core/blitzkrieg/iconPng.ts new file mode 100644 index 000000000..7a42c16e9 --- /dev/null +++ b/src/core/blitzkrieg/iconPng.ts @@ -0,0 +1,14 @@ +import sharp from 'sharp'; + +const cache: Record = {}; + +export async function iconPng(url: string) { + if (cache[url]) return cache[url]; + + const imageResponse = await fetch(url); + const imageBufferWEBP = await imageResponse.arrayBuffer(); + const imageBufferPNG = await sharp(imageBufferWEBP).png().toBuffer(); + cache[url] = `data:image/png;base64,${imageBufferPNG.toString('base64')}`; + + return cache[url]; +} diff --git a/src/core/blitzkrieg/tankDefinitions/index.ts b/src/core/blitzkrieg/tankDefinitions/index.ts index 5891d2ba0..8d3afa5b8 100644 --- a/src/core/blitzkrieg/tankDefinitions/index.ts +++ b/src/core/blitzkrieg/tankDefinitions/index.ts @@ -1,8 +1,8 @@ +import { readFile } from 'fs/promises'; import { deburr } from 'lodash'; import { Vector2Tuple } from 'three'; import { TankClass, TreeType } from '../../../components/Tanks'; -import { asset } from '../asset'; -import { fetchCdonLz4 } from '../fetchCdonLz4'; +import { superDecompress } from '../superDecompress'; import { TIERS } from './constants'; export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he'; @@ -142,14 +142,14 @@ export type ShellDefinition = { explosionRadius?: number; }; -export const tankDefinitions = fetchCdonLz4( - asset('definitions/tanks.cdon.lz4'), -); - -// export const tankDefinitions = readFile('test.tanks.cdon.lz4').then((data) => -// superDecompress(data.buffer), +// export const tankDefinitions = fetchCdonLz4( +// asset('definitions/tanks.cdon.lz4'), // ); +export const tankDefinitions = readFile('test.tanks.cdon.lz4').then((data) => + superDecompress(data.buffer), +); + const entries = new Promise(async (resolve) => { resolve(Object.entries(await tankDefinitions).map(([, entry]) => entry)); }); diff --git a/src/core/blitzkrieg/tankIconPng.ts b/src/core/blitzkrieg/tankIconPng.ts index 1fb197cfb..97b076bf9 100644 --- a/src/core/blitzkrieg/tankIconPng.ts +++ b/src/core/blitzkrieg/tankIconPng.ts @@ -1,9 +1,6 @@ -import sharp from 'sharp'; +import { iconPng } from './iconPng'; import { tankIcon } from './tankIcon'; -export async function tankIconPng(id: number) { - const imageResponse = await fetch(tankIcon(id)); - const imageBufferWEBP = await imageResponse.arrayBuffer(); - const imageBufferPNG = await sharp(imageBufferWEBP).png().toBuffer(); - return `data:image/png;base64,${imageBufferPNG.toString('base64')}`; +export function tankIconPng(id: number) { + return iconPng(tankIcon(id)); }