mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-22 03:07:04 +02:00
feat: core chunk 5
This commit is contained in:
1 parent
9bdb14e13a
commit
ed8bab7df8
37 files changed
+91
-165
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { TreeTypeEnum } from '../../components/Tanks';
|
||||
import { tankopedia } from '../../LEGACY_core/blitz/tankopedia';
|
||||
import { tankopedia } from '../blitzstars/tankopedia';
|
||||
|
||||
export default async function getTreeType(id: number) {
|
||||
const entry = (await tankopedia)[id];
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { go } from 'fuzzysort';
|
||||
import throwError from '../../LEGACY_core/node/throwError';
|
||||
import { TANKS, tankopedia } from '../blitzstars/tankopedia';
|
||||
|
||||
export default async function resolveTankId(tank: string | number) {
|
||||
const number = typeof tank === 'string' ? parseInt(tank) : tank;
|
||||
|
||||
if (Number.isNaN(number)) {
|
||||
const searchResult = go(`${tank}`, await TANKS, {
|
||||
keys: ['name'],
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
if (searchResult.length === 0) {
|
||||
throw throwError(
|
||||
'Tank not found',
|
||||
`Could not find tank by the name "${tank}".`,
|
||||
);
|
||||
} else {
|
||||
return searchResult[0].obj.tank_id;
|
||||
}
|
||||
} else {
|
||||
if ((await tankopedia)[number]) {
|
||||
return number;
|
||||
} else {
|
||||
throw throwError(
|
||||
'Tank not found',
|
||||
`Could not find tank by the ID "${number}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { tankopedia } from '../blitzstars/tankopedia';
|
||||
|
||||
export default async function resolveTankName(tankId: number) {
|
||||
const tankopediaName = (await tankopedia)[tankId]?.name;
|
||||
return tankopediaName ? tankopediaName : `Unknown Tank ${tankId}`;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
type Context = 'website' | 'bot';
|
||||
|
||||
export const context: Context =
|
||||
typeof window === 'undefined' ? 'bot' : 'website';
|
||||
@@ -0,0 +1,27 @@
|
||||
import { TREE_TYPE_NAMES } from '../../components/Tanks';
|
||||
import resolveTankName from '../blitz/resolveTankName';
|
||||
import {
|
||||
TIER_ROMAN_NUMERALS,
|
||||
Tier,
|
||||
tankopediaInfo,
|
||||
} from '../blitzstars/tankopedia';
|
||||
import { StatFilters } from '../statistics/filterStats';
|
||||
|
||||
export async function filtersToDescription({
|
||||
nation,
|
||||
tier,
|
||||
tankType,
|
||||
treeType,
|
||||
tank,
|
||||
}: StatFilters) {
|
||||
const awaitedTankopediaInfo = await tankopediaInfo;
|
||||
const info: string[] = [];
|
||||
|
||||
if (tank) return await resolveTankName(tank);
|
||||
if (nation) info.push(awaitedTankopediaInfo.vehicle_nations[nation]);
|
||||
if (tier) info.push(`Tier ${TIER_ROMAN_NUMERALS[tier as Tier]}`);
|
||||
if (tankType) info.push(awaitedTankopediaInfo.vehicle_types[tankType]);
|
||||
if (treeType) info.push(TREE_TYPE_NAMES[treeType]);
|
||||
|
||||
return info.length === 0 ? 'No filters' : info.join(', ');
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function getPeriodNow() {
|
||||
return Date.now() / 1000;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { PeriodSize } from '../../LEGACY_core/discord/addPeriodSubCommands';
|
||||
import { Region } from '../../constants/regions';
|
||||
import getTimeDaysAgo from './getTimeDaysAgo';
|
||||
|
||||
export default function getPeriodStart(region: Region, period: PeriodSize) {
|
||||
if (period === 'career') return 0;
|
||||
if (period === 'today') return getTimeDaysAgo(region, 1);
|
||||
return getTimeDaysAgo(region, parseInt(period));
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import { Region } from '../../constants/regions';
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
Histories,
|
||||
History,
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories';
|
||||
import getTankStats from '../blitz/getTankStats';
|
||||
import { emptyAllStats } from '../blitzstars/getStatsInPeriod';
|
||||
|
||||
export interface TankHistory extends History {
|
||||
tank_id: number;
|
||||
}
|
||||
|
||||
export type TankHistories = TankHistory[];
|
||||
|
||||
export interface TankHistoryRaw extends TankHistory {
|
||||
mark_of_mastery: number;
|
||||
battle_life_time: number;
|
||||
account_id: number;
|
||||
}
|
||||
|
||||
export type TankHistoriesRaw = TankHistoryRaw[];
|
||||
|
||||
export interface GetTankHistoriesOptions extends GetHistoriesOptions {
|
||||
tankId?: number;
|
||||
}
|
||||
|
||||
export const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
||||
...getHistoriesDefaultOptions,
|
||||
tankId: undefined,
|
||||
};
|
||||
|
||||
export default async function getTankHistories(
|
||||
server: Region,
|
||||
id: number,
|
||||
options?: Partial<GetTankHistoriesOptions>,
|
||||
) {
|
||||
const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options };
|
||||
const tankHistoriesResponse = await fetch(
|
||||
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
|
||||
);
|
||||
const tankHistories = (
|
||||
(await tankHistoriesResponse.json()) as TankHistoriesRaw
|
||||
).map(
|
||||
(history) =>
|
||||
({
|
||||
all: history.all,
|
||||
last_battle_time: history.last_battle_time,
|
||||
tank_id: history.tank_id,
|
||||
}) satisfies TankHistory,
|
||||
);
|
||||
const compliantTanks: number[] = [];
|
||||
const lastBattles: Record<number, number> = {};
|
||||
const lastNonCompliantIndex: Record<number, number> = {};
|
||||
const hasCompliedOnce: Record<number, boolean> = {};
|
||||
const filtered = tankHistories.filter(
|
||||
({ last_battle_time, tank_id, all: { battles } }, index) => {
|
||||
const complies =
|
||||
last_battle_time >= mergedOptions.start &&
|
||||
last_battle_time <= mergedOptions.end &&
|
||||
(mergedOptions.tankId === undefined ||
|
||||
tank_id === mergedOptions.tankId);
|
||||
|
||||
if (complies && !hasCompliedOnce[tank_id]) {
|
||||
hasCompliedOnce[tank_id] = true;
|
||||
compliantTanks.push(tank_id);
|
||||
}
|
||||
if (!hasCompliedOnce[tank_id]) lastNonCompliantIndex[tank_id] = index;
|
||||
lastBattles[tank_id] = battles;
|
||||
|
||||
return complies;
|
||||
},
|
||||
);
|
||||
|
||||
const latestNodes: TankHistories = [];
|
||||
|
||||
if (mergedOptions.includeLatestHistories) {
|
||||
(await getTankStats(server, id)).forEach((tankHistory) => {
|
||||
if (
|
||||
tankHistory.all.battles > (lastBattles[tankHistory.tank_id] ?? 0) &&
|
||||
(mergedOptions.tankId === undefined ||
|
||||
tankHistory.tank_id === mergedOptions.tankId)
|
||||
) {
|
||||
if (!hasCompliedOnce[tankHistory.tank_id]) {
|
||||
// was played today for the first time... I think???
|
||||
hasCompliedOnce[tankHistory.tank_id] = true;
|
||||
compliantTanks.push(tankHistory.tank_id);
|
||||
}
|
||||
latestNodes.push(tankHistory);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const previousNodes: TankHistories = [];
|
||||
|
||||
if (mergedOptions.includePreviousHistories) {
|
||||
compliantTanks.forEach((tankId) => {
|
||||
if (tankId in lastNonCompliantIndex) {
|
||||
previousNodes.push(
|
||||
tankHistories[lastNonCompliantIndex[tankId]] ??
|
||||
({
|
||||
all: emptyAllStats,
|
||||
last_battle_time: -Infinity,
|
||||
tank_id: tankId,
|
||||
} satisfies TankHistory),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return [...previousNodes, ...filtered, ...latestNodes] satisfies Histories;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Region } from '../../constants/regions';
|
||||
|
||||
export const TIME_ZONE_MAPPINGS: Record<Region, number> = {
|
||||
com: -5, // Central North American Time
|
||||
eu: +1, // Central European Time
|
||||
asia: +7, // Central Asia Standard Time
|
||||
};
|
||||
|
||||
export default function getTimeDaysAgo(region: Region, daysAgo: number) {
|
||||
const now = new Date();
|
||||
if (daysAgo === 0) now.getTime() / 1000;
|
||||
|
||||
const time = new Date();
|
||||
|
||||
time.setUTCHours(-TIME_ZONE_MAPPINGS[region], 0, 0, 0);
|
||||
if (time > now) time.setUTCDate(time.getUTCDate() - 1);
|
||||
time.setUTCDate(time.getUTCDate() - daysAgo + 1);
|
||||
|
||||
return time.getTime() / 1000;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import linkButton from '../../LEGACY_core/discord/linkButton';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { getAccountInfo } from '../blitz/getAccountInfo';
|
||||
|
||||
export async function getBlitzStarsLinkButton(
|
||||
region: Region,
|
||||
id: number,
|
||||
tankId?: number,
|
||||
) {
|
||||
const { nickname } = await getAccountInfo(region, id);
|
||||
|
||||
return linkButton(
|
||||
`https://www.blitzstars.com/player/${region}/${nickname}${
|
||||
tankId ? `/tank/${tankId!}` : ''
|
||||
}`,
|
||||
'BlitzStars',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Region } from '../../constants/regions';
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
Histories,
|
||||
History,
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories';
|
||||
import { getAccountInfo } from '../blitz/getAccountInfo';
|
||||
import { PlayerStats } from './getPlayerStats';
|
||||
import { emptyAllStats } from './getStatsInPeriod';
|
||||
|
||||
export default async function getPlayerHistories(
|
||||
server: Region,
|
||||
id: number,
|
||||
options?: Partial<GetHistoriesOptions>,
|
||||
) {
|
||||
const mergedOptions = { ...getHistoriesDefaultOptions, ...options };
|
||||
const response = await fetch(
|
||||
`https://www.blitzstars.com/api/playerstats/${id}`,
|
||||
);
|
||||
const playerHistories = ((await response.json()) as PlayerStats).map(
|
||||
(history) =>
|
||||
({
|
||||
all: history.statistics.all,
|
||||
last_battle_time: history.last_battle_time,
|
||||
}) satisfies History,
|
||||
);
|
||||
let lastNonCompliantIndex = -1;
|
||||
let hasCompliedOnce = false;
|
||||
const filtered = playerHistories.filter(({ last_battle_time }, index) => {
|
||||
const complies =
|
||||
last_battle_time >= mergedOptions.start &&
|
||||
last_battle_time <= mergedOptions.end;
|
||||
|
||||
if (complies && !hasCompliedOnce) hasCompliedOnce = true;
|
||||
if (!hasCompliedOnce) lastNonCompliantIndex = index;
|
||||
|
||||
return complies;
|
||||
});
|
||||
|
||||
const latestNodes: Histories = [];
|
||||
|
||||
if (mergedOptions.includeLatestHistories) {
|
||||
const { last_battle_time, statistics } = await getAccountInfo(server, id);
|
||||
|
||||
latestNodes.push({ all: statistics.all, last_battle_time });
|
||||
}
|
||||
|
||||
const previousNodes: Histories = [];
|
||||
|
||||
if (mergedOptions.includePreviousHistories) {
|
||||
if (lastNonCompliantIndex !== -1) {
|
||||
previousNodes.push(
|
||||
playerHistories[lastNonCompliantIndex] ??
|
||||
({
|
||||
all: emptyAllStats,
|
||||
last_battle_time: -Infinity,
|
||||
} satisfies History),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [...latestNodes, ...filtered, ...previousNodes] satisfies Histories;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import { Region } from '../../constants/regions';
|
||||
import { AllStats } from '../blitz/getAccountInfo';
|
||||
import getTankHistories, {
|
||||
TankHistories,
|
||||
TankHistoryRaw,
|
||||
} from '../blitzkrieg/getTankHistories';
|
||||
|
||||
export const emptyAllStats: AllStats = {
|
||||
battles: 0,
|
||||
capture_points: 0,
|
||||
damage_dealt: 0,
|
||||
damage_received: 0,
|
||||
dropped_capture_points: 0,
|
||||
frags: 0,
|
||||
frags8p: 0,
|
||||
hits: 0,
|
||||
losses: 0,
|
||||
max_frags: 0,
|
||||
max_xp: 0,
|
||||
shots: 0,
|
||||
spotted: 0,
|
||||
survived_battles: 0,
|
||||
xp: 0,
|
||||
win_and_survived: 0,
|
||||
wins: 0,
|
||||
};
|
||||
|
||||
export const emptyTankHistoryNode: TankHistoryRaw = {
|
||||
account_id: 0,
|
||||
battle_life_time: 0,
|
||||
last_battle_time: 0,
|
||||
mark_of_mastery: 0,
|
||||
tank_id: 0,
|
||||
all: emptyAllStats,
|
||||
};
|
||||
|
||||
export interface DiffedTankStats {
|
||||
diff: Record<number, AllStats>;
|
||||
order: number[];
|
||||
}
|
||||
|
||||
// BIG TODO: merge this neatly with diffNormalizedTankStats
|
||||
export default async function getStatsInPeriod(
|
||||
server: Region,
|
||||
id: number,
|
||||
start: number,
|
||||
end: number,
|
||||
) {
|
||||
const history = await getTankHistories(server, id, {
|
||||
includeLatestHistories: true,
|
||||
});
|
||||
|
||||
// sort them even though most won't be used
|
||||
const tankSortedHistory: Record<number, TankHistories> = {};
|
||||
const tanks: number[] = [];
|
||||
history.forEach((node) => {
|
||||
if (!tankSortedHistory[node.tank_id]) {
|
||||
tankSortedHistory[node.tank_id] = [node];
|
||||
tanks.push(node.tank_id);
|
||||
} else {
|
||||
tankSortedHistory[node.tank_id].push(node);
|
||||
}
|
||||
});
|
||||
|
||||
// find the ones played within the range
|
||||
const inRangeTanks = tanks.filter((tankId) =>
|
||||
tankSortedHistory[tankId].some(
|
||||
(node) => node.last_battle_time > start && node.last_battle_time < end,
|
||||
),
|
||||
);
|
||||
const playedTanks: number[] = [];
|
||||
|
||||
// fetch history node right before "start" and right before "end"
|
||||
const diff: Record<number, AllStats> = {};
|
||||
inRangeTanks.forEach((tankId) => {
|
||||
const sortedHistory = tankSortedHistory[tankId];
|
||||
const previousIndex =
|
||||
sortedHistory.findIndex((node) => node.last_battle_time > start) - 1;
|
||||
const latestIndex =
|
||||
sortedHistory.findIndex((node) => node.last_battle_time > end) - 1;
|
||||
const previous = sortedHistory[previousIndex] ?? emptyTankHistoryNode;
|
||||
const latest =
|
||||
sortedHistory[latestIndex] ?? sortedHistory[sortedHistory.length - 1];
|
||||
|
||||
function d(get: (allStats: AllStats) => number) {
|
||||
return get(latest.all) - get(previous.all);
|
||||
}
|
||||
|
||||
// check if there was a change in battles as games in ratings do update last_battle_time
|
||||
if (d((a) => a.battles) > 0) {
|
||||
diff[tankId] = {
|
||||
battles: d((a) => a.battles),
|
||||
capture_points: d((a) => a.capture_points),
|
||||
damage_dealt: d((a) => a.damage_dealt),
|
||||
damage_received: d((a) => a.damage_received),
|
||||
dropped_capture_points: d((a) => a.dropped_capture_points),
|
||||
frags: d((a) => a.frags),
|
||||
frags8p: d((a) => a.frags8p),
|
||||
hits: d((a) => a.hits),
|
||||
losses: d((a) => a.losses),
|
||||
max_frags: latest.all.max_frags,
|
||||
max_xp: latest.all.max_xp,
|
||||
shots: d((a) => a.shots),
|
||||
spotted: d((a) => a.spotted),
|
||||
survived_battles: d((a) => a.survived_battles),
|
||||
win_and_survived: d((a) => a.win_and_survived),
|
||||
wins: d((a) => a.wins),
|
||||
xp: d((a) => a.xp),
|
||||
};
|
||||
|
||||
playedTanks.push(tankId);
|
||||
}
|
||||
});
|
||||
|
||||
const order = playedTanks.sort((a, b) => {
|
||||
const aHistories = tankSortedHistory[a];
|
||||
const bHistories = tankSortedHistory[b];
|
||||
const aLatest = aHistories[aHistories.length - 1];
|
||||
const bLatest = bHistories[bHistories.length - 1];
|
||||
|
||||
return bLatest.last_battle_time - aLatest.last_battle_time;
|
||||
});
|
||||
|
||||
return { diff, order } satisfies DiffedTankStats;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { AllStats } from '../blitz/getAccountInfo';
|
||||
import { context } from '../blitzkrieg/context';
|
||||
import { tankopedia } from './tankopedia';
|
||||
|
||||
export interface SpecialStats {
|
||||
winrate: number;
|
||||
damageRatio: number;
|
||||
kdr: number;
|
||||
damagePerBattle: number;
|
||||
killsPerBattle: number;
|
||||
hitsPerBattle: number;
|
||||
spotsPerBattle: number;
|
||||
wpm: number;
|
||||
dpm: number;
|
||||
kpm: number;
|
||||
hitRate: number;
|
||||
survivalRate: number;
|
||||
}
|
||||
|
||||
export type Percentiles = [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
];
|
||||
|
||||
export interface SpecialPercentiles {
|
||||
winRate: Percentiles;
|
||||
damageRatio: Percentiles;
|
||||
kdr: Percentiles;
|
||||
damagePerBattle: Percentiles;
|
||||
killsPerBattle: Percentiles;
|
||||
hitsPerBattle: Percentiles;
|
||||
spotsPerBattle: Percentiles;
|
||||
wpm: Percentiles;
|
||||
dpm: Percentiles;
|
||||
kpm: Percentiles;
|
||||
hitRate: Percentiles;
|
||||
survivalRate: Percentiles;
|
||||
}
|
||||
|
||||
export interface MeanSd {
|
||||
winrateMean: number;
|
||||
winrateSd: number;
|
||||
hitRateMean: number;
|
||||
hitRateSd: number;
|
||||
survivalRateMean: number;
|
||||
survivalRateSd: number;
|
||||
damageRatioMean: number;
|
||||
damageRatioSd: number;
|
||||
kdrMean: number;
|
||||
kdrSd: number;
|
||||
dpbMean: number;
|
||||
dpbSd: number;
|
||||
kpbMean: number;
|
||||
kpbSd: number;
|
||||
hpbMean: number;
|
||||
hpbSd: number;
|
||||
spbMean: number;
|
||||
spbSd: number;
|
||||
}
|
||||
|
||||
export interface IndividualTankAverage {
|
||||
_id: number;
|
||||
battle_life_time: number;
|
||||
total_battle_life_time: number;
|
||||
number_of_players: number;
|
||||
tank_id: number;
|
||||
all: AllStats;
|
||||
special: SpecialStats;
|
||||
percentiles: SpecialPercentiles;
|
||||
percentiles_player_count: number;
|
||||
meanSd: MeanSd;
|
||||
shotEff: number;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
tankopedia;
|
||||
|
||||
export type TankAverages = Record<number, IndividualTankAverage>;
|
||||
|
||||
console.log('Caching tank averages...');
|
||||
export const tankAverages = fetch(
|
||||
context === 'bot'
|
||||
? 'https://www.blitzstars.com/api/tankaverages.json'
|
||||
: '/api/tank-averages',
|
||||
)
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
if (context === 'bot') {
|
||||
const partialTankAverages: TankAverages = {};
|
||||
|
||||
(json as IndividualTankAverage[]).forEach((individualTankAverage) => {
|
||||
partialTankAverages[individualTankAverage.tank_id] =
|
||||
individualTankAverage;
|
||||
});
|
||||
|
||||
console.log('Tank averages cached');
|
||||
|
||||
return partialTankAverages;
|
||||
} else {
|
||||
return json as TankAverages;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
import { deburr } from 'lodash';
|
||||
import { TreeTypeString } from '../../components/Tanks';
|
||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||
import fetchBlitz from '../blitz/fetchWargaming';
|
||||
import { context } from '../blitzkrieg/context';
|
||||
|
||||
export interface TankopediaEntry {
|
||||
name: string;
|
||||
nation: string;
|
||||
is_premium: boolean;
|
||||
is_collectible: boolean;
|
||||
tier: Tier;
|
||||
cost: { price_credit: number; price_gold: number };
|
||||
images: { preview: string; normal: string };
|
||||
tank_id: number;
|
||||
type: string;
|
||||
description: string;
|
||||
}
|
||||
type asd = TreeTypeString;
|
||||
export interface Tankopedia {
|
||||
[id: number]: TankopediaEntry | undefined;
|
||||
}
|
||||
|
||||
console.log('Caching tankopedia...');
|
||||
export const tankopedia = fetch(
|
||||
context === 'bot'
|
||||
? 'https://www.blitzstars.com/bs-tankopedia.json'
|
||||
: '/api/tankopedia',
|
||||
)
|
||||
.then(async (response) => response.json())
|
||||
.then((wrapperTankopedia) => {
|
||||
console.log('Tankopedia cached');
|
||||
return context === 'bot'
|
||||
? (wrapperTankopedia as { data: Tankopedia }).data
|
||||
: (wrapperTankopedia as Tankopedia);
|
||||
});
|
||||
const entries = new Promise<TankopediaEntry[]>(async (resolve) => {
|
||||
resolve(Object.entries(await tankopedia).map(([, entry]) => entry));
|
||||
});
|
||||
export const TANKS = new Promise<TankopediaEntry[]>(async (resolve) => {
|
||||
resolve((await entries).map((entry) => entry));
|
||||
});
|
||||
export const TANK_NAMES = new Promise<string[]>(async (resolve) => {
|
||||
resolve((await entries).map(({ name }) => name));
|
||||
});
|
||||
export const TANK_NAMES_DIACRITICS = TANK_NAMES.then((tankNames) =>
|
||||
Promise.all(
|
||||
tankNames.map(async (tankName, index) => {
|
||||
const id = (await TANKS)[index].tank_id;
|
||||
const name = tankName ?? `Unknown Tank ${id}`;
|
||||
const diacriticless = deburr(name);
|
||||
|
||||
return {
|
||||
original: name,
|
||||
diacriticless,
|
||||
combined: `${name}${diacriticless}`,
|
||||
id,
|
||||
};
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
export interface TankopediaInfo {
|
||||
achievement_sections: Record<string, { name: string; order: number }>;
|
||||
tanks_updated_at: number;
|
||||
languages: Record<string, string>;
|
||||
vehicle_types: Record<string, string>;
|
||||
vehicle_nations: Record<string, string>;
|
||||
game_version: string;
|
||||
}
|
||||
|
||||
// this is blocking because info is needed for command creation
|
||||
console.log('Caching tankopedia info...');
|
||||
export const tankopediaInfo = fetchBlitz<TankopediaInfo>(
|
||||
`https://api.wotblitz.com/wotb/encyclopedia/info/?application_id=${WARGAMING_APPLICATION_ID}`,
|
||||
);
|
||||
console.log('Cached tankopedia info');
|
||||
|
||||
export const TANK_ICONS: Record<string, string> = {
|
||||
'AT-SPG': 'https://i.imgur.com/BIHSEH0.png',
|
||||
lightTank: 'https://i.imgur.com/CSNha5V.png',
|
||||
mediumTank: 'https://i.imgur.com/wvf3ltm.png',
|
||||
heavyTank: 'https://i.imgur.com/ECeqlZa.png',
|
||||
};
|
||||
|
||||
export const TANK_ICONS_PREMIUM: Record<string, string> = {
|
||||
'AT-SPG': 'https://i.imgur.com/TCu3EdR.png',
|
||||
lightTank: 'https://i.imgur.com/zdkpTRb.png',
|
||||
mediumTank: 'https://i.imgur.com/3z7eHX6.png',
|
||||
heavyTank: 'https://i.imgur.com/P3vbmyA.png',
|
||||
};
|
||||
|
||||
export const TANK_ICONS_COLLECTOR: Record<string, string> = {
|
||||
'AT-SPG': 'https://i.imgur.com/WTjeirB.png',
|
||||
lightTank: 'https://i.imgur.com/EwhtKkU.png',
|
||||
mediumTank: 'https://i.imgur.com/u8YDMBh.png',
|
||||
heavyTank: 'https://i.imgur.com/8xRf3nc.png',
|
||||
};
|
||||
|
||||
export type Tier = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
||||
|
||||
export const TIER_ROMAN_NUMERALS: Record<Tier, string> = {
|
||||
1: 'I',
|
||||
2: 'II',
|
||||
3: 'III',
|
||||
4: 'IV',
|
||||
5: 'V',
|
||||
6: 'VI',
|
||||
7: 'VII',
|
||||
8: 'VIII',
|
||||
9: 'IX',
|
||||
10: 'X',
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import { TreeTypeString } from '../../components/Tanks';
|
||||
import sumStats from '../../LEGACY_core/blitz/sumStats';
|
||||
import { tankopedia } from '../../LEGACY_core/blitz/tankopedia';
|
||||
import { DiffedTankStats } from '../../LEGACY_core/blitzstars/getDiffedTankStats';
|
||||
import { tankAverages } from '../../LEGACY_core/blitzstars/tankAverages';
|
||||
import { DiffedTankStats } from '../blitzstars/getStatsInPeriod';
|
||||
import { tankAverages } from '../blitzstars/tankAverages';
|
||||
import { tankopedia } from '../blitzstars/tankopedia';
|
||||
import calculateWN8 from './calculateWN8';
|
||||
import sumStats from './sumStats';
|
||||
|
||||
export interface StatFilters {
|
||||
nation?: string;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { TierWeightsRecord } from '../../components/TierWeights';
|
||||
import { AllStats } from '../blitz/getAccountInfo';
|
||||
import { tankopedia } from '../blitzstars/tankopedia';
|
||||
|
||||
export async function getTierWeights(
|
||||
stats: Record<number, AllStats>,
|
||||
order: number[] = Object.keys(stats).map(parseInt),
|
||||
) {
|
||||
const awaitedTankopedia = await tankopedia;
|
||||
const tierWeights: TierWeightsRecord = {};
|
||||
|
||||
order.map((id) => {
|
||||
const entry = awaitedTankopedia[id];
|
||||
|
||||
if (!entry) return;
|
||||
if (!tierWeights[entry.tier]) tierWeights[entry.tier] = 0;
|
||||
|
||||
tierWeights[entry.tier]! += stats[id].battles;
|
||||
});
|
||||
|
||||
return tierWeights;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { AllStats } from '../blitz/getAccountInfo';
|
||||
import { emptyAllStats } from '../blitzstars/getStatsInPeriod';
|
||||
|
||||
export default function sumStats(allStatsCollection: AllStats[]) {
|
||||
return allStatsCollection.reduce((accumulated, current) => {
|
||||
function sum(value: (allStats: AllStats) => number) {
|
||||
return value(accumulated) + value(current);
|
||||
}
|
||||
function max(value: (allStats: AllStats) => number) {
|
||||
return Math.max(value(accumulated), value(current));
|
||||
}
|
||||
|
||||
return {
|
||||
battles: sum((a) => a.battles),
|
||||
wins: sum((a) => a.wins),
|
||||
capture_points: sum((a) => a.capture_points),
|
||||
damage_dealt: sum((a) => a.damage_dealt),
|
||||
damage_received: sum((a) => a.damage_received),
|
||||
dropped_capture_points: sum((a) => a.dropped_capture_points),
|
||||
frags: sum((a) => a.frags),
|
||||
frags8p: sum((a) => a.frags8p),
|
||||
hits: sum((a) => a.hits),
|
||||
losses: sum((a) => a.losses),
|
||||
max_frags: max((a) => a.max_frags),
|
||||
max_xp: max((a) => a.max_xp),
|
||||
shots: sum((a) => a.shots),
|
||||
spotted: sum((a) => a.spotted),
|
||||
survived_battles: sum((a) => a.survived_battles),
|
||||
win_and_survived: sum((a) => a.win_and_survived),
|
||||
xp: sum((a) => a.xp),
|
||||
} satisfies AllStats;
|
||||
}, emptyAllStats);
|
||||
}
|
||||
Reference in new issue
Block a user