import { Locale, 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 { emblemURL } from '../core/blitzkrieg/emblemURL'; import { webpToPng } from '../core/blitzkrieg/iconPng'; import { resolveAncestry } from '../core/blitzkrieg/resolveAncestry'; import { tankDefinitions } from '../core/blitzkrieg/tankDefinitions'; import { TANK_ICONS, TIER_ROMAN_NUMERALS, } from '../core/blitzkrieg/tankDefinitions/constants'; import { tankIconPng } from '../core/blitzkrieg/tankIconPng'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteTanks from '../core/discord/autocompleteTanks'; import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { localizationObject } from '../core/discord/localizationObject'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { translator } from '../core/localization/translator'; import { CommandRegistry } from '../events/interactionCreate'; import { theme } from '../stitches.config'; export const researchCommand = new Promise((resolve) => { const { t } = translator(Locale.EnglishUS); resolve({ inProduction: true, inPublic: true, command: createLocalizedCommand('research') .addStringOption((option) => option .setName(t`bot.commands.research.options.target_tank`) .setNameLocalizations( localizationObject('bot.commands.research.options.target_tank'), ) .setDescription( t`bot.commands.research.options.target_tank.description`, ) .setDescriptionLocalizations( localizationObject( 'bot.commands.research.options.target_tank.description', ), ) .setAutocomplete(true) .setRequired(true), ) .addStringOption((option) => option .setName(t`bot.commands.research.options.starting_tank`) .setNameLocalizations( localizationObject('bot.commands.research.options.starting_tank'), ) .setDescription( t`bot.commands.research.options.starting_tank.description`, ) .setDescriptionLocalizations( localizationObject( 'bot.commands.research.options.starting_tank.description', ), ) .setAutocomplete(true) .setRequired(false), ) .addStringOption(addUsernameChoices), async handler(interaction) { const { t, translate } = translator(interaction.locale); const awaitedTankDefinitions = await tankDefinitions; const targetTankId = await resolveTankId( interaction.options.getString('target-tank', true), interaction.locale, true, ); const startingTankRaw = interaction.options.getString('starting-tank'); const targetTank = awaitedTankDefinitions[targetTankId]; const { id, region } = await resolvePlayerFromCommand(interaction); let startingTankId: number; const targetTankAncestry = await resolveAncestry(targetTankId); if (startingTankRaw) { startingTankId = await resolveTankId( startingTankRaw, interaction.locale, true, ); if (targetTankId === startingTankId) { return translate('bot.commands.research.errors.start_end_equal', [ escapeMarkdown(targetTank.name), ]); } const startingTank = awaitedTankDefinitions[startingTankId]; if (startingTank.tier > targetTank.tier) { return translate('bot.commands.research.errors.unordered', [ escapeMarkdown(startingTank.name), escapeMarkdown(targetTank.name), escapeMarkdown(targetTank.name), ]); } if (!targetTankAncestry.includes(startingTankId)) { return translate('bot.commands.research.errors.tanks_not_on_line', [ escapeMarkdown(targetTank.name), escapeMarkdown(startingTank.name), ]); } } else { if (targetTank.treeType !== 'researchable') { return translate('bot.commands.research.errors.non_tech_tree', [ escapeMarkdown(targetTank.name), ]); } if (targetTank.tier === 1) { return translate('bot.commands.research.errors.tier_1', [ escapeMarkdown(targetTank.name), ]); } const tankStats = await getTankStats(region, id, interaction.locale); if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) { return translate('bot.commands.research.errors.already_researched', [ escapeMarkdown(targetTank.name), ]); } const foundAncestor = targetTankAncestry.find( (id) => awaitedTankDefinitions[id].tier === 1 || tankStats.some(({ tank_id }) => tank_id === id), ); if (foundAncestor === undefined) { throw new Error('No first owned tech tree found.'); } startingTankId = foundAncestor; } const techTreeLine = await buildTechTreeLine( startingTankId, targetTankId, ); const line = [...techTreeLine, startingTankId].reverse(); const { nickname } = await getAccountInfo(region, id); const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan; const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined; const costs = 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 { research: tank.xp, upgrades: moduleXp, purchase: tank.price.value, equipment: equipmentPriceMatrix[tank.tier].reduce( (a, b) => a + 3 * b, 0, ), }; }), ); const image = (
{t`bot.commands.research.body.total`}
Experience:{' '} {costs .reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0) .toLocaleString(interaction.locale)}
Purchase:{' '} {costs .reduce((a, b) => a + b.purchase, 0) .toLocaleString(interaction.locale)}
Equipment:{' '} {costs .reduce((a, b) => a + b.equipment, 0) .toLocaleString(interaction.locale)}
{await Promise.all( line.map(async (id, index) => { const tank = awaitedTankDefinitions[id]; return (
{TIER_ROMAN_NUMERALS[tank.tier]}
{tank.name}
{translate('bot.commands.research.body.research', [ `${costs[index].research?.toLocaleString( interaction.locale, )}`, ])}
{costs[index].upgrades > 0 && (
{translate('bot.commands.research.body.upgrades', [ costs[index].upgrades.toLocaleString(), ])}
)}
{translate('bot.commands.research.body.purchase', [ costs[index].purchase.toLocaleString( interaction.locale, ), ])}
{translate('bot.commands.research.body.equipment', [ costs[index].equipment.toLocaleString( interaction.locale, ), ])}
); }), )}
); return startingTankRaw ? image : [t`bot.commands.research.body.estimation`, image]; }, autocomplete(interaction) { autocompleteTanks(interaction, true, ['target-tank', 'starting-tank']); }, }); });