mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: ratings stats
This commit is contained in:
1 parent
5f28b541e3
commit
ca3a1fbe3f
12 files changed
+117
-78
No files matched your search
@@ -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';
|
||||||
|
|||||||
@@ -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),
|
|
||||||
// ],
|
|
||||||
// ])}`;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
+7
-6
@@ -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,
|
||||||
|
];
|
||||||
+26
-7
@@ -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>
|
||||||
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 },
|
||||||
];
|
];
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { LEAGUES } from '../../constants/leagues';
|
||||||
|
|
||||||
|
export function getLeagueFromScore(score: number) {
|
||||||
|
return LEAGUES.find(({ minScore }) => score >= minScore)!;
|
||||||
|
}
|
||||||
@@ -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
@@ -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": {
|
||||||
|
|||||||
Reference in new issue
Block a user