mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: start on final design
This commit is contained in:
1 parent
298b89b227
commit
afa0cfaeef
21 files changed
+178
-116
No files matched your search
+2
-2
@@ -17,7 +17,7 @@ Header {
|
|||||||
|
|
||||||
Format enum<uint8> {
|
Format enum<uint8> {
|
||||||
Minimal,
|
Minimal,
|
||||||
Comprehensive1,
|
Superset1,
|
||||||
}
|
}
|
||||||
|
|
||||||
Body<Header header> match<header.format> {
|
Body<Header header> match<header.format> {
|
||||||
@@ -28,7 +28,7 @@ Body<Header header> match<header.format> {
|
|||||||
}[header.count] entries;
|
}[header.count] entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
Comprehensive1 {
|
Superset1 {
|
||||||
primary {
|
primary {
|
||||||
uint32 id;
|
uint32 id;
|
||||||
uint16 score;
|
uint16 score;
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@
|
|||||||
"react-fuzzysort": "^1.0.1",
|
"react-fuzzysort": "^1.0.1",
|
||||||
"react-promise-suspense": "^0.3.4",
|
"react-promise-suspense": "^0.3.4",
|
||||||
"react-spinners": "^0.13.8",
|
"react-spinners": "^0.13.8",
|
||||||
"satori": "^0.10.8",
|
"satori": "^0.10.13",
|
||||||
"sharp": "0.33.1",
|
"sharp": "0.33.1",
|
||||||
"three": "0.159.0",
|
"three": "0.159.0",
|
||||||
"three-stdlib": "^2.29.4",
|
"three-stdlib": "^2.29.4",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import TitleBar from '../components/TitleBar';
|
|||||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||||
import resolveTankId from '../core/blitz/resolveTankId';
|
import resolveTankId from '../core/blitz/resolveTankId';
|
||||||
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
import { emblemURL } from '../core/blitzkrieg/emblemURL';
|
||||||
import getTankHistories from '../core/blitzkrieg/getTankHistories';
|
import getTankHistories from '../core/blitzkrieg/getTankHistories';
|
||||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
||||||
import getPlayerHistories from '../core/blitzstars/getPlayerHistories';
|
import getPlayerHistories from '../core/blitzstars/getPlayerHistories';
|
||||||
@@ -41,7 +41,7 @@ async function render(
|
|||||||
) {
|
) {
|
||||||
const accountInfo = await getAccountInfo(region, id);
|
const accountInfo = await getAccountInfo(region, id);
|
||||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||||
const logo = clan ? emblemIdToURL(clan.emblem_set_id) : undefined;
|
const logo = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||||
const histories = await (type === 'player'
|
const histories = await (type === 'player'
|
||||||
? getPlayerHistories(region, id, {
|
? getPlayerHistories(region, id, {
|
||||||
start,
|
start,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import TierWeights from '../components/TierWeights';
|
|||||||
import TitleBar from '../components/TitleBar';
|
import TitleBar from '../components/TitleBar';
|
||||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||||
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
import { emblemURL } from '../core/blitzkrieg/emblemURL';
|
||||||
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
|
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
|
||||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
||||||
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
|
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
|
||||||
@@ -40,7 +40,7 @@ async function render(
|
|||||||
) {
|
) {
|
||||||
const { nickname } = await getAccountInfo(region, id);
|
const { nickname } = await getAccountInfo(region, id);
|
||||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||||
const clanImage = clan ? emblemIdToURL(clan.emblem_set_id) : undefined;
|
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||||
const diffedTankStats = await getStatsInPeriod(
|
const diffedTankStats = await getStatsInPeriod(
|
||||||
region,
|
region,
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import AllStatsOverview from '../components/AllStatsOverview';
|
|
||||||
import CommandWrapper from '../components/CommandWrapper';
|
import CommandWrapper from '../components/CommandWrapper';
|
||||||
|
import { DeltaCaret } from '../components/DeltaCaret';
|
||||||
import NoData from '../components/NoData';
|
import NoData from '../components/NoData';
|
||||||
import TitleBar from '../components/TitleBar';
|
import TitleBar from '../components/TitleBar';
|
||||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||||
|
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||||
|
import { getLeagueFromScore } from '../core/blitz/getLeagueFromScore';
|
||||||
|
import getRatingInfo from '../core/blitz/getRatingInfo';
|
||||||
import { getRatingNeighbors } from '../core/blitz/getRatingNeighbors';
|
import { getRatingNeighbors } from '../core/blitz/getRatingNeighbors';
|
||||||
|
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
|
||||||
|
import { emblemURL } from '../core/blitzkrieg/emblemURL';
|
||||||
import { getArchivedRatingLeaderboard } from '../core/blitzkrieg/getArchivedRatingLeaderboard';
|
import { getArchivedRatingLeaderboard } from '../core/blitzkrieg/getArchivedRatingLeaderboard';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
@@ -13,6 +18,7 @@ import { translator } from '../core/localization/translator';
|
|||||||
import { deltaBkrlBlitzStats } from '../core/statistics/deltaBkrlBlitzStats';
|
import { deltaBkrlBlitzStats } from '../core/statistics/deltaBkrlBlitzStats';
|
||||||
import { BkrlFormat } from '../core/streams/bkrl';
|
import { BkrlFormat } from '../core/streams/bkrl';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
|
export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
@@ -27,6 +33,8 @@ export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { t, translate } = translator(interaction.locale);
|
const { t, translate } = translator(interaction.locale);
|
||||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||||
|
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||||
|
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||||
const leaderboard = await getArchivedRatingLeaderboard(region, 53);
|
const leaderboard = await getArchivedRatingLeaderboard(region, 53);
|
||||||
|
|
||||||
if (leaderboard.format === BkrlFormat.Minimal) {
|
if (leaderboard.format === BkrlFormat.Minimal) {
|
||||||
@@ -35,13 +43,18 @@ export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ratingInfo = await getRatingInfo(region);
|
||||||
const accountInfo = await getAccountInfo(region, id, [
|
const accountInfo = await getAccountInfo(region, id, [
|
||||||
'statistics.rating',
|
'statistics.rating',
|
||||||
]);
|
]);
|
||||||
const statsA = leaderboard.entries.find((entry) => entry.id === id);
|
const statsA = leaderboard.entries.find((entry) => entry.id === id);
|
||||||
const statsB1 = accountInfo.statistics.rating!;
|
const statsB1 = accountInfo.statistics.rating!;
|
||||||
const [statsB2] = (await getRatingNeighbors(region, id, 0)).neighbors;
|
const [statsB2] = (await getRatingNeighbors(region, id, 0)).neighbors;
|
||||||
|
const league = getLeagueFromScore(statsB2.score);
|
||||||
|
|
||||||
|
if (ratingInfo.detail) {
|
||||||
|
throw new Error(`Rating info not available for ${region}`);
|
||||||
|
}
|
||||||
if (!statsA) {
|
if (!statsA) {
|
||||||
return t`bot.commands.rating_session.errors.unrecorded_stats`;
|
return t`bot.commands.rating_session.errors.unrecorded_stats`;
|
||||||
}
|
}
|
||||||
@@ -52,13 +65,17 @@ export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const delta = deltaBkrlBlitzStats(statsA, accountInfo.statistics.rating!);
|
const delta = deltaBkrlBlitzStats(
|
||||||
|
statsA,
|
||||||
|
accountInfo.statistics.rating!,
|
||||||
|
statsB2,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommandWrapper>
|
<CommandWrapper>
|
||||||
<TitleBar
|
<TitleBar
|
||||||
title={accountInfo.nickname}
|
title={accountInfo.nickname}
|
||||||
// image={clanImage} // TODO: add league icon here
|
image={clanImage}
|
||||||
description={t`bot.commands.rating_session.body.subtitle`}
|
description={t`bot.commands.rating_session.body.subtitle`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -66,24 +83,84 @@ export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<NoData type="battles_in_season" locale={interaction.locale} />
|
<NoData type="battles_in_season" locale={interaction.locale} />
|
||||||
)}
|
)}
|
||||||
{delta.battles > 0 && (
|
{delta.battles > 0 && (
|
||||||
<AllStatsOverview
|
<div
|
||||||
locale={interaction.locale}
|
style={{
|
||||||
stats={{
|
padding: 16,
|
||||||
battles: delta.battles,
|
backgroundColor: theme.colors.appBackground2_purple,
|
||||||
damage_dealt: delta.damageDealt,
|
borderRadius: 4,
|
||||||
damage_received: delta.damageReceived,
|
border: `1px solid ${theme.colors.componentInteractive_purple}`,
|
||||||
frags: delta.kills,
|
display: 'flex',
|
||||||
hits: delta.hits,
|
|
||||||
shots: delta.shots,
|
|
||||||
survived_battles: delta.survived,
|
|
||||||
wins: delta.wins,
|
|
||||||
}}
|
}}
|
||||||
supplementaryStats={{
|
>
|
||||||
type: 'rating',
|
<div
|
||||||
score: statsB2.score,
|
style={{
|
||||||
delta: statsB2.score - statsA.score,
|
display: 'flex',
|
||||||
}}
|
gap: 8,
|
||||||
/>
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
src={normalizeLeagueIcon(
|
||||||
|
ratingInfo.leagues[league.index].big_icon,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: theme.colors.textHighContrast_purple,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 900,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{statsB2.score.toLocaleString(interaction.locale)}
|
||||||
|
</span>
|
||||||
|
{delta.score !== 0 && (
|
||||||
|
<>
|
||||||
|
<DeltaCaret delta={delta.score} />
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
delta.score > 0
|
||||||
|
? theme.colors.textLowContrast_green
|
||||||
|
: theme.colors.textLowContrast_red,
|
||||||
|
fontSize: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{delta.score.toLocaleString(interaction.locale)}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: theme.colors.textLowContrast_purple,
|
||||||
|
fontSize: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
#{statsB2.number.toLocaleString(interaction.locale)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</CommandWrapper>
|
</CommandWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import getTankStats from '../core/blitz/getTankStats';
|
|||||||
import resolveTankId from '../core/blitz/resolveTankId';
|
import resolveTankId from '../core/blitz/resolveTankId';
|
||||||
import { asset } from '../core/blitzkrieg/asset';
|
import { asset } from '../core/blitzkrieg/asset';
|
||||||
import { buildTechTreeLine } from '../core/blitzkrieg/buildTechTreeLine';
|
import { buildTechTreeLine } from '../core/blitzkrieg/buildTechTreeLine';
|
||||||
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
import { emblemURL } from '../core/blitzkrieg/emblemURL';
|
||||||
import { iconPng } from '../core/blitzkrieg/iconPng';
|
import { iconPng } from '../core/blitzkrieg/iconPng';
|
||||||
import { resolveAncestry } from '../core/blitzkrieg/resolveAncestry';
|
import { resolveAncestry } from '../core/blitzkrieg/resolveAncestry';
|
||||||
import { tankDefinitions } from '../core/blitzkrieg/tankDefinitions';
|
import { tankDefinitions } from '../core/blitzkrieg/tankDefinitions';
|
||||||
@@ -155,7 +155,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const line = [...techTreeLine, startingTankId].reverse();
|
const line = [...techTreeLine, startingTankId].reverse();
|
||||||
const { nickname } = await getAccountInfo(region, id);
|
const { nickname } = await getAccountInfo(region, id);
|
||||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||||
const clanImage = clan ? emblemIdToURL(clan.emblem_set_id) : undefined;
|
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||||
const costs = await Promise.all(
|
const costs = await Promise.all(
|
||||||
line.map(async (id) => {
|
line.map(async (id) => {
|
||||||
const tank = awaitedTankDefinitions[id];
|
const tank = awaitedTankDefinitions[id];
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import NoData from '../components/NoData';
|
|||||||
import TitleBar from '../components/TitleBar';
|
import TitleBar from '../components/TitleBar';
|
||||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||||
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
import { emblemURL } from '../core/blitzkrieg/emblemURL';
|
||||||
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
|
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
|
||||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
||||||
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
|
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
|
||||||
@@ -38,7 +38,7 @@ async function render(
|
|||||||
) {
|
) {
|
||||||
const { nickname } = await getAccountInfo(region, id);
|
const { nickname } = await getAccountInfo(region, id);
|
||||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||||
const clanImage = clan ? emblemIdToURL(clan.emblem_set_id) : undefined;
|
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||||
const diffedTankStats = await getStatsInPeriod(
|
const diffedTankStats = await getStatsInPeriod(
|
||||||
region,
|
region,
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import { Percentile } from '../../../../constants/percentiles';
|
import { Percentile } from '../../../../constants/percentiles';
|
||||||
import { SupplementaryStats } from '../../../../core/blitz/getAccountInfo';
|
import { SupplementaryStats } from '../../../../core/blitz/getAccountInfo';
|
||||||
import { getLeagueFromScore } from '../../../../core/blitz/getLeagueFromScore';
|
|
||||||
import { translator } from '../../../../core/localization/translator';
|
import { translator } from '../../../../core/localization/translator';
|
||||||
import getWN8Percentile from '../../../../core/statistics/getWN8Percentile';
|
import getWN8Percentile from '../../../../core/statistics/getWN8Percentile';
|
||||||
import { theme } from '../../../../stitches.config';
|
import { theme } from '../../../../stitches.config';
|
||||||
import { PERCENTILE_COLORS } from '../../../PercentileIndicator/constants';
|
import { PERCENTILE_COLORS } from '../../../PercentileIndicator/constants';
|
||||||
import { Glow } from './components/Glow';
|
import { Glow } from './components/Glow';
|
||||||
import { LEAGUE_COLORS } from './constants';
|
|
||||||
|
|
||||||
export interface WN8DisplayProps {
|
export interface WN8DisplayProps {
|
||||||
stats: SupplementaryStats;
|
stats: SupplementaryStats;
|
||||||
@@ -16,24 +14,12 @@ export interface WN8DisplayProps {
|
|||||||
|
|
||||||
export function HeroStat({ stats, locale }: WN8DisplayProps) {
|
export function HeroStat({ stats, locale }: WN8DisplayProps) {
|
||||||
const { translate } = translator(locale);
|
const { translate } = translator(locale);
|
||||||
let color: string;
|
|
||||||
let heroStat: string | number;
|
|
||||||
let subtitle: string | number | undefined;
|
|
||||||
|
|
||||||
if (stats.type === 'random') {
|
const percentile =
|
||||||
const percentile =
|
stats.WN8 === undefined ? Percentile.VeryBad : getWN8Percentile(stats.WN8);
|
||||||
stats.WN8 === undefined
|
const color = PERCENTILE_COLORS[percentile];
|
||||||
? Percentile.VeryBad
|
const heroStat = stats.WN8 === undefined ? '--' : stats.WN8.toFixed(0);
|
||||||
: getWN8Percentile(stats.WN8);
|
const subtitle = translate(`common.wn8_percentile.${percentile}`);
|
||||||
color = PERCENTILE_COLORS[percentile];
|
|
||||||
heroStat = stats.WN8 === undefined ? '--' : stats.WN8.toFixed(0);
|
|
||||||
subtitle = translate(`common.wn8_percentile.${percentile}`);
|
|
||||||
} else {
|
|
||||||
heroStat = stats.score;
|
|
||||||
const league = getLeagueFromScore(stats.score);
|
|
||||||
color = LEAGUE_COLORS[league.index];
|
|
||||||
subtitle = translate(`common.leagues.${league.name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -43,28 +29,26 @@ export function HeroStat({ stats, locale }: WN8DisplayProps) {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
gap: stats.type === 'rating' ? -20 : -16,
|
gap: -16,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Glow color={color} />
|
<Glow color={color} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: stats.type === 'rating' ? 112 : 128,
|
width: 128,
|
||||||
height: stats.type === 'rating' ? 112 : 80,
|
height: 80,
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
borderRadius: stats.type === 'rating' ? 8 : 40,
|
borderRadius: 40,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
gap: -4,
|
gap: -4,
|
||||||
transform: stats.type === 'rating' ? 'rotate(45deg)' : '',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
transform: stats.type === 'rating' ? 'rotate(-45deg)' : '',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|||||||
@@ -83,22 +83,12 @@ export default function AllStatsOverview({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{supplementaryStats.type === 'random' && (
|
<WN8SurroundingStat
|
||||||
<WN8SurroundingStat
|
label={t`bot.common.hero_stats.tier`}
|
||||||
label={t`bot.common.hero_stats.tier`}
|
value={
|
||||||
value={
|
supplementaryStats.tier ? supplementaryStats.tier.toFixed(2) : '--'
|
||||||
supplementaryStats.tier
|
}
|
||||||
? supplementaryStats.tier.toFixed(2)
|
/>
|
||||||
: '--'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{supplementaryStats.type === 'rating' && (
|
|
||||||
<WN8SurroundingStat
|
|
||||||
label={t`bot.common.hero_stats.tier`}
|
|
||||||
value="idk lol"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<WN8SurroundingStat
|
<WN8SurroundingStat
|
||||||
label={t`bot.common.hero_stats.survival`}
|
label={t`bot.common.hero_stats.survival`}
|
||||||
value={`${(100 * (stats.survived_battles / stats.battles)).toFixed(
|
value={`${(100 * (stats.survived_battles / stats.battles)).toFixed(
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export default function CommandWrapper({ children, fat }: CommandWrapperProps) {
|
|||||||
gap: 16,
|
gap: 16,
|
||||||
width: 480,
|
width: 480,
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
background: theme.colors.appBackground1,
|
position: 'relative',
|
||||||
|
background: 'url(https://i.imgur.com/zjwyGv1.png)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
interface DeltaCaretProps {
|
||||||
|
delta: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeltaCaret({ delta }: DeltaCaretProps) {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
src={
|
||||||
|
delta > 0
|
||||||
|
? 'https://i.imgur.com/gjI4a5f.png'
|
||||||
|
: 'https://i.imgur.com/lLGZygN.png'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -23,12 +23,6 @@ export default function GenericAllStats({
|
|||||||
const killsToDeathRatio =
|
const killsToDeathRatio =
|
||||||
stats.frags / (stats.battles - stats.survived_battles);
|
stats.frags / (stats.battles - stats.survived_battles);
|
||||||
|
|
||||||
if (supplementaryStats && supplementaryStats.type !== 'random') {
|
|
||||||
throw new Error(
|
|
||||||
'Generic all stats does not support random supplementary stats',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Root>
|
<Root>
|
||||||
<Row>
|
<Row>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default function TitleBar({ title, image, description }: TitleBarProps) {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
padding: '8px 0px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -23,22 +23,11 @@ export interface AllStats {
|
|||||||
dropped_capture_points: number;
|
dropped_capture_points: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SupplementaryStatsRandom {
|
export interface SupplementaryStats {
|
||||||
type: 'random';
|
|
||||||
WN8?: number;
|
WN8?: number;
|
||||||
tier?: number;
|
tier?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SupplementaryStatsRating {
|
|
||||||
type: 'rating';
|
|
||||||
score: number;
|
|
||||||
delta: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SupplementaryStats =
|
|
||||||
| SupplementaryStatsRandom
|
|
||||||
| SupplementaryStatsRating;
|
|
||||||
|
|
||||||
export interface ClanStats {
|
export interface ClanStats {
|
||||||
spotted: number;
|
spotted: number;
|
||||||
max_frags_tank_id: number;
|
max_frags_tank_id: number;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export function normalizeLeagueIcon(url: string) {
|
||||||
|
return url.startsWith('http') ? url : `https:${url}`;
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export function emblemIdToURL(emblemId: number) {
|
export function emblemURL(emblemId: number) {
|
||||||
return `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${emblemId}.png`;
|
return `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${emblemId}.png`;
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
|
import { RatingPlayer } from '../../commands/ratingLeaderboard';
|
||||||
import { RatingStats } from '../blitz/getAccountInfo';
|
import { RatingStats } from '../blitz/getAccountInfo';
|
||||||
import { BkrlComprehensive1Entry, BkrlMinimalEntry } from '../streams/bkrl';
|
import { BkrlSuperset1Entry } from '../streams/bkrl';
|
||||||
|
|
||||||
export function deltaBkrlBlitzStats(
|
export function deltaBkrlBlitzStats(
|
||||||
a: BkrlComprehensive1Entry,
|
a: BkrlSuperset1Entry,
|
||||||
b: RatingStats,
|
b1: RatingStats,
|
||||||
|
b2: RatingPlayer,
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
battles: b.battles - a.battles,
|
battles: b1.battles - a.battles,
|
||||||
damageDealt: b.damage_dealt - a.damageDealt,
|
damageDealt: b1.damage_dealt - a.damageDealt,
|
||||||
damageReceived: b.damage_received - a.damageReceived,
|
damageReceived: b1.damage_received - a.damageReceived,
|
||||||
hits: b.hits - a.hits,
|
hits: b1.hits - a.hits,
|
||||||
kills: b.frags - a.kills,
|
kills: b1.frags - a.kills,
|
||||||
shots: b.shots - a.shots,
|
shots: b1.shots - a.shots,
|
||||||
survived: b.survived_battles - a.survived,
|
survived: b1.survived_battles - a.survived,
|
||||||
wins: b.wins - a.wins,
|
wins: b1.wins - a.wins,
|
||||||
} satisfies Omit<BkrlComprehensive1Entry, keyof BkrlMinimalEntry>;
|
score: b2.score - a.score,
|
||||||
|
} satisfies Omit<BkrlSuperset1Entry, 'id'>;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TreeType } from '../../components/Tanks';
|
import { TreeType } from '../../components/Tanks';
|
||||||
import { SupplementaryStatsRandom } from '../blitz/getAccountInfo';
|
import { SupplementaryStats } from '../blitz/getAccountInfo';
|
||||||
import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
|
import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
|
||||||
import { DiffedTankStats } from '../blitzstars/getStatsInPeriod';
|
import { DiffedTankStats } from '../blitzstars/getStatsInPeriod';
|
||||||
import { tankAverages } from '../blitzstars/tankAverages';
|
import { tankAverages } from '../blitzstars/tankAverages';
|
||||||
@@ -51,7 +51,6 @@ export async function filterStats(
|
|||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
const supplementary = {
|
const supplementary = {
|
||||||
type: 'random',
|
|
||||||
WN8:
|
WN8:
|
||||||
filteredOrder.reduce<number>(
|
filteredOrder.reduce<number>(
|
||||||
(accumulator, id) =>
|
(accumulator, id) =>
|
||||||
@@ -70,7 +69,7 @@ export async function filterStats(
|
|||||||
: accumulator,
|
: accumulator,
|
||||||
0,
|
0,
|
||||||
) / battlesOfTanksWithTankDefinition,
|
) / battlesOfTanksWithTankDefinition,
|
||||||
} satisfies SupplementaryStatsRandom;
|
} satisfies SupplementaryStats;
|
||||||
|
|
||||||
return { stats, supplementary, filteredOrder };
|
return { stats, supplementary, filteredOrder };
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ import { ReadStream, WriteStream } from './buffer';
|
|||||||
|
|
||||||
export enum BkrlFormat {
|
export enum BkrlFormat {
|
||||||
Minimal,
|
Minimal,
|
||||||
Comprehensive1,
|
Superset1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BkrlMinimalEntry {
|
export interface BkrlMinimalEntry {
|
||||||
@@ -11,7 +11,7 @@ export interface BkrlMinimalEntry {
|
|||||||
score: number;
|
score: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BkrlComprehensive1Entry {
|
export interface BkrlSuperset1Entry {
|
||||||
id: number;
|
id: number;
|
||||||
score: number;
|
score: number;
|
||||||
|
|
||||||
@@ -34,8 +34,8 @@ export type BkrlDiscriminatedEntries =
|
|||||||
entries: BkrlMinimalEntry[];
|
entries: BkrlMinimalEntry[];
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
format: BkrlFormat.Comprehensive1;
|
format: BkrlFormat.Superset1;
|
||||||
entries: BkrlComprehensive1Entry[];
|
entries: BkrlSuperset1Entry[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BkrlReadStream extends ReadStream {
|
export class BkrlReadStream extends ReadStream {
|
||||||
@@ -61,9 +61,9 @@ export class BkrlReadStream extends ReadStream {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case BkrlFormat.Comprehensive1: {
|
case BkrlFormat.Superset1: {
|
||||||
return {
|
return {
|
||||||
format: BkrlFormat.Comprehensive1,
|
format: BkrlFormat.Superset1,
|
||||||
entries: times(
|
entries: times(
|
||||||
header.count,
|
header.count,
|
||||||
() =>
|
() =>
|
||||||
@@ -82,7 +82,7 @@ export class BkrlReadStream extends ReadStream {
|
|||||||
hits: this.uint32(),
|
hits: this.uint32(),
|
||||||
|
|
||||||
kills: this.uint32(),
|
kills: this.uint32(),
|
||||||
}) satisfies BkrlComprehensive1Entry,
|
}) satisfies BkrlSuperset1Entry,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ export class BkrlWriteStream extends WriteStream {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BkrlFormat.Comprehensive1: {
|
case BkrlFormat.Superset1: {
|
||||||
discriminatedEntries.entries.forEach((entry) => {
|
discriminatedEntries.entries.forEach((entry) => {
|
||||||
this.uint32(entry.id);
|
this.uint32(entry.id);
|
||||||
this.uint16(entry.score);
|
this.uint16(entry.score);
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import {
|
|||||||
amberDark,
|
amberDark,
|
||||||
amberDarkA,
|
amberDarkA,
|
||||||
blueDark,
|
blueDark,
|
||||||
|
greenDark,
|
||||||
orangeDark,
|
orangeDark,
|
||||||
purpleDark,
|
purpleDark,
|
||||||
|
redDark,
|
||||||
slate,
|
slate,
|
||||||
slateDark,
|
slateDark,
|
||||||
slateDarkA,
|
slateDarkA,
|
||||||
@@ -26,6 +28,8 @@ export const theme = {
|
|||||||
...createColors(purpleDark, 'purple'),
|
...createColors(purpleDark, 'purple'),
|
||||||
...createColors(blueDark, 'blue'),
|
...createColors(blueDark, 'blue'),
|
||||||
...createColors(tealDark, 'teal'),
|
...createColors(tealDark, 'teal'),
|
||||||
|
...createColors(greenDark, 'green'),
|
||||||
|
...createColors(redDark, 'red'),
|
||||||
},
|
},
|
||||||
|
|
||||||
borderStyles: {
|
borderStyles: {
|
||||||
|
|||||||
@@ -4984,7 +4984,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
satori@^0.10.8:
|
satori@^0.10.13:
|
||||||
version "0.10.13"
|
version "0.10.13"
|
||||||
resolved "https://registry.yarnpkg.com/satori/-/satori-0.10.13.tgz#658a9920f55268d2002819387a80a0b6d4bdc262"
|
resolved "https://registry.yarnpkg.com/satori/-/satori-0.10.13.tgz#658a9920f55268d2002819387a80a0b6d4bdc262"
|
||||||
integrity sha512-klCwkVYMQ/ZN5inJLHzrUmGwoRfsdP7idB5hfpJ1jfiJk1ErDitK8Hkc6Kll1+Ox2WtqEuGecSZLnmup3CGzvQ==
|
integrity sha512-klCwkVYMQ/ZN5inJLHzrUmGwoRfsdP7idB5hfpJ1jfiJk1ErDitK8Hkc6Kll1+Ox2WtqEuGecSZLnmup3CGzvQ==
|
||||||
|
|||||||
Reference in new issue
Block a user