Files
blitzkit/packages/bot/src/commands/breakdown/render.tsx
T

287 lines
9.2 KiB
TypeScript
Raw Normal View History

2024-09-07 21:45:00 -05:00
import {
2024-09-08 10:11:24 -05:00
blitzStarsTankAverages,
2024-09-07 21:45:00 -05:00
BlitzStats,
2024-09-08 10:11:24 -05:00
calculateWN8,
2024-09-07 21:45:00 -05:00
filterStats,
2024-09-08 10:17:40 -05:00
getAccountInfo,
getClanAccountInfo,
getTankStats,
2024-09-08 10:11:24 -05:00
getWN8Percentile,
2024-09-07 21:45:00 -05:00
StatFilters,
TankDefinition,
tankDefinitions,
} from '@blitzkit/core';
import { Locale } from 'discord.js';
2024-09-14 19:43:14 -05:00
import { chunk } from 'lodash-es';
2024-03-12 23:36:56 -05:00
import * as Breakdown from '../../components/Breakdown';
2024-09-08 10:11:24 -05:00
import { CommandWrapper } from '../../components/CommandWrapper';
import { NoData } from '../../components/NoData';
import { TitleBar } from '../../components/TitleBar';
2024-09-07 23:00:56 -05:00
import { filtersToDescription } from '../../core/blitzkit/filtersToDescription';
2024-09-08 10:42:34 -05:00
import { UserError } from '../../core/blitzkit/userError';
2024-09-08 10:11:24 -05:00
import { getStatsInPeriod } from '../../core/blitzstars/getStatsInPeriod';
2024-03-12 23:36:56 -05:00
import { ResolvedPeriod } from '../../core/discord/resolvePeriodFromCommand';
import { ResolvedPlayer } from '../../core/discord/resolvePlayerFromCommand';
import { translator } from '../../core/localization/translator';
2023-10-15 15:24:07 -05:00
2023-10-20 10:22:12 -05:00
const ROWS_PER_PAGE = 8;
2024-02-23 16:36:11 -06:00
const MAX_PAGES = 9;
2023-10-20 10:22:12 -05:00
2024-01-19 21:32:46 -06:00
export async function renderBreakdown(
2023-10-15 15:24:07 -05:00
{ region, id }: ResolvedPlayer,
{ start, end, name }: ResolvedPeriod,
filters: StatFilters,
locale: Locale,
2023-10-15 15:24:07 -05:00
) {
2024-03-07 17:02:37 -06:00
const { t, translate } = translator(locale);
2023-12-05 17:36:18 -06:00
const awaitedTankDefinitions = await tankDefinitions;
2024-09-07 23:00:56 -05:00
const awaitedTankAverages = await blitzStarsTankAverages;
2024-03-09 11:48:35 -06:00
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
2023-10-15 15:24:07 -05:00
const { filteredOrder } = await filterStats(statsInPeriod, filters);
const accountInfo = await getAccountInfo(region, id);
2023-10-16 20:16:23 -05:00
const clanData = await getClanAccountInfo(region, id, ['clan']);
2024-04-15 13:25:35 -05:00
const tankStats = await getTankStats(region, id);
const filterDescriptions = await filtersToDescription(filters, locale);
2024-08-10 13:29:10 -05:00
const orderedCurrentStats: BlitzStats[] = [];
const orderedCareerStats: BlitzStats[] = [];
2023-10-15 15:24:07 -05:00
const orderedCurrentWN8: (number | undefined)[] = [];
const orderedCareerWN8: (number | undefined)[] = [];
const orderedCareerWN8Full: (number | undefined)[] = [];
2024-04-15 13:25:35 -05:00
if (tankStats === null) {
throw new UserError(t`bot.common.errors.no_tank_stats`);
}
2023-10-15 15:24:07 -05:00
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<number>(
(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<number>(
(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;
2023-10-20 10:22:12 -05:00
const children: JSX.Element[] = [];
2023-10-15 15:24:07 -05:00
2023-10-20 10:22:12 -05:00
if (filteredOrder.length > 0) {
children.push(
<Breakdown.Row
key={id}
type="summary"
2024-03-07 17:02:37 -06:00
title={t`bot.commands.breakdown.body.total`}
2023-10-20 10:22:12 -05:00
stats={[
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.battles`,
2023-10-20 10:22:12 -05:00
current: currentBattles,
career: careerBattles,
},
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.winrate`,
2023-10-20 10:22:12 -05:00
current: `${(100 * currentWinrate).toFixed(2)}%`,
career: `${(100 * careerWinrate).toFixed(2)}%`,
delta: currentWinrate - careerWinrate,
},
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.wn8`,
2023-10-20 10:22:12 -05:00
current:
currentWN8 === undefined
? undefined
: Math.round(currentWN8).toLocaleString(),
career:
careerWN8 === undefined
? undefined
: Math.round(careerWN8).toLocaleString(),
percentile:
currentWN8 === undefined
? undefined
: getWN8Percentile(currentWN8),
},
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.damage`,
2023-10-20 10:22:12 -05:00
current: Math.round(currentDamage).toLocaleString(),
career: Math.round(careerDamage).toLocaleString(),
delta: currentDamage - careerDamage,
},
]}
/>,
);
2024-02-23 16:36:11 -06:00
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];
2023-10-20 10:22:12 -05:00
2024-02-23 16:36:11 -06:00
children.push(
<Breakdown.Row
key={id}
type="tank"
2024-03-07 18:30:02 -06:00
tankType={tankDefinition?.class}
2024-02-23 16:36:11 -06:00
treeType={tankDefinition?.treeType}
2024-03-07 17:02:37 -06:00
title={
tankDefinition?.name ??
translate('bot.commands.breakdown.body.unknown_tank', [`${id}`])
}
2024-02-23 16:36:11 -06:00
stats={[
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.battles`,
2024-02-23 16:36:11 -06:00
current: current.battles.toLocaleString(),
career: career.battles.toLocaleString(),
},
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.winrate`,
2024-02-23 16:36:11 -06:00
current: `${(100 * (current.wins / current.battles)).toFixed(
2,
)}%`,
career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`,
delta:
current.wins / current.battles - career.wins / career.battles,
},
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.wn8`,
2024-02-23 16:36:11 -06:00
current:
currentWN8 === undefined
? undefined
: Math.round(currentWN8).toLocaleString(),
career:
careerWN8 === undefined
? undefined
: Math.round(careerWN8).toLocaleString(),
percentile:
currentWN8 === undefined
? undefined
: getWN8Percentile(currentWN8),
},
{
2024-03-07 17:02:37 -06:00
title: t`bot.commands.breakdown.body.damage`,
2024-02-23 16:36:11 -06:00
current: Math.round(
current.damage_dealt / current.battles,
).toLocaleString(),
career: Math.round(
career.damage_dealt / career.battles,
).toLocaleString(),
delta:
current.damage_dealt / current.battles -
career.damage_dealt / career.battles,
},
]}
/>,
);
});
2023-10-20 10:22:12 -05:00
}
2023-10-15 15:24:07 -05:00
2023-10-20 10:22:12 -05:00
const pages = chunk(
children,
Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE),
2023-10-15 15:24:07 -05:00
);
2023-10-20 10:22:12 -05:00
if (filteredOrder.length > 0) {
return pages
.map((page, index) => (
2024-03-06 09:52:09 -06:00
<CommandWrapper>
2023-10-20 10:22:12 -05:00
{index === 0 && (
<TitleBar
2024-03-06 09:52:09 -06:00
title={accountInfo.nickname}
2023-10-20 10:22:12 -05:00
image={
clanData?.clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData?.clan?.emblem_set_id}.png`
: undefined
}
description={`${name} • ${filterDescriptions}`}
/>
)}
<Breakdown.Root>{page}</Breakdown.Root>
2024-03-06 09:52:09 -06:00
</CommandWrapper>
2023-10-20 10:22:12 -05:00
))
.reverse();
} else {
return [
2024-03-06 09:52:09 -06:00
<CommandWrapper>
2023-10-20 10:22:12 -05:00
<TitleBar
2024-03-06 09:52:09 -06:00
title={accountInfo.nickname}
2023-10-20 10:22:12 -05:00
image={
clanData?.clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData?.clan?.emblem_set_id}.png`
: undefined
}
description={`${name} • ${filterDescriptions}`}
/>
2024-03-06 16:26:36 -06:00
<NoData type="battles_in_period" locale={locale} />
2024-03-06 09:52:09 -06:00
</CommandWrapper>,
2023-10-20 10:22:12 -05:00
];
}
2023-10-15 15:24:07 -05:00
}