import { Locale } from 'discord.js'; import { chunk } from 'lodash'; import * as Breakdown from '../components/Breakdown'; import CommandWrapper from '../components/CommandWrapper'; import NoData from '../components/NoData'; import TitleBar from '../components/TitleBar'; import { AllStats, getAccountInfo } from '../core/blitz/getAccountInfo'; import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; import getTankStats from '../core/blitz/getTankStats'; import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription'; import { TankDefinition, tankDefinitions, } from '../core/blitzkrieg/tankDefinitions'; import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod'; import { tankAverages } from '../core/blitzstars/tankAverages'; import addPeriodicFilterOptions from '../core/discord/addPeriodicFilterOptions'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteTanks from '../core/discord/autocompleteTanks'; import autocompleteUsername from '../core/discord/autocompleteUsername'; import { buttonRefresh } from '../core/discord/buttonRefresh'; import commandToURL from '../core/discord/commandToURL'; import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams'; import { getFiltersFromButton } from '../core/discord/getFiltersFromButton'; import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand'; import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton'; import resolvePeriodFromCommand, { ResolvedPeriod, } from '../core/discord/resolvePeriodFromCommand'; import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; import resolvePlayerFromCommand, { ResolvedPlayer, } from '../core/discord/resolvePlayerFromCommand'; import { translator } from '../core/localization/translator'; import calculateWN8 from '../core/statistics/calculateWN8'; import { StatFilters, filterStats } from '../core/statistics/filterStats'; import getWN8Percentile from '../core/statistics/getWN8Percentile'; import { CommandRegistry } from '../events/interactionCreate'; const ROWS_PER_PAGE = 8; const MAX_PAGES = 9; export async function renderBreakdown( { region, id }: ResolvedPlayer, { start, end, name }: ResolvedPeriod, filters: StatFilters, locale: Locale, ) { const { t, translate } = translator(locale); const awaitedTankDefinitions = await tankDefinitions; const awaitedTankAverages = await tankAverages; const statsInPeriod = await getStatsInPeriod(region, id, start, end); const { filteredOrder } = await filterStats(statsInPeriod, filters); const accountInfo = await getAccountInfo(region, id); const clanData = await getClanAccountInfo(region, id, ['clan']); const tankStats = await getTankStats(region, id); const filterDescriptions = await filtersToDescription(filters, locale); const orderedCurrentStats: AllStats[] = []; const orderedCareerStats: AllStats[] = []; const orderedCurrentWN8: (number | undefined)[] = []; const orderedCareerWN8: (number | undefined)[] = []; const orderedCareerWN8Full: (number | undefined)[] = []; filteredOrder.forEach((id) => { const tankAveragesEntry = awaitedTankAverages[id]; const current = statsInPeriod.diff[id]; const career = tankStats.find((tank) => tank.tank_id === id)!.all; const currentWN8 = tankAveragesEntry ? calculateWN8(tankAveragesEntry.all, current) : undefined; const careerWN8 = tankAveragesEntry ? calculateWN8(tankAveragesEntry.all, career) : undefined; orderedCurrentStats.push(current); orderedCareerStats.push(career); orderedCurrentWN8.push(currentWN8); orderedCareerWN8.push(careerWN8); }); tankStats.forEach((tank) => { const tankAveragesEntry = awaitedTankAverages[tank.tank_id]; orderedCareerWN8Full.push( tankAveragesEntry ? calculateWN8(tankAveragesEntry.all, tank.all) : undefined, ); }); const currentBattles = orderedCurrentStats.reduce( (accumulator, current) => accumulator + current.battles, 0, ); const careerBattles = tankStats.reduce( (accumulator, current) => accumulator + current.all.battles, 0, ); const currentWinrate = orderedCurrentStats.reduce( (accumulator, current) => accumulator + current.wins, 0, ) / currentBattles; const careerWinrate = tankStats.reduce( (accumulator, current) => accumulator + current.all.wins, 0, ) / careerBattles; const currentWN8 = orderedCurrentWN8.reduce( (accumulator, current, index) => accumulator + (current ?? 0) * orderedCurrentStats[index].battles, 0, ) / orderedCurrentStats.reduce( (accumulator, current, index) => accumulator + (orderedCurrentWN8[index] === undefined ? 0 : current.battles), 0, ); const careerWN8 = orderedCareerWN8Full.reduce( (accumulator, current) => accumulator + (current ?? 0), 0, ) / tankStats.reduce( (accumulator, current, index) => accumulator + (orderedCareerWN8Full[index] === undefined ? 0 : current.all.battles), 0, ); const currentDamage = orderedCurrentStats.reduce( (accumulator, current) => accumulator + current.damage_dealt, 0, ) / currentBattles; const careerDamage = tankStats.reduce( (accumulator, current) => accumulator + current.all.damage_dealt, 0, ) / careerBattles; const children: JSX.Element[] = []; if (filteredOrder.length > 0) { children.push( , ); filteredOrder.forEach((id, index) => { const tankDefinition = awaitedTankDefinitions[id] as | TankDefinition | undefined; const current = orderedCurrentStats[index]; const career = orderedCareerStats[index]; const currentWN8 = orderedCurrentWN8[index]; const careerWN8 = orderedCareerWN8[index]; children.push( , ); }); } const pages = chunk( children, Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE), ); if (filteredOrder.length > 0) { return pages .map((page, index) => ( {index === 0 && ( )} {page} )) .reverse(); } else { return [ , ]; } } export const breakdownCommand = new Promise( async (resolve) => { resolve({ inProduction: true, inPublic: true, command: await addPeriodicFilterOptions( createLocalizedCommand('breakdown'), (option) => option.addStringOption(addUsernameChoices), ), async handler(interaction) { const player = await resolvePlayerFromCommand(interaction); const period = resolvePeriodFromCommand(player.region, interaction); const filters = await getFiltersFromCommand(interaction); const path = commandToURL(interaction, { ...player, ...getCustomPeriodParams(interaction), ...filters, }); return [ ...(await renderBreakdown( player, period, filters, interaction.locale, )), buttonRefresh(interaction, path), await getBlitzStarsLinkButton(player.region, player.id), ]; }, autocomplete: (interaction) => { autocompleteUsername(interaction); autocompleteTanks(interaction); }, async button(interaction) { const player = await resolvePlayerFromButton(interaction); const period = resolvePeriodFromButton(player.region, interaction); const filters = getFiltersFromButton(interaction); return await renderBreakdown( player, period, filters, interaction.locale, ); }, }); }, );