Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a90e9312d | ||
|
|
2c7082fe04 | ||
|
|
518a83ee73 | ||
|
|
25c40bcea2 | ||
|
|
f88ac8daf7 | ||
|
|
c4be6b78b6 | ||
|
|
2cfc65a990 | ||
|
|
a8eb751048 | ||
|
|
f030fd53b1 | ||
|
|
da506083c9 | ||
|
|
8bdc4d9b1b | ||
|
|
e0718d9ebb | ||
|
|
dbf1718d39 | ||
|
|
84dd13c0ac | ||
|
|
fdecef5f9c | ||
|
|
198e800958 | ||
|
|
29bb8ada06 | ||
|
|
662f116afe | ||
|
|
02f0c078a5 | ||
|
|
4dd1113b6d | ||
|
|
e656b83719 | ||
|
|
b90b355f8f | ||
|
|
165a9cd719 | ||
|
|
5b8da36fb8 | ||
|
|
bbe682fa73 | ||
|
|
af08df1526 | ||
|
|
47a60adac7 | ||
|
|
10f3bc0026 | ||
|
|
e7da1d582f | ||
|
|
df70816f7e | ||
|
|
091364d7f3 | ||
|
|
753c06eee8 | ||
|
|
17713243b5 | ||
|
|
4e4f070368 | ||
|
|
4064f163ec | ||
|
|
b911f64847 | ||
|
|
b86b6e09d0 | ||
|
|
d2e7a477bb | ||
|
|
f9c182e68c | ||
|
|
7c497ab681 | ||
|
|
c840086c11 | ||
|
|
8649d09a94 | ||
|
|
c764564a96 | ||
|
|
ad8cbb96b7 | ||
|
|
b409e26ca5 | ||
|
|
fbbfa5307c | ||
|
|
4165533838 | ||
|
|
fb47ab3e64 | ||
|
|
57971453ad | ||
|
|
c8cb490b48 | ||
|
|
232f24181f | ||
|
|
46441d74d8 | ||
|
|
a2274a6f0c | ||
|
|
0eb2827e7b | ||
|
|
41cb3f8511 | ||
|
|
9405dccee9 | ||
|
|
83b0c6abc3 | ||
|
|
bf1621001b | ||
|
|
8a5a4bf696 | ||
|
|
a99cce07dc | ||
|
|
d8c26ba24a | ||
|
|
f0b393c48f | ||
|
|
d0d205bb93 | ||
|
|
aeaf52d3ac | ||
|
|
e05358952a | ||
|
|
c1000e1608 | ||
|
|
46c86c5352 | ||
|
|
507b503560 | ||
|
|
37ca981d33 | ||
|
|
efe6d2b1a5 | ||
|
|
9f79f9c1e8 | ||
|
|
38b4c62777 | ||
|
|
df1bc56a4c | ||
|
|
c3d2b063bc | ||
|
|
1b94748668 | ||
|
|
f4f804727f | ||
|
|
3729072594 | ||
|
|
c174f32e15 | ||
|
|
2443ecde2f | ||
|
|
9c027b3dde | ||
|
|
22d794ac65 | ||
|
|
35073c184e | ||
|
|
4741406e4a | ||
|
|
24107433c6 | ||
|
|
05c6fcfb34 | ||
|
|
be7a16d399 | ||
|
|
c7f781dbb2 | ||
|
|
a66c6ebb27 | ||
|
|
83aeca2e5e | ||
|
|
89a3db05d8 | ||
|
|
6050145b16 | ||
|
|
77c78741b3 | ||
|
|
f35670d6a8 | ||
|
|
3392a57480 | ||
|
|
ed64ee8228 | ||
|
|
4f47d6ce85 | ||
|
|
92d56ac086 | ||
|
|
671b8b60b9 | ||
|
|
ba5a5245d9 | ||
|
|
eee262add7 | ||
|
|
e5eaf3fd75 | ||
|
|
5fdc9044ca | ||
|
|
0924a77776 | ||
|
|
1a46e176f5 | ||
|
|
efb8df9769 | ||
|
|
04e09b84ff | ||
|
|
b52c107af9 | ||
|
|
c5e6f5e2eb | ||
|
|
365c68aa4d | ||
|
|
ff9ec55da6 | ||
|
|
2a2994ae69 | ||
|
|
e3c6497d29 | ||
|
|
bdc013b4c8 | ||
|
|
e20d65d218 | ||
|
|
8b726c12d9 | ||
|
|
da704e6181 | ||
|
|
64d346ccb6 | ||
|
|
c090ad6492 | ||
|
|
8b6610151e | ||
|
|
545b655d4e | ||
|
|
0e6aa734cb | ||
|
|
2bef96475c | ||
|
|
45938c875f | ||
|
|
9d916a588d | ||
|
|
f0295079cc | ||
|
|
6d8387fa9d | ||
|
|
7a18de6650 | ||
|
|
cc6c4faa2d | ||
|
|
4253cd3e1b | ||
|
|
75cf827082 | ||
|
|
73c8807f14 | ||
|
|
9fac6dd40d | ||
|
|
c06c338536 | ||
|
|
9258623931 | ||
|
|
3b02c25973 | ||
|
|
f90768d155 | ||
|
|
d77bd6a5f9 | ||
|
|
6836cb327f | ||
|
|
4373efd0cd | ||
|
|
f13165e77d | ||
|
|
8a5b0c1170 | ||
|
|
f16511a72e | ||
|
|
df0dc4f118 | ||
|
|
92399de5e2 | ||
|
|
8d9dff8d73 | ||
|
|
f91744f2f0 | ||
|
|
e946dfdb69 | ||
|
|
6283ddc57a | ||
|
|
2d66e0ab28 | ||
|
|
c3d8f15a2a | ||
|
|
21a37ec6cc | ||
|
|
de471400ad | ||
|
|
0b16295853 | ||
|
|
63eea81856 | ||
|
|
ba57d61af5 | ||
|
|
3d940f4b47 | ||
|
|
8ef355c574 | ||
|
|
71e1ab1362 | ||
|
|
53dc108356 | ||
|
|
57b3218c5a | ||
|
|
7472e39faf | ||
|
|
034417d19f | ||
|
|
0368e9ceda | ||
|
|
28f365f762 | ||
|
|
40a81f6b1b | ||
|
|
9475d22472 | ||
|
|
9538a03afb | ||
|
|
d5d171bea6 | ||
|
|
a9c9a90ac1 | ||
|
|
f7c562f8b6 | ||
|
|
836cf69f01 |
No files matched your search
@@ -50,6 +50,7 @@ env:
|
|||||||
PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }}
|
PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }}
|
||||||
PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }}
|
PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }}
|
||||||
PUBLIC_DEBUG_MISSING_I18N: false
|
PUBLIC_DEBUG_MISSING_I18N: false
|
||||||
|
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
download_client:
|
download_client:
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ env:
|
|||||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||||
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
|
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
|
||||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||||
ALLOW_ROBOTS: true
|
ALLOW_ROBOTS: "true"
|
||||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||||
|
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -28,3 +28,4 @@ target
|
|||||||
/.astro
|
/.astro
|
||||||
.vercel
|
.vercel
|
||||||
/src
|
/src
|
||||||
|
/packages/bot/src/prisma
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
[submodule "submodules/blitzkit-closed"]
|
[submodule "submodules/blitzkit-closed"]
|
||||||
path = submodules/blitzkit-closed
|
path = submodules/blitzkit-closed
|
||||||
url = https://github.com/tresabhi/blitzkit-closed
|
url = https://github.com/tresabhi/blitzkit-closed
|
||||||
|
[submodule "submodules/CUE4Parse"]
|
||||||
|
path = submodules/CUE4Parse
|
||||||
|
url = https://github.com/FabianFG/CUE4Parse
|
||||||
|
[submodule "submodules/UAssetAPI"]
|
||||||
|
path = submodules/UAssetAPI
|
||||||
|
url = https://github.com/atenfyr/UAssetAPI
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"applypatches",
|
"applypatches",
|
||||||
"artefacts",
|
"artefacts",
|
||||||
"astrojs",
|
"astrojs",
|
||||||
|
"astrolib",
|
||||||
"atgm",
|
"atgm",
|
||||||
"Autoloader",
|
"Autoloader",
|
||||||
"Autoreloader",
|
"Autoreloader",
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
"cacheable",
|
"cacheable",
|
||||||
"Callouts",
|
"Callouts",
|
||||||
"Camo",
|
"Camo",
|
||||||
|
"Carro",
|
||||||
"CCPAOPTOUT",
|
"CCPAOPTOUT",
|
||||||
"CDON",
|
"CDON",
|
||||||
"classname",
|
"classname",
|
||||||
@@ -42,6 +44,7 @@
|
|||||||
"closedkrieg",
|
"closedkrieg",
|
||||||
"cockroackdb",
|
"cockroackdb",
|
||||||
"Coef",
|
"Coef",
|
||||||
|
"Combattimento",
|
||||||
"cooldown",
|
"cooldown",
|
||||||
"corelations",
|
"corelations",
|
||||||
"corsproxy",
|
"corsproxy",
|
||||||
@@ -53,6 +56,7 @@
|
|||||||
"dbid",
|
"dbid",
|
||||||
"Ddpf",
|
"Ddpf",
|
||||||
"Ddsd",
|
"Ddsd",
|
||||||
|
"deadrail",
|
||||||
"debugflags",
|
"debugflags",
|
||||||
"deburr",
|
"deburr",
|
||||||
"decalmask",
|
"decalmask",
|
||||||
@@ -130,9 +134,12 @@
|
|||||||
"outdir",
|
"outdir",
|
||||||
"outfile",
|
"outfile",
|
||||||
"ownedtanks",
|
"ownedtanks",
|
||||||
|
"padder",
|
||||||
|
"partytown",
|
||||||
"PIXELFORMAT",
|
"PIXELFORMAT",
|
||||||
"playerachievements",
|
"playerachievements",
|
||||||
"playerinfo",
|
"playerinfo",
|
||||||
|
"playform",
|
||||||
"pngjs",
|
"pngjs",
|
||||||
"POSITIVEX",
|
"POSITIVEX",
|
||||||
"POSITIVEY",
|
"POSITIVEY",
|
||||||
@@ -181,6 +188,7 @@
|
|||||||
"TEXCOORD",
|
"TEXCOORD",
|
||||||
"tresabhi",
|
"tresabhi",
|
||||||
"tungstentip",
|
"tungstentip",
|
||||||
|
"Turretless",
|
||||||
"TYPELESS",
|
"TYPELESS",
|
||||||
"ubjson",
|
"ubjson",
|
||||||
"Unicum",
|
"Unicum",
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# BlitzKit 1.27.10
|
||||||
|
|
||||||
|
This update changes the URLs across the whole website, removing the "tools" prefix and simplifying other aspects too.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Removed "full" tank names
|
||||||
|
- For example, tanks like the "Carro 45t" had full names like "Carro Combattimento 45t" which remained pretty much unused, so they have been removed
|
||||||
|
- Reduced the shadow below the navigation bar
|
||||||
|
- Removed the "tools" prefix from the URLs; here are the paths:
|
||||||
|
- `/tools/tankopedia` -> `/tanks`
|
||||||
|
- `/tools/tankopedia/12345` -> `/tanks/tank-name`
|
||||||
|
- `/tools/compare` -> `/compare`
|
||||||
|
- `/tools/embed` -> `/embed`
|
||||||
|
- `/tools/embed/embed-name` -> `/embed/embed-name`
|
||||||
|
- `/tools/gallery` -> `/gallery`
|
||||||
|
- `/tools/performance` -> `/performance`
|
||||||
|
- `/tools/session` -> `/session`
|
||||||
|
- `/tools/tier-list` -> `/tier-list`
|
||||||
|
|
||||||
|
## Technical Features
|
||||||
|
|
||||||
|
- The asset mangler now uses http headers and even more heuristics to minimize re-creating unchanged assets
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# BlitzKit 1.27.11
|
||||||
|
|
||||||
|
This update affects both the bot and the website.
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- Added a proper CDN domain to avoid error 429s and any slowdowns
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# FreakKit 1.27.4
|
||||||
|
|
||||||
|
It has come to our attention that tanks are very harsh objects. To even it out, we made the website a pretty pretty princess <3
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# BlitzKit 1.27.5
|
||||||
|
|
||||||
|
This update only affects the bot.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Removed `/auction`
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Fixed a bug that caused `/rating` to disappear
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# BlitzKit 1.27.6
|
||||||
|
|
||||||
|
This update only affects the website.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added a promotional card for [the official Discord server](https://discord.gg/nDt7AjGJQH) to the homepage
|
||||||
|
- Added a better navbar
|
||||||
|
- The added spaces and padding helps add more room for error when clicking, especially on mobile
|
||||||
|
- The dropdown menu is now a lost smaller, disposing of the need to scroll at all
|
||||||
|
- Added shell filters to the tank search
|
||||||
|
- Reworked the filter buttons and their positions to allow for more space
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Moved most images from imgur to BlitzKit allowing for users in certain countries to load BlitzKit without getting blocked
|
||||||
|
|
||||||
|
## Technical Features
|
||||||
|
|
||||||
|
- Added Partytown to offload some workload off the main thread, allowing for a more responsive experience
|
||||||
|
- Reworked `robots.txt` to disallow indexing of BlitzKit variants
|
||||||
|
- Improved the SEO further for most pages
|
||||||
|
- Added a few compressor library to speed up load times
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# BlitzKit 1.27.7
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This update only affects the website.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added a new foggy-wireframe transition between tabs to smooth out the jarring jumps
|
||||||
|
- Added animations pop-in and out animations on a few elements
|
||||||
|
- The tank name title now shrinks in size as soon as a disturbance is caused
|
||||||
|
- The shell selector, antagonist switcher, and thicknesses range now ease into frame
|
||||||
|
- The Tankopedia search page now loads as a skeleton by default before populating with tanks, allowing for the recently viewed tanks to load without cumulative shift
|
||||||
|
- Something similar was done to the Gallery tool
|
||||||
|
- Gave the meta section of the website some zest with nation-based backdrops
|
||||||
|
- The compare tabs in the Tankopedia are now centered
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- The camera no longer snaps to the initial position when switching between tabs
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- Backported some technologies from BlitzKit Reforged; you will not notice any differences in usage
|
||||||
|
- The popular tanks and gallery indexings have been moved to the asynchronous cron job
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# BlitzKit 1.27.8
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- The website now uses Inter as the font
|
||||||
|
- Added a cool new non-disturbed state for the Tankopedia because why not
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Removed the Discord plug and language changer from the homepage
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# BlitzKit 1.27.9
|
||||||
|
|
||||||
|
BlitzKit's great, but there's far too many numbers. This update adds a few visualizers to help new players understand how things relate numerically.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added the turret and hull traverse speed visualizer
|
||||||
|
- Added the shell ricochet and normalization angles visualizer
|
||||||
|
- Added the view-range visualizer
|
||||||
|
- Added a search bar to the Tankopedia characteristics
|
||||||
|
- Bumped the decimal places for vehicle dimensions and volume
|
||||||
|
- Made tank ranks softer (the bars beneath the tank characteristics)
|
||||||
|
- Gave the navigation bar better distinction from the rest of the page
|
||||||
|
- Increased the number of characteristics rows to 3 on select devices
|
||||||
|
- Switched to a more contrasting classic style of components across the website (it's subtle, but beautiful)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Removed units
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Clarified a few confusing row labels in the Compare tool
|
||||||
|
- Made the Tankopedia hero section dynamically smaller on mobile phones to allow scrolling
|
||||||
|
- Tank ranks no longer reach 0 or go above the number of vehicles in the comparison
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkit",
|
"name": "blitzkit",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.27.3",
|
"version": "1.27.11",
|
||||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "./types.d.ts",
|
"types": "./types.d.ts",
|
||||||
@@ -40,15 +40,17 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sharp": "^0.33.5",
|
"googleapis": "^144.0.0",
|
||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21",
|
||||||
|
"quicklime": "^1.2.0",
|
||||||
|
"sharp": "^0.33.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^22.5.4",
|
"@types/node": "^22.14.1",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.5.3",
|
||||||
"prisma": "^6.0.0",
|
"prisma": "^6.6.0",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,8 +107,8 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
if (!tank) return '';
|
if (!tank) return '';
|
||||||
|
|
||||||
const name = `[${tank.name}](<https://blitzkit.app/tools/tankopedia/${
|
const name = `[${tank.name}](<https://blitzkit.app/tanks/${
|
||||||
tank.id
|
tank.slug
|
||||||
}>)`;
|
}>)`;
|
||||||
const next =
|
const next =
|
||||||
data.next_price === null
|
data.next_price === null
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import {
|
import { getAccountInfo, getClanAccountInfo } from '@blitzkit/core';
|
||||||
getAccountInfo,
|
|
||||||
getClanAccountInfo,
|
|
||||||
linkBlitzAndDiscord,
|
|
||||||
} from '@blitzkit/core';
|
|
||||||
import { teal } from '@radix-ui/colors';
|
import { teal } from '@radix-ui/colors';
|
||||||
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
|
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
|
||||||
import { CommandWrapper } from '../components/CommandWrapper';
|
import { CommandWrapper } from '../components/CommandWrapper';
|
||||||
|
import { linkBlitzAndDiscord } from '../core/db/linkBlitzAndDiscord';
|
||||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
(tank) =>
|
(tank) =>
|
||||||
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
|
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`,
|
||||||
);
|
);
|
||||||
const lines = [title, ...tankList];
|
const lines = [title, ...tankList];
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
|
|||||||
gap: 16,
|
gap: 16,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
|
backgroundColor: theme.colors.appBackground1,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
background: theme.colors.appBackground1,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { idToRegion, usersDatabase } from '@blitzkit/core';
|
import { idToRegion } from '@blitzkit/core';
|
||||||
import { flagUserActivity } from '@blitzkit/core/src/blitzkit/flagUserActivity';
|
import { flagUserActivity } from '../db/flagUserActivity';
|
||||||
|
import { usersDatabase } from '../db/users';
|
||||||
|
|
||||||
export async function getBlitzFromDiscord(discordId: bigint) {
|
export async function getBlitzFromDiscord(discordId: bigint) {
|
||||||
const unique = await usersDatabase.discordUser.findUnique({
|
const unique = await usersDatabase.discordUser.findUnique({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { usersDatabase } from '@blitzkit/core';
|
import { usersDatabase } from '../db/users';
|
||||||
import { client } from '../discord/client';
|
import { client } from '../discord/client';
|
||||||
|
|
||||||
const EXIT_EVENTS = [
|
const EXIT_EVENTS = [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { usersDatabase } from '@blitzkit/core';
|
import { usersDatabase } from './users';
|
||||||
|
|
||||||
export async function flagUserActivity(blitzId: number) {
|
export async function flagUserActivity(blitzId: number) {
|
||||||
await usersDatabase.user.upsert({
|
await usersDatabase.user.upsert({
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { usersDatabase } from '@blitzkit/core';
|
|
||||||
import { flagUserActivity } from './flagUserActivity';
|
import { flagUserActivity } from './flagUserActivity';
|
||||||
|
import { usersDatabase } from './users';
|
||||||
|
|
||||||
export async function linkBlitzAndDiscord(discordId: bigint, blitzId: number) {
|
export async function linkBlitzAndDiscord(discordId: bigint, blitzId: number) {
|
||||||
await usersDatabase.discordUser.upsert({
|
await usersDatabase.discordUser.upsert({
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '../../prisma';
|
||||||
|
|
||||||
export const usersDatabase = new PrismaClient();
|
export const usersDatabase = new PrismaClient();
|
||||||
@@ -15,7 +15,8 @@ export function localizationObject(
|
|||||||
(localizations, locale) => {
|
(localizations, locale) => {
|
||||||
const { strings } = translator(locale);
|
const { strings } = translator(locale);
|
||||||
const translation = literalsFunc(path(strings), literals);
|
const translation = literalsFunc(path(strings), literals);
|
||||||
const isValid = !strict || validNameRegex.test(translation);
|
const isValid =
|
||||||
|
(!strict || validNameRegex.test(translation)) && translation.length > 0;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ import { Writeable } from '../../../types/writable';
|
|||||||
export async function handleChatInputCommand(
|
export async function handleChatInputCommand(
|
||||||
interaction: ChatInputCommandInteraction<CacheType>,
|
interaction: ChatInputCommandInteraction<CacheType>,
|
||||||
) {
|
) {
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
const awaitedCommands = await commands;
|
const awaitedCommands = await commands;
|
||||||
const registry = awaitedCommands[interaction.commandName];
|
const registry = awaitedCommands[interaction.commandName];
|
||||||
|
|
||||||
await interaction.deferReply();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const returnable = await registry.handler(interaction);
|
const returnable = await registry.handler(interaction);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
SlashCommandSubcommandsOnlyBuilder,
|
SlashCommandSubcommandsOnlyBuilder,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { aboutCommand } from '../../commands/about';
|
import { aboutCommand } from '../../commands/about';
|
||||||
import { auctionCommand } from '../../commands/auction';
|
|
||||||
import { breakdownCommand } from '../../commands/breakdown';
|
import { breakdownCommand } from '../../commands/breakdown';
|
||||||
import { debugCommand } from '../../commands/debug';
|
import { debugCommand } from '../../commands/debug';
|
||||||
import { evolutionCommand } from '../../commands/evolution';
|
import { evolutionCommand } from '../../commands/evolution';
|
||||||
@@ -80,7 +79,7 @@ interface CommandRegistryExplicit extends CommandRegistryBase {
|
|||||||
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
|
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
|
||||||
|
|
||||||
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
|
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
|
||||||
auctionCommand,
|
// auctionCommand,
|
||||||
permissionsCommand,
|
permissionsCommand,
|
||||||
debugCommand,
|
debugCommand,
|
||||||
aboutCommand,
|
aboutCommand,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const interval = setInterval(async () => {
|
|||||||
const usernames = awaitedDiscoveredIdsDefinitions.count;
|
const usernames = awaitedDiscoveredIdsDefinitions.count;
|
||||||
|
|
||||||
manager.broadcastEval(
|
manager.broadcastEval(
|
||||||
(client, { servers, users, channels, tanks, usernames }) => {
|
async (client, { servers, users, channels, tanks, usernames }) => {
|
||||||
const bios = [
|
const bios = [
|
||||||
`Living in ${servers.toLocaleString()} servers`,
|
`Living in ${servers.toLocaleString()} servers`,
|
||||||
`Serving ${users.toLocaleString()} users`,
|
`Serving ${users.toLocaleString()} users`,
|
||||||
@@ -62,6 +62,13 @@ const interval = setInterval(async () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const guild of client.guilds.cache.values()) {
|
||||||
|
try {
|
||||||
|
const botMember = await guild.members.fetch(client.user.id);
|
||||||
|
await botMember.setNickname('BlitzKit');
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ context: { servers, channels, users, tanks, usernames } },
|
{ context: { servers, channels, users, tanks, usernames } },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const theme = {
|
|||||||
spaces: createSpaces(),
|
spaces: createSpaces(),
|
||||||
|
|
||||||
colors: {
|
colors: {
|
||||||
...createColors(slateDark),
|
...createColors(mauveDark),
|
||||||
...createColors(slateDarkA, 'alpha'),
|
...createColors(slateDarkA, 'alpha'),
|
||||||
...createColors(slate, 'light'),
|
...createColors(slate, 'light'),
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/plugin-throttling": "^8.2.0",
|
"@octokit/plugin-throttling": "^8.2.0",
|
||||||
"@octokit/rest": "^20.1.1",
|
"@octokit/rest": "^20.1.1",
|
||||||
"@prisma/client": "^5.19.1",
|
"@prisma/client": "^6.6.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { assertSecret, type BlitzGlossary } from '@blitzkit/core';
|
import { assertSecret } from '../blitzkit';
|
||||||
|
import { BlitzGlossary } from '../types';
|
||||||
|
|
||||||
export async function fetchGlossary(locale: string) {
|
export async function fetchGlossary(locale: string) {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -4,6 +4,7 @@ export * from './classes';
|
|||||||
export * from './constants';
|
export * from './constants';
|
||||||
export * from './equipmentPrice';
|
export * from './equipmentPrice';
|
||||||
export * from './fetchBlitz';
|
export * from './fetchBlitz';
|
||||||
|
export * from './fetchGlossary';
|
||||||
export * from './generateStats';
|
export * from './generateStats';
|
||||||
export * from './getAccountAchievements';
|
export * from './getAccountAchievements';
|
||||||
export * from './getAccountInfo';
|
export * from './getAccountInfo';
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { assertSecret } from './assertSecret';
|
import { assertSecret } from './assertSecret';
|
||||||
|
|
||||||
|
const repo = assertSecret(import.meta.env.PUBLIC_ASSET_REPO);
|
||||||
|
const branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
|
||||||
|
const fastUser = assertSecret(import.meta.env.PUBLIC_ASSET_FAST_USER);
|
||||||
|
const fastRepo = assertSecret(import.meta.env.PUBLIC_ASSET_FAST_REPO);
|
||||||
|
|
||||||
export function asset(path: string) {
|
export function asset(path: string) {
|
||||||
return `https://raw.githubusercontent.com/${assertSecret(
|
return branch === 'main'
|
||||||
import.meta.env.PUBLIC_ASSET_REPO,
|
? `https://${fastUser}.github.io/${fastRepo}/${path}`
|
||||||
)}/${assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH)}/${path}`;
|
: `https://raw.githubusercontent.com/${repo}/${branch}/${path}`;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { fetchPB } from '../protobuf';
|
||||||
|
import { Gallery } from '../protos';
|
||||||
|
import { asset } from './asset';
|
||||||
|
|
||||||
|
export function fetchGallery() {
|
||||||
|
return fetchPB(asset('definitions/gallery.pb'), Gallery);
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ export * from './discoveredIdDefinitions';
|
|||||||
export * from './emblemURL';
|
export * from './emblemURL';
|
||||||
export * from './equipmentDefinitions';
|
export * from './equipmentDefinitions';
|
||||||
export * from './eventManager';
|
export * from './eventManager';
|
||||||
// export * from './flagUserActivity';
|
|
||||||
export * from './gameDefinitions';
|
export * from './gameDefinitions';
|
||||||
export * from './getArchivedLatestSeasonNumber';
|
export * from './getArchivedLatestSeasonNumber';
|
||||||
// export * from './getArchivedRatingInfo';
|
// export * from './getArchivedRatingInfo';
|
||||||
@@ -21,7 +20,6 @@ export * from './getArchivedRatingLeaderboard';
|
|||||||
// export * from './getArchivedRatingMidnightLeaderboard';
|
// export * from './getArchivedRatingMidnightLeaderboard';
|
||||||
export * from './getTimeDaysAgo';
|
export * from './getTimeDaysAgo';
|
||||||
export * from './isExplosive';
|
export * from './isExplosive';
|
||||||
export * from './linkBlitzAndDiscord';
|
|
||||||
export * from './mapDefinitions';
|
export * from './mapDefinitions';
|
||||||
export * from './modelDefinitions';
|
export * from './modelDefinitions';
|
||||||
export * from './normalizeBoundingBox';
|
export * from './normalizeBoundingBox';
|
||||||
@@ -44,6 +42,8 @@ export * from './unwrapBlitzkitResponse';
|
|||||||
export * from './votes';
|
export * from './votes';
|
||||||
// export * from './withCORSProxy';
|
// export * from './withCORSProxy';
|
||||||
export * from './createDefaultProvisions';
|
export * from './createDefaultProvisions';
|
||||||
|
export * from './gallery';
|
||||||
export * from './metaSortTank';
|
export * from './metaSortTank';
|
||||||
|
export * from './popularTanks';
|
||||||
export * from './romanize';
|
export * from './romanize';
|
||||||
export * from './youtubers';
|
export * from './youtubers';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { fetchPB } from '../protobuf';
|
||||||
|
import { PopularTanks } from '../protos';
|
||||||
|
import { asset } from './asset';
|
||||||
|
|
||||||
|
export function fetchPopularTanks() {
|
||||||
|
return fetchPB(asset('definitions/popular-tanks.pb'), PopularTanks);
|
||||||
|
}
|
||||||
@@ -15,18 +15,22 @@ export async function fetchTankNames() {
|
|||||||
tankDefinitionsArray.map(async (tank) => {
|
tankDefinitionsArray.map(async (tank) => {
|
||||||
const searchableNameDeburr: I18nString = { locales: {} };
|
const searchableNameDeburr: I18nString = { locales: {} };
|
||||||
|
|
||||||
Object.entries(tank.name_full ?? tank.name).forEach(([key, value]) => {
|
Object.entries(tank.name).forEach(([key, value]) => {
|
||||||
searchableNameDeburr.locales[key] = deburr(value);
|
searchableNameDeburr.locales[key] = deburr(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: tank.id,
|
id: tank.id,
|
||||||
name: tank.name,
|
name: tank.name,
|
||||||
nameFull: tank.name_full,
|
searchableName: tank.name,
|
||||||
searchableName: tank.name_full ?? tank.name,
|
|
||||||
searchableNameDeburr,
|
searchableNameDeburr,
|
||||||
camouflages: tank.camouflages
|
camouflages: tank.camouflages
|
||||||
?.map((id) => SUPPORTED_LOCALES.map((locale) => camouflageDefinitions.camouflages[id]?.name.locales[locale]))
|
?.map((id) =>
|
||||||
|
SUPPORTED_LOCALES.map(
|
||||||
|
(locale) =>
|
||||||
|
camouflageDefinitions.camouflages[id]?.name.locales[locale],
|
||||||
|
),
|
||||||
|
)
|
||||||
.flat()
|
.flat()
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map(deburr)
|
.map(deburr)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from './users';
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
export * from './blitz';
|
export * from './blitz';
|
||||||
export * from './blitzkit';
|
export * from './blitzkit';
|
||||||
export * from './databases';
|
|
||||||
export * from './imgur';
|
export * from './imgur';
|
||||||
export * from './math';
|
export * from './math';
|
||||||
export * from './patreon';
|
export * from './patreon';
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package blitzkit;
|
||||||
|
|
||||||
|
import "packages/core/src/protos/i18n.proto";
|
||||||
|
|
||||||
|
message Gallery {
|
||||||
|
repeated Avatar avatars = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Avatar {
|
||||||
|
required string id = 1;
|
||||||
|
required I18nString name = 2;
|
||||||
|
required string extension = 3;
|
||||||
|
}
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-ts_proto v2.2.2
|
||||||
|
// protoc v5.28.2
|
||||||
|
// source: packages/core/src/protos/gallery.proto
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
import { BinaryReader, BinaryWriter } from '@bufbuild/protobuf/wire';
|
||||||
|
import { I18nString, createBaseI18nString } from './i18n';
|
||||||
|
|
||||||
|
export const protobufPackage = 'blitzkit';
|
||||||
|
|
||||||
|
export interface Gallery {
|
||||||
|
avatars: Avatar[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Avatar {
|
||||||
|
id: string;
|
||||||
|
name: I18nString;
|
||||||
|
extension: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBaseGallery(): Gallery {
|
||||||
|
return { avatars: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Gallery: MessageFns<Gallery> = {
|
||||||
|
encode(
|
||||||
|
message: Gallery,
|
||||||
|
writer: BinaryWriter = new BinaryWriter(),
|
||||||
|
): BinaryWriter {
|
||||||
|
for (const v of message.avatars) {
|
||||||
|
Avatar.encode(v!, writer.uint32(10).fork()).join();
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(input: BinaryReader | Uint8Array, length?: number): Gallery {
|
||||||
|
const reader =
|
||||||
|
input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||||
|
let end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = createBaseGallery();
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1: {
|
||||||
|
if (tag !== 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.avatars.push(Avatar.decode(reader, reader.uint32()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((tag & 7) === 4 || tag === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reader.skip(tag & 7);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): Gallery {
|
||||||
|
return {
|
||||||
|
avatars: globalThis.Array.isArray(object?.avatars)
|
||||||
|
? object.avatars.map((e: any) => Avatar.fromJSON(e))
|
||||||
|
: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: Gallery): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
if (message.avatars?.length) {
|
||||||
|
obj.avatars = message.avatars.map((e) => Avatar.toJSON(e));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
create<I extends Exact<DeepPartial<Gallery>, I>>(base?: I): Gallery {
|
||||||
|
return Gallery.fromPartial(base ?? ({} as any));
|
||||||
|
},
|
||||||
|
fromPartial<I extends Exact<DeepPartial<Gallery>, I>>(object: I): Gallery {
|
||||||
|
const message = createBaseGallery();
|
||||||
|
message.avatars = object.avatars?.map((e) => Avatar.fromPartial(e)) || [];
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function createBaseAvatar(): Avatar {
|
||||||
|
return { id: '', name: createBaseI18nString(), extension: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Avatar: MessageFns<Avatar> = {
|
||||||
|
encode(
|
||||||
|
message: Avatar,
|
||||||
|
writer: BinaryWriter = new BinaryWriter(),
|
||||||
|
): BinaryWriter {
|
||||||
|
if (message.id !== '') {
|
||||||
|
writer.uint32(10).string(message.id);
|
||||||
|
}
|
||||||
|
if (message.name !== undefined) {
|
||||||
|
I18nString.encode(message.name, writer.uint32(18).fork()).join();
|
||||||
|
}
|
||||||
|
if (message.extension !== '') {
|
||||||
|
writer.uint32(26).string(message.extension);
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(input: BinaryReader | Uint8Array, length?: number): Avatar {
|
||||||
|
const reader =
|
||||||
|
input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||||
|
let end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = createBaseAvatar();
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1: {
|
||||||
|
if (tag !== 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.id = reader.string();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
if (tag !== 18) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.name = I18nString.decode(reader, reader.uint32());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
if (tag !== 26) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.extension = reader.string();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((tag & 7) === 4 || tag === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reader.skip(tag & 7);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): Avatar {
|
||||||
|
return {
|
||||||
|
id: globalThis.String(assertSet('Avatar.id', object.id)),
|
||||||
|
name: I18nString.fromJSON(assertSet('Avatar.name', object.name)),
|
||||||
|
extension: globalThis.String(
|
||||||
|
assertSet('Avatar.extension', object.extension),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: Avatar): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
if (message.id !== '') {
|
||||||
|
obj.id = message.id;
|
||||||
|
}
|
||||||
|
if (message.name !== undefined) {
|
||||||
|
obj.name = I18nString.toJSON(message.name);
|
||||||
|
}
|
||||||
|
if (message.extension !== '') {
|
||||||
|
obj.extension = message.extension;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
create<I extends Exact<DeepPartial<Avatar>, I>>(base?: I): Avatar {
|
||||||
|
return Avatar.fromPartial(base ?? ({} as any));
|
||||||
|
},
|
||||||
|
fromPartial<I extends Exact<DeepPartial<Avatar>, I>>(object: I): Avatar {
|
||||||
|
const message = createBaseAvatar();
|
||||||
|
message.id = object.id ?? '';
|
||||||
|
message.name =
|
||||||
|
object.name !== undefined && object.name !== null
|
||||||
|
? I18nString.fromPartial(object.name)
|
||||||
|
: createBaseI18nString();
|
||||||
|
message.extension = object.extension ?? '';
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
type Builtin =
|
||||||
|
| Date
|
||||||
|
| Function
|
||||||
|
| Uint8Array
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
export type DeepPartial<T> = T extends Builtin
|
||||||
|
? T
|
||||||
|
: T extends globalThis.Array<infer U>
|
||||||
|
? globalThis.Array<DeepPartial<U>>
|
||||||
|
: T extends ReadonlyArray<infer U>
|
||||||
|
? ReadonlyArray<DeepPartial<U>>
|
||||||
|
: T extends { $case: string; value: unknown }
|
||||||
|
? { $case: T['$case']; value?: DeepPartial<T['value']> }
|
||||||
|
: T extends {}
|
||||||
|
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||||
|
: Partial<T>;
|
||||||
|
|
||||||
|
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
||||||
|
export type Exact<P, I extends P> = P extends Builtin
|
||||||
|
? P
|
||||||
|
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
|
||||||
|
[K in Exclude<keyof I, KeysOfUnion<P>>]: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isSet(value: any): boolean {
|
||||||
|
return value !== null && value !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertSet<T>(field: string, value: T | undefined): T {
|
||||||
|
if (!isSet(value)) {
|
||||||
|
throw new TypeError(`Required field ${field} is not set`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageFns<T> {
|
||||||
|
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
||||||
|
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
||||||
|
fromJSON(object: any): T;
|
||||||
|
toJSON(message: T): unknown;
|
||||||
|
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
|
||||||
|
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
|
||||||
|
}
|
||||||
@@ -3,10 +3,12 @@ export * from './average_definitions.ts';
|
|||||||
export * from './camouflage_definitions.ts';
|
export * from './camouflage_definitions.ts';
|
||||||
export * from './consumable_definitions.ts';
|
export * from './consumable_definitions.ts';
|
||||||
export * from './equipment_definitions.ts';
|
export * from './equipment_definitions.ts';
|
||||||
|
export * from './gallery.ts';
|
||||||
export * from './game_definitions.ts';
|
export * from './game_definitions.ts';
|
||||||
export * from './i18n.ts';
|
export * from './i18n.ts';
|
||||||
export * from './map_definitions.ts';
|
export * from './map_definitions.ts';
|
||||||
export * from './model_definitions.ts';
|
export * from './model_definitions.ts';
|
||||||
|
export * from './popular_tanks.ts';
|
||||||
export * from './provision_definitions.ts';
|
export * from './provision_definitions.ts';
|
||||||
export * from './rating_leaderboard.ts';
|
export * from './rating_leaderboard.ts';
|
||||||
export * from './reviews.ts';
|
export * from './reviews.ts';
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package blitzkit;
|
||||||
|
|
||||||
|
message PopularTanks {
|
||||||
|
repeated PopularTank tanks = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PopularTank {
|
||||||
|
required uint32 id = 1;
|
||||||
|
required uint32 views = 2; // views in the last 7 days
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-ts_proto v2.2.2
|
||||||
|
// protoc v5.28.2
|
||||||
|
// source: packages/core/src/protos/popular_tanks.proto
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
|
|
||||||
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
export interface PopularTanks {
|
||||||
|
tanks: PopularTank[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PopularTank {
|
||||||
|
id: number;
|
||||||
|
/** views in the last 7 days */
|
||||||
|
views: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBasePopularTanks(): PopularTanks {
|
||||||
|
return { tanks: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PopularTanks: MessageFns<PopularTanks> = {
|
||||||
|
encode(message: PopularTanks, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||||
|
for (const v of message.tanks) {
|
||||||
|
PopularTank.encode(v!, writer.uint32(10).fork()).join();
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(input: BinaryReader | Uint8Array, length?: number): PopularTanks {
|
||||||
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||||
|
let end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = createBasePopularTanks();
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1: {
|
||||||
|
if (tag !== 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.tanks.push(PopularTank.decode(reader, reader.uint32()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((tag & 7) === 4 || tag === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reader.skip(tag & 7);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): PopularTanks {
|
||||||
|
return {
|
||||||
|
tanks: globalThis.Array.isArray(object?.tanks) ? object.tanks.map((e: any) => PopularTank.fromJSON(e)) : [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: PopularTanks): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
if (message.tanks?.length) {
|
||||||
|
obj.tanks = message.tanks.map((e) => PopularTank.toJSON(e));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
create<I extends Exact<DeepPartial<PopularTanks>, I>>(base?: I): PopularTanks {
|
||||||
|
return PopularTanks.fromPartial(base ?? ({} as any));
|
||||||
|
},
|
||||||
|
fromPartial<I extends Exact<DeepPartial<PopularTanks>, I>>(object: I): PopularTanks {
|
||||||
|
const message = createBasePopularTanks();
|
||||||
|
message.tanks = object.tanks?.map((e) => PopularTank.fromPartial(e)) || [];
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function createBasePopularTank(): PopularTank {
|
||||||
|
return { id: 0, views: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PopularTank: MessageFns<PopularTank> = {
|
||||||
|
encode(message: PopularTank, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||||
|
if (message.id !== 0) {
|
||||||
|
writer.uint32(8).uint32(message.id);
|
||||||
|
}
|
||||||
|
if (message.views !== 0) {
|
||||||
|
writer.uint32(16).uint32(message.views);
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(input: BinaryReader | Uint8Array, length?: number): PopularTank {
|
||||||
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||||
|
let end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = createBasePopularTank();
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1: {
|
||||||
|
if (tag !== 8) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.id = reader.uint32();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
if (tag !== 16) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.views = reader.uint32();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((tag & 7) === 4 || tag === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reader.skip(tag & 7);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): PopularTank {
|
||||||
|
return {
|
||||||
|
id: globalThis.Number(assertSet("PopularTank.id", object.id)),
|
||||||
|
views: globalThis.Number(assertSet("PopularTank.views", object.views)),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: PopularTank): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
if (message.id !== 0) {
|
||||||
|
obj.id = Math.round(message.id);
|
||||||
|
}
|
||||||
|
if (message.views !== 0) {
|
||||||
|
obj.views = Math.round(message.views);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
create<I extends Exact<DeepPartial<PopularTank>, I>>(base?: I): PopularTank {
|
||||||
|
return PopularTank.fromPartial(base ?? ({} as any));
|
||||||
|
},
|
||||||
|
fromPartial<I extends Exact<DeepPartial<PopularTank>, I>>(object: I): PopularTank {
|
||||||
|
const message = createBasePopularTank();
|
||||||
|
message.id = object.id ?? 0;
|
||||||
|
message.views = object.views ?? 0;
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
||||||
|
|
||||||
|
export type DeepPartial<T> = T extends Builtin ? T
|
||||||
|
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
|
||||||
|
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
|
||||||
|
: T extends { $case: string; value: unknown } ? { $case: T["$case"]; value?: DeepPartial<T["value"]> }
|
||||||
|
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||||
|
: Partial<T>;
|
||||||
|
|
||||||
|
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
||||||
|
export type Exact<P, I extends P> = P extends Builtin ? P
|
||||||
|
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
|
||||||
|
|
||||||
|
function isSet(value: any): boolean {
|
||||||
|
return value !== null && value !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertSet<T>(field: string, value: T | undefined): T {
|
||||||
|
if (!isSet(value)) {
|
||||||
|
throw new TypeError(`Required field ${field} is not set`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageFns<T> {
|
||||||
|
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
||||||
|
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
||||||
|
fromJSON(object: any): T;
|
||||||
|
toJSON(message: T): unknown;
|
||||||
|
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
|
||||||
|
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
|
||||||
|
}
|
||||||
@@ -10,16 +10,16 @@ message TankDefinitions {
|
|||||||
|
|
||||||
message TankDefinition {
|
message TankDefinition {
|
||||||
required uint32 id = 1;
|
required uint32 id = 1;
|
||||||
map<uint32, uint32> roles = 2;
|
required string slug = 2;
|
||||||
required bool fixed_camouflage = 4;
|
map<uint32, uint32> roles = 3;
|
||||||
repeated int32 camouflages = 5;
|
required bool fixed_camouflage = 5;
|
||||||
repeated int32 ancestors = 6;
|
repeated int32 camouflages = 6;
|
||||||
repeated int32 successors = 7;
|
repeated int32 ancestors = 7;
|
||||||
repeated Crew crew = 8;
|
repeated int32 successors = 8;
|
||||||
required uint32 health = 9;
|
repeated Crew crew = 9;
|
||||||
required string nation = 10;
|
required uint32 health = 10;
|
||||||
required I18nString name = 11;
|
required string nation = 11;
|
||||||
optional I18nString name_full = 12;
|
required I18nString name = 12;
|
||||||
required TankType type = 13;
|
required TankType type = 13;
|
||||||
required uint32 max_consumables = 14;
|
required uint32 max_consumables = 14;
|
||||||
required uint32 max_provisions = 15;
|
required uint32 max_provisions = 15;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"default": "en",
|
||||||
|
"supported": [
|
||||||
|
{ "locale": "en" },
|
||||||
|
{ "locale": "es" },
|
||||||
|
{ "locale": "fr" },
|
||||||
|
{ "locale": "ja" },
|
||||||
|
{ "locale": "pl" },
|
||||||
|
{ "locale": "pt" },
|
||||||
|
{ "locale": "ru" },
|
||||||
|
{ "locale": "uk" },
|
||||||
|
{ "locale": "zh", "blitz": "zh_Hans_SG" }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -11,7 +11,9 @@
|
|||||||
"mm": "%s1mm",
|
"mm": "%s1mm",
|
||||||
"deg": "%s1°",
|
"deg": "%s1°",
|
||||||
"hp": "%s1hp",
|
"hp": "%s1hp",
|
||||||
"x": "x%s1"
|
"x": "x%s1",
|
||||||
|
"percentage": "%s1%",
|
||||||
|
"deg_s": "%s1°/s"
|
||||||
},
|
},
|
||||||
|
|
||||||
"gun_types": {
|
"gun_types": {
|
||||||
@@ -739,6 +741,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"website": {
|
"website": {
|
||||||
|
"redirect": {
|
||||||
|
"title": "301: moved permanently",
|
||||||
|
"body": "This page has moved. Redirecting..."
|
||||||
|
},
|
||||||
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"dev_mode": "Development mode",
|
"dev_mode": "Development mode",
|
||||||
"language": "Language"
|
"language": "Language"
|
||||||
@@ -756,6 +763,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "Search tanks...",
|
"search_bar_hint": "Search tanks...",
|
||||||
"recent": "Recently viewed",
|
"recent": "Recently viewed",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
@@ -824,6 +832,12 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
|
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "By %s1 ❤️"
|
"credit": "By %s1 ❤️"
|
||||||
},
|
},
|
||||||
@@ -838,8 +852,32 @@
|
|||||||
"seo_description": "Tools for everything World of Tanks Blitz"
|
"seo_description": "Tools for everything World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"tools": {
|
"tools": {
|
||||||
|
"guess": {
|
||||||
|
"name": "Guess the tank",
|
||||||
|
"button": "Guess away",
|
||||||
|
"description": "Silhouette mini-game",
|
||||||
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
|
"search": {
|
||||||
|
"placeholder": "Type to guess...",
|
||||||
|
"skip": "Skip",
|
||||||
|
"guess": "Guess",
|
||||||
|
"next": "Next",
|
||||||
|
"no_results": "No results"
|
||||||
|
},
|
||||||
|
"stats": "%s1 / %s2 guessed correctly"
|
||||||
|
},
|
||||||
|
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
|
"search": "Search a stat...",
|
||||||
|
|
||||||
"name": "Tankopedia",
|
"name": "Tankopedia",
|
||||||
"button": "Find tanks",
|
"button": "Find tanks",
|
||||||
"description": "Blitz tank encyclopedia",
|
"description": "Blitz tank encyclopedia",
|
||||||
@@ -847,6 +885,32 @@
|
|||||||
"tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
|
"tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
|
||||||
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
||||||
|
|
||||||
|
"visualizers": {
|
||||||
|
"rotator": {
|
||||||
|
"hull": "Hull",
|
||||||
|
"turret": "Turret",
|
||||||
|
"hard": "Hard",
|
||||||
|
"medium": "Medium",
|
||||||
|
"soft": "Soft",
|
||||||
|
"effective": "Effective"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ricochet": {
|
||||||
|
"angle": "Angle",
|
||||||
|
"normalization": "Normalization",
|
||||||
|
"effective_angle": "Effective angle",
|
||||||
|
"armor": "Armor",
|
||||||
|
"no_ricochet": "Explosive shells don't ricochet"
|
||||||
|
},
|
||||||
|
|
||||||
|
"view_range": {
|
||||||
|
"camo": "Camo",
|
||||||
|
"percentage": "%s1%",
|
||||||
|
"ghost_factory": "Ghost Factory",
|
||||||
|
"dead_rail": "Dead Rail"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"sandbox": {
|
"sandbox": {
|
||||||
"poses": {
|
"poses": {
|
||||||
"hull_down": "Hull down",
|
"hull_down": "Hull down",
|
||||||
@@ -996,7 +1060,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "Characteristics",
|
"title": "Characteristics",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "Compare against",
|
"label": "Compare",
|
||||||
"class": "Tier %s1 %s2",
|
"class": "Tier %s1 %s2",
|
||||||
"tier": "All of tier %s1",
|
"tier": "All of tier %s1",
|
||||||
"all": "All tanks"
|
"all": "All tanks"
|
||||||
@@ -1033,7 +1097,7 @@
|
|||||||
"explosionRadius": "Explosion radius",
|
"explosionRadius": "Explosion radius",
|
||||||
"shellVelocity": "Shell velocity",
|
"shellVelocity": "Shell velocity",
|
||||||
"aimTime": "Aim time",
|
"aimTime": "Aim time",
|
||||||
"dispersion": "Still",
|
"dispersion": "Dispersion still",
|
||||||
"dispersionMoving": "Moving",
|
"dispersionMoving": "Moving",
|
||||||
"dispersionHullTraversing": "Hull traversing",
|
"dispersionHullTraversing": "Hull traversing",
|
||||||
"dispersionTurretTraversing": "Turret traversing",
|
"dispersionTurretTraversing": "Turret traversing",
|
||||||
@@ -1047,28 +1111,28 @@
|
|||||||
"gunRearElevation": "Rear elevation",
|
"gunRearElevation": "Rear elevation",
|
||||||
"azimuthLeft": "Azimuth left",
|
"azimuthLeft": "Azimuth left",
|
||||||
"azimuthRight": "Azimuth right",
|
"azimuthRight": "Azimuth right",
|
||||||
"speedForwards": "Forwards",
|
"speedForwards": "Speed forwards",
|
||||||
"speedBackwards": "Backwards",
|
"speedBackwards": "Backwards",
|
||||||
"hardTerrainCoefficient": "Hard",
|
"hardTerrainCoefficient": "Terrain coefficient hard",
|
||||||
"hardTerrainCoefficientRaw": "Hard",
|
"hardTerrainCoefficientRaw": "Raw terrain coefficient hard",
|
||||||
"mediumTerrainCoefficient": "Medium",
|
"mediumTerrainCoefficient": "Medium",
|
||||||
"mediumTerrainCoefficientRaw": "Medium",
|
"mediumTerrainCoefficientRaw": "Medium",
|
||||||
"softTerrainCoefficient": "Soft",
|
"softTerrainCoefficient": "Soft",
|
||||||
"softTerrainCoefficientRaw": "Soft",
|
"softTerrainCoefficientRaw": "Soft",
|
||||||
"enginePower": "Engine power",
|
"enginePower": "Engine power",
|
||||||
"powerToWeightRatioHardTerrain": "Hard",
|
"powerToWeightRatioHardTerrain": "Power to weight ratio hard",
|
||||||
"powerToWeightRatioMediumTerrain": "Medium",
|
"powerToWeightRatioMediumTerrain": "Medium",
|
||||||
"powerToWeightRatioSoftTerrain": "Soft",
|
"powerToWeightRatioSoftTerrain": "Soft",
|
||||||
"weight": "Weight",
|
"weight": "Weight",
|
||||||
"turretTraverseSpeed": "Turret traverse speed",
|
"turretTraverseSpeed": "Turret traverse speed",
|
||||||
"gunTraverseSpeed": "Gun traverse speed",
|
"gunTraverseSpeed": "Gun traverse speed",
|
||||||
"hullTraverseHardTerrain": "Hard",
|
"hullTraverseHardTerrain": "Hull traverse hard",
|
||||||
"hullTraverseMediumTerrain": "Medium",
|
"hullTraverseMediumTerrain": "Medium",
|
||||||
"hullTraverseSoftTerrain": "Soft",
|
"hullTraverseSoftTerrain": "Soft",
|
||||||
"health": "Health",
|
"health": "Health",
|
||||||
"fireChance": "Fire chance",
|
"fireChance": "Fire chance",
|
||||||
"viewRange": "View range",
|
"viewRange": "View range",
|
||||||
"camouflageStill": "Still",
|
"camouflageStill": "Camouflage still",
|
||||||
"camouflageMoving": "Moving",
|
"camouflageMoving": "Moving",
|
||||||
"camouflageShootingStill": "Still",
|
"camouflageShootingStill": "Still",
|
||||||
"camouflageShootingMoving": "Moving",
|
"camouflageShootingMoving": "Moving",
|
||||||
|
|||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "Copiado!"
|
"copied": "Copiado!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "Buscar tanques...",
|
"search_bar_hint": "Buscar tanques...",
|
||||||
"recent": "Visto recientemente",
|
"recent": "Visto recientemente",
|
||||||
"select_all": "Seleccionar todo",
|
"select_all": "Seleccionar todo",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "Por %s1 ❤️"
|
"credit": "Por %s1 ❤️"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "¡Por favor ayuda a traducir %s1 a %s2!",
|
"help_translate": "¡Por favor ayuda a traducir %s1 a %s2!",
|
||||||
"seo_description": "Herramientas para todo lo que es World of Tanks Blitz"
|
"seo_description": "Herramientas para todo lo que es World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "Tankopedia",
|
"name": "Tankopedia",
|
||||||
@@ -843,105 +855,105 @@
|
|||||||
},
|
},
|
||||||
"tech_tree": {
|
"tech_tree": {
|
||||||
"title": "Árbol tecnológico",
|
"title": "Árbol tecnológico",
|
||||||
"battle": "1 battle",
|
"battle": "1 batalla",
|
||||||
"battles": "%s1 battles",
|
"battles": "%s1 batallas",
|
||||||
"route": "Route %s1 of %s2"
|
"route": "Ruta %s1 de %s2"
|
||||||
},
|
},
|
||||||
"game_modes": {
|
"game_modes": {
|
||||||
"title": "Game mode abilities"
|
"title": "Habilidades del modo de juego"
|
||||||
},
|
},
|
||||||
"review": {
|
"review": {
|
||||||
"title": "Review videos",
|
"title": "Vídeos de reseña",
|
||||||
"disclaimer": "BlitzKit no respalda ningún vídeo de reseñas.",
|
"disclaimer": "BlitzKit no respalda ningún vídeo de reseñas.",
|
||||||
"join": "Apply to %s1 YouTuber program"
|
"join": "Aplica para el programa de YouTube de %s1"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"title": "",
|
"title": "",
|
||||||
"modules": {
|
"modules": {
|
||||||
"title": "Modules",
|
"title": "Módulos",
|
||||||
"stock": "Stock",
|
"stock": "Base",
|
||||||
"upgrade": "Upgrade"
|
"upgrade": "Mejorar"
|
||||||
},
|
},
|
||||||
"equipment": {
|
"equipment": {
|
||||||
"title": "Equipment",
|
"title": "Equipo",
|
||||||
"clear": "Clear"
|
"clear": "Limpiar"
|
||||||
},
|
},
|
||||||
"provisions": {
|
"provisions": {
|
||||||
"title": "Provisiones",
|
"title": "Provisiones",
|
||||||
"clear": "Limpiar"
|
"clear": "Limpiar"
|
||||||
},
|
},
|
||||||
"consumables": {
|
"consumables": {
|
||||||
"title": "Consumables",
|
"title": "Consumibles",
|
||||||
"clear": "Clear",
|
"clear": "Limpiar",
|
||||||
"info": "Yellow consumables do not affect the statistics."
|
"info": "Consumibles amarillos no afectan las estadísticas."
|
||||||
},
|
},
|
||||||
"skills": {
|
"skills": {
|
||||||
"title": "Crew skills",
|
"title": "Habilidades de tripulación",
|
||||||
"clear": "Clear",
|
"clear": "Limpiar",
|
||||||
"maximize": "Maximize",
|
"maximize": "Maximizar",
|
||||||
"info": "Yellow skills only apply under special circumstances. Hold Space to quickly toggle between level 0 and 7."
|
"info": "Las habilidades en amarillo solo se aplican en circunstancias especiales. Mantenga el espacio para cambiar rápidamente entre los niveles 0 y 7."
|
||||||
},
|
},
|
||||||
"miscellaneous": {
|
"miscellaneous": {
|
||||||
"title": "Misceláneo"
|
"title": "Misceláneo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "Characteristics",
|
"title": "Características",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "Compare against",
|
"label": "Comparar",
|
||||||
"class": "Tier %s1 %s2",
|
"class": "Nivel %s1 - %s2",
|
||||||
"tier": "All of tier %s1",
|
"tier": "Todos de nivel %s1",
|
||||||
"all": "All tanks"
|
"all": "Todos los tanques"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"shell_index": "Shell %s1",
|
"shell_index": "Proyectil %s1",
|
||||||
"at_distance": "At %s1m",
|
"at_distance": "A %s1m",
|
||||||
"at_distance_slider_label": "Distance",
|
"at_distance_slider_label": "Distancia",
|
||||||
"dispersion_at_distance": "Dispersion at %s1m",
|
"dispersion_at_distance": "Dispersión a %s1m",
|
||||||
"gun_flexibility": "Gun flexibility",
|
"gun_flexibility": "Flexibilidad del arma",
|
||||||
"speed": "Speed",
|
"speed": "Velocidad",
|
||||||
"terrain_coefficients": "Terrain coefficients",
|
"terrain_coefficients": "Coeficientes de terreno",
|
||||||
"raw_terrain_coefficients": "Raw terrain coefficients",
|
"raw_terrain_coefficients": "Coeficientes de terreno en bruto",
|
||||||
"power_to_weight_ratio": "Power to weight ratio",
|
"power_to_weight_ratio": "Relación peso-potencia",
|
||||||
"hull_traverse_speed": "Hull traverse speed",
|
"hull_traverse_speed": "Velocidad de rotación del casco",
|
||||||
"camouflage": "Camouflage",
|
"camouflage": "Camuflaje",
|
||||||
"reload": "Reload",
|
"reload": "Recarga",
|
||||||
"crewCount": "Crew count",
|
"crewCount": "Cantidad de tripulantes",
|
||||||
"shellNormalization": "Normalización",
|
"shellNormalization": "Normalización",
|
||||||
"shellRicochet": "Rebote",
|
"shellRicochet": "Rebote",
|
||||||
"dpm": "DPM",
|
"dpm": "DPM",
|
||||||
"dpmEffective": "Effective at 60s",
|
"dpmEffective": "Effective at 60s",
|
||||||
"shells": "Shells",
|
"shells": "Proyectiles",
|
||||||
"mostOptimalShellIndex": "Most optimal shell",
|
"mostOptimalShellIndex": "Proyectil más óptimo",
|
||||||
"shellReloads": "Shell reloads",
|
"shellReloads": "Tiempos de recarga",
|
||||||
"shellReload": "Shell reload",
|
"shellReload": "Tiempo de recarga",
|
||||||
"intraClip": "Intra-clip",
|
"intraClip": "Intra-clip",
|
||||||
"caliber": "Calibre",
|
"caliber": "Calibre",
|
||||||
"penetration": "Penetración",
|
"penetration": "Penetración",
|
||||||
"damage": "Daño",
|
"damage": "Daño",
|
||||||
"clipDamage": "Clipping potential",
|
"clipDamage": "Clipping potential",
|
||||||
"moduleDamage": "Module damage",
|
"moduleDamage": "Daño a módulos",
|
||||||
"explosionRadius": "Radio de Explosión",
|
"explosionRadius": "Radio de Explosión",
|
||||||
"shellVelocity": "Shell velocity",
|
"shellVelocity": "Velocidad del proyectil",
|
||||||
"aimTime": "Aim time",
|
"aimTime": "Tiempo de apuntado",
|
||||||
"dispersion": "Still",
|
"dispersion": "Quieto",
|
||||||
"dispersionMoving": "Moving",
|
"dispersionMoving": "En movimiento",
|
||||||
"dispersionHullTraversing": "Hull traversing",
|
"dispersionHullTraversing": "Hull traversing",
|
||||||
"dispersionTurretTraversing": "Turret traversing",
|
"dispersionTurretTraversing": "Turret traversing",
|
||||||
"dispersionShooting": "Shooting",
|
"dispersionShooting": "Disparando",
|
||||||
"dispersionGunDamaged": "Gun damaged",
|
"dispersionGunDamaged": "Arma dañada",
|
||||||
"gunDepression": "Depression",
|
"gunDepression": "Depresión",
|
||||||
"gunElevation": "Elevation",
|
"gunElevation": "Elevación",
|
||||||
"gunFrontalDepression": "Frontal depression",
|
"gunFrontalDepression": "Depresión frontal",
|
||||||
"gunFrontalElevation": "Frontal elevation",
|
"gunFrontalElevation": "Elevación frontal",
|
||||||
"gunRearDepression": "Rear depression",
|
"gunRearDepression": "Depresión trasera",
|
||||||
"gunRearElevation": "Rear elevation",
|
"gunRearElevation": "Elevación trasera",
|
||||||
"azimuthLeft": "Azimuth left",
|
"azimuthLeft": "Azimuth left",
|
||||||
"azimuthRight": "Azimuth right",
|
"azimuthRight": "Azimuth right",
|
||||||
"speedForwards": "Forwards",
|
"speedForwards": "Avance",
|
||||||
"speedBackwards": "Backwards",
|
"speedBackwards": "Marcha atrás",
|
||||||
"hardTerrainCoefficient": "Hard",
|
"hardTerrainCoefficient": "Duro",
|
||||||
"hardTerrainCoefficientRaw": "Hard",
|
"hardTerrainCoefficientRaw": "Duro",
|
||||||
"mediumTerrainCoefficient": "Medio",
|
"mediumTerrainCoefficient": "Medio",
|
||||||
"mediumTerrainCoefficientRaw": "Medio",
|
"mediumTerrainCoefficientRaw": "Medio",
|
||||||
"softTerrainCoefficient": "Suave",
|
"softTerrainCoefficient": "Suave",
|
||||||
@@ -1026,7 +1038,7 @@
|
|||||||
"maximize": "Maximizar"
|
"maximize": "Maximizar"
|
||||||
},
|
},
|
||||||
"modules": {
|
"modules": {
|
||||||
"stock": "Stock",
|
"stock": "Base",
|
||||||
"upgrade": "Mejorar"
|
"upgrade": "Mejorar"
|
||||||
},
|
},
|
||||||
"provisions": {
|
"provisions": {
|
||||||
@@ -1107,22 +1119,22 @@
|
|||||||
},
|
},
|
||||||
"no_battles": {
|
"no_battles": {
|
||||||
"title": "No battles",
|
"title": "No battles",
|
||||||
"hint": "Go ahead and play a game"
|
"hint": "Sigue adelante y juega una partida"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"tank": "Tanque",
|
"tank": "Tanque",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"remove": "Remove"
|
"remove": "Remover"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tier_list": {
|
"tier_list": {
|
||||||
"name": "Tier list",
|
"name": "Tier list",
|
||||||
"button": "Rank tanks",
|
"button": "Rank tanks",
|
||||||
"description": "Rank Blitz tanks",
|
"description": "Rank Blitz tanks",
|
||||||
"seo_description": "Explore tier lists ranking tanks from best to worst, based on player ratings and statistical performance in World of Tanks Blitz",
|
"seo_description": "Explora las listas de clasificación de los tanques de mejor a peor, basadas en las calificaciones de los jugadores y el rendimiento estadístico en World of Tanks Blitz",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"reset": "Restablecer",
|
"reset": "Restablecer",
|
||||||
"share": "Share"
|
"share": "Compartir"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"embed": {
|
"embed": {
|
||||||
@@ -1131,54 +1143,54 @@
|
|||||||
"description": "Embeds for streaming",
|
"description": "Embeds for streaming",
|
||||||
"seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization",
|
"seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization",
|
||||||
"coming_soon": "More embeds coming soon.",
|
"coming_soon": "More embeds coming soon.",
|
||||||
"suggest": "Suggest ideas.",
|
"suggest": "Sugerir ideas.",
|
||||||
"preview": {
|
"preview": {
|
||||||
"title": "Example preview",
|
"title": "Example preview",
|
||||||
"upload": "Upload test background"
|
"upload": "Upload test background"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"import": {
|
"import": {
|
||||||
"title": "Import",
|
"title": "Importar",
|
||||||
"description": "Paste your current URL if you have one already to import your settings",
|
"description": "Pega tu URL actual si ya tienes una para importar la configuración",
|
||||||
"hint": "Paste URL...",
|
"hint": "Pegar URL...",
|
||||||
"apply": "Apply",
|
"apply": "Aplicar",
|
||||||
"idiot_title": "Invalid URL",
|
"idiot_title": "URL inválida",
|
||||||
"idiot_description": "Try pasting the URL that BlitzKit previously generated, you silly goose!",
|
"idiot_description": "Intenta pegar la URL que BlitzKit generó anteriormente, ¡Torpe!",
|
||||||
"idiot_button": "Okay, my bad"
|
"idiot_button": "Okay, my bad"
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"title": "Export",
|
"title": "Exportar",
|
||||||
"description": "Copy all properties for you streaming software",
|
"description": "Copiar todas las propiedades para tu software de streaming",
|
||||||
"generate": {
|
"generate": {
|
||||||
"button": "Generate URL",
|
"button": "Generar URL",
|
||||||
"title": "Generate URL",
|
"title": "Generar URL",
|
||||||
"subtitle": "Search an account to track",
|
"subtitle": "Buscar una cuenta para seguir",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancelar",
|
||||||
"success": {
|
"success": {
|
||||||
"title": "Copy URL",
|
"title": "Copiar URL",
|
||||||
"info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
"info": "Se generó la URL para %s1. El embebido comenzará a rastrear tan pronto como se pegue. Restablece en cualquier momento haciendo clic derecho en PC o manteniendo presionado en móvil y luego seleccionando \"Restablecer\".",
|
||||||
"back": "Volver",
|
"back": "Volver",
|
||||||
"copy": "Copy URL"
|
"copy": "Copiar URL"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"css": "Copy CSS",
|
"css": "Copiar CSS",
|
||||||
"width": "Copy Width",
|
"width": "Copiar ancho",
|
||||||
"height": "Copy Height",
|
"height": "Copiar altura",
|
||||||
"framerate": "Copy Framerate"
|
"framerate": "Copy Framerate"
|
||||||
},
|
},
|
||||||
"customize": {
|
"customize": {
|
||||||
"title": "Customize",
|
"title": "Personalizar",
|
||||||
"reset": "Restablecer",
|
"reset": "Restablecer",
|
||||||
"description": "Bells and whistles of your embed"
|
"description": "Bells and whistles of your embed"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"types": {
|
"types": {
|
||||||
"breakdown": {
|
"breakdown": {
|
||||||
"name": "Breakdown",
|
"name": "Desglose",
|
||||||
"options": {
|
"options": {
|
||||||
"width": "Ancho",
|
"width": "Ancho",
|
||||||
"height": "Altura",
|
"height": "Altura",
|
||||||
"showTotal": "Show total",
|
"showTotal": "Mostrar total",
|
||||||
"listGap": "List gap",
|
"listGap": "List gap",
|
||||||
"listMaxTanks": "Max tanks",
|
"listMaxTanks": "Max tanks",
|
||||||
"cardRadius": "Card radius",
|
"cardRadius": "Card radius",
|
||||||
@@ -1189,25 +1201,25 @@
|
|||||||
"cardTitleClassIcon": "Card title class icon",
|
"cardTitleClassIcon": "Card title class icon",
|
||||||
"cardBodyBackgroundColor": "Card body background color",
|
"cardBodyBackgroundColor": "Card body background color",
|
||||||
"cardBodyPadding": "Card body padding",
|
"cardBodyPadding": "Card body padding",
|
||||||
"columnGap": "Column gap",
|
"columnGap": "Espacio entre columnas",
|
||||||
"columnValue": "Column value",
|
"columnValue": "Valor de columna",
|
||||||
"columnLabel": "Column label",
|
"columnLabel": "Etiqueta de columna",
|
||||||
"column1": "Column 1",
|
"column1": "Columna 1",
|
||||||
"column2": "Column 2",
|
"column2": "Columna 2",
|
||||||
"column3": "Column 3",
|
"column3": "Columna 3",
|
||||||
"column4": "Column 4",
|
"column4": "Columna 4",
|
||||||
"column1CustomLabel": "Column 1 label",
|
"column1CustomLabel": "Etiqueta de la columna 1",
|
||||||
"column2CustomLabel": "Column 2 label",
|
"column2CustomLabel": "Etiqueta de la columna 2",
|
||||||
"column3CustomLabel": "Column 3 label"
|
"column3CustomLabel": "Etiqueta de la columna 3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"charts": {
|
"charts": {
|
||||||
"name": "Charts",
|
"name": "Gráficos",
|
||||||
"button": "Nerd out",
|
"button": "Nerd out",
|
||||||
"description": "Lots of graphs",
|
"description": "Lots of graphs",
|
||||||
"seo_description": "Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz"
|
"seo_description": "Gráficos y tablas sobre todo lo relacionado con tanques, jugadores y eventos en World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
"rating": {
|
"rating": {
|
||||||
"name": "Rating",
|
"name": "Rating",
|
||||||
@@ -1218,11 +1230,11 @@
|
|||||||
"discord": {
|
"discord": {
|
||||||
"name": "Discord bot",
|
"name": "Discord bot",
|
||||||
"button": "Install now",
|
"button": "Install now",
|
||||||
"description": "Stats right in Discord",
|
"description": "Estadísticas en Discord",
|
||||||
"seo_description": "Get stats right in Discord with BlitzKit's Discord bot"
|
"seo_description": "Obtén estadísticas en Discord con el bot de Discord de BlitzKit"
|
||||||
},
|
},
|
||||||
"more": {
|
"more": {
|
||||||
"name": "More coming soon",
|
"name": "Más próximamente",
|
||||||
"button": "Join Discord",
|
"button": "Join Discord",
|
||||||
"description": "What we're making",
|
"description": "What we're making",
|
||||||
"seo_description": "What we're making with BlitzKit"
|
"seo_description": "What we're making with BlitzKit"
|
||||||
@@ -1241,7 +1253,7 @@
|
|||||||
},
|
},
|
||||||
"blitz_analysis": {
|
"blitz_analysis": {
|
||||||
"name": "BlitzAnalysis[]",
|
"name": "BlitzAnalysis[]",
|
||||||
"button": "Analyze now",
|
"button": "Analizar ahora",
|
||||||
"description": "Rich historic average stats",
|
"description": "Rich historic average stats",
|
||||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
@@ -1253,19 +1265,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"motto": "Everything World of Tanks Blitz",
|
"motto": "Todo sobre World of Tanks Blitz",
|
||||||
"copyright": "Unaffiliated with Wargaming or any service",
|
"copyright": "No afiliado con Wargaming u otros servicios",
|
||||||
"about": {
|
"about": {
|
||||||
"title": "About",
|
"title": "About",
|
||||||
"credits": "Credits",
|
"credits": "Credits",
|
||||||
"discord": "Discord",
|
"discord": "Discord",
|
||||||
"github": "GitHub",
|
"github": "GitHub",
|
||||||
"patches": "Patches"
|
"patches": "Parches"
|
||||||
},
|
},
|
||||||
"legal": {
|
"legal": {
|
||||||
"title": "Legal",
|
"title": "Legal",
|
||||||
"privacy": "Privacy policy",
|
"privacy": "Política de Privacidad",
|
||||||
"terms": "Terms of service"
|
"terms": "Términos del servicio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,9 +129,9 @@
|
|||||||
"asia": "Asie-Pacifique"
|
"asia": "Asie-Pacifique"
|
||||||
},
|
},
|
||||||
"short": {
|
"short": {
|
||||||
"com": "NA",
|
"com": "Amérique du Nord",
|
||||||
"eu": "Europe",
|
"eu": "Europe",
|
||||||
"asia": "APAC"
|
"asia": "Asie-Pacifique"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"leagues": {
|
"leagues": {
|
||||||
@@ -350,7 +350,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"subtitle": "Research",
|
"subtitle": "Recherche",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"research": "Recherche: %s1",
|
"research": "Recherche: %s1",
|
||||||
"upgrades": "Améliorations : %s1",
|
"upgrades": "Améliorations : %s1",
|
||||||
@@ -377,7 +377,7 @@
|
|||||||
"body": "# Commandes\n\nBlitzKit offre les commandes suivantes :\n\n"
|
"body": "# Commandes\n\nBlitzKit offre les commandes suivantes :\n\n"
|
||||||
},
|
},
|
||||||
"embeds": {
|
"embeds": {
|
||||||
"name": "embeds",
|
"name": "intègre",
|
||||||
"description": "How embeds work"
|
"description": "How embeds work"
|
||||||
},
|
},
|
||||||
"introduction": {
|
"introduction": {
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
"body": {
|
"body": {
|
||||||
"cards": {
|
"cards": {
|
||||||
"games": {
|
"games": {
|
||||||
"title": "Games",
|
"title": "Parties",
|
||||||
"winrate": "Taux de victoires",
|
"winrate": "Taux de victoires",
|
||||||
"battles": "Batailles",
|
"battles": "Batailles",
|
||||||
"wins_losses": "Victoires/Défaites"
|
"wins_losses": "Victoires/Défaites"
|
||||||
@@ -505,45 +505,45 @@
|
|||||||
"description": "Search clans",
|
"description": "Search clans",
|
||||||
"options": {
|
"options": {
|
||||||
"limit": {
|
"limit": {
|
||||||
"name": "limit",
|
"name": "limite",
|
||||||
"description": "The max number of clan results (default: %s1)"
|
"description": "The max number of clan results (default: %s1)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"title": "Clan search for %s1 in region %s2",
|
"title": "Clan search for %s1 in region %s2",
|
||||||
"no_results": "No clans found"
|
"no_results": "Pas de clan trouvé"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"search_players": {
|
"search_players": {
|
||||||
"name": "search-players",
|
"name": "recherche de joueur",
|
||||||
"description": "Search players",
|
"description": "Recherche des joueurs",
|
||||||
"options": {
|
"options": {
|
||||||
"limit": {
|
"limit": {
|
||||||
"name": "limit",
|
"name": "limite",
|
||||||
"description": "The max number of player results (default: %s1)"
|
"description": "Le nombre maximum de résultats de joueur (par défaut: %s1)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"title": "Player search for %s1 in region %s2",
|
"title": "Recherche de %s1 dans la région %s2",
|
||||||
"no_results": "No players found"
|
"no_results": "Pas de joueur de trouvé"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rating_leaderboard": {
|
"rating_leaderboard": {
|
||||||
"name": "rating-leaderboard",
|
"name": "Classement de côté",
|
||||||
"description": "Rating battles statistics",
|
"description": "Statistiques des batailles de cotés",
|
||||||
"subcommands": {
|
"subcommands": {
|
||||||
"neighbors": {
|
"neighbors": {
|
||||||
"name": "neighbors",
|
"name": "neighbors",
|
||||||
"description": "Neighboring players on the leaderboard"
|
"description": "Neighboring players on the leaderboard"
|
||||||
},
|
},
|
||||||
"league": {
|
"league": {
|
||||||
"name": "league",
|
"name": "ligue",
|
||||||
"description": "Top players of a league"
|
"description": "Top players of a league"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"limit": {
|
"limit": {
|
||||||
"name": "limit",
|
"name": "limite",
|
||||||
"description": "How many neighbors to display (default: %s1)"
|
"description": "How many neighbors to display (default: %s1)"
|
||||||
},
|
},
|
||||||
"season": {
|
"season": {
|
||||||
@@ -554,7 +554,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"league": {
|
"league": {
|
||||||
"name": "league",
|
"name": "ligue",
|
||||||
"description": "The league to display"
|
"description": "The league to display"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -572,7 +572,7 @@
|
|||||||
"description": "Today's played tanks and statistics"
|
"description": "Today's played tanks and statistics"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"name": "stats",
|
"name": "statistiques",
|
||||||
"description": "Quick statistics for a period"
|
"description": "Quick statistics for a period"
|
||||||
},
|
},
|
||||||
"owned_tanks": {
|
"owned_tanks": {
|
||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "Copied!"
|
"copied": "Copied!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "Search tanks...",
|
"search_bar_hint": "Search tanks...",
|
||||||
"recent": "Recently viewed",
|
"recent": "Recently viewed",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "By %s1 ❤️"
|
"credit": "By %s1 ❤️"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "Please help translate %s1 to %s2!",
|
"help_translate": "Please help translate %s1 to %s2!",
|
||||||
"seo_description": "Tools for everything World of Tanks Blitz"
|
"seo_description": "Tools for everything World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "Tankopedia",
|
"name": "Tankopedia",
|
||||||
@@ -888,7 +900,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "Characteristics",
|
"title": "Characteristics",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "Compare against",
|
"label": "Compare",
|
||||||
"class": "Tier %s1 %s2",
|
"class": "Tier %s1 %s2",
|
||||||
"tier": "All of tier %s1",
|
"tier": "All of tier %s1",
|
||||||
"all": "All tanks"
|
"all": "All tanks"
|
||||||
@@ -1092,7 +1104,7 @@
|
|||||||
"lookup": "Look up a player to get started",
|
"lookup": "Look up a player to get started",
|
||||||
"hint": "Search players...",
|
"hint": "Search players...",
|
||||||
"searching": "Searching...",
|
"searching": "Searching...",
|
||||||
"no_results": "No players found"
|
"no_results": "Pas de joueur de trouvé"
|
||||||
},
|
},
|
||||||
"controls": {
|
"controls": {
|
||||||
"title": "Tracking %s1",
|
"title": "Tracking %s1",
|
||||||
|
|||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "コピーしました!"
|
"copied": "コピーしました!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "車輌名を入力…",
|
"search_bar_hint": "車輌名を入力…",
|
||||||
"recent": "最近の閲覧",
|
"recent": "最近の閲覧",
|
||||||
"select_all": "全て選択",
|
"select_all": "全て選択",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "By %s1 ❤️"
|
"credit": "By %s1 ❤️"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "%s1の %s2への翻訳にご協力ください!",
|
"help_translate": "%s1の %s2への翻訳にご協力ください!",
|
||||||
"seo_description": "Tools for everything World of Tanks Blitz"
|
"seo_description": "Tools for everything World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "Tankopedia",
|
"name": "Tankopedia",
|
||||||
@@ -888,7 +900,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "性能値",
|
"title": "性能値",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "比較対象",
|
"label": "性能比較",
|
||||||
"class": "Tier %s1 %s2",
|
"class": "Tier %s1 %s2",
|
||||||
"tier": "Tier %s1 全車輌",
|
"tier": "Tier %s1 全車輌",
|
||||||
"all": "全車輌"
|
"all": "全車輌"
|
||||||
|
|||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "Copied!"
|
"copied": "Copied!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "Search tanks...",
|
"search_bar_hint": "Search tanks...",
|
||||||
"recent": "Recently viewed",
|
"recent": "Recently viewed",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "By %s1 ❤️"
|
"credit": "By %s1 ❤️"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "Please help translate %s1 to %s2!",
|
"help_translate": "Please help translate %s1 to %s2!",
|
||||||
"seo_description": "Tools for everything World of Tanks Blitz"
|
"seo_description": "Tools for everything World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "Tankopedia",
|
"name": "Tankopedia",
|
||||||
@@ -888,7 +900,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "Characteristics",
|
"title": "Characteristics",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "Compare against",
|
"label": "Compare",
|
||||||
"class": "Tier %s1 %s2",
|
"class": "Tier %s1 %s2",
|
||||||
"tier": "All of tier %s1",
|
"tier": "All of tier %s1",
|
||||||
"all": "All tanks"
|
"all": "All tanks"
|
||||||
|
|||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "Copiado!"
|
"copied": "Copiado!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "Procurar tanques...",
|
"search_bar_hint": "Procurar tanques...",
|
||||||
"recent": "Visto recentemente",
|
"recent": "Visto recentemente",
|
||||||
"select_all": "Selecionar todos",
|
"select_all": "Selecionar todos",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "Por %s1 ❤️"
|
"credit": "Por %s1 ❤️"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "Por favor, ajude a traduzir %s1 para %s2!",
|
"help_translate": "Por favor, ajude a traduzir %s1 para %s2!",
|
||||||
"seo_description": "Ferramentas para tudo World of Tanks Blitz"
|
"seo_description": "Ferramentas para tudo World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "Tanquepedia",
|
"name": "Tanquepedia",
|
||||||
@@ -888,7 +900,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "Características",
|
"title": "Características",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "Comparar com",
|
"label": "Comparar",
|
||||||
"class": "Nível %s1 %s2",
|
"class": "Nível %s1 %s2",
|
||||||
"tier": "Tudo de nível %s1",
|
"tier": "Tudo de nível %s1",
|
||||||
"all": "Todos os tanques"
|
"all": "Todos os tanques"
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
"local": "dev",
|
"local": "dev",
|
||||||
"dev": "бета",
|
"dev": "бета",
|
||||||
"opentest": "открытый тест",
|
"opentest": "открытый тест",
|
||||||
"preview": "предварительный просмотр"
|
"preview": "превью"
|
||||||
},
|
},
|
||||||
"locales": {
|
"locales": {
|
||||||
"en": "Английский",
|
"en": "Английский",
|
||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "Скопировано!"
|
"copied": "Скопировано!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Сбросить фильтры",
|
||||||
"search_bar_hint": "Поиск танков...",
|
"search_bar_hint": "Поиск танков...",
|
||||||
"recent": "Недавние",
|
"recent": "Недавние",
|
||||||
"select_all": "Выбрать всё",
|
"select_all": "Выбрать всё",
|
||||||
@@ -716,7 +717,7 @@
|
|||||||
"label": "Сортировка",
|
"label": "Сортировка",
|
||||||
"fire": "Стрельба",
|
"fire": "Стрельба",
|
||||||
"maneuverability": "Манёвренность",
|
"maneuverability": "Манёвренность",
|
||||||
"survivability": "Живучесть"
|
"survivability": "Выживаемость"
|
||||||
},
|
},
|
||||||
"direction": {
|
"direction": {
|
||||||
"label": "Направление",
|
"label": "Направление",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Присоединяйтесь к крутым ребятам!",
|
||||||
|
"join_long": "Присоединяйся к супер-дупер крутым ребятам!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "От %s1 ❤️"
|
"credit": "От %s1 ❤️"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "Пожалуйста, помогите перевести %s1 на %s2!",
|
"help_translate": "Пожалуйста, помогите перевести %s1 на %s2!",
|
||||||
"seo_description": "Инструменты для World of Tanks Blitz"
|
"seo_description": "Инструменты для World of Tanks Blitz"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "Списки изменений BlitzKit",
|
||||||
|
"seo_description": "Список изменений для бота Discord и сайта BlitzKit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "Танкопедия",
|
"name": "Танкопедия",
|
||||||
@@ -888,7 +900,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "Характеристики",
|
"title": "Характеристики",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "Сравнивать с",
|
"label": "Сравнение",
|
||||||
"class": "%s2 %s1 уровня",
|
"class": "%s2 %s1 уровня",
|
||||||
"tier": "Все %s1 уровня",
|
"tier": "Все %s1 уровня",
|
||||||
"all": "Все танки"
|
"all": "Все танки"
|
||||||
@@ -975,7 +987,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"firepower": {
|
"firepower": {
|
||||||
"title": "Огневая мощь"
|
"title": "Стрельба"
|
||||||
},
|
},
|
||||||
"crew": {
|
"crew": {
|
||||||
"title": "Мастерство экипажа",
|
"title": "Мастерство экипажа",
|
||||||
@@ -991,7 +1003,7 @@
|
|||||||
"title": "Манёвренность"
|
"title": "Манёвренность"
|
||||||
},
|
},
|
||||||
"survivability": {
|
"survivability": {
|
||||||
"title": "Живучесть"
|
"title": "Выживаемость"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"compare": {
|
"compare": {
|
||||||
@@ -1131,7 +1143,7 @@
|
|||||||
"description": "Встраиваемые элементы для стримов",
|
"description": "Встраиваемые элементы для стримов",
|
||||||
"seo_description": "Создать вставки для стримов по World of Tanks Blitz с невероятной кастомизации",
|
"seo_description": "Создать вставки для стримов по World of Tanks Blitz с невероятной кастомизации",
|
||||||
"coming_soon": "Скоро появятся новые встраиваемые элементы.",
|
"coming_soon": "Скоро появятся новые встраиваемые элементы.",
|
||||||
"suggest": "Предложить идеи.",
|
"suggest": "Предложить идеи",
|
||||||
"preview": {
|
"preview": {
|
||||||
"title": "Предпросмотр",
|
"title": "Предпросмотр",
|
||||||
"upload": "Загрузить фон для теста"
|
"upload": "Загрузить фон для теста"
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
"m": "%s1м",
|
"m": "%s1м",
|
||||||
"mm": "%s1мм",
|
"mm": "%s1мм",
|
||||||
"deg": "%s1°",
|
"deg": "%s1°",
|
||||||
"hp": "%s1hp",
|
"hp": "%s1хп",
|
||||||
"x": "x%s1"
|
"x": "х%s1"
|
||||||
},
|
},
|
||||||
"gun_types": {
|
"gun_types": {
|
||||||
"auto_loader": "Магазинна система заряджання",
|
"auto_loader": "Магазинна система заряджання",
|
||||||
@@ -86,14 +86,14 @@
|
|||||||
"other": "Збірна нація"
|
"other": "Збірна нація"
|
||||||
},
|
},
|
||||||
"nations_adjectives": {
|
"nations_adjectives": {
|
||||||
"ussr": "Радянська",
|
"ussr": "Радянський",
|
||||||
"germany": "Німецька",
|
"germany": "Німецький",
|
||||||
"usa": "Американська",
|
"usa": "Американський",
|
||||||
"china": "Китайська",
|
"china": "Китайський",
|
||||||
"uk": "Британська",
|
"uk": "Британський",
|
||||||
"france": "Французька",
|
"france": "Французький",
|
||||||
"japan": "Японська",
|
"japan": "Японський",
|
||||||
"european": "Європейська",
|
"european": "Європейський",
|
||||||
"other": "Гібрид"
|
"other": "Гібрид"
|
||||||
},
|
},
|
||||||
"tank_class_short": {
|
"tank_class_short": {
|
||||||
@@ -184,11 +184,11 @@
|
|||||||
"common": {
|
"common": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"clan_not_found": "# Не вдалося знайти клан\nНе вдалося знайти клан \"%s1\". Спробуйте вибрати клан з пошукового результату.",
|
"clan_not_found": "# Не вдалося знайти клан\nНе вдалося знайти клан \"%s1\". Спробуйте вибрати клан з пошукового результату.",
|
||||||
"player_not_found": "# Could not find user\nI couldn't find user \"%s1\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
"player_not_found": "# Не вдалося знайти користувача\nЯ не зміг знайти користувача \"%s1\". Спробуйте вибрати користувача з результату пошуку, ввести коректний нікнейм, або використати команду '/link'.",
|
||||||
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
|
"player_not_linked": "# Використайте команду \"/link\" щоб почати\nЯ не знаю, хто ви, але ви можете сказати мені сьогодні!",
|
||||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s1\".",
|
"tank_name_not_found": "# Танк не знайдено\nНе вдалось знайти танк за ім'ям \"%s1\".",
|
||||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s1\". Try typing the name of the tank instead of a number.",
|
"tank_id_not_found": "# Танк не знайдено\nНе вдалось знайти танк за ID \"%s1\". Спробуйте ввести назву танка замість числа.",
|
||||||
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
"no_tank_stats": "# Немає статистики для танка\nЦей гравець не має статистики.\nМожливо, це не той гравець, якого ви шукаєте.",
|
||||||
"get_help": "Допомогти Discord серверу",
|
"get_help": "Допомогти Discord серверу",
|
||||||
"uncaught_error": "# Упс! BlitzKit зіткнувся з помилкою!\nНам дуже шкода. Можете попросити допомоги на [офіційному сервері Discord](https://discord.gg/nDt7AjGJQH).",
|
"uncaught_error": "# Упс! BlitzKit зіткнувся з помилкою!\nНам дуже шкода. Можете попросити допомоги на [офіційному сервері Discord](https://discord.gg/nDt7AjGJQH).",
|
||||||
"preview": "# Ця команда є в попередньому перегляді\n\nВи можете отримати доступ лише до цієї команди на [офіційному сервері Discord](https://discord.gg/nDt7AjGJQH). Команда в цілому буде доступна найближчим часом.",
|
"preview": "# Ця команда є в попередньому перегляді\n\nВи можете отримати доступ лише до цієї команди на [офіційному сервері Discord](https://discord.gg/nDt7AjGJQH). Команда в цілому буде доступна найближчим часом.",
|
||||||
@@ -363,8 +363,8 @@
|
|||||||
"tier_1": "# %s1 це рівень I\n\nТанки рівню I завжди відкриті для всіх гравців безкоштовно.",
|
"tier_1": "# %s1 це рівень I\n\nТанки рівню I завжди відкриті для всіх гравців безкоштовно.",
|
||||||
"already_researched": "# %s1 уже досліджено!\n\nСпробуйте забезпечити `starting-tank` коли використовуєте команду щоб подивитись підсумки гілки, яку ви вже дослідили.",
|
"already_researched": "# %s1 уже досліджено!\n\nСпробуйте забезпечити `starting-tank` коли використовуєте команду щоб подивитись підсумки гілки, яку ви вже дослідили.",
|
||||||
"start_end_equal": "# %s1 було надано двічі\n\nПереконайся, що `starting-tank` та `target-tank` різні.",
|
"start_end_equal": "# %s1 було надано двічі\n\nПереконайся, що `starting-tank` та `target-tank` різні.",
|
||||||
"tanks_not_on_line": "# %s1 and %s2 are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
|
"tanks_not_on_line": ".",
|
||||||
"unordered": "# %s1 higher tiered to %s2\n\nMake sure to provide %s3 as the `starting-tank` instead as it is the lower tiered tank."
|
"unordered": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
@@ -399,8 +399,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"breakdown": {
|
"breakdown": {
|
||||||
"name": "breakdown",
|
"name": "аналіз",
|
||||||
"description": "A period's breakdown by tanks played",
|
"description": "Аналіз періоду за зіграними танками",
|
||||||
"body": {
|
"body": {
|
||||||
"total": "Загалом",
|
"total": "Загалом",
|
||||||
"battles": "Бої",
|
"battles": "Бої",
|
||||||
@@ -511,7 +511,7 @@
|
|||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"title": "Clan search for %s1 in region %s2",
|
"title": "Clan search for %s1 in region %s2",
|
||||||
"no_results": "No clans found"
|
"no_results": "Жодного клану не знайдено"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"search_players": {
|
"search_players": {
|
||||||
@@ -544,7 +544,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"limit": {
|
"limit": {
|
||||||
"name": "ліміт",
|
"name": "ліміт",
|
||||||
"description": "How many neighbors to display (default: %s1)"
|
"description": ""
|
||||||
},
|
},
|
||||||
"season": {
|
"season": {
|
||||||
"name": "season",
|
"name": "season",
|
||||||
@@ -594,7 +594,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ping": {
|
"ping": {
|
||||||
"name": "ping",
|
"name": "",
|
||||||
"description": "Check wether the bot is alive.",
|
"description": "Check wether the bot is alive.",
|
||||||
"body": {
|
"body": {
|
||||||
"pong": "Pong 🏓",
|
"pong": "Pong 🏓",
|
||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "Скопійовано!"
|
"copied": "Скопійовано!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "Шукайте техніку...",
|
"search_bar_hint": "Шукайте техніку...",
|
||||||
"recent": "Недавно переглянуті",
|
"recent": "Недавно переглянуті",
|
||||||
"select_all": "Вибрати все",
|
"select_all": "Вибрати все",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "Зроблено %s1 ❤"
|
"credit": "Зроблено %s1 ❤"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "Будь ласка, допоможіть перекласти %s1 на %s2!",
|
"help_translate": "Будь ласка, допоможіть перекласти %s1 на %s2!",
|
||||||
"seo_description": "Інструменти для всього, пов'язаного з WoT Blitz"
|
"seo_description": "Інструменти для всього, пов'язаного з WoT Blitz"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "Танкопедія",
|
"name": "Танкопедія",
|
||||||
@@ -888,7 +900,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "Characteristics",
|
"title": "Characteristics",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "Compare against",
|
"label": "Порівняти",
|
||||||
"class": "Tier %s1 %s2",
|
"class": "Tier %s1 %s2",
|
||||||
"tier": "All of tier %s1",
|
"tier": "All of tier %s1",
|
||||||
"all": "Усі танки"
|
"all": "Усі танки"
|
||||||
|
|||||||
@@ -665,6 +665,7 @@
|
|||||||
"copied": "已複製!"
|
"copied": "已複製!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
|
"clear_filters": "Clear filters",
|
||||||
"search_bar_hint": "搜尋戰車....",
|
"search_bar_hint": "搜尋戰車....",
|
||||||
"recent": "最近查看",
|
"recent": "最近查看",
|
||||||
"select_all": "選擇全部",
|
"select_all": "選擇全部",
|
||||||
@@ -731,6 +732,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
|
"discord": {
|
||||||
|
"join_short": "Join cool people!",
|
||||||
|
"join_long": "Join super duper cool people!",
|
||||||
|
"action": "Discord"
|
||||||
|
},
|
||||||
"hero": {
|
"hero": {
|
||||||
"credit": "由 %s1 製作❤️"
|
"credit": "由 %s1 製作❤️"
|
||||||
},
|
},
|
||||||
@@ -744,6 +750,12 @@
|
|||||||
"help_translate": "請幫忙將 %s1 翻譯成 %s2!",
|
"help_translate": "請幫忙將 %s1 翻譯成 %s2!",
|
||||||
"seo_description": "戰車世界閃擊戰 全方位工具"
|
"seo_description": "戰車世界閃擊戰 全方位工具"
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"patches": {
|
||||||
|
"name": "BlitzKit Changelogs",
|
||||||
|
"seo_description": "Changelogs for BlitzKit Discord bot and website"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"tankopedia": {
|
"tankopedia": {
|
||||||
"name": "戰車百科",
|
"name": "戰車百科",
|
||||||
@@ -754,7 +766,7 @@
|
|||||||
"tank_seo_description": "%s1-Tier %s2 %s3 %s4 | %s5 Tankopedia | 戰車世界閃擊戰 (WoTB)",
|
"tank_seo_description": "%s1-Tier %s2 %s3 %s4 | %s5 Tankopedia | 戰車世界閃擊戰 (WoTB)",
|
||||||
"sandbox": {
|
"sandbox": {
|
||||||
"poses": {
|
"poses": {
|
||||||
"hull_down": "坦克下部",
|
"hull_down": "戰車下部",
|
||||||
"face_hug": "面部色调",
|
"face_hug": "面部色调",
|
||||||
"default": "預設"
|
"default": "預設"
|
||||||
},
|
},
|
||||||
@@ -888,7 +900,7 @@
|
|||||||
"characteristics": {
|
"characteristics": {
|
||||||
"title": "属性",
|
"title": "属性",
|
||||||
"compare": {
|
"compare": {
|
||||||
"label": "比较",
|
"label": "比較",
|
||||||
"class": "%s1 %s2 级",
|
"class": "%s1 %s2 级",
|
||||||
"tier": "所有级别 %s1",
|
"tier": "所有级别 %s1",
|
||||||
"all": "所有坦克"
|
"all": "所有坦克"
|
||||||
@@ -897,21 +909,21 @@
|
|||||||
"shell_index": "炮弹 %s1",
|
"shell_index": "炮弹 %s1",
|
||||||
"at_distance": "在 %s1m",
|
"at_distance": "在 %s1m",
|
||||||
"at_distance_slider_label": "距离",
|
"at_distance_slider_label": "距离",
|
||||||
"dispersion_at_distance": "%s1米的散射度",
|
"dispersion_at_distance": "%s1公尺擴散度",
|
||||||
"gun_flexibility": "炮控灵活度",
|
"gun_flexibility": "炮控灵活度",
|
||||||
"speed": "速度",
|
"speed": "速度",
|
||||||
"terrain_coefficients": "地势系数",
|
"terrain_coefficients": "地势系数",
|
||||||
"raw_terrain_coefficients": "原始地形系数",
|
"raw_terrain_coefficients": "原始地形系数",
|
||||||
"power_to_weight_ratio": "推重比",
|
"power_to_weight_ratio": "推重比",
|
||||||
"hull_traverse_speed": "炮塔旋转速度",
|
"hull_traverse_speed": "炮塔旋转速度",
|
||||||
"camouflage": "伪装",
|
"camouflage": "偽裝",
|
||||||
"reload": "裝填",
|
"reload": "裝填",
|
||||||
"crewCount": "乘员人数",
|
"crewCount": "乘员人数",
|
||||||
"shellNormalization": "轉正",
|
"shellNormalization": "轉正",
|
||||||
"shellRicochet": "跳彈",
|
"shellRicochet": "跳彈",
|
||||||
"dpm": "每分钟伤害",
|
"dpm": "每分鍾傷害血量",
|
||||||
"dpmEffective": "60秒内生效",
|
"dpmEffective": "60秒内生效",
|
||||||
"shells": "炮弹 ",
|
"shells": "砲彈",
|
||||||
"mostOptimalShellIndex": "最优炮弹",
|
"mostOptimalShellIndex": "最优炮弹",
|
||||||
"shellReloads": "炮弹重装",
|
"shellReloads": "炮弹重装",
|
||||||
"shellReload": "炮弹重装",
|
"shellReload": "炮弹重装",
|
||||||
@@ -1040,19 +1052,19 @@
|
|||||||
"performance": {
|
"performance": {
|
||||||
"name": "性能表现",
|
"name": "性能表现",
|
||||||
"button": "查看坦克",
|
"button": "查看坦克",
|
||||||
"description": "坦克性能统计",
|
"description": "戰車性能統計",
|
||||||
"seo_description": "最好的坦克及其在世界坦克闪电战中的表现",
|
"seo_description": "最好的戰車及其在戰車世界閃擊戰中的表現",
|
||||||
"estimation": {
|
"estimation": {
|
||||||
"body": "基于 %s1个玩家的估计数据。更新 %s2 之前。",
|
"body": "基于 %s1个玩家的估计数据。更新 %s2 之前。",
|
||||||
"hours": "%s1 小时",
|
"hours": "%s1 小時",
|
||||||
"minutes": "%s1 分钟"
|
"minutes": "%s1 分鐘"
|
||||||
},
|
},
|
||||||
"player_count": {
|
"player_count": {
|
||||||
"label": "玩家数量"
|
"label": "玩家数量"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"tanks": {
|
"tanks": {
|
||||||
"header": "坦克",
|
"header": "戰車",
|
||||||
"total": "全部"
|
"total": "全部"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
@@ -1068,15 +1080,15 @@
|
|||||||
"hits": "命中數",
|
"hits": "命中數",
|
||||||
"damage_ratio": "伤害效率",
|
"damage_ratio": "伤害效率",
|
||||||
"damage_taken": "伤害值",
|
"damage_taken": "伤害值",
|
||||||
"capture_points": "占领值"
|
"capture_points": "佔領點數"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gallery": {
|
"gallery": {
|
||||||
"name": "图库",
|
"name": "圖庫",
|
||||||
"button": "浏览全部",
|
"button": "瀏覽所有",
|
||||||
"description": "Blitz玩家头像",
|
"description": "Blitz 玩家頭像",
|
||||||
"seo_description": "所有坦克闪电战玩家头像",
|
"seo_description": "所有戰車世界閃擊戰玩家頭像",
|
||||||
"last_updated": "最後更新於 %s1",
|
"last_updated": "最後更新於 %s1",
|
||||||
"search": {
|
"search": {
|
||||||
"hint": "搜尋頭像……",
|
"hint": "搜尋頭像……",
|
||||||
@@ -1131,17 +1143,17 @@
|
|||||||
"description": "串流嵌入功能",
|
"description": "串流嵌入功能",
|
||||||
"seo_description": "創建具有極高自訂化的《戰車世界:閃擊戰》串流嵌入功能",
|
"seo_description": "創建具有極高自訂化的《戰車世界:閃擊戰》串流嵌入功能",
|
||||||
"coming_soon": "更多嵌入功能即將推出。",
|
"coming_soon": "更多嵌入功能即將推出。",
|
||||||
"suggest": "建议与想法",
|
"suggest": "建議與想法。",
|
||||||
"preview": {
|
"preview": {
|
||||||
"title": "示例预览",
|
"title": "範例",
|
||||||
"upload": "上传测试背景"
|
"upload": "上傳測試背景"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"import": {
|
"import": {
|
||||||
"title": "导入",
|
"title": "導入",
|
||||||
"description": "如果您已经有一个要导入您的设置,请粘贴您当前的 URL",
|
"description": "如果您已经有一个要导入您的设置,请粘贴您当前的 URL",
|
||||||
"hint": "粘贴链接...",
|
"hint": "貼上 URL……",
|
||||||
"apply": "应用",
|
"apply": "套用",
|
||||||
"idiot_title": "無效的 URL",
|
"idiot_title": "無效的 URL",
|
||||||
"idiot_description": "試著貼上 BlitzKit 先前生成的網址吧,你這隻傻鵝!",
|
"idiot_description": "試著貼上 BlitzKit 先前生成的網址吧,你這隻傻鵝!",
|
||||||
"idiot_button": "好吧,我的錯"
|
"idiot_button": "好吧,我的錯"
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
SUPPORTED_LOCALES,
|
SUPPORTED_LOCALES,
|
||||||
} from '@blitzkit/i18n';
|
} from '@blitzkit/i18n';
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from 'fs/promises';
|
||||||
|
import { deburr } from 'lodash-es';
|
||||||
import { parse as parsePath } from 'path';
|
import { parse as parsePath } from 'path';
|
||||||
import { Vector3Tuple } from 'three';
|
import { Vector3Tuple } from 'three';
|
||||||
import { parse as parseYaml } from 'yaml';
|
import { parse as parseYaml } from 'yaml';
|
||||||
@@ -48,6 +49,23 @@ import { DATA } from './constants';
|
|||||||
import { Avatar } from './skillIcons';
|
import { Avatar } from './skillIcons';
|
||||||
import { TankParameters } from './tankIcons';
|
import { TankParameters } from './tankIcons';
|
||||||
|
|
||||||
|
const nonAlphanumericRegex = /[^a-z0-9]/g;
|
||||||
|
const multipleDashesRegex = /--+/g;
|
||||||
|
const trailingDashRegex = /-$/g;
|
||||||
|
const leadingDashRegex = /^-/g;
|
||||||
|
|
||||||
|
const nationSlugDiscriminators = {
|
||||||
|
china: 'cn',
|
||||||
|
european: 'eu',
|
||||||
|
france: 'fr',
|
||||||
|
germany: 'de',
|
||||||
|
japan: 'jp',
|
||||||
|
other: 'ot',
|
||||||
|
uk: 'gb',
|
||||||
|
usa: 'us',
|
||||||
|
ussr: 'ru',
|
||||||
|
};
|
||||||
|
|
||||||
function parseResearchCost(raw: number | string) {
|
function parseResearchCost(raw: number | string) {
|
||||||
if (typeof raw === 'number') {
|
if (typeof raw === 'number') {
|
||||||
return {
|
return {
|
||||||
@@ -502,7 +520,7 @@ const blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = {
|
|||||||
HIGH_EXPLOSIVE: ShellType.HE,
|
HIGH_EXPLOSIVE: ShellType.HE,
|
||||||
HOLLOW_CHARGE: ShellType.HEAT,
|
HOLLOW_CHARGE: ShellType.HEAT,
|
||||||
};
|
};
|
||||||
export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU(R?)))$/;
|
export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU))$/;
|
||||||
const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = {
|
const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = {
|
||||||
chassis: ModuleType.TRACKS,
|
chassis: ModuleType.TRACKS,
|
||||||
engine: ModuleType.ENGINE,
|
engine: ModuleType.ENGINE,
|
||||||
@@ -703,6 +721,93 @@ export async function definitions() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const slugRequesters = new Map<string, { id: number; key: string }[]>();
|
||||||
|
const idToNation: Record<number, string> = {};
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
nations.map(async (nation) => {
|
||||||
|
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const tankKey in tankList.root) {
|
||||||
|
if (botPattern.test(tankKey)) continue;
|
||||||
|
|
||||||
|
const tank = tankList.root[tankKey];
|
||||||
|
const tankId = toUniqueId(nation, tank.id);
|
||||||
|
const name = (
|
||||||
|
(tank.shortUserString
|
||||||
|
? getString(tank.shortUserString)
|
||||||
|
: undefined) ?? getString(tank.userString)
|
||||||
|
).locales.en;
|
||||||
|
|
||||||
|
let slug = deburr(name).toLowerCase();
|
||||||
|
slug = slug.replaceAll(nonAlphanumericRegex, '-');
|
||||||
|
slug = slug.replaceAll(multipleDashesRegex, '-');
|
||||||
|
slug = slug.replaceAll(trailingDashRegex, '');
|
||||||
|
slug = slug.replaceAll(leadingDashRegex, '');
|
||||||
|
|
||||||
|
idToNation[tankId] = nation;
|
||||||
|
|
||||||
|
if (slugRequesters.has(slug)) {
|
||||||
|
slugRequesters.get(slug)!.push({ id: tankId, key: tankKey });
|
||||||
|
} else {
|
||||||
|
slugRequesters.set(slug, [{ id: tankId, key: tankKey }]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const slugs = new Map<number, string>();
|
||||||
|
|
||||||
|
slugRequesters.forEach((requesters, slug) => {
|
||||||
|
if (requesters.length === 1) {
|
||||||
|
slugs.set(requesters[0].id, slug);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(
|
||||||
|
`Multiple tanks share slug ${slug}: ${requesters.map(({ key }) => key).join(', ')}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (requesters.length !== 2) {
|
||||||
|
throw new Error('Unresolvable number of duplicates :(');
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonCanonical = requesters.find(({ key }) => key.endsWith('TUR'));
|
||||||
|
|
||||||
|
if (nonCanonical === undefined) {
|
||||||
|
console.log('Using nations to discriminate');
|
||||||
|
// both are non-tutorial tanks, will have to discriminate using nation
|
||||||
|
|
||||||
|
requesters.forEach(({ id, key }) => {
|
||||||
|
const nation = idToNation[id];
|
||||||
|
const discriminator =
|
||||||
|
nationSlugDiscriminators[
|
||||||
|
nation as keyof typeof nationSlugDiscriminators
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log(`Solution: ${key} -> ${slug}-${discriminator}`);
|
||||||
|
slugs.set(id, `${slug}-${discriminator}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('Using tutorial bot suffix to discriminate');
|
||||||
|
|
||||||
|
const canonical = requesters.find(({ key }) => !key.endsWith('TUR'));
|
||||||
|
|
||||||
|
if (canonical === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
'Two tutorial bots share the same slug? The world is truly broken.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Solution: ${canonical.key} -> ${slug}`);
|
||||||
|
slugs.set(canonical.id, slug);
|
||||||
|
console.log(`Solution: ${nonCanonical.key} -> ${slug}-tur`);
|
||||||
|
slugs.set(nonCanonical.id, `${slug}-tur`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
nations.map(async (nation) => {
|
nations.map(async (nation) => {
|
||||||
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
@@ -787,6 +892,13 @@ export async function definitions() {
|
|||||||
const hullArmor: Armor = { spaced: [], thickness: {} };
|
const hullArmor: Armor = { spaced: [], thickness: {} };
|
||||||
const equipment = tankDefinition.root.optDevicePreset;
|
const equipment = tankDefinition.root.optDevicePreset;
|
||||||
tankStringIdMap[`${nation}:${tankKey}`] = tankId;
|
tankStringIdMap[`${nation}:${tankKey}`] = tankId;
|
||||||
|
const slug = slugs.get(tankId);
|
||||||
|
|
||||||
|
if (slug === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find slug for ${nation}/${tankKey} (${tankId})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (tank.sellPrice) {
|
if (tank.sellPrice) {
|
||||||
tankPrice = {
|
tankPrice = {
|
||||||
@@ -903,7 +1015,7 @@ export async function definitions() {
|
|||||||
(tank.shortUserString
|
(tank.shortUserString
|
||||||
? getString(tank.shortUserString)
|
? getString(tank.shortUserString)
|
||||||
: undefined) ?? getString(tank.userString),
|
: undefined) ?? getString(tank.userString),
|
||||||
name_full: getString(tank.userString),
|
slug,
|
||||||
nation,
|
nation,
|
||||||
type: tankTags.includes('collectible')
|
type: tankTags.includes('collectible')
|
||||||
? TankType.COLLECTOR
|
? TankType.COLLECTOR
|
||||||
@@ -939,13 +1051,6 @@ export async function definitions() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
tankDefinitions.tanks[tankId].name ===
|
|
||||||
tankDefinitions.tanks[tankId].name_full
|
|
||||||
) {
|
|
||||||
delete tankDefinitions.tanks[tankId].name_full;
|
|
||||||
}
|
|
||||||
|
|
||||||
modelDefinitions.models[tankId] = {
|
modelDefinitions.models[tankId] = {
|
||||||
armor: hullArmor,
|
armor: hullArmor,
|
||||||
turret_origin: vector3TupleToBlitzkit(turretOrigin),
|
turret_origin: vector3TupleToBlitzkit(turretOrigin),
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { Avatar, encodePBBuffer, fetchGlossary, Gallery } from '@blitzkit/core';
|
||||||
|
import locales from '@blitzkit/i18n/locales.json';
|
||||||
|
import { extname } from 'path';
|
||||||
|
import ProgressBar from 'progress';
|
||||||
|
import { commitAssets } from '../core/github/commitAssets';
|
||||||
|
import { FileChange } from '../core/github/commitMultipleFiles';
|
||||||
|
|
||||||
|
export async function glossary() {
|
||||||
|
console.log('Building glossary...');
|
||||||
|
|
||||||
|
const avatars: Record<string, { avatar: Avatar; url: string }> = {};
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
locales.supported.map(async (supported) => {
|
||||||
|
const glossary = await fetchGlossary(supported.blitz ?? supported.locale);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Found ${Object.keys(glossary).length} things for ${supported.locale}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const key in glossary) {
|
||||||
|
const glossaryEntry = glossary[key];
|
||||||
|
|
||||||
|
if (
|
||||||
|
!key.startsWith('avatar') ||
|
||||||
|
key.endsWith('_part') ||
|
||||||
|
glossaryEntry.image_url === null
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key in avatars) {
|
||||||
|
avatars[key].avatar.name.locales[supported.locale] =
|
||||||
|
glossaryEntry.title;
|
||||||
|
} else {
|
||||||
|
const extension = extname(glossaryEntry.image_url);
|
||||||
|
|
||||||
|
avatars[key] = {
|
||||||
|
url: glossaryEntry.image_url,
|
||||||
|
avatar: {
|
||||||
|
id: key,
|
||||||
|
name: { locales: { [supported.locale]: glossaryEntry.title } },
|
||||||
|
extension,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const changes: FileChange[] = [];
|
||||||
|
const gallery: Gallery = { avatars: [] };
|
||||||
|
const totalCount = Object.keys(avatars).length;
|
||||||
|
|
||||||
|
const bar = new ProgressBar(
|
||||||
|
`Fetching avatars of ${totalCount} things :bar`,
|
||||||
|
totalCount,
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(avatars).map(async ([key, { url, avatar }]) => {
|
||||||
|
if (!(locales.default in avatars[key].avatar.name.locales)) {
|
||||||
|
console.warn(`Avatar ${key} has no ${locales.default} name`);
|
||||||
|
bar.tick();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gallery.avatars.push(avatar);
|
||||||
|
|
||||||
|
const content = await fetch(url).then((response) =>
|
||||||
|
response.arrayBuffer(),
|
||||||
|
);
|
||||||
|
|
||||||
|
changes.push({
|
||||||
|
path: `gallery/avatars/${key}${avatar.extension}`,
|
||||||
|
content: Buffer.from(content),
|
||||||
|
});
|
||||||
|
|
||||||
|
bar.tick();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
changes.push({
|
||||||
|
path: 'definitions/gallery.pb',
|
||||||
|
content: encodePBBuffer(Gallery, gallery),
|
||||||
|
});
|
||||||
|
|
||||||
|
await commitAssets('glossary', changes);
|
||||||
|
}
|
||||||
@@ -18,7 +18,10 @@ import { definitions } from './definitions';
|
|||||||
import { equipmentIcons } from './equipmentIcons';
|
import { equipmentIcons } from './equipmentIcons';
|
||||||
import { flags } from './flags';
|
import { flags } from './flags';
|
||||||
import { gameModeBanners } from './gameModeBanners';
|
import { gameModeBanners } from './gameModeBanners';
|
||||||
|
import { glossary } from './glossary';
|
||||||
|
import { migration } from './migration';
|
||||||
import { moduleIcons } from './moduleIcons';
|
import { moduleIcons } from './moduleIcons';
|
||||||
|
import { popularTanks } from './popularTanks';
|
||||||
import { shellIcons } from './shellIcons';
|
import { shellIcons } from './shellIcons';
|
||||||
import { skillIcons } from './skillIcons';
|
import { skillIcons } from './skillIcons';
|
||||||
import { tankArmors } from './tankArmors';
|
import { tankArmors } from './tankArmors';
|
||||||
@@ -28,6 +31,7 @@ import { tankModels } from './tankModels';
|
|||||||
if (!targets && !allTargets) throw new Error('No target(s) specified');
|
if (!targets && !allTargets) throw new Error('No target(s) specified');
|
||||||
|
|
||||||
const methods = [
|
const methods = [
|
||||||
|
migration,
|
||||||
definitions,
|
definitions,
|
||||||
tankModels,
|
tankModels,
|
||||||
tankArmors,
|
tankArmors,
|
||||||
@@ -43,6 +47,8 @@ const methods = [
|
|||||||
boosterIcons,
|
boosterIcons,
|
||||||
tankIcons,
|
tankIcons,
|
||||||
blitzkitTankIcons,
|
blitzkitTankIcons,
|
||||||
|
popularTanks,
|
||||||
|
glossary,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const method of methods) {
|
for (const method of methods) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { toUniqueId } from '@blitzkit/core';
|
||||||
|
import { readdir } from 'fs/promises';
|
||||||
|
import { readXMLDVPL } from '../core/blitz/readXMLDVPL';
|
||||||
|
import { commitAssets } from '../core/github/commitAssets';
|
||||||
|
import { DATA } from './constants';
|
||||||
|
import { VehicleDefinitionList } from './definitions';
|
||||||
|
|
||||||
|
export async function migration() {
|
||||||
|
console.log('Building migration map...');
|
||||||
|
|
||||||
|
const map: Record<string, number> = {};
|
||||||
|
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
||||||
|
(nations) => nations.filter((nation) => nation !== 'common'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
nations.map(async (nation) => {
|
||||||
|
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const tankKey in tankList.root) {
|
||||||
|
const tank = tankList.root[tankKey];
|
||||||
|
const tankId = toUniqueId(nation, tank.id);
|
||||||
|
const davaKey = `${nation}:${tankKey}`;
|
||||||
|
|
||||||
|
map[davaKey] = tankId;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await commitAssets('migration', [
|
||||||
|
{
|
||||||
|
path: 'definitions/migration.json',
|
||||||
|
content: Buffer.from(JSON.stringify(map)),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import {
|
||||||
|
assertSecret,
|
||||||
|
encodePBBuffer,
|
||||||
|
fetchTankDefinitions,
|
||||||
|
PopularTanks,
|
||||||
|
} from '@blitzkit/core';
|
||||||
|
import { google } from 'googleapis';
|
||||||
|
import { commitAssets } from '../core/github/commitAssets';
|
||||||
|
|
||||||
|
export async function popularTanks() {
|
||||||
|
const tankDefinitions = await fetchTankDefinitions();
|
||||||
|
|
||||||
|
const auth = await google.auth.getClient({
|
||||||
|
keyFile: import.meta.env.GOOGLE_APPLICATION_CREDENTIALS,
|
||||||
|
scopes: ['https://www.googleapis.com/auth/analytics.readonly'],
|
||||||
|
});
|
||||||
|
const analytics = google.analyticsdata({ version: 'v1beta', auth });
|
||||||
|
const report = await analytics.properties.runReport({
|
||||||
|
property: `properties/${assertSecret(import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID)}`,
|
||||||
|
requestBody: {
|
||||||
|
dateRanges: [{ startDate: '7daysAgo', endDate: 'today' }],
|
||||||
|
dimensions: [{ name: 'pagePath' }],
|
||||||
|
metrics: [{ name: 'screenPageViews' }],
|
||||||
|
orderBys: [{ metric: { metricName: 'screenPageViews' }, desc: true }],
|
||||||
|
dimensionFilter: {
|
||||||
|
filter: {
|
||||||
|
fieldName: 'pagePath',
|
||||||
|
stringFilter: {
|
||||||
|
matchType: 'BEGINS_WITH',
|
||||||
|
value: '/tanks/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!report.data.rows) {
|
||||||
|
throw new Error('No rows in report');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tanks = report.data.rows
|
||||||
|
.filter(
|
||||||
|
(row) =>
|
||||||
|
row.dimensionValues &&
|
||||||
|
row.dimensionValues[0].value &&
|
||||||
|
row.dimensionValues[0].value !== '/tanks/' &&
|
||||||
|
row.metricValues &&
|
||||||
|
row.metricValues[0].value,
|
||||||
|
)
|
||||||
|
.map((row) => ({
|
||||||
|
id: Number(
|
||||||
|
row.dimensionValues![0].value!.match(
|
||||||
|
/\/tools\/tankopedia\/(\d+)\/?/,
|
||||||
|
)?.[1],
|
||||||
|
),
|
||||||
|
views: Number(row.metricValues![0].value!),
|
||||||
|
}))
|
||||||
|
.filter((row) => {
|
||||||
|
if (!(row.id in tankDefinitions.tanks)) {
|
||||||
|
console.warn('Skipping unknown popular tank', row.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return row.id in tankDefinitions.tanks;
|
||||||
|
})
|
||||||
|
.slice(0, 8)
|
||||||
|
.map(({ id, views }) => ({ id, views }));
|
||||||
|
const popularTanks: PopularTanks = { tanks };
|
||||||
|
|
||||||
|
await commitAssets('popular tanks', [
|
||||||
|
{
|
||||||
|
path: 'definitions/popular-tanks.pb',
|
||||||
|
content: encodePBBuffer(PopularTanks, popularTanks),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -166,28 +166,9 @@ export async function extractModel(data: string, path: string) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'TransformComponent': {
|
case 'TransformComponent': {
|
||||||
const translation = new Vector3();
|
node.setTranslation(component['tc.localTranslation']);
|
||||||
const rotation = new Quaternion();
|
node.setRotation(component['tc.localRotation']);
|
||||||
const scale = new Vector3();
|
node.setScale(component['tc.localScale']);
|
||||||
|
|
||||||
new Matrix4()
|
|
||||||
.multiplyMatrices(
|
|
||||||
new Matrix4().compose(
|
|
||||||
new Vector3().fromArray(component['tc.worldTranslation']),
|
|
||||||
new Quaternion().fromArray(component['tc.worldRotation']),
|
|
||||||
new Vector3().fromArray(component['tc.worldScale']),
|
|
||||||
),
|
|
||||||
new Matrix4().compose(
|
|
||||||
new Vector3().fromArray(component['tc.localTranslation']),
|
|
||||||
new Quaternion().fromArray(component['tc.localRotation']),
|
|
||||||
new Vector3().fromArray(component['tc.localScale']),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.decompose(translation, rotation, scale);
|
|
||||||
|
|
||||||
node.setTranslation(translation.toArray());
|
|
||||||
node.setRotation(rotation.toArray() as Vector4Tuple);
|
|
||||||
node.setScale(scale.toArray());
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { assertSecret } from '@blitzkit/core';
|
import { assertSecret } from '@blitzkit/core';
|
||||||
|
import { chunk } from 'lodash-es';
|
||||||
import { commitMultipleFiles, FileChange } from './commitMultipleFiles';
|
import { commitMultipleFiles, FileChange } from './commitMultipleFiles';
|
||||||
|
|
||||||
export async function commitAssets(message: string, changes: FileChange[]) {
|
export async function commitAssets(message: string, changes: FileChange[]) {
|
||||||
@@ -6,10 +7,19 @@ export async function commitAssets(message: string, changes: FileChange[]) {
|
|||||||
|
|
||||||
if (changes.length === 0) return;
|
if (changes.length === 0) return;
|
||||||
|
|
||||||
|
const chunks = chunk(changes, 64);
|
||||||
|
let chunkIndex = 0;
|
||||||
|
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
console.log(`Committing chunk ${chunkIndex + 1}/${chunks.length}`);
|
||||||
|
|
||||||
await commitMultipleFiles(
|
await commitMultipleFiles(
|
||||||
assertSecret(import.meta.env.PUBLIC_ASSET_REPO),
|
assertSecret(import.meta.env.PUBLIC_ASSET_REPO),
|
||||||
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH),
|
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH),
|
||||||
`${message} - ${new Date().toDateString()}`,
|
`${message} - ${new Date().toDateString()} (${chunkIndex + 1}/${chunks.length})`,
|
||||||
changes,
|
chunk,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
chunkIndex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,8 @@ export interface FileChange {
|
|||||||
content: Buffer;
|
content: Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const heuristicFormats = ['glb', 'webp', 'jpg', 'png'];
|
||||||
|
|
||||||
export async function commitMultipleFiles(
|
export async function commitMultipleFiles(
|
||||||
repoRaw: string,
|
repoRaw: string,
|
||||||
branch: string,
|
branch: string,
|
||||||
@@ -21,29 +23,69 @@ export async function commitMultipleFiles(
|
|||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://raw.githubusercontent.com/${blobPath}`,
|
`https://raw.githubusercontent.com/${blobPath}`,
|
||||||
);
|
);
|
||||||
|
let isNew: boolean;
|
||||||
|
let isDifferent: boolean;
|
||||||
|
let diff: number;
|
||||||
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
console.log(
|
isNew = true;
|
||||||
`🟢 (+${change.content.length.toLocaleString()}B) ${blobPath}`,
|
diff = change.content.length;
|
||||||
);
|
isDifferent = true;
|
||||||
changes.push(change);
|
} else if (
|
||||||
|
response.headers.has('Content-Length') &&
|
||||||
|
parseInt(response.headers.get('Content-Length')!) !==
|
||||||
|
change.content.length
|
||||||
|
) {
|
||||||
|
isNew = false;
|
||||||
|
diff =
|
||||||
|
change.content.length -
|
||||||
|
parseInt(response.headers.get('Content-Length')!);
|
||||||
|
isDifferent = true;
|
||||||
} else if (response.status === 200) {
|
} else if (response.status === 200) {
|
||||||
|
isNew = false;
|
||||||
const buffer = Buffer.from(await response.arrayBuffer());
|
const buffer = Buffer.from(await response.arrayBuffer());
|
||||||
|
|
||||||
if (!buffer.equals(change.content)) {
|
if (buffer.equals(change.content)) {
|
||||||
const diff = change.content.length - buffer.length;
|
diff = 0;
|
||||||
|
isDifferent = false;
|
||||||
console.log(
|
} else {
|
||||||
`🟡 (${diff > 0 ? '+' : ''}${diff.toLocaleString()}B) ${blobPath}`,
|
diff = change.content.length - buffer.length;
|
||||||
);
|
isDifferent = true;
|
||||||
|
|
||||||
changes.push(change);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected status code ${response.status} for ${blobPath}`,
|
`Unexpected status code ${response.status} for ${blobPath}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heuristic: if it's a large blob like a .glb, a diff = 0 might end up
|
||||||
|
* being a false positive with isDifferent = true. So, it's okay to
|
||||||
|
* assume it's unchanged.
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
diff === 0 &&
|
||||||
|
heuristicFormats.some((format) => change.path.endsWith(`.${format}`))
|
||||||
|
) {
|
||||||
|
isDifferent = false;
|
||||||
|
isNew = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
console.log(
|
||||||
|
`🟢 (+${change.content.length.toLocaleString()}B) ${blobPath}`,
|
||||||
|
);
|
||||||
|
changes.push(change);
|
||||||
|
} else if (isDifferent) {
|
||||||
|
console.log(
|
||||||
|
`🟡 (${diff > 0 ? '+' : ''}${diff.toLocaleString()}B) ${blobPath}`,
|
||||||
|
);
|
||||||
|
changes.push(change);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`🔵 (${change.content.length.toLocaleString()}B) ${blobPath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
import partytown from '@astrojs/partytown';
|
||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react';
|
||||||
import sitemap from '@astrojs/sitemap';
|
import sitemap from '@astrojs/sitemap';
|
||||||
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||||
@@ -20,7 +21,7 @@ export default defineConfig({
|
|||||||
outDir: '../../dist/website',
|
outDir: '../../dist/website',
|
||||||
prefetch: true,
|
prefetch: true,
|
||||||
|
|
||||||
integrations: [react(), sitemap()],
|
integrations: [react(), sitemap(), partytown()],
|
||||||
vite: {
|
vite: {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
@@ -10,8 +10,11 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@astrojs/partytown": "^2.1.4",
|
||||||
|
"@astrolib/seo": "^1.0.0-beta.8",
|
||||||
"@blitzkit/core": "workspace:*",
|
"@blitzkit/core": "workspace:*",
|
||||||
"@blitzkit/i18n": "workspace:*",
|
"@blitzkit/i18n": "workspace:*",
|
||||||
|
"@fontsource/inter": "^5.2.5",
|
||||||
"@nanostores/persistent": "^0.10.2",
|
"@nanostores/persistent": "^0.10.2",
|
||||||
"@nanostores/react": "^0.7.3",
|
"@nanostores/react": "^0.7.3",
|
||||||
"@nivo/bar": "^0.87.0",
|
"@nivo/bar": "^0.87.0",
|
||||||
@@ -23,11 +26,11 @@
|
|||||||
"@react-three/fiber": "^8.17.10",
|
"@react-three/fiber": "^8.17.10",
|
||||||
"compare-versions": "^6.1.1",
|
"compare-versions": "^6.1.1",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"googleapis": "^144.0.0",
|
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"markdown-to-jsx": "^7.5.0",
|
"markdown-to-jsx": "^7.5.0",
|
||||||
"nanostores": "^0.11.3",
|
"nanostores": "^0.11.3",
|
||||||
|
"quicklime": "^1.2.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-device-detect": "^2.2.3",
|
"react-device-detect": "^2.2.3",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -40,10 +43,13 @@
|
|||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@astrojs/react": "^3.6.2",
|
"@astrojs/react": "^3.6.2",
|
||||||
"@astrojs/sitemap": "^3.2.1",
|
"@astrojs/sitemap": "^3.2.1",
|
||||||
|
"@playform/compress": "^0.1.9",
|
||||||
|
"@playform/inline": "^0.1.2",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@types/three": "^0.160.0",
|
"@types/three": "^0.160.0",
|
||||||
"astro": "^4.16.7"
|
"astro": "^4.16.7",
|
||||||
|
"astro-compressor": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
After Width: | Height: | Size: 490 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 365 KiB |
|
After Width: | Height: | Size: 771 KiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 379 KiB |
|
After Width: | Height: | Size: 371 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 397 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 443 KiB |
|
After Width: | Height: | Size: 330 KiB |