mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7bbb17f66 | ||
|
|
f407ffe4bb | ||
|
|
791a09daae | ||
|
|
9592c0b55f | ||
|
|
d9a34dc60e | ||
|
|
30e4e4e9b3 | ||
|
|
bd3ea9a8ec | ||
|
|
8131b69422 | ||
|
|
340d9c2666 | ||
|
|
2a169fac34 | ||
|
|
4819100ec6 | ||
|
|
f39a94cbe0 | ||
|
|
e61598f768 | ||
|
|
04ee3a0a44 | ||
|
|
8fa613f1e2 | ||
|
|
bdc240df08 | ||
|
|
889ed7e191 | ||
|
|
1fb8dac805 | ||
|
|
9f71ac68b9 | ||
|
|
c1f3517a79 | ||
|
|
adb0af0f18 | ||
|
|
f79f1ffdc3 | ||
|
|
f02fb00e8c | ||
|
|
3d353cc3cf |
No files matched your search
@@ -2,7 +2,6 @@ name: CD Build Assets
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev, main]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
@@ -18,6 +17,9 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v4
|
||||
@@ -46,6 +48,14 @@ jobs:
|
||||
- name: Run DepotDownloader
|
||||
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
|
||||
run: |
|
||||
$env:GH_TOKEN = "${{ secrets.GH_TOKEN }}"
|
||||
|
||||
@@ -18,10 +18,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
uses: actions/deploy-pages@v1
|
||||
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: docs/.vitepress/dist
|
||||
@@ -6,6 +6,8 @@ on:
|
||||
schedule:
|
||||
- cron: '0 */5 * * *'
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created, published, released, edited]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -17,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
||||
@@ -13,10 +13,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
run: yarn build:bot
|
||||
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bot
|
||||
path: dist/bot
|
||||
@@ -13,10 +13,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
run: yarn build:docs
|
||||
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docs
|
||||
path: dist/docs
|
||||
@@ -13,10 +13,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
run: yarn build:website
|
||||
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: website
|
||||
path: dist/website
|
||||
+2
-1
@@ -17,4 +17,5 @@ tsconfig.tsbuildinfo
|
||||
temp/
|
||||
/.yarn
|
||||
/.VSCodeCounter
|
||||
/.yarn
|
||||
/.yarn
|
||||
depots
|
||||
Vendored
+2
@@ -7,6 +7,7 @@
|
||||
"alphatest",
|
||||
"APAC",
|
||||
"apcr",
|
||||
"applypatches",
|
||||
"artefacts",
|
||||
"atgm",
|
||||
"AYUV",
|
||||
@@ -21,6 +22,7 @@
|
||||
"CDON",
|
||||
"classname",
|
||||
"cleandist",
|
||||
"closedkrieg",
|
||||
"cockroackdb",
|
||||
"Coef",
|
||||
"cooldown",
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# Blitzkrieg 1.16.2
|
||||
|
||||
¡Hola desde el mundo del español!
|
||||
|
||||
## Features
|
||||
|
||||
- Added translations for Spanish to the bot
|
||||
- Your language will be inferred automatically from your Discord settings
|
||||
- Any untranslated languages and words we forgot to translate will be left in English
|
||||
- If you have your language set to Spanish and see English in any places, please let us know on [the official Discord server](https://discord.gg/nDt7AjGJQH)
|
||||
- All `/about` commands except for `commands` remain in English
|
||||
- Tank names are still in English
|
||||
- The website remains unchanged
|
||||
- The title bar (featured as the big text on top of most commands) has now been made more compact
|
||||
|
||||
## Changes
|
||||
|
||||
- "Tank type" is now called "tank class"
|
||||
- Removed `/player-achievements` and `search-tanks`
|
||||
- They will be reintroduced in the future in a far better way
|
||||
- Also removed a `SKLLD` server exclusive `/eligible` command
|
||||
- This will be generalized for all clans in the future
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fixed the red glowing floor underneath the tank sandbox
|
||||
- This has been replaced with a static image
|
||||
- The refresh button is now properly removed when lacking permissions
|
||||
- Tank names that start with a number are now properly inferred from command inputs
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Command registration is now error tolerant meaning that if a single command fails to initiate, it will be discarded until the next restart
|
||||
- We are currently looking into how we can allow the command to retry registration after a fixed amount of time
|
||||
- Due to the implications of the code added which can be very easily exploited by hackers, a single file has been added to the repository as a closed source submodule and will remain closed unless a third party open-source implementation is made public
|
||||
- Removed preview commands
|
||||
@@ -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",
|
||||
"version": "1.16.2",
|
||||
"version": "1.16.4",
|
||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||
"main": "src/server.ts",
|
||||
"type": "module",
|
||||
@@ -19,6 +19,7 @@
|
||||
"start:website": "next start",
|
||||
"host:docs": "vitepress preview docs",
|
||||
"publish:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard --publish",
|
||||
"setup:patches": "vite-node --script scripts/applyPatches",
|
||||
"lint": "npx tsc",
|
||||
"lint:website": "next lint",
|
||||
"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',
|
||||
optionalDevices: 'XML/item_defs/vehicles/common/optional_devices.xml',
|
||||
optionalDeviceSlots:
|
||||
'XML/item_defs/vehicles/common/optional_device_slots.xml',
|
||||
'XML/item_defs/vehicles/common/optional_device_slots.xml',
|
||||
optionalDeviceImageMappings:
|
||||
'UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml',
|
||||
optionalDevicesImage:
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
import { NodeIO } from '@gltf-transform/core';
|
||||
import { readdir } from 'fs/promises';
|
||||
import { Vector3Tuple } from 'three';
|
||||
import { extractModel } from '../../src/core/blitz/extractModel';
|
||||
import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
|
||||
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
|
||||
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
|
||||
// import { AssetCommit } from '../../src/core/github/assetCommit';
|
||||
import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
|
||||
import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles';
|
||||
import { DATA, POI } from './constants';
|
||||
import { VehicleDefinitionList } from './definitions';
|
||||
import { TankParameters } from './tankIcons';
|
||||
|
||||
interface VehicleCustomization {
|
||||
armorColor: string;
|
||||
}
|
||||
|
||||
export async function tankModels(production: boolean) {
|
||||
console.log('Building tank models...');
|
||||
|
||||
@@ -27,18 +21,9 @@ export async function tankModels(production: boolean) {
|
||||
for (const nationIndex in nations) {
|
||||
const nation = nations[nationIndex];
|
||||
const changes: FileChange[] = [];
|
||||
// const assetCommit = new AssetCommit(`tank models ${nation}`, production);
|
||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||
`${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}`);
|
||||
|
||||
@@ -56,11 +41,8 @@ export async function tankModels(production: boolean) {
|
||||
const model = await extractModel(
|
||||
DATA,
|
||||
parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, ''),
|
||||
baseColor,
|
||||
);
|
||||
|
||||
// writeFile(`test.${tankKey}.glb`, await nodeIO.writeBinary(model));
|
||||
|
||||
changes.push({
|
||||
path: `3d/tanks/models/${id}.glb`,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
|
||||
import * as Breakdown from '../../../components/Breakdown';
|
||||
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
||||
import fetchBlitz from '../../../core/blitz/fetchBlitz';
|
||||
import { resetSession } from '../../../core/blitzkrieg/resetSession';
|
||||
import {
|
||||
TankDefinition,
|
||||
tankDefinitions,
|
||||
@@ -13,10 +14,7 @@ import { tankAverages } from '../../../core/blitzstars/tankAverages';
|
||||
import calculateWN8 from '../../../core/statistics/calculateWN8';
|
||||
import { deltaTankStats } from '../../../core/statistics/deltaTankStats';
|
||||
import getWN8Percentile from '../../../core/statistics/getWN8Percentile';
|
||||
import mutateSession, {
|
||||
resetSession,
|
||||
useSession,
|
||||
} from '../../../stores/session';
|
||||
import mutateSession, { useSession } from '../../../stores/session';
|
||||
import { IndividualTankStats, TanksStats } from '../../../types/tanksStats';
|
||||
import { CustomColumnDisplay } from '../../tools/session/components/CustomColumn';
|
||||
import { Menu } from '../../tools/session/components/Menu';
|
||||
|
||||
@@ -22,7 +22,9 @@ import { UNLOCALIZED_REGION_NAMES } from '../../../constants/regions';
|
||||
import searchPlayersAcrossRegions, {
|
||||
AccountListWithServer,
|
||||
} 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 { Menu } from './components/Menu';
|
||||
import * as styles from './page.css';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { pushTankopediaPath } from '../../../../../core/blitzkrieg/pushTankopedi
|
||||
import {
|
||||
SHELL_NAMES,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
||||
} from '../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||
import { mutateDuel, useDuel } from '../../../../../stores/duel';
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Flex, Heading } from '@radix-ui/themes';
|
||||
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 { mutateTankopediaTemporary } from '../../../../../../../stores/tankopedia';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
CREW_MEMBER_NAMES,
|
||||
GUN_TYPE_NAMES,
|
||||
SHELL_NAMES,
|
||||
} from '../../../../../../core/blitzkrieg/tankDefinitions';
|
||||
} from '../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||
import { unionBoundingBox } from '../../../../../../core/blitzkrieg/unionBoundingBox';
|
||||
import { useConsumable } from '../../../../../../hooks/useConsumable';
|
||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||
@@ -268,9 +268,13 @@ export function Characteristics() {
|
||||
<>
|
||||
<InfoWithDelta decimals={0} indent name="Maximum" unit="hp / min">
|
||||
{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
|
||||
: (shell.damage.armor / gun.reload[0]) * 60}
|
||||
: ((damageCoefficient * shell.damage.armor) /
|
||||
(gun.reload[0] * reloadCoefficient)) *
|
||||
60}
|
||||
</InfoWithDelta>
|
||||
<InfoWithDelta
|
||||
decimals={0}
|
||||
@@ -279,13 +283,26 @@ export function Characteristics() {
|
||||
unit="hp / min"
|
||||
>
|
||||
{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 -
|
||||
(gun.reload.slice(0, -1).length - 1) * gun.intraClip) +
|
||||
shell.damage.armor * gun.reload.slice(0, -1).length
|
||||
: (shell.damage.armor / (gun.reload[0] + gun.intraClip)) *
|
||||
(60 - (gun.reload.slice(1).length - 1) * gun.intraClip) +
|
||||
shell.damage.armor * gun.reload.slice(1).length}
|
||||
(gun.reload.slice(0, -1).length - 1) *
|
||||
gun.intraClip *
|
||||
intraClipCoefficient) +
|
||||
damageCoefficient *
|
||||
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>
|
||||
<Info
|
||||
indent
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { UpdateIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Dialog, Flex, Heading } from '@radix-ui/themes';
|
||||
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 { pushTankopediaPath } from '../../../../../core/blitzkrieg/pushTankopediaPath';
|
||||
import { useDuel } from '../../../../../stores/duel';
|
||||
|
||||
+2
-4
@@ -1,9 +1,7 @@
|
||||
import { Button, Flex, Text } from '@radix-ui/themes';
|
||||
import { SmallTankIcon } from '../../../../../../components/SmallTankIcon';
|
||||
import {
|
||||
TIER_ROMAN_NUMERALS,
|
||||
TankDefinition,
|
||||
} from '../../../../../../core/blitzkrieg/tankDefinitions';
|
||||
import { TankDefinition } from '../../../../../../core/blitzkrieg/tankDefinitions';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||
|
||||
interface CompactSearchResultRowProps {
|
||||
tanks: TankDefinition[];
|
||||
|
||||
@@ -4,17 +4,18 @@ import { CaretDownIcon } from '@radix-ui/react-icons';
|
||||
import { Button, DropdownMenu, Flex } from '@radix-ui/themes';
|
||||
import { use } from 'react';
|
||||
import { Flag } from '../../../../../../../components/Flag';
|
||||
import {} from '../../../../../../../components/Tanks';
|
||||
import {
|
||||
TANK_CLASSES,
|
||||
TREE_TYPES,
|
||||
TREE_TYPE_ICONS,
|
||||
TREE_TYPE_IMAGES,
|
||||
} from '../../../../../../../components/Tanks';
|
||||
} from '../../../../../../../components/Tanks/components/Item/constants';
|
||||
import { NATIONS } from '../../../../../../../core/blitzkrieg/tankDefinitions';
|
||||
import {
|
||||
NATIONS,
|
||||
TIERS,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
} from '../../../../../../../core/blitzkrieg/tankDefinitions';
|
||||
} from '../../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||
import mutateTankopediaPersistent, {
|
||||
TankopediaTestTankDisplay,
|
||||
useTankopediaPersistent,
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
} from '@radix-ui/themes';
|
||||
import { go } from 'fuzzysort';
|
||||
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 { asset } from '../../../../../core/blitzkrieg/asset';
|
||||
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
||||
@@ -24,13 +25,15 @@ import { resolveDpm } from '../../../../../core/blitzkrieg/resolveDpm';
|
||||
import { resolveReload } from '../../../../../core/blitzkrieg/resolveReload';
|
||||
import {
|
||||
NATIONS,
|
||||
TankDefinition,
|
||||
tanksDefinitionsArray,
|
||||
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
||||
import {
|
||||
TANK_ICONS,
|
||||
TANK_ICONS_COLLECTOR,
|
||||
TANK_ICONS_PREMIUM,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
TankDefinition,
|
||||
tanksDefinitionsArray,
|
||||
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
||||
} from '../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||
import { tankIcon } from '../../../../../core/blitzkrieg/tankIcon';
|
||||
import { unionBoundingBox } from '../../../../../core/blitzkrieg/unionBoundingBox';
|
||||
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 { chunk } from 'lodash';
|
||||
import * as Breakdown from '../components/Breakdown';
|
||||
import CommandWrapper from '../components/CommandWrapper';
|
||||
import NoData from '../components/NoData';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import { AllStats, getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||
import getTankStats from '../core/blitz/getTankStats';
|
||||
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
|
||||
import * as Breakdown from '../../components/Breakdown';
|
||||
import CommandWrapper from '../../components/CommandWrapper';
|
||||
import NoData from '../../components/NoData';
|
||||
import TitleBar from '../../components/TitleBar';
|
||||
import { AllStats, getAccountInfo } from '../../core/blitz/getAccountInfo';
|
||||
import { getClanAccountInfo } from '../../core/blitz/getClanAccountInfo';
|
||||
import getTankStats from '../../core/blitz/getTankStats';
|
||||
import { filtersToDescription } from '../../core/blitzkrieg/filtersToDescription';
|
||||
import {
|
||||
TankDefinition,
|
||||
tankDefinitions,
|
||||
} from '../core/blitzkrieg/tankDefinitions';
|
||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
||||
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
|
||||
import { tankAverages } from '../core/blitzstars/tankAverages';
|
||||
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, {
|
||||
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';
|
||||
} from '../../core/blitzkrieg/tankDefinitions';
|
||||
import getStatsInPeriod from '../../core/blitzstars/getStatsInPeriod';
|
||||
import { tankAverages } from '../../core/blitzstars/tankAverages';
|
||||
import { ResolvedPeriod } from '../../core/discord/resolvePeriodFromCommand';
|
||||
import { 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';
|
||||
|
||||
const ROWS_PER_PAGE = 8;
|
||||
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 CommandWrapper from '../components/CommandWrapper';
|
||||
import * as Graph from '../components/Graph';
|
||||
import { LineColor } from '../components/Graph/components/Line/constants';
|
||||
import NoData from '../components/NoData';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||
@@ -93,7 +94,7 @@ async function render(
|
||||
xMaxLabel={new Date(maxTime * 1000).toLocaleDateString(locale)}
|
||||
>
|
||||
{Graph.Line({
|
||||
color: Graph.LineColor.Red,
|
||||
color: LineColor.Red,
|
||||
plot: plot,
|
||||
minY,
|
||||
maxY,
|
||||
|
||||
@@ -7,12 +7,11 @@ import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||
import getTankStats from '../core/blitz/getTankStats';
|
||||
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
||||
import {
|
||||
TIER_ROMAN_NUMERALS,
|
||||
TankDefinition,
|
||||
Tier,
|
||||
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 addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||
@@ -100,7 +99,6 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
key={tank.id}
|
||||
name={tank.name}
|
||||
tankType={tank.class}
|
||||
image={tankIcon(tank.id)}
|
||||
treeType={tank.treeType}
|
||||
/>
|
||||
)),
|
||||
@@ -113,7 +111,6 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
key={tank.id}
|
||||
name={tank.name}
|
||||
tankType={tank.class}
|
||||
image={tankIcon(tank.id)}
|
||||
treeType={tank.treeType}
|
||||
/>
|
||||
)),
|
||||
|
||||
@@ -3,10 +3,8 @@ import markdownEscape from 'markdown-escape';
|
||||
import { Region } from '../constants/regions';
|
||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||
import fetchBlitz from '../core/blitz/fetchBlitz';
|
||||
import {
|
||||
AccountList,
|
||||
usernamePattern,
|
||||
} from '../core/blitz/searchPlayersAcrossRegions';
|
||||
import { AccountList } from '../core/blitz/searchPlayersAcrossRegions';
|
||||
import { usernamePattern } from '../core/blitz/searchPlayersAcrossRegions/constants';
|
||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
|
||||
@@ -15,7 +15,7 @@ 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 './breakdown';
|
||||
import { renderBreakdown } from './breakdown/render';
|
||||
|
||||
export const todayCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||
const nations = await NATIONS;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Percentile } from '../../../../constants/percentiles';
|
||||
import { translator } from '../../../../core/localization/translator';
|
||||
import getWN8Percentile from '../../../../core/statistics/getWN8Percentile';
|
||||
import { theme } from '../../../../stitches.config';
|
||||
import { PERCENTILE_COLORS } from '../../../PercentileIndicator';
|
||||
import { PERCENTILE_COLORS } from '../../../PercentileIndicator/constants';
|
||||
import { Glow } from './components/Glow';
|
||||
|
||||
export interface WN8DisplayProps {
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
MeshBasicMaterial,
|
||||
Object3D,
|
||||
ShaderMaterial,
|
||||
Vector2,
|
||||
WebGLRenderTarget,
|
||||
} from 'three';
|
||||
import { MeshBasicMaterial, Object3D, ShaderMaterial, Vector2 } from 'three';
|
||||
import { degToRad } from 'three/src/math/MathUtils';
|
||||
import { canSplash } from '../../../../core/blitz/canSplash';
|
||||
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
||||
@@ -21,8 +15,7 @@ import {
|
||||
} from '../../../../stores/tankopedia';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
|
||||
export const spacedArmorRenderTarget = new WebGLRenderTarget();
|
||||
import { spacedArmorRenderTarget } from './target';
|
||||
|
||||
interface CoreArmorSceneComponentProps {
|
||||
thickness: number;
|
||||
|
||||
@@ -53,7 +53,7 @@ void main() {
|
||||
float spacedArmorThickness = spacedArmorBufferFragment.r * penetration;
|
||||
remainingPenetration -= spacedArmorThickness;
|
||||
|
||||
if (isExplosive && !canSplash && remainingPenetration > 0.0) {
|
||||
if (isExplosive && remainingPenetration > 0.0) {
|
||||
// there is a 50% penetration loss per meter for HEAT shells
|
||||
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 { useFrame } from '@react-three/fiber';
|
||||
import { useEffect, useRef } from 'react';
|
||||
@@ -182,33 +182,38 @@ export function ShotDisplay() {
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
<div
|
||||
style={{
|
||||
backgroundColor:
|
||||
trueStatus === 'penetration'
|
||||
? '#00ff00c0'
|
||||
? '#00ff0080'
|
||||
: trueStatus === 'blocked'
|
||||
? '#ff0000c0'
|
||||
: '#ffff00c0',
|
||||
? '#ff000080'
|
||||
: '#ffff0080',
|
||||
borderRadius: 8,
|
||||
border: 'none',
|
||||
padding: 4,
|
||||
width: hasMultipleLayers ? undefined : 8,
|
||||
height: hasMultipleLayers ? undefined : 8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Inset>
|
||||
{hasMultipleLayers && (
|
||||
<Text
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'block',
|
||||
textAlign: 'center',
|
||||
color:
|
||||
trueStatus === 'ricochet' ? 'black' : undefined,
|
||||
}}
|
||||
>
|
||||
<b>{layer.index + 1}</b>
|
||||
</Text>
|
||||
)}
|
||||
</Inset>
|
||||
</Card>
|
||||
{hasMultipleLayers && (
|
||||
<Text
|
||||
size="2"
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'block',
|
||||
textAlign: 'center',
|
||||
color: trueStatus === 'ricochet' ? 'black' : undefined,
|
||||
}}
|
||||
>
|
||||
<b>{layer.index + 1}</b>
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</Html>
|
||||
</group>
|
||||
</>
|
||||
|
||||
@@ -170,7 +170,7 @@ export function SpacedArmorSceneComponent({
|
||||
const distance = (
|
||||
shot.layers.at(-1) as ShotLayerBase
|
||||
).point.distanceTo(intersections[0].point);
|
||||
if (explosive && !splashing)
|
||||
if (explosive)
|
||||
remainingPenetration -= 0.5 * remainingPenetration * distance;
|
||||
const blocked = remainingPenetration < 0;
|
||||
|
||||
@@ -196,7 +196,7 @@ export function SpacedArmorSceneComponent({
|
||||
const previousIntersection = intersections[layerIndex - 1];
|
||||
const distance =
|
||||
intersection.distance - previousIntersection.distance;
|
||||
if (explosive && !splashing)
|
||||
if (explosive)
|
||||
remainingPenetration -=
|
||||
0.5 * remainingPenetration * distance;
|
||||
const wasted = remainingPenetration < 0;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createPortal, useFrame } from '@react-three/fiber';
|
||||
import { memo, useState } from 'react';
|
||||
import { DepthTexture, Scene, Vector2 } from 'three';
|
||||
import { CoreArmorScene } from './components/CoreArmorScene';
|
||||
import { spacedArmorRenderTarget } from './components/CoreArmorSceneComponent';
|
||||
import { spacedArmorRenderTarget } from './components/CoreArmorSceneComponent/target';
|
||||
import { SpacedArmorScene } from './components/SpacedArmorScene';
|
||||
|
||||
export const Armor = memo(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface RootProps {
|
||||
interface RootProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
GrayColor,
|
||||
PALETTES,
|
||||
} 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';
|
||||
|
||||
export interface RowStatItem {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '../../../constants/radixColors';
|
||||
import PercentileIndicator from '../../PercentileIndicator';
|
||||
|
||||
export interface RowStatProps {
|
||||
interface RowStatProps {
|
||||
name: string;
|
||||
value?: string | number;
|
||||
delta?: number;
|
||||
|
||||
@@ -2,13 +2,13 @@ import { Percentile } from '../../../constants/percentiles';
|
||||
import { theme } from '../../../stitches.config';
|
||||
import PercentileIndicator from '../../PercentileIndicator';
|
||||
|
||||
export interface CardItem {
|
||||
interface CardItem {
|
||||
label: string;
|
||||
value: string | number | undefined;
|
||||
percentile?: Percentile;
|
||||
}
|
||||
|
||||
export interface CardProps {
|
||||
interface CardProps {
|
||||
items: CardItem[];
|
||||
title: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface RootProps {
|
||||
interface RootProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface RowProps {
|
||||
interface RowProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Card } from './components/Card';
|
||||
import { Root } from './components/Root';
|
||||
import { Row } from './components/Row';
|
||||
|
||||
export interface GenericAllStatsProps {
|
||||
interface GenericAllStatsProps {
|
||||
stats: AllStats;
|
||||
supplementaryStats?: SupplementaryStats;
|
||||
locale: Locale;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Stat } from '..';
|
||||
import { GenericStat } from '..';
|
||||
import GenericStatsRow from './GenericStatsRow';
|
||||
|
||||
export interface GenericStatsColumnProps {
|
||||
stats: Stat[];
|
||||
interface GenericStatsColumnProps {
|
||||
stats: GenericStat[];
|
||||
}
|
||||
|
||||
export default function GenericStatsColumn({ stats }: GenericStatsColumnProps) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Stat } from '..';
|
||||
import { GenericStat } from '..';
|
||||
import { theme } from '../../../stitches.config';
|
||||
import PercentileIndicator from '../../PercentileIndicator';
|
||||
|
||||
export interface GenericStatsRowProps {
|
||||
stat: Stat;
|
||||
interface GenericStatsRowProps {
|
||||
stat: GenericStat;
|
||||
}
|
||||
|
||||
export default function GenericStatsRow({ stat }: GenericStatsRowProps) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Percentile } from '../../constants/percentiles';
|
||||
import GenericStatsColumn from './components/GenericStatsColumn';
|
||||
|
||||
export type Stat =
|
||||
export type GenericStat =
|
||||
| [string, string | number | undefined]
|
||||
| [string, string | number | undefined, Percentile | undefined];
|
||||
|
||||
export interface GenericStatsProps {
|
||||
stats: Stat[];
|
||||
stats: GenericStat[];
|
||||
columnCount?: number;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function GenericStats({
|
||||
}: GenericStatsProps) {
|
||||
const filteredStats = stats.filter(([, value]) => value !== undefined);
|
||||
const itemsPerRow = Math.ceil(filteredStats.length / columnCount);
|
||||
const columns: Stat[][] = [];
|
||||
const columns: GenericStat[][] = [];
|
||||
|
||||
filteredStats.forEach((stat, index) => {
|
||||
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 { GRAPH_HEIGHT, GRAPH_WIDTH } from './Root';
|
||||
|
||||
export enum LineColor {
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
White,
|
||||
}
|
||||
import { GRAPH_HEIGHT, GRAPH_WIDTH } from '../Root/constants';
|
||||
import { LineColor, LineStyle } from './constants';
|
||||
|
||||
const LINE_COLORS = {
|
||||
[LineColor.Red]: red.red9,
|
||||
@@ -15,12 +9,6 @@ const LINE_COLORS = {
|
||||
[LineColor.White]: slateDark.slate11,
|
||||
};
|
||||
|
||||
export enum LineStyle {
|
||||
Solid,
|
||||
Dashes,
|
||||
DashesAndDots,
|
||||
}
|
||||
|
||||
const LINE_STYLES = {
|
||||
[LineStyle.Solid]: undefined,
|
||||
[LineStyle.Dashes]: '8, 8',
|
||||
@@ -28,9 +16,9 @@ const LINE_STYLES = {
|
||||
};
|
||||
|
||||
export type PlotItem = [number, number];
|
||||
export type Plot = PlotItem[];
|
||||
type Plot = PlotItem[];
|
||||
|
||||
export interface LineProps {
|
||||
interface LineProps {
|
||||
plot: Plot;
|
||||
minY?: number;
|
||||
maxY?: number;
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum MarginOrientation {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
+4
-8
@@ -1,13 +1,9 @@
|
||||
import { times } from 'lodash';
|
||||
import { theme } from '../../../stitches.config';
|
||||
import { MarginInputProps } from './Root';
|
||||
import { theme } from '../../../../stitches.config';
|
||||
import { MarginInputProps } from '../Root';
|
||||
import { MarginOrientation } from './constants';
|
||||
|
||||
export enum MarginOrientation {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
export interface MarginProps extends MarginInputProps {
|
||||
interface MarginProps extends MarginInputProps {
|
||||
verticalSeparations: number;
|
||||
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 { ReactNode } from 'react';
|
||||
import { theme } from '../../../stitches.config';
|
||||
import { Margin, MarginOrientation } from './Margin';
|
||||
|
||||
export const GRAPH_WIDTH = 384;
|
||||
export const GRAPH_HEIGHT = 320;
|
||||
import { theme } from '../../../../stitches.config';
|
||||
import { Margin } from '../Margin';
|
||||
import { MarginOrientation } from '../Margin/constants';
|
||||
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './constants';
|
||||
|
||||
export interface MarginInputProps {
|
||||
min: number;
|
||||
@@ -13,7 +12,7 @@ export interface MarginInputProps {
|
||||
precision?: number;
|
||||
}
|
||||
|
||||
export interface RootProps {
|
||||
interface RootProps {
|
||||
children: ReactNode;
|
||||
verticalMargin?: MarginInputProps;
|
||||
horizontalMargin?: MarginInputProps;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { theme } from '../../../stitches.config';
|
||||
|
||||
export interface GapProps {
|
||||
interface GapProps {
|
||||
message: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ComponentProps } from 'react';
|
||||
import { RatingsReward } from '../../../commands/ratings';
|
||||
import { theme } from '../../../stitches.config';
|
||||
|
||||
export interface ItemProps extends ComponentProps<'div'> {
|
||||
interface ItemProps extends ComponentProps<'div'> {
|
||||
position: number;
|
||||
deltaPosition?: number;
|
||||
score?: number;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface RootProps {
|
||||
interface RootProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DiscordLogoIcon } from '@radix-ui/react-icons';
|
||||
import { Flex, Text, Theme } from '@radix-ui/themes';
|
||||
import Link from 'next/link';
|
||||
import { theme } from '../../stitches.config';
|
||||
import { BlitzkriegWormWide } from '../../icons/BlitzkriegWormWide';
|
||||
import { theme } from '../../stitches.config';
|
||||
|
||||
export default function Navbar() {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum NoDataType {
|
||||
BattlesInPeriod,
|
||||
PlayersInPeriod,
|
||||
TanksFound,
|
||||
}
|
||||
@@ -1,14 +1,8 @@
|
||||
import { Locale } from 'discord.js';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { theme } from '../stitches.config';
|
||||
import { translator } from '../../core/localization/translator';
|
||||
import { theme } from '../../stitches.config';
|
||||
|
||||
export enum NoDataType {
|
||||
BattlesInPeriod,
|
||||
PlayersInPeriod,
|
||||
TanksFound,
|
||||
}
|
||||
|
||||
export interface NoDataProps {
|
||||
interface NoDataProps {
|
||||
type: 'battles_in_period' | 'players_in_period' | 'tanks_found';
|
||||
locale: Locale;
|
||||
}
|
||||
+1
-20
@@ -10,11 +10,7 @@ import {
|
||||
slate,
|
||||
teal,
|
||||
} from '@radix-ui/colors';
|
||||
import { Percentile } from '../constants/percentiles';
|
||||
|
||||
export interface PercentileIndicatorProps {
|
||||
percentile: Percentile;
|
||||
}
|
||||
import { Percentile } from '../../constants/percentiles';
|
||||
|
||||
export const PERCENTILE_COLORS = {
|
||||
[Percentile.VeryBad]: slate.slate9,
|
||||
@@ -28,18 +24,3 @@ export const PERCENTILE_COLORS = {
|
||||
[Percentile.Unicum]: pink.pink9,
|
||||
[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';
|
||||
|
||||
export interface ColumnProps {
|
||||
interface ColumnProps {
|
||||
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 {
|
||||
TANK_ICONS,
|
||||
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;
|
||||
import { theme } from '../../../../stitches.config';
|
||||
import { TANK_CLASSES, TREE_TYPES, TREE_TYPE_ICONS } from './constants';
|
||||
|
||||
export type TankClass = (typeof TANK_CLASSES)[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 {
|
||||
image?: string;
|
||||
tankType?: TankClass;
|
||||
name: string;
|
||||
treeType: TreeType;
|
||||
@@ -43,13 +16,7 @@ const TREE_TYPE_COLOR = {
|
||||
collector: '_blue',
|
||||
} as const;
|
||||
|
||||
export const TREE_TYPE_ICONS: Record<TreeType, Record<TankClass, string>> = {
|
||||
researchable: TANK_ICONS,
|
||||
premium: TANK_ICONS_PREMIUM,
|
||||
collector: TANK_ICONS_COLLECTOR,
|
||||
};
|
||||
|
||||
export function Item({ image, tankType, name, treeType }: ItemProps) {
|
||||
export function Item({ tankType, name, treeType }: ItemProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface RootProps {
|
||||
interface RootProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface RowProps {
|
||||
interface RowProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { theme } from '../../../stitches.config';
|
||||
|
||||
export interface TitleProps {
|
||||
interface TitleProps {
|
||||
children: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { theme } from '../stitches.config';
|
||||
|
||||
export type TierWeightsRecord = Partial<Record<Tier, number>>;
|
||||
|
||||
export interface TierWeightsProps {
|
||||
interface TierWeightsProps {
|
||||
weights: TierWeightsRecord;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { theme } from '../stitches.config';
|
||||
|
||||
export interface TitleBarProps {
|
||||
interface TitleBarProps {
|
||||
title: string;
|
||||
image?: 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 {
|
||||
vertexAttributeGLTFName,
|
||||
vertexAttributeGltfVectorSizes,
|
||||
} from './extractModel';
|
||||
} from './extractModel/constants';
|
||||
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) {
|
||||
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2.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 { times } from 'lodash';
|
||||
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 {
|
||||
Matrix4,
|
||||
Quaternion,
|
||||
Vector3,
|
||||
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';
|
||||
vertexAttributeGLTFName,
|
||||
vertexAttributeGltfVectorSizes,
|
||||
} from './constants';
|
||||
|
||||
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 = {
|
||||
start: ['chassis_chassis_', 'chassis_track_crash_', 'HP_'],
|
||||
end: ['_POINT'],
|
||||
};
|
||||
|
||||
export async function extractModel(
|
||||
data: string,
|
||||
path: string,
|
||||
baseColor?: Vector3Tuple,
|
||||
) {
|
||||
export async function extractModel(data: string, path: string) {
|
||||
const sc2Path = `${data}/3d/${path}.sc2.dvpl`;
|
||||
const scgPath = `${data}/3d/${path}.scg.dvpl`;
|
||||
const sc2 = new Sc2ReadStream((await readDVPLFile(sc2Path)).buffer).sc2();
|
||||
@@ -86,9 +61,7 @@ export async function extractModel(
|
||||
.setImage(
|
||||
await readTexture(
|
||||
`${data}/3d/${dirname(path)}/${textures.albedo}`,
|
||||
baseColor
|
||||
? { mutation: TextureMutation.BaseColor, baseColor }
|
||||
: undefined,
|
||||
TextureMutation.Albedo,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -101,7 +74,7 @@ export async function extractModel(
|
||||
.setImage(
|
||||
await readTexture(
|
||||
`${data}/3d/${dirname(path)}/${textures.baseRMMap}`,
|
||||
{ mutation: TextureMutation.RoughnessMetallicness },
|
||||
TextureMutation.RoughnessMetallicness,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -119,7 +92,7 @@ export async function extractModel(
|
||||
`${data}/3d/${dirname(path)}/${
|
||||
textures.baseNormalMap ?? textures.normalmap
|
||||
}`,
|
||||
isBase ? { mutation: TextureMutation.Normal } : undefined,
|
||||
isBase ? TextureMutation.Normal : undefined,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -133,7 +106,7 @@ export async function extractModel(
|
||||
.setImage(
|
||||
await readTexture(
|
||||
`${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';
|
||||
error: { field: string; message: string; code: 402; value: null };
|
||||
|
||||
@@ -4,7 +4,7 @@ import fetchBlitz from './fetchBlitz';
|
||||
import { normalizeExtra } from './normalizeExtra';
|
||||
import { normalizeIds } from './normalizeIds';
|
||||
|
||||
export type IndividualClanAccountInfo = {
|
||||
type IndividualClanAccountInfo = {
|
||||
account_id: number;
|
||||
joined_at: number;
|
||||
clan_id: number;
|
||||
@@ -21,7 +21,7 @@ export type IndividualClanAccountInfo = {
|
||||
};
|
||||
} | null;
|
||||
|
||||
export interface ClanAccountInfo {
|
||||
interface ClanAccountInfo {
|
||||
[accountId: number]: IndividualClanAccountInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID
|
||||
import fetchBlitz from './fetchBlitz';
|
||||
import { normalizeIds } from './normalizeIds';
|
||||
|
||||
export interface IndividualClanInfo {
|
||||
interface IndividualClanInfo {
|
||||
recruiting_options: {
|
||||
vehicles_level: number;
|
||||
wins_ratio: number;
|
||||
@@ -32,7 +32,7 @@ export interface IndividualClanInfo {
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ClanInfo {
|
||||
interface ClanInfo {
|
||||
[clanId: number]: IndividualClanInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { Region } from '../../constants/regions';
|
||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||
import { Clan, ClanList } from '../../types/clanList';
|
||||
import { ClanList } from '../../types/clanList';
|
||||
import fetchBlitz from './fetchBlitz';
|
||||
|
||||
export type ClanListWithRegion = (Clan & {
|
||||
region: 'com' | 'eu' | 'asia';
|
||||
})[];
|
||||
|
||||
export default async function searchClansAcrossRegions(
|
||||
search: string,
|
||||
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 { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||
import fetchBlitz from './fetchBlitz';
|
||||
import { Region } from '../../../constants/regions';
|
||||
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
||||
import fetchBlitz from '../fetchBlitz';
|
||||
import { usernamePattern } from './constants';
|
||||
|
||||
export interface AccountListItem {
|
||||
nickname: string;
|
||||
@@ -11,9 +12,6 @@ export type AccountListWithServer = (AccountListItem & {
|
||||
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(
|
||||
search: string,
|
||||
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 {
|
||||
DuelMember,
|
||||
genericDefaultEquipmentMatrix,
|
||||
mutateDuel,
|
||||
} from '../../stores/duel';
|
||||
import { DuelMember, mutateDuel } from '../../stores/duel';
|
||||
import { genericDefaultEquipmentMatrix } from '../../stores/duel/constants';
|
||||
import { availableProvisions } from './availableProvisions';
|
||||
import { modelDefinitions } from './modelDefinitions';
|
||||
import { provisionDefinitions } from './provisionDefinitions';
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { Region } from '../../constants/regions';
|
||||
|
||||
export const discordBlitzDB = new PrismaClient();
|
||||
import { discordBlitzDB } from './discordBlitzDB';
|
||||
|
||||
export function linkBlitzAndDiscord(
|
||||
discord: number,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
export const discordBlitzDB = new PrismaClient();
|
||||
@@ -1,7 +1,7 @@
|
||||
import { superDecompress } from './superDecompress';
|
||||
|
||||
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((buffer) => superDecompress<Type>(new Uint8Array(buffer)));
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Locale } from 'discord.js';
|
||||
import { translator } from '../localization/translator';
|
||||
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(
|
||||
{ nation, tier, tankType, treeType, tank }: StatFilters,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { BlitzkriegRatingsLeaderboard } from '../../../scripts/buildRatingsLeaderboard';
|
||||
import { Region } from '../../constants/regions';
|
||||
|
||||
export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-assets' };
|
||||
|
||||
const MIDNIGHT_LEADERBOARD_CACHE: Record<
|
||||
Region,
|
||||
Record<number, BlitzkriegRatingsLeaderboard>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories';
|
||||
import getTankStats from '../blitz/getTankStats';
|
||||
import { emptyAllStats } from '../blitzstars/getStatsInPeriod';
|
||||
import { emptyAllStats } from '../blitzstars/getStatsInPeriod/constants';
|
||||
|
||||
export interface TankHistory extends History {
|
||||
tank_id: number;
|
||||
@@ -27,7 +27,7 @@ export interface GetTankHistoriesOptions extends GetHistoriesOptions {
|
||||
tankId?: number;
|
||||
}
|
||||
|
||||
export const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
||||
const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
||||
...getHistoriesDefaultOptions,
|
||||
tankId: undefined,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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
|
||||
eu: +1, // Central European Time
|
||||
asia: +7, // Central Asia Standard Time
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import satori from 'satori';
|
||||
import robotoBlack from './Roboto-Black.ttf';
|
||||
import robotoBold from './Roboto-Bold.ttf';
|
||||
import roboto from './Roboto.ttf';
|
||||
|
||||
const fonts = [
|
||||
{
|
||||
file: require('./Roboto.ttf') as string,
|
||||
weight: 400 as const,
|
||||
},
|
||||
{
|
||||
file: require('./Roboto-Bold.ttf') as string,
|
||||
weight: 700 as const,
|
||||
},
|
||||
{
|
||||
file: require('./Roboto-Black.ttf') as string,
|
||||
weight: 900 as const,
|
||||
},
|
||||
{ file: roboto, weight: 400 as const },
|
||||
{ file: robotoBold, weight: 700 as const },
|
||||
{ file: robotoBlack, weight: 900 as const },
|
||||
];
|
||||
|
||||
export default async function jsxToSvg(jsx: JSX.Element) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { client } from '../discord/client';
|
||||
import { discordBlitzDB } from './discordBlitz';
|
||||
import { discordBlitzDB } from './discordBlitzDB';
|
||||
|
||||
const EXIT_EVENTS = [
|
||||
'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 {
|
||||
return (
|
||||
((alpha * gun.count) /
|
||||
(gun.reload.reduce((a, b) => a + b, 0) +
|
||||
(gun.count - 1) * gun.intraClip)) *
|
||||
(reloadCoefficient * gun.reload.reduce((a, b) => a + b, 0) +
|
||||
(gun.count - 1) * gun.intraClip * intraClipCoefficient)) *
|
||||
60
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { config } from 'dotenv';
|
||||
|
||||
config();
|
||||
|
||||
export const SECRET_KEYS = [
|
||||
const SECRET_KEYS = [
|
||||
'DISCORD_TOKEN',
|
||||
'GH_TOKEN',
|
||||
'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',
|
||||
};
|
||||
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user