feat: ratings stats

This commit is contained in:
TrèsAbhi committed 2024-03-20 20:40:01 -05:00
1 parent 5f28b541e3
commit ca3a1fbe3f
12 files changed
+117 -78

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ import { teal } from '@radix-ui/colors';
import { GuildMemberRoleManager } from 'discord.js'; import { GuildMemberRoleManager } from 'discord.js';
import markdownEscape from 'markdown-escape'; import markdownEscape from 'markdown-escape';
import discord from '../../discord.json' assert { type: 'json' }; import discord from '../../discord.json' assert { type: 'json' };
import { Glow } from '../components/AllStatsOverview/components/WN8Display/components/Glow'; import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
import CommandWrapper from '../components/CommandWrapper'; import CommandWrapper from '../components/CommandWrapper';
import { getAccountInfo } from '../core/blitz/getAccountInfo'; import { getAccountInfo } from '../core/blitz/getAccountInfo';
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
+5 -26
View File
@@ -52,7 +52,6 @@ export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
} }
const delta = deltaBkrlBlitzStats(statsA, accountInfo.statistics.rating!); const delta = deltaBkrlBlitzStats(statsA, accountInfo.statistics.rating!);
const scoreDiff = statsB2.score - statsA.score;
return ( return (
<CommandWrapper> <CommandWrapper>
@@ -78,35 +77,15 @@ export const ratingStatsCommand = new Promise<CommandRegistry>((resolve) => {
survived_battles: delta.survived, survived_battles: delta.survived,
wins: delta.wins, wins: delta.wins,
}} }}
supplementaryStats={{ WN8: 3000, tier: 10 }} supplementaryStats={{
type: 'rating',
score: statsB2.score,
delta: statsB2.score - statsA.score,
}}
/> />
)} )}
</CommandWrapper> </CommandWrapper>
); );
// return `# ${markdownEscape(accountInfo.nickname)}\n\n${markdownTable([
// [
// 'score',
// `${statsB2.score}${scoreDiff === 0 ? '' : ` (${scoreDiff > 0 ? '+' : ''}${scoreDiff})`}`,
// ],
// ['winrate', `${(100 * (delta.wins / delta.battles)).toFixed(0)}%`],
// ['battles', delta.battles],
// ['wins / losses', `${delta.wins} / ${delta.battles - delta.wins}`],
// ['wn8', 'impossible'],
// ['survival', `${(100 * (delta.survived / delta.battles)).toFixed(0)}%`],
// ['accuracy', `${(100 * (delta.hits / delta.shots)).toFixed(0)}%`],
// ['average damage', (delta.damageDealt / delta.battles).toFixed(0)],
// ['damager per tier', 'impossible'],
// ['damage ratio', (delta.damageDealt / delta.damageReceived).toFixed(2)],
// ['average kills', (delta.kills / delta.battles).toFixed(2)],
// ['average tier', 'impossible'],
// ['average spots', 'possible'],
// ['average xp', 'possible'],
// [
// 'kills to death ratio',
// (delta.kills / (delta.wins - delta.survived)).toFixed(2),
// ],
// ])}`;
}, },
}); });
}); });
@@ -4,6 +4,8 @@ export interface GlowProps {
rotation?: number; rotation?: number;
} }
const width = 16;
export function Glow({ export function Glow({
color, color,
direction = 'default', direction = 'default',
@@ -11,7 +13,6 @@ export function Glow({
}: GlowProps) { }: GlowProps) {
const color1 = color; const color1 = color;
const color2 = `${color}00`; const color2 = `${color}00`;
const background = `linear-gradient(${ const background = `linear-gradient(${
direction === 'default' ? 0 : 180 direction === 'default' ? 0 : 180
}deg, ${color1} 0%, ${color2} 100%)`; }deg, ${color1} 0%, ${color2} 100%)`;
@@ -25,11 +26,11 @@ export function Glow({
transform: `rotate(${rotation}deg)`, transform: `rotate(${rotation}deg)`,
}} }}
> >
<div style={{ height: 54, width: 16, background }} /> <div style={{ height: 54, width, background }} />
<div style={{ height: 72, width: 16, background }} /> <div style={{ height: 72, width, background }} />
<div style={{ height: 80, width: 16, background }} /> <div style={{ height: 80, width, background }} />
<div style={{ height: 72, width: 16, background }} /> <div style={{ height: 72, width, background }} />
<div style={{ height: 54, width: 16, background }} /> <div style={{ height: 54, width, background }} />
</div> </div>
); );
} }
@@ -0,0 +1,9 @@
import { amber, brown, purple, sandDark, slate } from '@radix-ui/colors';
export const LEAGUE_COLORS = [
purple.purple9,
sandDark.sand11,
amber.amber10,
slate.slate9,
brown.brown9,
];
@@ -1,21 +1,40 @@
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 { 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 {
wn8?: number; stats: SupplementaryStats;
locale: Locale; locale: Locale;
} }
export function WN8Display({ wn8, locale }: WN8DisplayProps) { export function HeroStat({ stats, locale }: WN8DisplayProps) {
const percentile =
wn8 === undefined ? Percentile.VeryBad : getWN8Percentile(wn8);
const color = PERCENTILE_COLORS[percentile];
const { translate } = translator(locale); const { translate } = translator(locale);
let color: string;
let heroStat: string | number;
let subtitle: string | number | undefined;
let subtitleImage: string | undefined;
if (stats.type === 'random') {
const percentile =
stats.WN8 === undefined
? Percentile.VeryBad
: getWN8Percentile(stats.WN8);
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
@@ -50,7 +69,7 @@ export function WN8Display({ wn8, locale }: WN8DisplayProps) {
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
}} }}
> >
{wn8 === undefined ? '--' : wn8.toFixed(0)} {heroStat}
</span> </span>
<span <span
style={{ style={{
@@ -58,7 +77,7 @@ export function WN8Display({ wn8, locale }: WN8DisplayProps) {
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
}} }}
> >
{translate(`common.wn8_percentile.${percentile}`)} {subtitle}
</span> </span>
</div> </div>
+26 -19
View File
@@ -1,7 +1,7 @@
import { Locale } from 'discord.js'; import { Locale } from 'discord.js';
import { SupplementaryStats } from '../../core/blitz/getAccountInfo'; import { SupplementaryStats } from '../../core/blitz/getAccountInfo';
import { translator } from '../../core/localization/translator'; import { translator } from '../../core/localization/translator';
import { WN8Display } from './components/WN8Display'; import { HeroStat } from './components/HeroStat';
import { import {
WN8SurroundingStat, WN8SurroundingStat,
WN8SurroundingStatAlign, WN8SurroundingStatAlign,
@@ -47,18 +47,18 @@ export default function AllStatsOverview({
}} }}
> >
<WN8SurroundingStat <WN8SurroundingStat
label={t`bot.commands.stats.body.battles`} label={t`bot.common.hero_stats.battles`}
value={stats.battles.toLocaleString()} value={stats.battles.toLocaleString()}
align={WN8SurroundingStatAlign.Right} align={WN8SurroundingStatAlign.Right}
/> />
<WN8SurroundingStat <WN8SurroundingStat
label={t`bot.commands.stats.body.winrate`} label={t`bot.common.hero_stats.winrate`}
value={`${(100 * (stats.wins / stats.battles)).toFixed(2)}%`} value={`${(100 * (stats.wins / stats.battles)).toFixed(2)}%`}
align={WN8SurroundingStatAlign.Right} align={WN8SurroundingStatAlign.Right}
padded padded
/> />
<WN8SurroundingStat <WN8SurroundingStat
label={t`bot.commands.stats.body.damage`} label={t`bot.common.hero_stats.damage`}
value={Math.round( value={Math.round(
stats.damage_dealt / stats.battles, stats.damage_dealt / stats.battles,
).toLocaleString()} ).toLocaleString()}
@@ -66,13 +66,13 @@ export default function AllStatsOverview({
padded padded
/> />
<WN8SurroundingStat <WN8SurroundingStat
label={t`bot.commands.stats.body.damage_ratio`} label={t`bot.common.hero_stats.damage_ratio`}
value={(stats.damage_dealt / stats.damage_received).toFixed(2)} value={(stats.damage_dealt / stats.damage_received).toFixed(2)}
align={WN8SurroundingStatAlign.Right} align={WN8SurroundingStatAlign.Right}
/> />
</div> </div>
<WN8Display locale={locale} wn8={supplementaryStats.WN8} /> <HeroStat locale={locale} stats={supplementaryStats} />
<div <div
style={{ style={{
@@ -82,30 +82,37 @@ export default function AllStatsOverview({
flex: 1, flex: 1,
}} }}
> >
{supplementaryStats.type === 'random' && (
<WN8SurroundingStat
label={t`bot.common.hero_stats.tier`}
value={
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.commands.stats.body.tier`} label={t`bot.common.hero_stats.survival`}
value={
supplementaryStats.tier ? supplementaryStats.tier.toFixed(2) : '--'
}
/>
<WN8SurroundingStat
label={t`bot.commands.stats.body.survival`}
value={`${(100 * (stats.survived_battles / stats.battles)).toFixed( value={`${(100 * (stats.survived_battles / stats.battles)).toFixed(
2, 2,
)}%`} )}%`}
padded padded
/> />
<WN8SurroundingStat <WN8SurroundingStat
label={t`bot.commands.stats.body.accuracy`} label={t`bot.common.hero_stats.accuracy`}
value={`${(100 * (stats.hits / stats.shots)).toFixed(2)}%`} value={`${(100 * (stats.hits / stats.shots)).toFixed(2)}%`}
padded padded
/> />
<WN8SurroundingStat <WN8SurroundingStat
label={t`bot.commands.stats.body.kill_ratio`} label={t`bot.common.hero_stats.kills`}
value={( value={(stats.frags / stats.battles).toFixed(2)}
stats.frags /
(stats.battles - stats.survived_battles)
).toFixed(2)}
/> />
</div> </div>
</div> </div>
+6
View File
@@ -23,6 +23,12 @@ 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>
+5 -5
View File
@@ -1,7 +1,7 @@
export const LEAGUES = [ export const LEAGUES = [
{ name: 'diamond', minScore: 5000 }, { name: 'diamond', minScore: 5000, index: 0 },
{ name: 'platinum', minScore: 4000 }, { name: 'platinum', minScore: 4000, index: 1 },
{ name: 'gold', minScore: 3000 }, { name: 'gold', minScore: 3000, index: 2 },
{ name: 'silver', minScore: 2000 }, { name: 'silver', minScore: 2000, index: 3 },
{ name: 'bronze', minScore: 0 }, { name: 'bronze', minScore: -Infinity, index: 4 },
]; ];
+12 -1
View File
@@ -23,11 +23,22 @@ export interface AllStats {
dropped_capture_points: number; dropped_capture_points: number;
} }
export interface SupplementaryStats { export interface SupplementaryStatsRandom {
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;
+5
View File
@@ -0,0 +1,5 @@
import { LEAGUES } from '../../constants/leagues';
export function getLeagueFromScore(score: number) {
return LEAGUES.find(({ minScore }) => score >= minScore)!;
}
+3 -1
View File
@@ -1,4 +1,5 @@
import { TreeType } from '../../components/Tanks'; import { TreeType } from '../../components/Tanks';
import { SupplementaryStatsRandom } 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';
@@ -50,6 +51,7 @@ export async function filterStats(
0, 0,
); );
const supplementary = { const supplementary = {
type: 'random',
WN8: WN8:
filteredOrder.reduce<number>( filteredOrder.reduce<number>(
(accumulator, id) => (accumulator, id) =>
@@ -68,7 +70,7 @@ export async function filterStats(
: accumulator, : accumulator,
0, 0,
) / battlesOfTanksWithTankDefinition, ) / battlesOfTanksWithTankDefinition,
}; } satisfies SupplementaryStatsRandom;
return { stats, supplementary, filteredOrder }; return { stats, supplementary, filteredOrder };
} }
+12 -12
View File
@@ -193,6 +193,17 @@
"get_help": "Help Discord server", "get_help": "Help Discord server",
"uncaught_error": "# Blitzkrieg ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.", "uncaught_error": "# Blitzkrieg ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon." "preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
} }
}, },
@@ -469,18 +480,7 @@
"stats": { "stats": {
"$": "stats", "$": "stats",
"description": "Quick statistics for a period", "description": "Quick statistics for a period"
"body": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kill_ratio": "Kill ratio"
}
}, },
"owned_tanks": { "owned_tanks": {