mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
Merge pull request #96 from tresabhi/1.16.4
This commit is contained in:
120 files changed
+780
-710
No files matched your search
@@ -2,7 +2,6 @@ name: CD Build Assets
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [dev, main]
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
@@ -18,6 +17,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
token: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
- name: Use node
|
- name: Use node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@@ -46,6 +48,14 @@ jobs:
|
|||||||
- name: Run DepotDownloader
|
- name: Run DepotDownloader
|
||||||
run: ./temp/depotDownloader/DepotDownloader.exe -app 444200 -username ${{ secrets.DEPOT_DOWNLOADER_USERNAME }} -password "${{ secrets.DEPOT_DOWNLOADER_PASSWORD }}"
|
run: ./temp/depotDownloader/DepotDownloader.exe -app 444200 -username ${{ secrets.DEPOT_DOWNLOADER_USERNAME }} -password "${{ secrets.DEPOT_DOWNLOADER_PASSWORD }}"
|
||||||
|
|
||||||
|
- name: Apply patches
|
||||||
|
run: |
|
||||||
|
$env:GH_TOKEN = "${{ secrets.GH_TOKEN }}"
|
||||||
|
$env:DISCORD_TOKEN = "${{ secrets.DISCORD_TOKEN }}"
|
||||||
|
$env:DISCORD_BLITZ_DB = "${{ secrets.DISCORD_BLITZ_DB }}"
|
||||||
|
$env:WOTB_DLC_CDN = "${{ secrets.WOTB_DLC_CDN }}"
|
||||||
|
yarn setup:patches --depot
|
||||||
|
|
||||||
- name: Build assets
|
- name: Build assets
|
||||||
run: |
|
run: |
|
||||||
$env:GH_TOKEN = "${{ secrets.GH_TOKEN }}"
|
$env:GH_TOKEN = "${{ secrets.GH_TOKEN }}"
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '0 */5 * * *'
|
- cron: '0 */5 * * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
release:
|
||||||
|
types: [created, published, released, edited]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|||||||
+2
-1
@@ -17,4 +17,5 @@ tsconfig.tsbuildinfo
|
|||||||
temp/
|
temp/
|
||||||
/.yarn
|
/.yarn
|
||||||
/.VSCodeCounter
|
/.VSCodeCounter
|
||||||
/.yarn
|
/.yarn
|
||||||
|
depots
|
||||||
Vendored
+2
@@ -7,6 +7,7 @@
|
|||||||
"alphatest",
|
"alphatest",
|
||||||
"APAC",
|
"APAC",
|
||||||
"apcr",
|
"apcr",
|
||||||
|
"applypatches",
|
||||||
"artefacts",
|
"artefacts",
|
||||||
"atgm",
|
"atgm",
|
||||||
"AYUV",
|
"AYUV",
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
"CDON",
|
"CDON",
|
||||||
"classname",
|
"classname",
|
||||||
"cleandist",
|
"cleandist",
|
||||||
|
"closedkrieg",
|
||||||
"cockroackdb",
|
"cockroackdb",
|
||||||
"Coef",
|
"Coef",
|
||||||
"cooldown",
|
"cooldown",
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Blitzkrieg 1.16.4
|
||||||
|
|
||||||
|
A few issues here and there addressed for good measure.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Armor layer penetration indices are now much more compact.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Reverted splash mechanics changes from `1.16.1` as it made them less accurate.
|
||||||
|
- Auto-reloaders now report their DPM stats correctly with crew training.
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- All files in the project were reviewed and constant exports were isolated.
|
||||||
|
- The downloaded game in the workflow now also includes the latest patches.
|
||||||
|
- Asset building now uses sharp under the hood which should in theory be faster.
|
||||||
|
- Fetching CDON LZ4 files now uses `no-store` instead of `no-cache`
|
||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkrieg",
|
"name": "blitzkrieg",
|
||||||
"version": "1.16.2",
|
"version": "1.16.4",
|
||||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||||
"main": "src/server.ts",
|
"main": "src/server.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
"start:website": "next start",
|
"start:website": "next start",
|
||||||
"host:docs": "vitepress preview docs",
|
"host:docs": "vitepress preview docs",
|
||||||
"publish:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard --publish",
|
"publish:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard --publish",
|
||||||
|
"setup:patches": "vite-node --script scripts/applyPatches",
|
||||||
"lint": "npx tsc",
|
"lint": "npx tsc",
|
||||||
"lint:website": "next lint",
|
"lint:website": "next lint",
|
||||||
"format": "prettier --write \"**/*.@(json|js|jsx|css|md|ts|tsx|html|xml|yml|gltf)\"",
|
"format": "prettier --write \"**/*.@(json|js|jsx|css|md|ts|tsx|html|xml|yml|gltf)\"",
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { mkdir, writeFile } from 'fs/promises';
|
||||||
|
import { parse as parsePath } from 'path';
|
||||||
|
import { parse as parseYaml } from 'yaml';
|
||||||
|
import { readStringDVPL } from '../src/core/blitz/readStringDVPL';
|
||||||
|
import { writeDVPL } from '../src/core/blitz/writeDVPL';
|
||||||
|
import { secrets } from '../src/core/blitzkrieg/secrets';
|
||||||
|
import { dvp } from '../submodules/closedkrieg/src/dvp';
|
||||||
|
import { DATA } from './buildAssets/constants';
|
||||||
|
|
||||||
|
const versionTextFile = await readStringDVPL(`${DATA}/version.txt.dvpl`);
|
||||||
|
const currentVersion = versionTextFile
|
||||||
|
.split(' ')[0]
|
||||||
|
.split('.')
|
||||||
|
.slice(0, 3)
|
||||||
|
.join('.');
|
||||||
|
|
||||||
|
console.log(`Installing patches for ${currentVersion}...`);
|
||||||
|
|
||||||
|
let patchIndex = 1;
|
||||||
|
while (true) {
|
||||||
|
const response = await fetch(
|
||||||
|
`${secrets.WOTB_DLC_CDN}/dlc/s${currentVersion}_${patchIndex}.yaml`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
console.log(`Applying patch ${patchIndex}...`);
|
||||||
|
|
||||||
|
const data = parseYaml(await response.text());
|
||||||
|
const dvpm = await fetch(`${secrets.WOTB_DLC_CDN}/dlc/${data.dx11}`).then(
|
||||||
|
(response) => response.arrayBuffer(),
|
||||||
|
);
|
||||||
|
const dvpd = await fetch(
|
||||||
|
`${secrets.WOTB_DLC_CDN}/dlc/${data.dx11.replace('.dvpm', '.dvpd')}`,
|
||||||
|
).then((response) => response.arrayBuffer());
|
||||||
|
const files = await dvp(dvpm, dvpd);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
files.map(async ({ path, data }) => {
|
||||||
|
const { dir } = parsePath(path);
|
||||||
|
|
||||||
|
await mkdir(`${DATA}/${dir}`, { recursive: true });
|
||||||
|
await writeFile(`${DATA}/${path}.dvpl`, writeDVPL(Buffer.from(data)));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
patchIndex++;
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ export const POI = {
|
|||||||
collisionMeshes: '3d/Tanks/CollisionMeshes',
|
collisionMeshes: '3d/Tanks/CollisionMeshes',
|
||||||
optionalDevices: 'XML/item_defs/vehicles/common/optional_devices.xml',
|
optionalDevices: 'XML/item_defs/vehicles/common/optional_devices.xml',
|
||||||
optionalDeviceSlots:
|
optionalDeviceSlots:
|
||||||
'XML/item_defs/vehicles/common/optional_device_slots.xml',
|
'XML/item_defs/vehicles/common/optional_device_slots.xml',
|
||||||
optionalDeviceImageMappings:
|
optionalDeviceImageMappings:
|
||||||
'UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml',
|
'UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml',
|
||||||
optionalDevicesImage:
|
optionalDevicesImage:
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
import { NodeIO } from '@gltf-transform/core';
|
import { NodeIO } from '@gltf-transform/core';
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from 'fs/promises';
|
||||||
import { Vector3Tuple } from 'three';
|
|
||||||
import { extractModel } from '../../src/core/blitz/extractModel';
|
import { extractModel } from '../../src/core/blitz/extractModel';
|
||||||
import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
|
import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
|
||||||
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
|
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
|
||||||
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
|
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
|
||||||
// import { AssetCommit } from '../../src/core/github/assetCommit';
|
|
||||||
import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
|
import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
|
||||||
import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles';
|
import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles';
|
||||||
import { DATA, POI } from './constants';
|
import { DATA, POI } from './constants';
|
||||||
import { VehicleDefinitionList } from './definitions';
|
import { VehicleDefinitionList } from './definitions';
|
||||||
import { TankParameters } from './tankIcons';
|
import { TankParameters } from './tankIcons';
|
||||||
|
|
||||||
interface VehicleCustomization {
|
|
||||||
armorColor: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function tankModels(production: boolean) {
|
export async function tankModels(production: boolean) {
|
||||||
console.log('Building tank models...');
|
console.log('Building tank models...');
|
||||||
|
|
||||||
@@ -27,18 +21,9 @@ export async function tankModels(production: boolean) {
|
|||||||
for (const nationIndex in nations) {
|
for (const nationIndex in nations) {
|
||||||
const nation = nations[nationIndex];
|
const nation = nations[nationIndex];
|
||||||
const changes: FileChange[] = [];
|
const changes: FileChange[] = [];
|
||||||
// const assetCommit = new AssetCommit(`tank models ${nation}`, production);
|
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/${POI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
|
`${DATA}/${POI.vehicleDefinitions}/${nation}/list.xml.dvpl`,
|
||||||
);
|
);
|
||||||
const customization = await readXMLDVPL<{ root: VehicleCustomization }>(
|
|
||||||
`${DATA}/${POI.vehicleDefinitions}/${nation}/customization.xml.dvpl`,
|
|
||||||
);
|
|
||||||
const baseColor = customization.root.armorColor
|
|
||||||
.split(' ')
|
|
||||||
.slice(0, 3)
|
|
||||||
.map(Number)
|
|
||||||
.map((channel) => channel / 255) as Vector3Tuple;
|
|
||||||
|
|
||||||
console.log(`Building models for ${nation}`);
|
console.log(`Building models for ${nation}`);
|
||||||
|
|
||||||
@@ -56,11 +41,8 @@ export async function tankModels(production: boolean) {
|
|||||||
const model = await extractModel(
|
const model = await extractModel(
|
||||||
DATA,
|
DATA,
|
||||||
parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, ''),
|
parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, ''),
|
||||||
baseColor,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// writeFile(`test.${tankKey}.glb`, await nodeIO.writeBinary(model));
|
|
||||||
|
|
||||||
changes.push({
|
changes.push({
|
||||||
path: `3d/tanks/models/${id}.glb`,
|
path: `3d/tanks/models/${id}.glb`,
|
||||||
content: Buffer.from(await nodeIO.writeBinary(model)).toString(
|
content: Buffer.from(await nodeIO.writeBinary(model)).toString(
|
||||||
@@ -71,7 +53,6 @@ export async function tankModels(production: boolean) {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// assetCommit.push();
|
|
||||||
await commitAssets(`tank models ${nation}`, changes, production);
|
await commitAssets(`tank models ${nation}`, changes, production);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import * as Breakdown from '../../../components/Breakdown';
|
import * as Breakdown from '../../../components/Breakdown';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../../../core/blitz/fetchBlitz';
|
import fetchBlitz from '../../../core/blitz/fetchBlitz';
|
||||||
|
import { resetSession } from '../../../core/blitzkrieg/resetSession';
|
||||||
import {
|
import {
|
||||||
TankDefinition,
|
TankDefinition,
|
||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
@@ -13,10 +14,7 @@ import { tankAverages } from '../../../core/blitzstars/tankAverages';
|
|||||||
import calculateWN8 from '../../../core/statistics/calculateWN8';
|
import calculateWN8 from '../../../core/statistics/calculateWN8';
|
||||||
import { deltaTankStats } from '../../../core/statistics/deltaTankStats';
|
import { deltaTankStats } from '../../../core/statistics/deltaTankStats';
|
||||||
import getWN8Percentile from '../../../core/statistics/getWN8Percentile';
|
import getWN8Percentile from '../../../core/statistics/getWN8Percentile';
|
||||||
import mutateSession, {
|
import mutateSession, { useSession } from '../../../stores/session';
|
||||||
resetSession,
|
|
||||||
useSession,
|
|
||||||
} from '../../../stores/session';
|
|
||||||
import { IndividualTankStats, TanksStats } from '../../../types/tanksStats';
|
import { IndividualTankStats, TanksStats } from '../../../types/tanksStats';
|
||||||
import { CustomColumnDisplay } from '../../tools/session/components/CustomColumn';
|
import { CustomColumnDisplay } from '../../tools/session/components/CustomColumn';
|
||||||
import { Menu } from '../../tools/session/components/Menu';
|
import { Menu } from '../../tools/session/components/Menu';
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ import { UNLOCALIZED_REGION_NAMES } from '../../../constants/regions';
|
|||||||
import searchPlayersAcrossRegions, {
|
import searchPlayersAcrossRegions, {
|
||||||
AccountListWithServer,
|
AccountListWithServer,
|
||||||
} from '../../../core/blitz/searchPlayersAcrossRegions';
|
} from '../../../core/blitz/searchPlayersAcrossRegions';
|
||||||
import { resetSession, setSession, useSession } from '../../../stores/session';
|
import { resetSession } from '../../../core/blitzkrieg/resetSession';
|
||||||
|
import { setSession } from '../../../core/blitzkrieg/setSession';
|
||||||
|
import { useSession } from '../../../stores/session';
|
||||||
import SessionPage from '../../embeds/session/page';
|
import SessionPage from '../../embeds/session/page';
|
||||||
import { Menu } from './components/Menu';
|
import { Menu } from './components/Menu';
|
||||||
import * as styles from './page.css';
|
import * as styles from './page.css';
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { pushTankopediaPath } from '../../../../../core/blitzkrieg/pushTankopedi
|
|||||||
import {
|
import {
|
||||||
SHELL_NAMES,
|
SHELL_NAMES,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
} from '../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
import { mutateDuel, useDuel } from '../../../../../stores/duel';
|
import { mutateDuel, useDuel } from '../../../../../stores/duel';
|
||||||
import {
|
import {
|
||||||
mutateTankopediaTemporary,
|
mutateTankopediaTemporary,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Flex, Heading } from '@radix-ui/themes';
|
import { Flex, Heading } from '@radix-ui/themes';
|
||||||
import { ModuleButton } from '../../../../../../../components/ModuleButtons/ModuleButton';
|
import { ModuleButton } from '../../../../../../../components/ModuleButtons/ModuleButton';
|
||||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../../core/blitzkrieg/tankDefinitions';
|
import { TIER_ROMAN_NUMERALS } from '../../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||||
import { mutateTankopediaTemporary } from '../../../../../../../stores/tankopedia';
|
import { mutateTankopediaTemporary } from '../../../../../../../stores/tankopedia';
|
||||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
CREW_MEMBER_NAMES,
|
CREW_MEMBER_NAMES,
|
||||||
GUN_TYPE_NAMES,
|
GUN_TYPE_NAMES,
|
||||||
SHELL_NAMES,
|
SHELL_NAMES,
|
||||||
} from '../../../../../../core/blitzkrieg/tankDefinitions';
|
} from '../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
import { unionBoundingBox } from '../../../../../../core/blitzkrieg/unionBoundingBox';
|
import { unionBoundingBox } from '../../../../../../core/blitzkrieg/unionBoundingBox';
|
||||||
import { useConsumable } from '../../../../../../hooks/useConsumable';
|
import { useConsumable } from '../../../../../../hooks/useConsumable';
|
||||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||||
@@ -268,9 +268,13 @@ export function Characteristics() {
|
|||||||
<>
|
<>
|
||||||
<InfoWithDelta decimals={0} indent name="Maximum" unit="hp / min">
|
<InfoWithDelta decimals={0} indent name="Maximum" unit="hp / min">
|
||||||
{gun.reload.at(-1)! < gun.reload.at(-2)!
|
{gun.reload.at(-1)! < gun.reload.at(-2)!
|
||||||
? (shell.damage.armor / (gun.reload.at(-1)! + gun.intraClip)) *
|
? ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload.at(-1)! * reloadCoefficient +
|
||||||
|
gun.intraClip * intraClipCoefficient)) *
|
||||||
60
|
60
|
||||||
: (shell.damage.armor / gun.reload[0]) * 60}
|
: ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload[0] * reloadCoefficient)) *
|
||||||
|
60}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={0}
|
decimals={0}
|
||||||
@@ -279,13 +283,26 @@ export function Characteristics() {
|
|||||||
unit="hp / min"
|
unit="hp / min"
|
||||||
>
|
>
|
||||||
{gun.reload.at(-1)! < gun.reload.at(-2)!
|
{gun.reload.at(-1)! < gun.reload.at(-2)!
|
||||||
? (shell.damage.armor / (gun.reload.at(-1)! + gun.intraClip)) *
|
? ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload.at(-1)! * reloadCoefficient +
|
||||||
|
gun.intraClip * intraClipCoefficient)) *
|
||||||
(60 -
|
(60 -
|
||||||
(gun.reload.slice(0, -1).length - 1) * gun.intraClip) +
|
(gun.reload.slice(0, -1).length - 1) *
|
||||||
shell.damage.armor * gun.reload.slice(0, -1).length
|
gun.intraClip *
|
||||||
: (shell.damage.armor / (gun.reload[0] + gun.intraClip)) *
|
intraClipCoefficient) +
|
||||||
(60 - (gun.reload.slice(1).length - 1) * gun.intraClip) +
|
damageCoefficient *
|
||||||
shell.damage.armor * gun.reload.slice(1).length}
|
shell.damage.armor *
|
||||||
|
gun.reload.slice(0, -1).length
|
||||||
|
: ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload[0] * reloadCoefficient +
|
||||||
|
gun.intraClip * intraClipCoefficient)) *
|
||||||
|
(60 -
|
||||||
|
(gun.reload.slice(1).length - 1) *
|
||||||
|
gun.intraClip *
|
||||||
|
intraClipCoefficient) +
|
||||||
|
damageCoefficient *
|
||||||
|
shell.damage.armor *
|
||||||
|
gun.reload.slice(1).length}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info
|
<Info
|
||||||
indent
|
indent
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { UpdateIcon } from '@radix-ui/react-icons';
|
import { UpdateIcon } from '@radix-ui/react-icons';
|
||||||
import { Button, Dialog, Flex, Heading } from '@radix-ui/themes';
|
import { Button, Dialog, Flex, Heading } from '@radix-ui/themes';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { TREE_TYPE_ICONS } from '../../../../../components/Tanks';
|
import { TREE_TYPE_ICONS } from '../../../../../components/Tanks/components/Item/constants';
|
||||||
import { assignDuelMember } from '../../../../../core/blitzkrieg/assignDuelMember';
|
import { assignDuelMember } from '../../../../../core/blitzkrieg/assignDuelMember';
|
||||||
import { pushTankopediaPath } from '../../../../../core/blitzkrieg/pushTankopediaPath';
|
import { pushTankopediaPath } from '../../../../../core/blitzkrieg/pushTankopediaPath';
|
||||||
import { useDuel } from '../../../../../stores/duel';
|
import { useDuel } from '../../../../../stores/duel';
|
||||||
|
|||||||
+2
-4
@@ -1,9 +1,7 @@
|
|||||||
import { Button, Flex, Text } from '@radix-ui/themes';
|
import { Button, Flex, Text } from '@radix-ui/themes';
|
||||||
import { SmallTankIcon } from '../../../../../../components/SmallTankIcon';
|
import { SmallTankIcon } from '../../../../../../components/SmallTankIcon';
|
||||||
import {
|
import { TankDefinition } from '../../../../../../core/blitzkrieg/tankDefinitions';
|
||||||
TIER_ROMAN_NUMERALS,
|
import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
TankDefinition,
|
|
||||||
} from '../../../../../../core/blitzkrieg/tankDefinitions';
|
|
||||||
|
|
||||||
interface CompactSearchResultRowProps {
|
interface CompactSearchResultRowProps {
|
||||||
tanks: TankDefinition[];
|
tanks: TankDefinition[];
|
||||||
|
|||||||
@@ -4,17 +4,18 @@ import { CaretDownIcon } from '@radix-ui/react-icons';
|
|||||||
import { Button, DropdownMenu, Flex } from '@radix-ui/themes';
|
import { Button, DropdownMenu, Flex } from '@radix-ui/themes';
|
||||||
import { use } from 'react';
|
import { use } from 'react';
|
||||||
import { Flag } from '../../../../../../../components/Flag';
|
import { Flag } from '../../../../../../../components/Flag';
|
||||||
|
import {} from '../../../../../../../components/Tanks';
|
||||||
import {
|
import {
|
||||||
TANK_CLASSES,
|
TANK_CLASSES,
|
||||||
TREE_TYPES,
|
TREE_TYPES,
|
||||||
TREE_TYPE_ICONS,
|
TREE_TYPE_ICONS,
|
||||||
TREE_TYPE_IMAGES,
|
TREE_TYPE_IMAGES,
|
||||||
} from '../../../../../../../components/Tanks';
|
} from '../../../../../../../components/Tanks/components/Item/constants';
|
||||||
|
import { NATIONS } from '../../../../../../../core/blitzkrieg/tankDefinitions';
|
||||||
import {
|
import {
|
||||||
NATIONS,
|
|
||||||
TIERS,
|
TIERS,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '../../../../../../../core/blitzkrieg/tankDefinitions';
|
} from '../../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
import mutateTankopediaPersistent, {
|
import mutateTankopediaPersistent, {
|
||||||
TankopediaTestTankDisplay,
|
TankopediaTestTankDisplay,
|
||||||
useTankopediaPersistent,
|
useTankopediaPersistent,
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import {
|
|||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import { use, useEffect, useMemo, useRef, useState } from 'react';
|
import { use, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { TANK_CLASSES, TreeType } from '../../../../../components/Tanks';
|
import { TreeType } from '../../../../../components/Tanks';
|
||||||
|
import { TANK_CLASSES } from '../../../../../components/Tanks/components/Item/constants';
|
||||||
import { resolveNearPenetration } from '../../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../../core/blitz/resolveNearPenetration';
|
||||||
import { asset } from '../../../../../core/blitzkrieg/asset';
|
import { asset } from '../../../../../core/blitzkrieg/asset';
|
||||||
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
@@ -24,13 +25,15 @@ import { resolveDpm } from '../../../../../core/blitzkrieg/resolveDpm';
|
|||||||
import { resolveReload } from '../../../../../core/blitzkrieg/resolveReload';
|
import { resolveReload } from '../../../../../core/blitzkrieg/resolveReload';
|
||||||
import {
|
import {
|
||||||
NATIONS,
|
NATIONS,
|
||||||
|
TankDefinition,
|
||||||
|
tanksDefinitionsArray,
|
||||||
|
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
||||||
|
import {
|
||||||
TANK_ICONS,
|
TANK_ICONS,
|
||||||
TANK_ICONS_COLLECTOR,
|
TANK_ICONS_COLLECTOR,
|
||||||
TANK_ICONS_PREMIUM,
|
TANK_ICONS_PREMIUM,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
TankDefinition,
|
} from '../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
tanksDefinitionsArray,
|
|
||||||
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
|
||||||
import { tankIcon } from '../../../../../core/blitzkrieg/tankIcon';
|
import { tankIcon } from '../../../../../core/blitzkrieg/tankIcon';
|
||||||
import { unionBoundingBox } from '../../../../../core/blitzkrieg/unionBoundingBox';
|
import { unionBoundingBox } from '../../../../../core/blitzkrieg/unionBoundingBox';
|
||||||
import { theme } from '../../../../../stitches.config';
|
import { theme } from '../../../../../stitches.config';
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { getBlitzStarsLinkButton } from '../../core/blitzstars/getBlitzStarsLinkButton';
|
||||||
|
import addPeriodicFilterOptions from '../../core/discord/addPeriodicFilterOptions';
|
||||||
|
import addUsernameChoices from '../../core/discord/addUsernameChoices';
|
||||||
|
import autocompleteTanks from '../../core/discord/autocompleteTanks';
|
||||||
|
import autocompleteUsername from '../../core/discord/autocompleteUsername';
|
||||||
|
import { buttonRefresh } from '../../core/discord/buttonRefresh';
|
||||||
|
import commandToURL from '../../core/discord/commandToURL';
|
||||||
|
import { createLocalizedCommand } from '../../core/discord/createLocalizedCommand';
|
||||||
|
import { getCustomPeriodParams } from '../../core/discord/getCustomPeriodParams';
|
||||||
|
import { getFiltersFromButton } from '../../core/discord/getFiltersFromButton';
|
||||||
|
import { getFiltersFromCommand } from '../../core/discord/getFiltersFromCommand';
|
||||||
|
import resolvePeriodFromButton from '../../core/discord/resolvePeriodFromButton';
|
||||||
|
import resolvePeriodFromCommand from '../../core/discord/resolvePeriodFromCommand';
|
||||||
|
import resolvePlayerFromButton from '../../core/discord/resolvePlayerFromButton';
|
||||||
|
import resolvePlayerFromCommand from '../../core/discord/resolvePlayerFromCommand';
|
||||||
|
import { CommandRegistry } from '../../events/interactionCreate';
|
||||||
|
import { renderBreakdown } from './render';
|
||||||
|
|
||||||
|
export const breakdownCommand = new Promise<CommandRegistry>(
|
||||||
|
async (resolve) => {
|
||||||
|
resolve({
|
||||||
|
inProduction: true,
|
||||||
|
inPublic: true,
|
||||||
|
|
||||||
|
command: await addPeriodicFilterOptions(
|
||||||
|
createLocalizedCommand('breakdown'),
|
||||||
|
(option) => option.addStringOption(addUsernameChoices),
|
||||||
|
),
|
||||||
|
|
||||||
|
async handler(interaction) {
|
||||||
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
|
const period = resolvePeriodFromCommand(player.region, interaction);
|
||||||
|
const filters = await getFiltersFromCommand(interaction);
|
||||||
|
const path = commandToURL(interaction, {
|
||||||
|
...player,
|
||||||
|
...getCustomPeriodParams(interaction),
|
||||||
|
...filters,
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
...(await renderBreakdown(
|
||||||
|
player,
|
||||||
|
period,
|
||||||
|
filters,
|
||||||
|
interaction.locale,
|
||||||
|
)),
|
||||||
|
buttonRefresh(interaction, path),
|
||||||
|
await getBlitzStarsLinkButton(player.region, player.id),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
autocomplete: (interaction) => {
|
||||||
|
autocompleteUsername(interaction);
|
||||||
|
autocompleteTanks(interaction);
|
||||||
|
},
|
||||||
|
|
||||||
|
async button(interaction) {
|
||||||
|
const player = await resolvePlayerFromButton(interaction);
|
||||||
|
const period = resolvePeriodFromButton(player.region, interaction);
|
||||||
|
const filters = getFiltersFromButton(interaction);
|
||||||
|
|
||||||
|
return await renderBreakdown(
|
||||||
|
player,
|
||||||
|
period,
|
||||||
|
filters,
|
||||||
|
interaction.locale,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -1,43 +1,25 @@
|
|||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import { chunk } from 'lodash';
|
import { chunk } from 'lodash';
|
||||||
import * as Breakdown from '../components/Breakdown';
|
import * as Breakdown from '../../components/Breakdown';
|
||||||
import CommandWrapper from '../components/CommandWrapper';
|
import CommandWrapper from '../../components/CommandWrapper';
|
||||||
import NoData from '../components/NoData';
|
import NoData from '../../components/NoData';
|
||||||
import TitleBar from '../components/TitleBar';
|
import TitleBar from '../../components/TitleBar';
|
||||||
import { AllStats, getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { AllStats, getAccountInfo } from '../../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../../core/blitz/getClanAccountInfo';
|
||||||
import getTankStats from '../core/blitz/getTankStats';
|
import getTankStats from '../../core/blitz/getTankStats';
|
||||||
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
|
import { filtersToDescription } from '../../core/blitzkrieg/filtersToDescription';
|
||||||
import {
|
import {
|
||||||
TankDefinition,
|
TankDefinition,
|
||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
} from '../core/blitzkrieg/tankDefinitions';
|
} from '../../core/blitzkrieg/tankDefinitions';
|
||||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
import getStatsInPeriod from '../../core/blitzstars/getStatsInPeriod';
|
||||||
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
|
import { tankAverages } from '../../core/blitzstars/tankAverages';
|
||||||
import { tankAverages } from '../core/blitzstars/tankAverages';
|
import { ResolvedPeriod } from '../../core/discord/resolvePeriodFromCommand';
|
||||||
import addPeriodicFilterOptions from '../core/discord/addPeriodicFilterOptions';
|
import { ResolvedPlayer } from '../../core/discord/resolvePlayerFromCommand';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import { translator } from '../../core/localization/translator';
|
||||||
import autocompleteTanks from '../core/discord/autocompleteTanks';
|
import calculateWN8 from '../../core/statistics/calculateWN8';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
import { StatFilters, filterStats } from '../../core/statistics/filterStats';
|
||||||
import { buttonRefresh } from '../core/discord/buttonRefresh';
|
import getWN8Percentile from '../../core/statistics/getWN8Percentile';
|
||||||
import commandToURL from '../core/discord/commandToURL';
|
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
|
||||||
import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams';
|
|
||||||
import { getFiltersFromButton } from '../core/discord/getFiltersFromButton';
|
|
||||||
import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand';
|
|
||||||
import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton';
|
|
||||||
import resolvePeriodFromCommand, {
|
|
||||||
ResolvedPeriod,
|
|
||||||
} from '../core/discord/resolvePeriodFromCommand';
|
|
||||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
|
||||||
import resolvePlayerFromCommand, {
|
|
||||||
ResolvedPlayer,
|
|
||||||
} from '../core/discord/resolvePlayerFromCommand';
|
|
||||||
import { translator } from '../core/localization/translator';
|
|
||||||
import calculateWN8 from '../core/statistics/calculateWN8';
|
|
||||||
import { StatFilters, filterStats } from '../core/statistics/filterStats';
|
|
||||||
import getWN8Percentile from '../core/statistics/getWN8Percentile';
|
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
|
||||||
|
|
||||||
const ROWS_PER_PAGE = 8;
|
const ROWS_PER_PAGE = 8;
|
||||||
const MAX_PAGES = 9;
|
const MAX_PAGES = 9;
|
||||||
@@ -296,57 +278,3 @@ export async function renderBreakdown(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const breakdownCommand = new Promise<CommandRegistry>(
|
|
||||||
async (resolve) => {
|
|
||||||
resolve({
|
|
||||||
inProduction: true,
|
|
||||||
inPublic: true,
|
|
||||||
|
|
||||||
command: await addPeriodicFilterOptions(
|
|
||||||
createLocalizedCommand('breakdown'),
|
|
||||||
(option) => option.addStringOption(addUsernameChoices),
|
|
||||||
),
|
|
||||||
|
|
||||||
async handler(interaction) {
|
|
||||||
const player = await resolvePlayerFromCommand(interaction);
|
|
||||||
const period = resolvePeriodFromCommand(player.region, interaction);
|
|
||||||
const filters = await getFiltersFromCommand(interaction);
|
|
||||||
const path = commandToURL(interaction, {
|
|
||||||
...player,
|
|
||||||
...getCustomPeriodParams(interaction),
|
|
||||||
...filters,
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
...(await renderBreakdown(
|
|
||||||
player,
|
|
||||||
period,
|
|
||||||
filters,
|
|
||||||
interaction.locale,
|
|
||||||
)),
|
|
||||||
buttonRefresh(interaction, path),
|
|
||||||
await getBlitzStarsLinkButton(player.region, player.id),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
autocomplete: (interaction) => {
|
|
||||||
autocompleteUsername(interaction);
|
|
||||||
autocompleteTanks(interaction);
|
|
||||||
},
|
|
||||||
|
|
||||||
async button(interaction) {
|
|
||||||
const player = await resolvePlayerFromButton(interaction);
|
|
||||||
const period = resolvePeriodFromButton(player.region, interaction);
|
|
||||||
const filters = getFiltersFromButton(interaction);
|
|
||||||
|
|
||||||
return await renderBreakdown(
|
|
||||||
player,
|
|
||||||
period,
|
|
||||||
filters,
|
|
||||||
interaction.locale,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Locale, SlashCommandSubcommandGroupBuilder } from 'discord.js';
|
import { Locale, SlashCommandSubcommandGroupBuilder } from 'discord.js';
|
||||||
import CommandWrapper from '../components/CommandWrapper';
|
import CommandWrapper from '../components/CommandWrapper';
|
||||||
import * as Graph from '../components/Graph';
|
import * as Graph from '../components/Graph';
|
||||||
|
import { LineColor } from '../components/Graph/components/Line/constants';
|
||||||
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';
|
||||||
@@ -93,7 +94,7 @@ async function render(
|
|||||||
xMaxLabel={new Date(maxTime * 1000).toLocaleDateString(locale)}
|
xMaxLabel={new Date(maxTime * 1000).toLocaleDateString(locale)}
|
||||||
>
|
>
|
||||||
{Graph.Line({
|
{Graph.Line({
|
||||||
color: Graph.LineColor.Red,
|
color: LineColor.Red,
|
||||||
plot: plot,
|
plot: plot,
|
||||||
minY,
|
minY,
|
||||||
maxY,
|
maxY,
|
||||||
|
|||||||
@@ -7,12 +7,11 @@ import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
|||||||
import getTankStats from '../core/blitz/getTankStats';
|
import getTankStats from '../core/blitz/getTankStats';
|
||||||
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
||||||
import {
|
import {
|
||||||
TIER_ROMAN_NUMERALS,
|
|
||||||
TankDefinition,
|
TankDefinition,
|
||||||
Tier,
|
Tier,
|
||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
} from '../core/blitzkrieg/tankDefinitions';
|
} from '../core/blitzkrieg/tankDefinitions';
|
||||||
import { tankIcon } from '../core/blitzkrieg/tankIcon';
|
import { TIER_ROMAN_NUMERALS } from '../core/blitzkrieg/tankDefinitions/constants';
|
||||||
import addTierChoices from '../core/discord/addTierChoices';
|
import addTierChoices from '../core/discord/addTierChoices';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
@@ -100,7 +99,6 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
key={tank.id}
|
key={tank.id}
|
||||||
name={tank.name}
|
name={tank.name}
|
||||||
tankType={tank.class}
|
tankType={tank.class}
|
||||||
image={tankIcon(tank.id)}
|
|
||||||
treeType={tank.treeType}
|
treeType={tank.treeType}
|
||||||
/>
|
/>
|
||||||
)),
|
)),
|
||||||
@@ -113,7 +111,6 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
key={tank.id}
|
key={tank.id}
|
||||||
name={tank.name}
|
name={tank.name}
|
||||||
tankType={tank.class}
|
tankType={tank.class}
|
||||||
image={tankIcon(tank.id)}
|
|
||||||
treeType={tank.treeType}
|
treeType={tank.treeType}
|
||||||
/>
|
/>
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ import markdownEscape from 'markdown-escape';
|
|||||||
import { Region } from '../constants/regions';
|
import { Region } from '../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../core/blitz/fetchBlitz';
|
import fetchBlitz from '../core/blitz/fetchBlitz';
|
||||||
import {
|
import { AccountList } from '../core/blitz/searchPlayersAcrossRegions';
|
||||||
AccountList,
|
import { usernamePattern } from '../core/blitz/searchPlayersAcrossRegions/constants';
|
||||||
usernamePattern,
|
|
||||||
} from '../core/blitz/searchPlayersAcrossRegions';
|
|
||||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand';
|
|||||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { renderBreakdown } from './breakdown';
|
import { renderBreakdown } from './breakdown/render';
|
||||||
|
|
||||||
export const todayCommand = new Promise<CommandRegistry>(async (resolve) => {
|
export const todayCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||||
const nations = await NATIONS;
|
const nations = await NATIONS;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Percentile } from '../../../../constants/percentiles';
|
|||||||
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';
|
import { PERCENTILE_COLORS } from '../../../PercentileIndicator/constants';
|
||||||
import { Glow } from './components/Glow';
|
import { Glow } from './components/Glow';
|
||||||
|
|
||||||
export interface WN8DisplayProps {
|
export interface WN8DisplayProps {
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
import { useFrame } from '@react-three/fiber';
|
import { useFrame } from '@react-three/fiber';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import {
|
import { MeshBasicMaterial, Object3D, ShaderMaterial, Vector2 } from 'three';
|
||||||
MeshBasicMaterial,
|
|
||||||
Object3D,
|
|
||||||
ShaderMaterial,
|
|
||||||
Vector2,
|
|
||||||
WebGLRenderTarget,
|
|
||||||
} from 'three';
|
|
||||||
import { degToRad } from 'three/src/math/MathUtils';
|
import { degToRad } from 'three/src/math/MathUtils';
|
||||||
import { canSplash } from '../../../../core/blitz/canSplash';
|
import { canSplash } from '../../../../core/blitz/canSplash';
|
||||||
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
||||||
@@ -21,8 +15,7 @@ import {
|
|||||||
} from '../../../../stores/tankopedia';
|
} from '../../../../stores/tankopedia';
|
||||||
import fragmentShader from './shaders/fragment.glsl';
|
import fragmentShader from './shaders/fragment.glsl';
|
||||||
import vertexShader from './shaders/vertex.glsl';
|
import vertexShader from './shaders/vertex.glsl';
|
||||||
|
import { spacedArmorRenderTarget } from './target';
|
||||||
export const spacedArmorRenderTarget = new WebGLRenderTarget();
|
|
||||||
|
|
||||||
interface CoreArmorSceneComponentProps {
|
interface CoreArmorSceneComponentProps {
|
||||||
thickness: number;
|
thickness: number;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ void main() {
|
|||||||
float spacedArmorThickness = spacedArmorBufferFragment.r * penetration;
|
float spacedArmorThickness = spacedArmorBufferFragment.r * penetration;
|
||||||
remainingPenetration -= spacedArmorThickness;
|
remainingPenetration -= spacedArmorThickness;
|
||||||
|
|
||||||
if (isExplosive && !canSplash && remainingPenetration > 0.0) {
|
if (isExplosive && remainingPenetration > 0.0) {
|
||||||
// there is a 50% penetration loss per meter for HEAT shells
|
// there is a 50% penetration loss per meter for HEAT shells
|
||||||
remainingPenetration -= 0.5 * remainingPenetration * distanceFromSpacedArmor;
|
remainingPenetration -= 0.5 * remainingPenetration * distanceFromSpacedArmor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { WebGLRenderTarget } from 'three';
|
||||||
|
|
||||||
|
export const spacedArmorRenderTarget = new WebGLRenderTarget();
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Card, Flex, Inset, Text } from '@radix-ui/themes';
|
import { Card, Flex, Text } from '@radix-ui/themes';
|
||||||
import { Html } from '@react-three/drei';
|
import { Html } from '@react-three/drei';
|
||||||
import { useFrame } from '@react-three/fiber';
|
import { useFrame } from '@react-three/fiber';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
@@ -182,33 +182,38 @@ export function ShotDisplay() {
|
|||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Card
|
<div
|
||||||
style={{
|
style={{
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
trueStatus === 'penetration'
|
trueStatus === 'penetration'
|
||||||
? '#00ff00c0'
|
? '#00ff0080'
|
||||||
: trueStatus === 'blocked'
|
: trueStatus === 'blocked'
|
||||||
? '#ff0000c0'
|
? '#ff000080'
|
||||||
: '#ffff00c0',
|
: '#ffff0080',
|
||||||
|
borderRadius: 8,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
|
padding: 4,
|
||||||
|
width: hasMultipleLayers ? undefined : 8,
|
||||||
|
height: hasMultipleLayers ? undefined : 8,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Inset>
|
{hasMultipleLayers && (
|
||||||
{hasMultipleLayers && (
|
<Text
|
||||||
<Text
|
size="2"
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color:
|
color: trueStatus === 'ricochet' ? 'black' : undefined,
|
||||||
trueStatus === 'ricochet' ? 'black' : undefined,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<b>{layer.index + 1}</b>
|
||||||
<b>{layer.index + 1}</b>
|
</Text>
|
||||||
</Text>
|
)}
|
||||||
)}
|
</div>
|
||||||
</Inset>
|
|
||||||
</Card>
|
|
||||||
</Html>
|
</Html>
|
||||||
</group>
|
</group>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export function SpacedArmorSceneComponent({
|
|||||||
const distance = (
|
const distance = (
|
||||||
shot.layers.at(-1) as ShotLayerBase
|
shot.layers.at(-1) as ShotLayerBase
|
||||||
).point.distanceTo(intersections[0].point);
|
).point.distanceTo(intersections[0].point);
|
||||||
if (explosive && !splashing)
|
if (explosive)
|
||||||
remainingPenetration -= 0.5 * remainingPenetration * distance;
|
remainingPenetration -= 0.5 * remainingPenetration * distance;
|
||||||
const blocked = remainingPenetration < 0;
|
const blocked = remainingPenetration < 0;
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ export function SpacedArmorSceneComponent({
|
|||||||
const previousIntersection = intersections[layerIndex - 1];
|
const previousIntersection = intersections[layerIndex - 1];
|
||||||
const distance =
|
const distance =
|
||||||
intersection.distance - previousIntersection.distance;
|
intersection.distance - previousIntersection.distance;
|
||||||
if (explosive && !splashing)
|
if (explosive)
|
||||||
remainingPenetration -=
|
remainingPenetration -=
|
||||||
0.5 * remainingPenetration * distance;
|
0.5 * remainingPenetration * distance;
|
||||||
const wasted = remainingPenetration < 0;
|
const wasted = remainingPenetration < 0;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createPortal, useFrame } from '@react-three/fiber';
|
|||||||
import { memo, useState } from 'react';
|
import { memo, useState } from 'react';
|
||||||
import { DepthTexture, Scene, Vector2 } from 'three';
|
import { DepthTexture, Scene, Vector2 } from 'three';
|
||||||
import { CoreArmorScene } from './components/CoreArmorScene';
|
import { CoreArmorScene } from './components/CoreArmorScene';
|
||||||
import { spacedArmorRenderTarget } from './components/CoreArmorSceneComponent';
|
import { spacedArmorRenderTarget } from './components/CoreArmorSceneComponent/target';
|
||||||
import { SpacedArmorScene } from './components/SpacedArmorScene';
|
import { SpacedArmorScene } from './components/SpacedArmorScene';
|
||||||
|
|
||||||
export const Armor = memo(() => {
|
export const Armor = memo(() => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface RootProps {
|
interface RootProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import {
|
|||||||
GrayColor,
|
GrayColor,
|
||||||
PALETTES,
|
PALETTES,
|
||||||
} from '../../../constants/radixColors';
|
} from '../../../constants/radixColors';
|
||||||
import { TREE_TYPE_ICONS, TankClass, TreeType } from '../../Tanks';
|
import { TankClass, TreeType } from '../../Tanks';
|
||||||
|
import { TREE_TYPE_ICONS } from '../../Tanks/components/Item/constants';
|
||||||
import { RowStat } from './RowStat';
|
import { RowStat } from './RowStat';
|
||||||
|
|
||||||
export interface RowStatItem {
|
export interface RowStatItem {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '../../../constants/radixColors';
|
} from '../../../constants/radixColors';
|
||||||
import PercentileIndicator from '../../PercentileIndicator';
|
import PercentileIndicator from '../../PercentileIndicator';
|
||||||
|
|
||||||
export interface RowStatProps {
|
interface RowStatProps {
|
||||||
name: string;
|
name: string;
|
||||||
value?: string | number;
|
value?: string | number;
|
||||||
delta?: number;
|
delta?: number;
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { Percentile } from '../../../constants/percentiles';
|
|||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
import PercentileIndicator from '../../PercentileIndicator';
|
import PercentileIndicator from '../../PercentileIndicator';
|
||||||
|
|
||||||
export interface CardItem {
|
interface CardItem {
|
||||||
label: string;
|
label: string;
|
||||||
value: string | number | undefined;
|
value: string | number | undefined;
|
||||||
percentile?: Percentile;
|
percentile?: Percentile;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CardProps {
|
interface CardProps {
|
||||||
items: CardItem[];
|
items: CardItem[];
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface RootProps {
|
interface RootProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface RowProps {
|
interface RowProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Card } from './components/Card';
|
|||||||
import { Root } from './components/Root';
|
import { Root } from './components/Root';
|
||||||
import { Row } from './components/Row';
|
import { Row } from './components/Row';
|
||||||
|
|
||||||
export interface GenericAllStatsProps {
|
interface GenericAllStatsProps {
|
||||||
stats: AllStats;
|
stats: AllStats;
|
||||||
supplementaryStats?: SupplementaryStats;
|
supplementaryStats?: SupplementaryStats;
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Stat } from '..';
|
import { GenericStat } from '..';
|
||||||
import GenericStatsRow from './GenericStatsRow';
|
import GenericStatsRow from './GenericStatsRow';
|
||||||
|
|
||||||
export interface GenericStatsColumnProps {
|
interface GenericStatsColumnProps {
|
||||||
stats: Stat[];
|
stats: GenericStat[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GenericStatsColumn({ stats }: GenericStatsColumnProps) {
|
export default function GenericStatsColumn({ stats }: GenericStatsColumnProps) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Stat } from '..';
|
import { GenericStat } from '..';
|
||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
import PercentileIndicator from '../../PercentileIndicator';
|
import PercentileIndicator from '../../PercentileIndicator';
|
||||||
|
|
||||||
export interface GenericStatsRowProps {
|
interface GenericStatsRowProps {
|
||||||
stat: Stat;
|
stat: GenericStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GenericStatsRow({ stat }: GenericStatsRowProps) {
|
export default function GenericStatsRow({ stat }: GenericStatsRowProps) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Percentile } from '../../constants/percentiles';
|
import { Percentile } from '../../constants/percentiles';
|
||||||
import GenericStatsColumn from './components/GenericStatsColumn';
|
import GenericStatsColumn from './components/GenericStatsColumn';
|
||||||
|
|
||||||
export type Stat =
|
export type GenericStat =
|
||||||
| [string, string | number | undefined]
|
| [string, string | number | undefined]
|
||||||
| [string, string | number | undefined, Percentile | undefined];
|
| [string, string | number | undefined, Percentile | undefined];
|
||||||
|
|
||||||
export interface GenericStatsProps {
|
export interface GenericStatsProps {
|
||||||
stats: Stat[];
|
stats: GenericStat[];
|
||||||
columnCount?: number;
|
columnCount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export default function GenericStats({
|
|||||||
}: GenericStatsProps) {
|
}: GenericStatsProps) {
|
||||||
const filteredStats = stats.filter(([, value]) => value !== undefined);
|
const filteredStats = stats.filter(([, value]) => value !== undefined);
|
||||||
const itemsPerRow = Math.ceil(filteredStats.length / columnCount);
|
const itemsPerRow = Math.ceil(filteredStats.length / columnCount);
|
||||||
const columns: Stat[][] = [];
|
const columns: GenericStat[][] = [];
|
||||||
|
|
||||||
filteredStats.forEach((stat, index) => {
|
filteredStats.forEach((stat, index) => {
|
||||||
const column = Math.floor(index / itemsPerRow);
|
const column = Math.floor(index / itemsPerRow);
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export enum LineColor {
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
White,
|
||||||
|
}
|
||||||
|
export enum LineStyle {
|
||||||
|
Solid,
|
||||||
|
Dashes,
|
||||||
|
DashesAndDots,
|
||||||
|
}
|
||||||
+4
-16
@@ -1,12 +1,6 @@
|
|||||||
import { blue, green, red, slateDark } from '@radix-ui/colors';
|
import { blue, green, red, slateDark } from '@radix-ui/colors';
|
||||||
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './Root';
|
import { GRAPH_HEIGHT, GRAPH_WIDTH } from '../Root/constants';
|
||||||
|
import { LineColor, LineStyle } from './constants';
|
||||||
export enum LineColor {
|
|
||||||
Red,
|
|
||||||
Green,
|
|
||||||
Blue,
|
|
||||||
White,
|
|
||||||
}
|
|
||||||
|
|
||||||
const LINE_COLORS = {
|
const LINE_COLORS = {
|
||||||
[LineColor.Red]: red.red9,
|
[LineColor.Red]: red.red9,
|
||||||
@@ -15,12 +9,6 @@ const LINE_COLORS = {
|
|||||||
[LineColor.White]: slateDark.slate11,
|
[LineColor.White]: slateDark.slate11,
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum LineStyle {
|
|
||||||
Solid,
|
|
||||||
Dashes,
|
|
||||||
DashesAndDots,
|
|
||||||
}
|
|
||||||
|
|
||||||
const LINE_STYLES = {
|
const LINE_STYLES = {
|
||||||
[LineStyle.Solid]: undefined,
|
[LineStyle.Solid]: undefined,
|
||||||
[LineStyle.Dashes]: '8, 8',
|
[LineStyle.Dashes]: '8, 8',
|
||||||
@@ -28,9 +16,9 @@ const LINE_STYLES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type PlotItem = [number, number];
|
export type PlotItem = [number, number];
|
||||||
export type Plot = PlotItem[];
|
type Plot = PlotItem[];
|
||||||
|
|
||||||
export interface LineProps {
|
interface LineProps {
|
||||||
plot: Plot;
|
plot: Plot;
|
||||||
minY?: number;
|
minY?: number;
|
||||||
maxY?: number;
|
maxY?: number;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export enum MarginOrientation {
|
||||||
|
Vertical,
|
||||||
|
Horizontal,
|
||||||
|
}
|
||||||
+4
-8
@@ -1,13 +1,9 @@
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../../stitches.config';
|
||||||
import { MarginInputProps } from './Root';
|
import { MarginInputProps } from '../Root';
|
||||||
|
import { MarginOrientation } from './constants';
|
||||||
|
|
||||||
export enum MarginOrientation {
|
interface MarginProps extends MarginInputProps {
|
||||||
Vertical,
|
|
||||||
Horizontal,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MarginProps extends MarginInputProps {
|
|
||||||
verticalSeparations: number;
|
verticalSeparations: number;
|
||||||
orientation?: MarginOrientation;
|
orientation?: MarginOrientation;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const GRAPH_WIDTH = 384;
|
||||||
|
export const GRAPH_HEIGHT = 320;
|
||||||
+5
-6
@@ -1,10 +1,9 @@
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../../stitches.config';
|
||||||
import { Margin, MarginOrientation } from './Margin';
|
import { Margin } from '../Margin';
|
||||||
|
import { MarginOrientation } from '../Margin/constants';
|
||||||
export const GRAPH_WIDTH = 384;
|
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './constants';
|
||||||
export const GRAPH_HEIGHT = 320;
|
|
||||||
|
|
||||||
export interface MarginInputProps {
|
export interface MarginInputProps {
|
||||||
min: number;
|
min: number;
|
||||||
@@ -13,7 +12,7 @@ export interface MarginInputProps {
|
|||||||
precision?: number;
|
precision?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RootProps {
|
interface RootProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
verticalMargin?: MarginInputProps;
|
verticalMargin?: MarginInputProps;
|
||||||
horizontalMargin?: MarginInputProps;
|
horizontalMargin?: MarginInputProps;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export interface GapProps {
|
interface GapProps {
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ComponentProps } from 'react';
|
|||||||
import { RatingsReward } from '../../../commands/ratings';
|
import { RatingsReward } from '../../../commands/ratings';
|
||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export interface ItemProps extends ComponentProps<'div'> {
|
interface ItemProps extends ComponentProps<'div'> {
|
||||||
position: number;
|
position: number;
|
||||||
deltaPosition?: number;
|
deltaPosition?: number;
|
||||||
score?: number;
|
score?: number;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface RootProps {
|
interface RootProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { DiscordLogoIcon } from '@radix-ui/react-icons';
|
import { DiscordLogoIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, Text, Theme } from '@radix-ui/themes';
|
import { Flex, Text, Theme } from '@radix-ui/themes';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { theme } from '../../stitches.config';
|
|
||||||
import { BlitzkriegWormWide } from '../../icons/BlitzkriegWormWide';
|
import { BlitzkriegWormWide } from '../../icons/BlitzkriegWormWide';
|
||||||
|
import { theme } from '../../stitches.config';
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum NoDataType {
|
||||||
|
BattlesInPeriod,
|
||||||
|
PlayersInPeriod,
|
||||||
|
TanksFound,
|
||||||
|
}
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from '../../core/localization/translator';
|
||||||
import { theme } from '../stitches.config';
|
import { theme } from '../../stitches.config';
|
||||||
|
|
||||||
export enum NoDataType {
|
interface NoDataProps {
|
||||||
BattlesInPeriod,
|
|
||||||
PlayersInPeriod,
|
|
||||||
TanksFound,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NoDataProps {
|
|
||||||
type: 'battles_in_period' | 'players_in_period' | 'tanks_found';
|
type: 'battles_in_period' | 'players_in_period' | 'tanks_found';
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
}
|
}
|
||||||
+1
-20
@@ -10,11 +10,7 @@ import {
|
|||||||
slate,
|
slate,
|
||||||
teal,
|
teal,
|
||||||
} from '@radix-ui/colors';
|
} from '@radix-ui/colors';
|
||||||
import { Percentile } from '../constants/percentiles';
|
import { Percentile } from '../../constants/percentiles';
|
||||||
|
|
||||||
export interface PercentileIndicatorProps {
|
|
||||||
percentile: Percentile;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PERCENTILE_COLORS = {
|
export const PERCENTILE_COLORS = {
|
||||||
[Percentile.VeryBad]: slate.slate9,
|
[Percentile.VeryBad]: slate.slate9,
|
||||||
@@ -28,18 +24,3 @@ export const PERCENTILE_COLORS = {
|
|||||||
[Percentile.Unicum]: pink.pink9,
|
[Percentile.Unicum]: pink.pink9,
|
||||||
[Percentile.SuperUnicum]: purple.purple9,
|
[Percentile.SuperUnicum]: purple.purple9,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PercentileIndicator({
|
|
||||||
percentile,
|
|
||||||
}: PercentileIndicatorProps) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
backgroundColor: PERCENTILE_COLORS[percentile],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Percentile } from '../../constants/percentiles';
|
||||||
|
import { PERCENTILE_COLORS } from './constants';
|
||||||
|
|
||||||
|
interface PercentileIndicatorProps {
|
||||||
|
percentile: Percentile;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PercentileIndicator({
|
||||||
|
percentile,
|
||||||
|
}: PercentileIndicatorProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: PERCENTILE_COLORS[percentile],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface ColumnProps {
|
interface ColumnProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { TankClass, TreeType } from '.';
|
||||||
|
import {
|
||||||
|
TANK_ICONS,
|
||||||
|
TANK_ICONS_COLLECTOR,
|
||||||
|
TANK_ICONS_PREMIUM,
|
||||||
|
} from '../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
|
|
||||||
|
export const TREE_TYPES = ['researchable', 'premium', 'collector'] as const; // BIG TODO: REMOVE THIS AFTER LOCALIZATION
|
||||||
|
|
||||||
|
export const TANK_CLASSES = [
|
||||||
|
'lightTank',
|
||||||
|
'mediumTank',
|
||||||
|
'heavyTank',
|
||||||
|
'AT-SPG',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const TREE_TYPE_NAMES: Record<TreeType, string> = {
|
||||||
|
researchable: 'Tech tree',
|
||||||
|
premium: 'Premium',
|
||||||
|
collector: 'Collector',
|
||||||
|
};
|
||||||
|
export const TREE_TYPE_IMAGES: Record<TreeType, string> = {
|
||||||
|
researchable: 'https://i.imgur.com/pJxO2XY.png',
|
||||||
|
premium: 'https://i.imgur.com/mZzSwOU.png',
|
||||||
|
collector: 'https://i.imgur.com/7A0RsG5.png',
|
||||||
|
};
|
||||||
|
export const TREE_TYPE_ICONS: Record<TreeType, Record<TankClass, string>> = {
|
||||||
|
researchable: TANK_ICONS,
|
||||||
|
premium: TANK_ICONS_PREMIUM,
|
||||||
|
collector: TANK_ICONS_COLLECTOR,
|
||||||
|
};
|
||||||
+3
-36
@@ -1,37 +1,10 @@
|
|||||||
import {
|
import { theme } from '../../../../stitches.config';
|
||||||
TANK_ICONS,
|
import { TANK_CLASSES, TREE_TYPES, TREE_TYPE_ICONS } from './constants';
|
||||||
TANK_ICONS_COLLECTOR,
|
|
||||||
TANK_ICONS_PREMIUM,
|
|
||||||
} from '../../../core/blitzkrieg/tankDefinitions';
|
|
||||||
import { theme } from '../../../stitches.config';
|
|
||||||
|
|
||||||
export const TANK_CLASSES = [
|
|
||||||
'lightTank',
|
|
||||||
'mediumTank',
|
|
||||||
'heavyTank',
|
|
||||||
'AT-SPG',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const TREE_TYPES = ['researchable', 'premium', 'collector'] as const;
|
|
||||||
|
|
||||||
export type TankClass = (typeof TANK_CLASSES)[number];
|
export type TankClass = (typeof TANK_CLASSES)[number];
|
||||||
export type TreeType = (typeof TREE_TYPES)[number];
|
export type TreeType = (typeof TREE_TYPES)[number];
|
||||||
|
|
||||||
// BIG TODO: REMOVE THIS AFTER LOCALIZATION
|
|
||||||
export const TREE_TYPE_NAMES: Record<TreeType, string> = {
|
|
||||||
researchable: 'Tech tree',
|
|
||||||
premium: 'Premium',
|
|
||||||
collector: 'Collector',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TREE_TYPE_IMAGES: Record<TreeType, string> = {
|
|
||||||
researchable: 'https://i.imgur.com/pJxO2XY.png',
|
|
||||||
premium: 'https://i.imgur.com/mZzSwOU.png',
|
|
||||||
collector: 'https://i.imgur.com/7A0RsG5.png',
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ItemProps {
|
export interface ItemProps {
|
||||||
image?: string;
|
|
||||||
tankType?: TankClass;
|
tankType?: TankClass;
|
||||||
name: string;
|
name: string;
|
||||||
treeType: TreeType;
|
treeType: TreeType;
|
||||||
@@ -43,13 +16,7 @@ const TREE_TYPE_COLOR = {
|
|||||||
collector: '_blue',
|
collector: '_blue',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const TREE_TYPE_ICONS: Record<TreeType, Record<TankClass, string>> = {
|
export function Item({ tankType, name, treeType }: ItemProps) {
|
||||||
researchable: TANK_ICONS,
|
|
||||||
premium: TANK_ICONS_PREMIUM,
|
|
||||||
collector: TANK_ICONS_COLLECTOR,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function Item({ image, tankType, name, treeType }: ItemProps) {
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface RootProps {
|
interface RootProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface RowProps {
|
interface RowProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export interface TitleProps {
|
interface TitleProps {
|
||||||
children: string;
|
children: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { theme } from '../stitches.config';
|
|||||||
|
|
||||||
export type TierWeightsRecord = Partial<Record<Tier, number>>;
|
export type TierWeightsRecord = Partial<Record<Tier, number>>;
|
||||||
|
|
||||||
export interface TierWeightsProps {
|
interface TierWeightsProps {
|
||||||
weights: TierWeightsRecord;
|
weights: TierWeightsRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { theme } from '../stitches.config';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
export interface TitleBarProps {
|
interface TitleBarProps {
|
||||||
title: string;
|
title: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
|
||||||
import fetchBlitz from './fetchBlitz';
|
|
||||||
|
|
||||||
export interface EncyclopediaInfo {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const encyclopediaInfo = fetchBlitz<EncyclopediaInfo>(
|
|
||||||
`https://api.wotblitz.com/wotb/encyclopedia/info/?application_id=${WARGAMING_APPLICATION_ID}`,
|
|
||||||
);
|
|
||||||
@@ -6,13 +6,9 @@ import { VertexAttribute } from '../streams/scpg';
|
|||||||
import {
|
import {
|
||||||
vertexAttributeGLTFName,
|
vertexAttributeGLTFName,
|
||||||
vertexAttributeGltfVectorSizes,
|
vertexAttributeGltfVectorSizes,
|
||||||
} from './extractModel';
|
} from './extractModel/constants';
|
||||||
import { readDVPLFile } from './readDVPLFile';
|
import { readDVPLFile } from './readDVPLFile';
|
||||||
|
|
||||||
Buffer.prototype.toJSON = function () {
|
|
||||||
return [...(this as Buffer)].map((int) => int.toString(16)).join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function extractArmor(data: string, fileName: string) {
|
export async function extractArmor(data: string, fileName: string) {
|
||||||
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2.dvpl`;
|
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2.dvpl`;
|
||||||
const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg.dvpl`;
|
const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg.dvpl`;
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { vertexAttributeVectorSizes } from '../../streams/scg';
|
||||||
|
import { VertexAttribute } from '../../streams/scpg';
|
||||||
|
|
||||||
|
export const vertexAttributeGLTFName: Partial<Record<VertexAttribute, string>> =
|
||||||
|
{
|
||||||
|
[VertexAttribute.VERTEX]: 'POSITION',
|
||||||
|
[VertexAttribute.NORMAL]: 'NORMAL',
|
||||||
|
[VertexAttribute.TEXCOORD0]: 'TEXCOORD_0',
|
||||||
|
[VertexAttribute.TEXCOORD1]: 'TEXCOORD_0',
|
||||||
|
[VertexAttribute.TEXCOORD2]: 'TEXCOORD_0',
|
||||||
|
[VertexAttribute.TEXCOORD3]: 'TEXCOORD_0',
|
||||||
|
[VertexAttribute.TANGENT]: 'TANGENT',
|
||||||
|
[VertexAttribute.JOINTINDEX]: 'JOINT_0',
|
||||||
|
[VertexAttribute.JOINTWEIGHT]: 'WEIGHT_0',
|
||||||
|
};
|
||||||
|
export const vertexAttributeGltfVectorSizes = {
|
||||||
|
...vertexAttributeVectorSizes,
|
||||||
|
[VertexAttribute.TANGENT]: 4,
|
||||||
|
} as const;
|
||||||
@@ -1,51 +1,26 @@
|
|||||||
import { Document, Material, Node, Scene } from '@gltf-transform/core';
|
import { Document, Material, Node, Scene } from '@gltf-transform/core';
|
||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
|
import { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three';
|
||||||
|
import { readTexture } from '../../blitzkrieg/readTexture';
|
||||||
|
import { TextureMutation } from '../../blitzkrieg/readTexture/constants';
|
||||||
|
import { Hierarchy, Sc2ReadStream, Textures } from '../../streams/sc2';
|
||||||
|
import { ScgReadStream } from '../../streams/scg';
|
||||||
|
import { VertexAttribute } from '../../streams/scpg';
|
||||||
|
import { readDVPLFile } from '../readDVPLFile';
|
||||||
import {
|
import {
|
||||||
Matrix4,
|
vertexAttributeGLTFName,
|
||||||
Quaternion,
|
vertexAttributeGltfVectorSizes,
|
||||||
Vector3,
|
} from './constants';
|
||||||
Vector3Tuple,
|
|
||||||
Vector4Tuple,
|
|
||||||
} from 'three';
|
|
||||||
import { TextureMutation, readTexture } from '../blitzkrieg/readTexture';
|
|
||||||
import { Hierarchy, Sc2ReadStream, Textures } from '../streams/sc2';
|
|
||||||
import { ScgReadStream, vertexAttributeVectorSizes } from '../streams/scg';
|
|
||||||
import { VertexAttribute } from '../streams/scpg';
|
|
||||||
import { readDVPLFile } from './readDVPLFile';
|
|
||||||
|
|
||||||
const ERROR_ON_UNKNOWN_COMPONENT = false;
|
const ERROR_ON_UNKNOWN_COMPONENT = false;
|
||||||
const MAX_FLOAT32 = 2 ** 127 * (2 - 2 ** -23);
|
|
||||||
|
|
||||||
export const vertexAttributeGLTFName: Partial<Record<VertexAttribute, string>> =
|
|
||||||
{
|
|
||||||
[VertexAttribute.VERTEX]: 'POSITION',
|
|
||||||
[VertexAttribute.NORMAL]: 'NORMAL',
|
|
||||||
[VertexAttribute.TEXCOORD0]: 'TEXCOORD_0',
|
|
||||||
[VertexAttribute.TEXCOORD1]: 'TEXCOORD_0',
|
|
||||||
[VertexAttribute.TEXCOORD2]: 'TEXCOORD_0',
|
|
||||||
[VertexAttribute.TEXCOORD3]: 'TEXCOORD_0',
|
|
||||||
[VertexAttribute.TANGENT]: 'TANGENT',
|
|
||||||
[VertexAttribute.JOINTINDEX]: 'JOINT_0',
|
|
||||||
[VertexAttribute.JOINTWEIGHT]: 'WEIGHT_0',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const vertexAttributeGltfVectorSizes = {
|
|
||||||
...vertexAttributeVectorSizes,
|
|
||||||
|
|
||||||
[VertexAttribute.TANGENT]: 4,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
const omitMeshNames = {
|
const omitMeshNames = {
|
||||||
start: ['chassis_chassis_', 'chassis_track_crash_', 'HP_'],
|
start: ['chassis_chassis_', 'chassis_track_crash_', 'HP_'],
|
||||||
end: ['_POINT'],
|
end: ['_POINT'],
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function extractModel(
|
export async function extractModel(data: string, path: string) {
|
||||||
data: string,
|
|
||||||
path: string,
|
|
||||||
baseColor?: Vector3Tuple,
|
|
||||||
) {
|
|
||||||
const sc2Path = `${data}/3d/${path}.sc2.dvpl`;
|
const sc2Path = `${data}/3d/${path}.sc2.dvpl`;
|
||||||
const scgPath = `${data}/3d/${path}.scg.dvpl`;
|
const scgPath = `${data}/3d/${path}.scg.dvpl`;
|
||||||
const sc2 = new Sc2ReadStream((await readDVPLFile(sc2Path)).buffer).sc2();
|
const sc2 = new Sc2ReadStream((await readDVPLFile(sc2Path)).buffer).sc2();
|
||||||
@@ -86,9 +61,7 @@ export async function extractModel(
|
|||||||
.setImage(
|
.setImage(
|
||||||
await readTexture(
|
await readTexture(
|
||||||
`${data}/3d/${dirname(path)}/${textures.albedo}`,
|
`${data}/3d/${dirname(path)}/${textures.albedo}`,
|
||||||
baseColor
|
TextureMutation.Albedo,
|
||||||
? { mutation: TextureMutation.BaseColor, baseColor }
|
|
||||||
: undefined,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -101,7 +74,7 @@ export async function extractModel(
|
|||||||
.setImage(
|
.setImage(
|
||||||
await readTexture(
|
await readTexture(
|
||||||
`${data}/3d/${dirname(path)}/${textures.baseRMMap}`,
|
`${data}/3d/${dirname(path)}/${textures.baseRMMap}`,
|
||||||
{ mutation: TextureMutation.RoughnessMetallicness },
|
TextureMutation.RoughnessMetallicness,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -119,7 +92,7 @@ export async function extractModel(
|
|||||||
`${data}/3d/${dirname(path)}/${
|
`${data}/3d/${dirname(path)}/${
|
||||||
textures.baseNormalMap ?? textures.normalmap
|
textures.baseNormalMap ?? textures.normalmap
|
||||||
}`,
|
}`,
|
||||||
isBase ? { mutation: TextureMutation.Normal } : undefined,
|
isBase ? TextureMutation.Normal : undefined,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -133,7 +106,7 @@ export async function extractModel(
|
|||||||
.setImage(
|
.setImage(
|
||||||
await readTexture(
|
await readTexture(
|
||||||
`${data}/3d/${dirname(path)}/${textures.miscMap}`,
|
`${data}/3d/${dirname(path)}/${textures.miscMap}`,
|
||||||
{ mutation: TextureMutation.Miscellaneous },
|
TextureMutation.Miscellaneous,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export type BlitzResponse<Data extends object> =
|
type BlitzResponse<Data extends object> =
|
||||||
| {
|
| {
|
||||||
status: 'error';
|
status: 'error';
|
||||||
error: { field: string; message: string; code: 402; value: null };
|
error: { field: string; message: string; code: 402; value: null };
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import fetchBlitz from './fetchBlitz';
|
|||||||
import { normalizeExtra } from './normalizeExtra';
|
import { normalizeExtra } from './normalizeExtra';
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from './normalizeIds';
|
||||||
|
|
||||||
export type IndividualClanAccountInfo = {
|
type IndividualClanAccountInfo = {
|
||||||
account_id: number;
|
account_id: number;
|
||||||
joined_at: number;
|
joined_at: number;
|
||||||
clan_id: number;
|
clan_id: number;
|
||||||
@@ -21,7 +21,7 @@ export type IndividualClanAccountInfo = {
|
|||||||
};
|
};
|
||||||
} | null;
|
} | null;
|
||||||
|
|
||||||
export interface ClanAccountInfo {
|
interface ClanAccountInfo {
|
||||||
[accountId: number]: IndividualClanAccountInfo;
|
[accountId: number]: IndividualClanAccountInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID
|
|||||||
import fetchBlitz from './fetchBlitz';
|
import fetchBlitz from './fetchBlitz';
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from './normalizeIds';
|
||||||
|
|
||||||
export interface IndividualClanInfo {
|
interface IndividualClanInfo {
|
||||||
recruiting_options: {
|
recruiting_options: {
|
||||||
vehicles_level: number;
|
vehicles_level: number;
|
||||||
wins_ratio: number;
|
wins_ratio: number;
|
||||||
@@ -32,7 +32,7 @@ export interface IndividualClanInfo {
|
|||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClanInfo {
|
interface ClanInfo {
|
||||||
[clanId: number]: IndividualClanInfo;
|
[clanId: number]: IndividualClanInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import { Clan, ClanList } from '../../types/clanList';
|
import { ClanList } from '../../types/clanList';
|
||||||
import fetchBlitz from './fetchBlitz';
|
import fetchBlitz from './fetchBlitz';
|
||||||
|
|
||||||
export type ClanListWithRegion = (Clan & {
|
|
||||||
region: 'com' | 'eu' | 'asia';
|
|
||||||
})[];
|
|
||||||
|
|
||||||
export default async function searchClansAcrossRegions(
|
export default async function searchClansAcrossRegions(
|
||||||
search: string,
|
search: string,
|
||||||
limit = 25,
|
limit = 25,
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const usernamePattern = /^[a-zA-Z0-9_]{3,24}$/;
|
||||||
|
export const usernamePatternWithoutPosition = /[a-zA-Z0-9_]{3,24}/;
|
||||||
+4
-6
@@ -1,6 +1,7 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from './fetchBlitz';
|
import fetchBlitz from '../fetchBlitz';
|
||||||
|
import { usernamePattern } from './constants';
|
||||||
|
|
||||||
export interface AccountListItem {
|
export interface AccountListItem {
|
||||||
nickname: string;
|
nickname: string;
|
||||||
@@ -11,9 +12,6 @@ export type AccountListWithServer = (AccountListItem & {
|
|||||||
region: Region;
|
region: Region;
|
||||||
})[];
|
})[];
|
||||||
|
|
||||||
export const usernamePattern = /^[a-zA-Z0-9_]{3,24}$/;
|
|
||||||
export const usernamePatternWithoutPosition = /[a-zA-Z0-9_]{3,24}/;
|
|
||||||
|
|
||||||
export default async function searchPlayersAcrossRegions(
|
export default async function searchPlayersAcrossRegions(
|
||||||
search: string,
|
search: string,
|
||||||
limit = 25,
|
limit = 25,
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { crc32 } from 'crc';
|
||||||
|
import { encodeBlockHC } from 'lz4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thanks Maddoxkkm! Modernized heavily.
|
||||||
|
*
|
||||||
|
* https://github.com/Maddoxkkm/dvpl_converter/
|
||||||
|
*/
|
||||||
|
export function writeDVPL(buffer: Buffer) {
|
||||||
|
let output = Buffer.alloc(buffer.length);
|
||||||
|
const compressedBlockSize = encodeBlockHC(buffer, output);
|
||||||
|
|
||||||
|
if (compressedBlockSize === 0 || compressedBlockSize >= buffer.length) {
|
||||||
|
const footerBuffer = toDVPLFooter(
|
||||||
|
buffer.length,
|
||||||
|
buffer.length,
|
||||||
|
crc32(buffer),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Buffer.concat([buffer, footerBuffer], buffer.length + 20);
|
||||||
|
} else {
|
||||||
|
output = output.subarray(0, compressedBlockSize);
|
||||||
|
const footerBuffer = toDVPLFooter(
|
||||||
|
buffer.length,
|
||||||
|
compressedBlockSize,
|
||||||
|
crc32(output),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Buffer.concat([output, footerBuffer], compressedBlockSize + 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toDVPLFooter(
|
||||||
|
inputSize: number,
|
||||||
|
compressedSize: number,
|
||||||
|
crc32: number,
|
||||||
|
type: number,
|
||||||
|
) {
|
||||||
|
const result = Buffer.alloc(20);
|
||||||
|
|
||||||
|
result.writeUInt32LE(inputSize, 0);
|
||||||
|
result.writeUInt32LE(compressedSize, 4);
|
||||||
|
result.writeUInt32LE(crc32, 8);
|
||||||
|
result.writeUInt32LE(type, 12);
|
||||||
|
result.write('DVPL', 16, 4, 'utf8');
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
import { degToRad } from 'three/src/math/MathUtils';
|
import { degToRad } from 'three/src/math/MathUtils';
|
||||||
import {
|
import { DuelMember, mutateDuel } from '../../stores/duel';
|
||||||
DuelMember,
|
import { genericDefaultEquipmentMatrix } from '../../stores/duel/constants';
|
||||||
genericDefaultEquipmentMatrix,
|
|
||||||
mutateDuel,
|
|
||||||
} from '../../stores/duel';
|
|
||||||
import { availableProvisions } from './availableProvisions';
|
import { availableProvisions } from './availableProvisions';
|
||||||
import { modelDefinitions } from './modelDefinitions';
|
import { modelDefinitions } from './modelDefinitions';
|
||||||
import { provisionDefinitions } from './provisionDefinitions';
|
import { provisionDefinitions } from './provisionDefinitions';
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
|
import { discordBlitzDB } from './discordBlitzDB';
|
||||||
export const discordBlitzDB = new PrismaClient();
|
|
||||||
|
|
||||||
export function linkBlitzAndDiscord(
|
export function linkBlitzAndDiscord(
|
||||||
discord: number,
|
discord: number,
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
export const discordBlitzDB = new PrismaClient();
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { superDecompress } from './superDecompress';
|
import { superDecompress } from './superDecompress';
|
||||||
|
|
||||||
export async function fetchCdonLz4<Type>(url: string) {
|
export async function fetchCdonLz4<Type>(url: string) {
|
||||||
return await fetch(url, { cache: 'no-cache' })
|
return await fetch(url, { cache: 'no-store' })
|
||||||
.then((response) => response.arrayBuffer())
|
.then((response) => response.arrayBuffer())
|
||||||
.then((buffer) => superDecompress<Type>(new Uint8Array(buffer)));
|
.then((buffer) => superDecompress<Type>(new Uint8Array(buffer)));
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from '../localization/translator';
|
||||||
import { StatFilters } from '../statistics/filterStats';
|
import { StatFilters } from '../statistics/filterStats';
|
||||||
import { TIER_ROMAN_NUMERALS, Tier, tankDefinitions } from './tankDefinitions';
|
import { Tier, tankDefinitions } from './tankDefinitions';
|
||||||
|
import { TIER_ROMAN_NUMERALS } from './tankDefinitions/constants';
|
||||||
|
|
||||||
export async function filtersToDescription(
|
export async function filtersToDescription(
|
||||||
{ nation, tier, tankType, treeType, tank }: StatFilters,
|
{ nation, tier, tankType, treeType, tank }: StatFilters,
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { BlitzkriegRatingsLeaderboard } from '../../../scripts/buildRatingsLeaderboard';
|
import { BlitzkriegRatingsLeaderboard } from '../../../scripts/buildRatingsLeaderboard';
|
||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
|
|
||||||
export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-assets' };
|
|
||||||
|
|
||||||
const MIDNIGHT_LEADERBOARD_CACHE: Record<
|
const MIDNIGHT_LEADERBOARD_CACHE: Record<
|
||||||
Region,
|
Region,
|
||||||
Record<number, BlitzkriegRatingsLeaderboard>
|
Record<number, BlitzkriegRatingsLeaderboard>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
getHistoriesDefaultOptions,
|
getHistoriesDefaultOptions,
|
||||||
} from '../../types/histories';
|
} from '../../types/histories';
|
||||||
import getTankStats from '../blitz/getTankStats';
|
import getTankStats from '../blitz/getTankStats';
|
||||||
import { emptyAllStats } from '../blitzstars/getStatsInPeriod';
|
import { emptyAllStats } from '../blitzstars/getStatsInPeriod/constants';
|
||||||
|
|
||||||
export interface TankHistory extends History {
|
export interface TankHistory extends History {
|
||||||
tank_id: number;
|
tank_id: number;
|
||||||
@@ -27,7 +27,7 @@ export interface GetTankHistoriesOptions extends GetHistoriesOptions {
|
|||||||
tankId?: number;
|
tankId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
||||||
...getHistoriesDefaultOptions,
|
...getHistoriesDefaultOptions,
|
||||||
tankId: undefined,
|
tankId: undefined,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
|
|
||||||
export const TIME_ZONE_MAPPINGS: Record<Region, number> = {
|
const TIME_ZONE_MAPPINGS: Record<Region, number> = {
|
||||||
com: -5, // Central North American Time
|
com: -5, // Central North American Time
|
||||||
eu: +1, // Central European Time
|
eu: +1, // Central European Time
|
||||||
asia: +7, // Central Asia Standard Time
|
asia: +7, // Central Asia Standard Time
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import satori from 'satori';
|
import satori from 'satori';
|
||||||
|
import robotoBlack from './Roboto-Black.ttf';
|
||||||
|
import robotoBold from './Roboto-Bold.ttf';
|
||||||
|
import roboto from './Roboto.ttf';
|
||||||
|
|
||||||
const fonts = [
|
const fonts = [
|
||||||
{
|
{ file: roboto, weight: 400 as const },
|
||||||
file: require('./Roboto.ttf') as string,
|
{ file: robotoBold, weight: 700 as const },
|
||||||
weight: 400 as const,
|
{ file: robotoBlack, weight: 900 as const },
|
||||||
},
|
|
||||||
{
|
|
||||||
file: require('./Roboto-Bold.ttf') as string,
|
|
||||||
weight: 700 as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
file: require('./Roboto-Black.ttf') as string,
|
|
||||||
weight: 900 as const,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default async function jsxToSvg(jsx: JSX.Element) {
|
export default async function jsxToSvg(jsx: JSX.Element) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { client } from '../discord/client';
|
import { client } from '../discord/client';
|
||||||
import { discordBlitzDB } from './discordBlitz';
|
import { discordBlitzDB } from './discordBlitzDB';
|
||||||
|
|
||||||
const EXIT_EVENTS = [
|
const EXIT_EVENTS = [
|
||||||
'beforeExit',
|
'beforeExit',
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
import { existsSync } from 'fs';
|
|
||||||
import sharp from 'sharp';
|
|
||||||
import { Vector3Tuple } from 'three';
|
|
||||||
import { readDVPLFile } from '../blitz/readDVPLFile';
|
|
||||||
import { DdsReadStream } from '../streams/dds';
|
|
||||||
import { PvrReadStream } from '../streams/pvr';
|
|
||||||
|
|
||||||
export enum TextureMutation {
|
|
||||||
Normal,
|
|
||||||
RoughnessMetallicness,
|
|
||||||
BaseColor,
|
|
||||||
Miscellaneous,
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReadTextureOptions =
|
|
||||||
| {
|
|
||||||
mutation:
|
|
||||||
| TextureMutation.Normal
|
|
||||||
| TextureMutation.RoughnessMetallicness
|
|
||||||
| TextureMutation.Miscellaneous;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
mutation: TextureMutation.BaseColor;
|
|
||||||
/**
|
|
||||||
* Normalized between 0 and 1
|
|
||||||
*/
|
|
||||||
baseColor: Vector3Tuple;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function readTexture(path: string, options?: ReadTextureOptions) {
|
|
||||||
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
|
||||||
const isDds = existsSync(ddsTexturePath);
|
|
||||||
const resolvedTexturePath = isDds
|
|
||||||
? ddsTexturePath
|
|
||||||
: ddsTexturePath.replace('.dds', '.pvr');
|
|
||||||
const decompressedDvpl = await readDVPLFile(resolvedTexturePath);
|
|
||||||
|
|
||||||
const raw = isDds
|
|
||||||
? await new DdsReadStream(decompressedDvpl.buffer).dds()
|
|
||||||
: new PvrReadStream(decompressedDvpl.buffer).pvr();
|
|
||||||
const channels = 4 * raw.width * raw.height;
|
|
||||||
|
|
||||||
switch (options?.mutation) {
|
|
||||||
case TextureMutation.Normal: {
|
|
||||||
for (let index = 0; index < channels; index += 4) {
|
|
||||||
/**
|
|
||||||
* Red is always 255 and blue is always 0. Only alpha and green contain any
|
|
||||||
* sort of information.
|
|
||||||
*/
|
|
||||||
let x = raw.data[index + 3] * (2 / 255) - 1;
|
|
||||||
let y = raw.data[index + 1] * (2 / 255) - 1;
|
|
||||||
const underSqrt = 1 - x ** 2 - y ** 2;
|
|
||||||
let z = underSqrt > 0 ? Math.sqrt(underSqrt) : -Math.sqrt(-underSqrt);
|
|
||||||
|
|
||||||
raw.data[index] = Math.round((x + 1) * (255 / 2));
|
|
||||||
raw.data[index + 1] = Math.round((y + 1) * (255 / 2));
|
|
||||||
raw.data[index + 2] = Math.round((z + 1) * (255 / 2));
|
|
||||||
raw.data[index + 3] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TextureMutation.RoughnessMetallicness: {
|
|
||||||
for (let index = 0; index < channels; index += 4) {
|
|
||||||
/**
|
|
||||||
* Same channel situation as normal maps.
|
|
||||||
*/
|
|
||||||
const metallicness = raw.data[index + 1];
|
|
||||||
const roughness = raw.data[index + 3];
|
|
||||||
|
|
||||||
raw.data[index] = 0;
|
|
||||||
raw.data[index + 1] = roughness;
|
|
||||||
raw.data[index + 2] = metallicness;
|
|
||||||
raw.data[index + 3] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TextureMutation.Miscellaneous: {
|
|
||||||
for (let index = 0; index < channels; index += 4) {
|
|
||||||
/**
|
|
||||||
* Alpha is ambient occlusion and green is emissive. But very few tanks
|
|
||||||
* use emissive so I am ignoring it for now.
|
|
||||||
*/
|
|
||||||
// const emissive = raw.data[index + 1];
|
|
||||||
const occlusion = raw.data[index + 3];
|
|
||||||
|
|
||||||
raw.data[index] = occlusion;
|
|
||||||
raw.data[index + 1] = 0;
|
|
||||||
raw.data[index + 2] = 0;
|
|
||||||
raw.data[index + 3] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TextureMutation.BaseColor: {
|
|
||||||
for (let index = 0; index < channels; index += 4) {
|
|
||||||
// alpha map is specularity but I am ignoring it for now
|
|
||||||
raw.data[index + 3] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await sharp(raw.data, { raw: raw }).png().toBuffer();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export enum TextureMutation {
|
||||||
|
Normal,
|
||||||
|
RoughnessMetallicness,
|
||||||
|
Albedo,
|
||||||
|
Miscellaneous,
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { existsSync } from 'fs';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import { readDVPLFile } from '../../blitz/readDVPLFile';
|
||||||
|
import { DdsReadStream } from '../../streams/dds';
|
||||||
|
import { PvrReadStream } from '../../streams/pvr';
|
||||||
|
import { TextureMutation } from './constants';
|
||||||
|
|
||||||
|
export async function readTexture(path: string, mutation?: TextureMutation) {
|
||||||
|
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
||||||
|
const isDds = existsSync(ddsTexturePath);
|
||||||
|
const resolvedTexturePath = isDds
|
||||||
|
? ddsTexturePath
|
||||||
|
: ddsTexturePath.replace('.dds', '.pvr');
|
||||||
|
const decompressedDvpl = await readDVPLFile(resolvedTexturePath);
|
||||||
|
|
||||||
|
const raw = isDds
|
||||||
|
? await new DdsReadStream(decompressedDvpl.buffer).dds()
|
||||||
|
: new PvrReadStream(decompressedDvpl.buffer).pvr();
|
||||||
|
|
||||||
|
switch (mutation) {
|
||||||
|
case TextureMutation.Normal: {
|
||||||
|
const bytes = 4 * raw.width * raw.height;
|
||||||
|
for (let index = 0; index < bytes; index += 4) {
|
||||||
|
/**
|
||||||
|
* Red is always 255 and blue is always 0. Only alpha and green contain any
|
||||||
|
* sort of information.
|
||||||
|
*/
|
||||||
|
let x = raw.data[index + 3] * (2 / 255) - 1;
|
||||||
|
let y = raw.data[index + 1] * (2 / 255) - 1;
|
||||||
|
const underSqrt = 1 - x ** 2 - y ** 2;
|
||||||
|
let z = underSqrt > 0 ? Math.sqrt(underSqrt) : -Math.sqrt(-underSqrt);
|
||||||
|
|
||||||
|
raw.data[index] = Math.round((x + 1) * (255 / 2));
|
||||||
|
raw.data[index + 1] = Math.round((y + 1) * (255 / 2));
|
||||||
|
raw.data[index + 2] = Math.round((z + 1) * (255 / 2));
|
||||||
|
raw.data[index + 3] = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await sharp(raw.data, { raw }).png().toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
case TextureMutation.RoughnessMetallicness: {
|
||||||
|
const newImage = sharp(Buffer.alloc(raw.width * raw.height), {
|
||||||
|
raw: { ...raw, channels: 1 },
|
||||||
|
});
|
||||||
|
const metallicness = sharp(raw.data, { raw })
|
||||||
|
.extractChannel('green')
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
const roughness = sharp(raw.data, { raw })
|
||||||
|
.extractChannel('alpha')
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
return await newImage
|
||||||
|
.joinChannel(await Promise.all([roughness, metallicness]))
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
case TextureMutation.Miscellaneous: {
|
||||||
|
/**
|
||||||
|
* Alpha is ambient occlusion and green is emissive. But very few tanks
|
||||||
|
* use emissive so I am ignoring it for now.
|
||||||
|
*/
|
||||||
|
return await sharp(raw.data, { raw })
|
||||||
|
.extractChannel('alpha')
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
case TextureMutation.Albedo: {
|
||||||
|
return await sharp(raw.data, { raw }).removeAlpha().png().toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return await sharp(raw.data, { raw }).png().toBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { useSession } from '../../stores/session';
|
||||||
|
import { setSession } from './setSession';
|
||||||
|
|
||||||
|
export function resetSession() {
|
||||||
|
const session = useSession.getState();
|
||||||
|
if (!session.isTracking) return;
|
||||||
|
setSession(session.region, session.id, session.nickname);
|
||||||
|
}
|
||||||
@@ -20,8 +20,8 @@ export function resolveDpm(
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
((alpha * gun.count) /
|
((alpha * gun.count) /
|
||||||
(gun.reload.reduce((a, b) => a + b, 0) +
|
(reloadCoefficient * gun.reload.reduce((a, b) => a + b, 0) +
|
||||||
(gun.count - 1) * gun.intraClip)) *
|
(gun.count - 1) * gun.intraClip * intraClipCoefficient)) *
|
||||||
60
|
60
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { config } from 'dotenv';
|
|||||||
|
|
||||||
config();
|
config();
|
||||||
|
|
||||||
export const SECRET_KEYS = [
|
const SECRET_KEYS = [
|
||||||
'DISCORD_TOKEN',
|
'DISCORD_TOKEN',
|
||||||
'GH_TOKEN',
|
'GH_TOKEN',
|
||||||
'DISCORD_BLITZ_DB',
|
'DISCORD_BLITZ_DB',
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Region } from '../../constants/regions';
|
||||||
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
|
import { useSession } from '../../stores/session';
|
||||||
|
import { NormalizedTankStats, TanksStats } from '../../types/tanksStats';
|
||||||
|
import fetchBlitz from '../blitz/fetchBlitz';
|
||||||
|
|
||||||
|
export async function setSession(region: Region, id: number, nickname: string) {
|
||||||
|
// TODO: generalize this function and also check for more of these kind
|
||||||
|
const rawTankStats = (
|
||||||
|
await fetchBlitz<TanksStats>(
|
||||||
|
`https://api.wotblitz.${region}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||||
|
)
|
||||||
|
)[id];
|
||||||
|
const tankStats = rawTankStats.reduce<NormalizedTankStats>(
|
||||||
|
(accumulator, tank) => ({
|
||||||
|
...accumulator,
|
||||||
|
[tank.tank_id]: tank,
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
useSession.setState({
|
||||||
|
isTracking: true,
|
||||||
|
id,
|
||||||
|
region,
|
||||||
|
nickname,
|
||||||
|
tankStats,
|
||||||
|
time: Date.now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { CrewMember, GunDefinition, ShellType, Tier } from '.';
|
||||||
|
import { TankClass } from '../../../components/Tanks';
|
||||||
|
|
||||||
|
export const SHELL_NAMES: Record<ShellType, string> = {
|
||||||
|
ap: 'AP',
|
||||||
|
ap_cr: 'APCR',
|
||||||
|
hc: 'HEAT',
|
||||||
|
he: 'HE',
|
||||||
|
};
|
||||||
|
export const CREW_MEMBER_NAMES: Record<CrewMember, string> = {
|
||||||
|
commander: 'Commander',
|
||||||
|
radioman: 'Radioman',
|
||||||
|
gunner: 'Gunner',
|
||||||
|
driver: 'Driver',
|
||||||
|
loader: 'Loader',
|
||||||
|
};
|
||||||
|
export const GUN_TYPE_NAMES: Record<GunDefinition['type'], string> = {
|
||||||
|
regular: 'Regular',
|
||||||
|
autoLoader: 'Auto loader',
|
||||||
|
autoReloader: 'Auto reloader',
|
||||||
|
};
|
||||||
|
export const TANK_ICONS: Record<TankClass, 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<TankClass, 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<TankClass, 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 const TIERS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const;
|
||||||
|
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',
|
||||||
|
};
|
||||||
+4
-59
@@ -1,18 +1,12 @@
|
|||||||
import { deburr } from 'lodash';
|
import { deburr } from 'lodash';
|
||||||
import { Vector2Tuple } from 'three';
|
import { Vector2Tuple } from 'three';
|
||||||
import { TankClass, TreeType } from '../../components/Tanks';
|
import { TankClass, TreeType } from '../../../components/Tanks';
|
||||||
import { asset } from './asset';
|
import { asset } from '../asset';
|
||||||
import { fetchCdonLz4 } from './fetchCdonLz4';
|
import { fetchCdonLz4 } from '../fetchCdonLz4';
|
||||||
|
import { TIERS } from './constants';
|
||||||
|
|
||||||
export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he';
|
export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he';
|
||||||
|
|
||||||
export const SHELL_NAMES: Record<ShellType, string> = {
|
|
||||||
ap: 'AP',
|
|
||||||
ap_cr: 'APCR',
|
|
||||||
hc: 'HEAT',
|
|
||||||
he: 'HE',
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CrewMember =
|
export type CrewMember =
|
||||||
| 'commander'
|
| 'commander'
|
||||||
| 'radioman'
|
| 'radioman'
|
||||||
@@ -20,13 +14,6 @@ export type CrewMember =
|
|||||||
| 'driver'
|
| 'driver'
|
||||||
| 'loader';
|
| 'loader';
|
||||||
export type TankDefinitions = Record<number, TankDefinition>;
|
export type TankDefinitions = Record<number, TankDefinition>;
|
||||||
export const CREW_MEMBER_NAMES: Record<CrewMember, string> = {
|
|
||||||
commander: 'Commander',
|
|
||||||
radioman: 'Radioman',
|
|
||||||
gunner: 'Gunner',
|
|
||||||
driver: 'Driver',
|
|
||||||
loader: 'Loader',
|
|
||||||
};
|
|
||||||
export interface Crew {
|
export interface Crew {
|
||||||
type: CrewMember;
|
type: CrewMember;
|
||||||
count?: number;
|
count?: number;
|
||||||
@@ -148,12 +135,6 @@ export type ShellDefinition = {
|
|||||||
explosionRadius?: number;
|
explosionRadius?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GUN_TYPE_NAMES: Record<GunDefinition['type'], string> = {
|
|
||||||
regular: 'Regular',
|
|
||||||
autoLoader: 'Auto loader',
|
|
||||||
autoReloader: 'Auto reloader',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tankDefinitions = fetchCdonLz4<TankDefinitions>(
|
export const tankDefinitions = fetchCdonLz4<TankDefinitions>(
|
||||||
asset('definitions/tanks.cdon.lz4'),
|
asset('definitions/tanks.cdon.lz4'),
|
||||||
);
|
);
|
||||||
@@ -180,44 +161,8 @@ export const tankNames = tanksDefinitionsArray.then((tanks) =>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const TANK_ICONS: Record<TankClass, 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<TankClass, 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<TankClass, 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 const TIERS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const;
|
|
||||||
|
|
||||||
export type Tier = (typeof TIERS)[number];
|
export type Tier = (typeof TIERS)[number];
|
||||||
|
|
||||||
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',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const NATIONS = tanksDefinitionsArray.then((tanks) => {
|
export const NATIONS = tanksDefinitionsArray.then((tanks) => {
|
||||||
const nations = Array.from(
|
const nations = Array.from(
|
||||||
new Set<string>(Object.values(tanks).map((tank) => tank.nation)),
|
new Set<string>(Object.values(tanks).map((tank) => tank.nation)),
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '../../types/histories';
|
} from '../../types/histories';
|
||||||
import { getAccountInfo } from '../blitz/getAccountInfo';
|
import { getAccountInfo } from '../blitz/getAccountInfo';
|
||||||
import { PlayerStats } from './getPlayerStats';
|
import { PlayerStats } from './getPlayerStats';
|
||||||
import { emptyAllStats } from './getStatsInPeriod';
|
import { emptyAllStats } from './getStatsInPeriod/constants';
|
||||||
|
|
||||||
export default async function getPlayerHistories(
|
export default async function getPlayerHistories(
|
||||||
server: Region,
|
server: Region,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { AllStats } from '../blitz/getAccountInfo';
|
import { AllStats } from '../blitz/getAccountInfo';
|
||||||
|
|
||||||
export interface IndividualPlayerStats {
|
interface IndividualPlayerStats {
|
||||||
clan: { clan_id: number; name: string; tag: string };
|
clan: { clan_id: number; name: string; tag: string };
|
||||||
statistics: {
|
statistics: {
|
||||||
all: AllStats;
|
all: AllStats;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { AllStats } from '../../blitz/getAccountInfo';
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
+5
-24
@@ -1,32 +1,13 @@
|
|||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../../constants/regions';
|
||||||
import { AllStats } from '../blitz/getAccountInfo';
|
import { AllStats } from '../../blitz/getAccountInfo';
|
||||||
import getTankHistories, {
|
import getTankHistories, {
|
||||||
TankHistories,
|
TankHistories,
|
||||||
TankHistoryRaw,
|
TankHistoryRaw,
|
||||||
} from '../blitzkrieg/getTankHistories';
|
} from '../../blitzkrieg/getTankHistories';
|
||||||
|
import { emptyAllStats } from './constants';
|
||||||
|
|
||||||
export const emptyAllStats: AllStats = {
|
const emptyTankHistoryNode: TankHistoryRaw = {
|
||||||
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,
|
account_id: 0,
|
||||||
battle_life_time: 0,
|
battle_life_time: 0,
|
||||||
last_battle_time: 0,
|
last_battle_time: 0,
|
||||||
@@ -2,7 +2,7 @@ import { AllStats } from '../blitz/getAccountInfo';
|
|||||||
import { context } from '../blitzkrieg/context';
|
import { context } from '../blitzkrieg/context';
|
||||||
import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
|
import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
|
||||||
|
|
||||||
export interface SpecialStats {
|
interface SpecialStats {
|
||||||
winrate: number;
|
winrate: number;
|
||||||
damageRatio: number;
|
damageRatio: number;
|
||||||
kdr: number;
|
kdr: number;
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import {
|
|||||||
SlashCommandBuilder,
|
SlashCommandBuilder,
|
||||||
SlashCommandSubcommandBuilder,
|
SlashCommandSubcommandBuilder,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { TANK_CLASSES, TREE_TYPES } from '../../components/Tanks';
|
import {
|
||||||
|
TANK_CLASSES,
|
||||||
|
TREE_TYPES,
|
||||||
|
} from '../../components/Tanks/components/Item/constants';
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from '../localization/translator';
|
||||||
import addTankChoices from './addTankChoices';
|
import addTankChoices from './addTankChoices';
|
||||||
import addTierChoices from './addTierChoices';
|
import addTierChoices from './addTierChoices';
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const DISCORD_CHOICES_MAX_NAME_SIZE = 25;
|
||||||
|
export const OVERFLOW_SUFFIX = '…';
|
||||||
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user