mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-10 11:16:10 +01:00
Compare commits
67
Commits
e4429f15bd
...
v1.29.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eed9d23646 | ||
|
|
2101dba7c6 | ||
|
|
1b398a5f66 | ||
|
|
d8f9a2633d | ||
|
|
2e79ccf7eb | ||
|
|
be778acf39 | ||
|
|
8d10d7cffa | ||
|
|
5b9041a972 | ||
|
|
1e60b1f61f | ||
|
|
13a636c023 | ||
|
|
8bc0693f3b | ||
|
|
9b842c501c | ||
|
|
8aadda4fb8 | ||
|
|
58c70f98e1 | ||
|
|
54b73d11ce | ||
|
|
d1ac5644a4 | ||
|
|
ae35c1516c | ||
|
|
fd364d434f | ||
|
|
fd37f72f8c | ||
|
|
1427f61c52 | ||
|
|
cd4fa366a0 | ||
|
|
344e7caa81 | ||
|
|
fe7b2aea02 | ||
|
|
fced1e96ef | ||
|
|
778efb2372 | ||
|
|
b67fd5b58c | ||
|
|
2559782a28 | ||
|
|
efb479fece | ||
|
|
fb829d4b81 | ||
|
|
91f4f2edcf | ||
|
|
a692d1ee0b | ||
|
|
31d2ab4746 | ||
|
|
e4e2c30748 | ||
|
|
d633874d90 | ||
|
|
9cb1d0cb3f | ||
|
|
a0bccc7973 | ||
|
|
3efe807e35 | ||
|
|
1cc15f64b0 | ||
|
|
f70864a0db | ||
|
|
5de40b220f | ||
|
|
bd19c9aac9 | ||
|
|
6bc5b7dcb6 | ||
|
|
00795eecdd | ||
|
|
f4e7b2ed98 | ||
|
|
81612793e3 | ||
|
|
d3bdbc39d2 | ||
|
|
7cb67bd44b | ||
|
|
9df09c9a53 | ||
|
|
a9f1d9ebec | ||
|
|
1b834eb848 | ||
|
|
c5518ec9bc | ||
|
|
8c705d7e2e | ||
|
|
efe9f91a2d | ||
|
|
e84bf6b31c | ||
|
|
46234b6cbb | ||
|
|
0bda743a91 | ||
|
|
b19866a1f5 | ||
|
|
9ca329e53f | ||
|
|
47b1eb9ab0 | ||
|
|
6c8fbf7245 | ||
|
|
2ea22512d4 | ||
|
|
697d1fa1b4 | ||
|
|
2c03b530d2 | ||
|
|
182471c4d6 | ||
|
|
aecc2adb07 | ||
|
|
f8ee2b64a6 | ||
|
|
7c4e28bd09 |
No files matched your search
@@ -55,117 +55,13 @@ env:
|
|||||||
PUBLIC_ASSET_FAST_REPO: assets
|
PUBLIC_ASSET_FAST_REPO: assets
|
||||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
|
STEAM_USERNAME: ${{ secrets.STEAM_USERNAME }}
|
||||||
|
STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
download_client:
|
|
||||||
name: Download client
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- if: github.event.inputs.archive_url != ''
|
|
||||||
uses: easimon/maximize-build-space@master
|
|
||||||
with:
|
|
||||||
root-reserve-mb: 512
|
|
||||||
swap-size-mb: 1024
|
|
||||||
remove-dotnet: "true"
|
|
||||||
remove-haskell: "true"
|
|
||||||
remove-android: "true"
|
|
||||||
remove-codeql: "true"
|
|
||||||
remove-docker-images: "true"
|
|
||||||
|
|
||||||
- if: github.event.inputs.archive_url != '' && endsWith(github.event.inputs.archive_url, '.wgpkg')
|
|
||||||
run: |
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y p7zip-full
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
if: github.event.inputs.archive_url == ''
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
|
||||||
|
|
||||||
- uses: arduino/setup-protoc@v3
|
|
||||||
if: github.event.inputs.archive_url == ''
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.GH_TOKEN }}
|
|
||||||
|
|
||||||
- uses: oven-sh/setup-bun@v1
|
|
||||||
if: github.event.inputs.archive_url == ''
|
|
||||||
|
|
||||||
- run: bun run setup
|
|
||||||
if: github.event.inputs.archive_url == ''
|
|
||||||
|
|
||||||
- name: (Steam) Setup DepotDownloader
|
|
||||||
if: github.event.inputs.archive_url == ''
|
|
||||||
run: bun run setup:depotdownloader
|
|
||||||
|
|
||||||
- name: (Steam) Download client
|
|
||||||
if: github.event.inputs.archive_url == ''
|
|
||||||
run: |
|
|
||||||
chmod +x ./temp/depotDownloader/DepotDownloader
|
|
||||||
./temp/depotDownloader/DepotDownloader -q -os windows -app 444200 -username ${{ secrets.DEPOT_DOWNLOADER_USERNAME }} -password "${{ secrets.DEPOT_DOWNLOADER_PASSWORD }}"
|
|
||||||
mkdir -p temp/game
|
|
||||||
mv depots/444202/*/* temp/game
|
|
||||||
rm -rf depots
|
|
||||||
|
|
||||||
- name: (Steam) Apply patches
|
|
||||||
if: github.event.inputs.archive_url == ''
|
|
||||||
run: bun setup:patches --manual
|
|
||||||
|
|
||||||
- name: (ZIP) Download client
|
|
||||||
if: github.event.inputs.archive_url != '' && endsWith(github.event.inputs.archive_url, '.zip')
|
|
||||||
env:
|
|
||||||
ARCHIVE_URL: ${{ github.event.inputs.archive_url }}
|
|
||||||
run: |
|
|
||||||
mkdir -p temp/game
|
|
||||||
wget -q -O temp/archive.zip $ARCHIVE_URL
|
|
||||||
unzip -q temp/archive.zip -d temp/archive
|
|
||||||
rm temp/archive.zip
|
|
||||||
mv temp/archive/app/* temp/game
|
|
||||||
|
|
||||||
- name: (WGPKG) Download client
|
|
||||||
if: github.event.inputs.archive_url != '' && endsWith(github.event.inputs.archive_url, '.wgpkg')
|
|
||||||
env:
|
|
||||||
ARCHIVE_URL: ${{ github.event.inputs.archive_url }}
|
|
||||||
run: |
|
|
||||||
mkdir -p temp/archives
|
|
||||||
mkdir -p temp/game
|
|
||||||
|
|
||||||
i=1
|
|
||||||
urls=()
|
|
||||||
|
|
||||||
while :; do
|
|
||||||
part=$(printf "%03d" $i)
|
|
||||||
url="${ARCHIVE_URL}.${part}"
|
|
||||||
if wget --spider -q "$url"; then
|
|
||||||
urls+=("$url")
|
|
||||||
((i++))
|
|
||||||
else
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
for url in "${urls[@]}"; do
|
|
||||||
dest="temp/archives/$(basename "$url")"
|
|
||||||
wget -q -O "$dest" "$url" &
|
|
||||||
done
|
|
||||||
wait
|
|
||||||
|
|
||||||
first=$(ls temp/archives/*.001 | head -n 1)
|
|
||||||
7z x "$first" -otemp/game
|
|
||||||
|
|
||||||
rm -rf temp/archives
|
|
||||||
|
|
||||||
- name: Archive game
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: game
|
|
||||||
path: temp/game
|
|
||||||
|
|
||||||
build_assets:
|
build_assets:
|
||||||
name: Build assets
|
name: Build assets
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: download_client
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -178,12 +74,6 @@ jobs:
|
|||||||
- uses: oven-sh/setup-bun@v1
|
- uses: oven-sh/setup-bun@v1
|
||||||
- run: bun run setup
|
- run: bun run setup
|
||||||
|
|
||||||
- name: Download game
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: game
|
|
||||||
path: temp/game
|
|
||||||
|
|
||||||
- name: AppArmor hack
|
- name: AppArmor hack
|
||||||
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
|
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
|
||||||
|
|
||||||
|
|||||||
Vendored
+3
@@ -11,6 +11,7 @@
|
|||||||
"analyticsreporting",
|
"analyticsreporting",
|
||||||
"APAC",
|
"APAC",
|
||||||
"apcr",
|
"apcr",
|
||||||
|
"appinfo",
|
||||||
"applypatches",
|
"applypatches",
|
||||||
"artefacts",
|
"artefacts",
|
||||||
"astrojs",
|
"astrojs",
|
||||||
@@ -111,6 +112,7 @@
|
|||||||
"leaderboards",
|
"leaderboards",
|
||||||
"lerp",
|
"lerp",
|
||||||
"LINEARSIZE",
|
"LINEARSIZE",
|
||||||
|
"linktarget",
|
||||||
"lmao",
|
"lmao",
|
||||||
"loadouts",
|
"loadouts",
|
||||||
"loddist",
|
"loddist",
|
||||||
@@ -171,6 +173,7 @@
|
|||||||
"ratingleaderboard",
|
"ratingleaderboard",
|
||||||
"raycaster",
|
"raycaster",
|
||||||
"Redirector",
|
"Redirector",
|
||||||
|
"Relogin",
|
||||||
"researchable",
|
"researchable",
|
||||||
"Resvg",
|
"Resvg",
|
||||||
"rizz",
|
"rizz",
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"dotenv": "^17.2.2",
|
"dotenv": "^17.2.2",
|
||||||
|
"fast-xml-parser": "^5.3.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"pickleparser": "^0.2.1",
|
"pickleparser": "^0.2.1",
|
||||||
@@ -104,6 +105,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/lz4js": "^0.2.1",
|
"@types/lz4js": "^0.2.1",
|
||||||
"@types/progress": "^2.0.7",
|
"@types/progress": "^2.0.7",
|
||||||
|
"@types/steam-user": "^5.1.0",
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"csv-parse": "^6.1.0",
|
"csv-parse": "^6.1.0",
|
||||||
@@ -117,6 +119,7 @@
|
|||||||
"progress": "^2.0.3",
|
"progress": "^2.0.3",
|
||||||
"puppeteer": "^24.22.3",
|
"puppeteer": "^24.22.3",
|
||||||
"sharp": "^0.34.4",
|
"sharp": "^0.34.4",
|
||||||
|
"steam-user": "^5.2.3",
|
||||||
"three": "^0.180.0",
|
"three": "^0.180.0",
|
||||||
"ts-proto": "^2.7.7",
|
"ts-proto": "^2.7.7",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
@@ -382,6 +385,10 @@
|
|||||||
|
|
||||||
"@babel/types": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
"@babel/types": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
||||||
|
|
||||||
|
"@bbob/parser": ["@bbob/[email protected]", "", { "dependencies": { "@bbob/plugin-helper": "^2.9.0" } }, "sha512-tldSYsMoEclke/B1nqL7+HbYMWZHTKvpbEHRSHuY+sZvS1o7Jpdfjb+KPpwP9wLI3p3r7GPv69/wGy+Xibs9yA=="],
|
||||||
|
|
||||||
|
"@bbob/plugin-helper": ["@bbob/[email protected]", "", {}, "sha512-idpUcNQ2co6T1oU/7/DG/ZRfipSSkTn9Ozw9f5vaXH7nzV3qhqZnhFVlHTzGGnRlzKlBwWOBzOdWi4Zeqg1c5A=="],
|
||||||
|
|
||||||
"@blitzkit/app": ["@blitzkit/app@workspace:packages/app"],
|
"@blitzkit/app": ["@blitzkit/app@workspace:packages/app"],
|
||||||
|
|
||||||
"@blitzkit/bot": ["@blitzkit/bot@workspace:packages/bot"],
|
"@blitzkit/bot": ["@blitzkit/bot@workspace:packages/bot"],
|
||||||
@@ -414,6 +421,12 @@
|
|||||||
|
|
||||||
"@discordjs/ws": ["@discordjs/[email protected]", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
"@discordjs/ws": ["@discordjs/[email protected]", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
||||||
|
|
||||||
|
"@doctormckay/stdlib": ["@doctormckay/[email protected]", "", { "dependencies": { "psl": "^1.9.0" } }, "sha512-bwy+gPn6oa2KTpfxJKX3leZoV/wHDVtO0/gq3usPvqPswG//dcf3jVB8LcbRRsKO3BXCt5DqctOQ+Xb07ivxnw=="],
|
||||||
|
|
||||||
|
"@doctormckay/steam-crypto": ["@doctormckay/[email protected]", "", {}, "sha512-lsxgLw640gEdZBOXpVIcYWcYD+V+QbtEsMPzRvjmjz2XXKc7QeEMyHL07yOFRmay+cUwO4ObKTJO0dSInEuq5g=="],
|
||||||
|
|
||||||
|
"@doctormckay/user-agents": ["@doctormckay/[email protected]", "", {}, "sha512-F+sL1YmebZTY2CnjoR9BXFEULpq7y8dxyLx48LZVa0BSDseXdLG/DtPISfM1iNv1XKCeiBzVNfAT/MOQ69v1Zw=="],
|
||||||
|
|
||||||
"@emmetio/abbreviation": ["@emmetio/[email protected]", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="],
|
"@emmetio/abbreviation": ["@emmetio/[email protected]", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="],
|
||||||
|
|
||||||
"@emmetio/css-abbreviation": ["@emmetio/[email protected]", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="],
|
"@emmetio/css-abbreviation": ["@emmetio/[email protected]", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="],
|
||||||
@@ -914,6 +927,8 @@
|
|||||||
|
|
||||||
"@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
"@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
||||||
|
|
||||||
|
"@types/bytebuffer": ["@types/[email protected]", "", { "dependencies": { "@types/long": "^3.0.0", "@types/node": "*" } }, "sha512-lV4YLiolMdD4upDmr4vnfiwV/FN9Jg33eNTSFMkHqyMKqTIAn5TmFTYsARwXwUXeFU7yzRpECmWV1SOhIvdkRQ=="],
|
||||||
|
|
||||||
"@types/debug": ["@types/[email protected]", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
"@types/debug": ["@types/[email protected]", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||||
|
|
||||||
"@types/decompress": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-9z+8yjKr5Wn73Pt17/ldnmQToaFHZxK0N1GHysuk/JIPT8RIdQeoInM01wWPgypRcvb6VH1drjuFpQ4zmY437g=="],
|
"@types/decompress": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-9z+8yjKr5Wn73Pt17/ldnmQToaFHZxK0N1GHysuk/JIPT8RIdQeoInM01wWPgypRcvb6VH1drjuFpQ4zmY437g=="],
|
||||||
@@ -922,6 +937,8 @@
|
|||||||
|
|
||||||
"@types/estree": ["@types/[email protected]", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/[email protected]", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
|
"@types/file-manager": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-gEq6de+iMbjmkL3Wj183JQ651h9b331wQ3svXp2K6RkgBg+TtiyIZTMols/a3UtXllVV9kSelz/N3Rgh0n6S4g=="],
|
||||||
|
|
||||||
"@types/fontkit": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew=="],
|
"@types/fontkit": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew=="],
|
||||||
|
|
||||||
"@types/hast": ["@types/[email protected]", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
"@types/hast": ["@types/[email protected]", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||||
@@ -930,6 +947,8 @@
|
|||||||
|
|
||||||
"@types/lodash-es": ["@types/[email protected]", "", { "dependencies": { "@types/lodash": "*" } }, "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ=="],
|
"@types/lodash-es": ["@types/[email protected]", "", { "dependencies": { "@types/lodash": "*" } }, "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ=="],
|
||||||
|
|
||||||
|
"@types/long": ["@types/[email protected]", "", {}, "sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA=="],
|
||||||
|
|
||||||
"@types/lz4js": ["@types/[email protected]", "", {}, "sha512-aAnbA4uKPNqZqu0XK1QAwKP0Wskb4Oa7ZFqxW5CMIyGgqYQKFgBxTfK3m3KODXoOLv5t14VregzgrEak13uGQA=="],
|
"@types/lz4js": ["@types/[email protected]", "", {}, "sha512-aAnbA4uKPNqZqu0XK1QAwKP0Wskb4Oa7ZFqxW5CMIyGgqYQKFgBxTfK3m3KODXoOLv5t14VregzgrEak13uGQA=="],
|
||||||
|
|
||||||
"@types/markdown-escape": ["@types/[email protected]", "", {}, "sha512-JIc1+s3y5ujKnt/+N+wq6s/QdL2qZ11fP79MijrVXsAAnzSxCbT2j/3prHRouJdZ2yFLN3vkP0HytfnoCczjOw=="],
|
"@types/markdown-escape": ["@types/[email protected]", "", {}, "sha512-JIc1+s3y5ujKnt/+N+wq6s/QdL2qZ11fP79MijrVXsAAnzSxCbT2j/3prHRouJdZ2yFLN3vkP0HytfnoCczjOw=="],
|
||||||
@@ -960,6 +979,10 @@
|
|||||||
|
|
||||||
"@types/stats.js": ["@types/[email protected]", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
|
"@types/stats.js": ["@types/[email protected]", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
|
||||||
|
|
||||||
|
"@types/steam-user": ["@types/[email protected]", "", { "dependencies": { "@types/bytebuffer": "*", "@types/file-manager": "*", "@types/node": "*", "@types/steamid": "*" } }, "sha512-jMCzwmvBA1DaqLTD9h23LzVmzyHy53cdzpJjBSYDI6qT/1LqEUM9xRt4Vb0Y9kOIr+BZckCewDw8Bk6n8e3BTA=="],
|
||||||
|
|
||||||
|
"@types/steamid": ["@types/[email protected]", "", {}, "sha512-LC0oaiNq3gqMI18MV935CnlyHmDooKRszK56jHv0+b7EbJwf5k++YsD52zWeJUD8nuYqP9LpIgpXGiYBOxpcYQ=="],
|
||||||
|
|
||||||
"@types/three": ["@types/[email protected]", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": "*", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" } }, "sha512-ykFtgCqNnY0IPvDro7h+9ZeLY+qjgUWv+qEvUt84grhenO60Hqd4hScHE7VTB9nOQ/3QM8lkbNE+4vKjEpUxKg=="],
|
"@types/three": ["@types/[email protected]", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": "*", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" } }, "sha512-ykFtgCqNnY0IPvDro7h+9ZeLY+qjgUWv+qEvUt84grhenO60Hqd4hScHE7VTB9nOQ/3QM8lkbNE+4vKjEpUxKg=="],
|
||||||
|
|
||||||
"@types/trusted-types": ["@types/[email protected]", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
"@types/trusted-types": ["@types/[email protected]", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||||
@@ -1006,7 +1029,7 @@
|
|||||||
|
|
||||||
"adm-zip": ["[email protected]", "", {}, "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ=="],
|
"adm-zip": ["[email protected]", "", {}, "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ=="],
|
||||||
|
|
||||||
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
"agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||||
|
|
||||||
"ajv": ["[email protected]", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
"ajv": ["[email protected]", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||||
|
|
||||||
@@ -1086,6 +1109,8 @@
|
|||||||
|
|
||||||
"bignumber.js": ["[email protected]", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
"bignumber.js": ["[email protected]", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||||
|
|
||||||
|
"binarykvparser": ["[email protected]", "", { "dependencies": { "long": "^3.2.0" } }, "sha512-B1N5ZxC8I9oSLis7Rg36DxsZJoIikUGU2XwpI0FKFCaPIJIEYi0B9UeIk3QU006axzq0TI9KC3iXelfGGgnWew=="],
|
||||||
|
|
||||||
"bl": ["[email protected]", "", { "dependencies": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" } }, "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww=="],
|
"bl": ["[email protected]", "", { "dependencies": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" } }, "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww=="],
|
||||||
|
|
||||||
"boolbase": ["[email protected]", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
"boolbase": ["[email protected]", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||||
@@ -1118,6 +1143,8 @@
|
|||||||
|
|
||||||
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||||
|
|
||||||
|
"bytebuffer": ["[email protected]", "", { "dependencies": { "long": "~3" } }, "sha512-IuzSdmADppkZ6DlpycMkm8l9zeEq16fWtLvunEwFiYciR/BHo4E8/xs5piFquG+Za8OWmMqHF8zuRviz2LHvRQ=="],
|
||||||
|
|
||||||
"c12": ["[email protected]", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="],
|
"c12": ["[email protected]", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="],
|
||||||
|
|
||||||
"call-bind": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
"call-bind": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
||||||
@@ -1238,6 +1265,8 @@
|
|||||||
|
|
||||||
"csv-parse": ["[email protected]", "", {}, "sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw=="],
|
"csv-parse": ["[email protected]", "", {}, "sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw=="],
|
||||||
|
|
||||||
|
"cuint": ["[email protected]", "", {}, "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw=="],
|
||||||
|
|
||||||
"cwise-compiler": ["[email protected]", "", { "dependencies": { "uniq": "^1.0.0" } }, "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ=="],
|
"cwise-compiler": ["[email protected]", "", { "dependencies": { "uniq": "^1.0.0" } }, "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ=="],
|
||||||
|
|
||||||
"data-uri-to-buffer": ["[email protected]", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
"data-uri-to-buffer": ["[email protected]", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||||
@@ -1410,7 +1439,7 @@
|
|||||||
|
|
||||||
"fast-uri": ["[email protected]", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
"fast-uri": ["[email protected]", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||||
|
|
||||||
"fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
"fast-xml-parser": ["fast-xml-parser@5.3.2", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-n8v8b6p4Z1sMgqRmqLJm3awW4NX7NkaKPfb3uJIBTSH7Pdvufi3PQ3/lJLQrvxcMYl7JI2jnDO90siPEpD8JBA=="],
|
||||||
|
|
||||||
"fastq": ["[email protected]", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
"fastq": ["[email protected]", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||||
|
|
||||||
@@ -1424,6 +1453,8 @@
|
|||||||
|
|
||||||
"fflate": ["[email protected]", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
"fflate": ["[email protected]", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||||
|
|
||||||
|
"file-manager": ["[email protected]", "", { "dependencies": { "@doctormckay/stdlib": "^1.14.1" } }, "sha512-y/K/1OCha04OXOxzo3cXJYtIzEk/CUMBb7Okipxueu0u+xCiuoocbwPyh1smUBasOobo4GAYmjgjD9Vh5zI51w=="],
|
||||||
|
|
||||||
"file-type": ["[email protected]", "", {}, "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ=="],
|
"file-type": ["[email protected]", "", {}, "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ=="],
|
||||||
|
|
||||||
"filelist": ["[email protected]", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="],
|
"filelist": ["[email protected]", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="],
|
||||||
@@ -1702,6 +1733,8 @@
|
|||||||
|
|
||||||
"ktx-parse": ["[email protected]", "", {}, "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ=="],
|
"ktx-parse": ["[email protected]", "", {}, "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ=="],
|
||||||
|
|
||||||
|
"kvparser": ["[email protected]", "", {}, "sha512-5P/5qpTAHjVYWqcI55B3yQwSY2FUrYYrJj5i65V1Wmg7/4W4OnBcaodaEvLyVuugeOnS+BAaKm9LbPazGJcRyA=="],
|
||||||
|
|
||||||
"leven": ["[email protected]", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
|
"leven": ["[email protected]", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
|
||||||
|
|
||||||
"lie": ["[email protected]", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
"lie": ["[email protected]", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
||||||
@@ -1728,6 +1761,8 @@
|
|||||||
|
|
||||||
"lz4js": ["[email protected]", "", {}, "sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg=="],
|
"lz4js": ["[email protected]", "", {}, "sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg=="],
|
||||||
|
|
||||||
|
"lzma": ["[email protected]", "", { "bin": { "lzma.js": "bin/lzma.js" } }, "sha512-DcfiawQ1avYbW+hsILhF38IKAlnguc/fjHrychs9hdxe4qLykvhT5VTGNs5YRWgaNePh7NTxGD4uv4gKsRomCQ=="],
|
||||||
|
|
||||||
"maath": ["[email protected]", "", { "peerDependencies": { "@types/three": ">=0.134.0", "three": ">=0.134.0" } }, "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g=="],
|
"maath": ["[email protected]", "", { "peerDependencies": { "@types/three": ">=0.134.0", "three": ">=0.134.0" } }, "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g=="],
|
||||||
|
|
||||||
"magic-bytes.js": ["[email protected]", "", {}, "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="],
|
"magic-bytes.js": ["[email protected]", "", {}, "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="],
|
||||||
@@ -1868,6 +1903,8 @@
|
|||||||
|
|
||||||
"nlcst-to-string": ["[email protected]", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
|
"nlcst-to-string": ["[email protected]", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
|
||||||
|
|
||||||
|
"node-bignumber": ["[email protected]", "", {}, "sha512-VoTZHmdFQpZH1+q1dz2qcHNCwTWsJg2T3PYwlAyDNFOfVhSYUKQBLFcCpCud+wJBGgCttGavZILaIggDIKqEQQ=="],
|
||||||
|
|
||||||
"node-domexception": ["[email protected]", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
"node-domexception": ["[email protected]", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||||
|
|
||||||
"node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
"node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||||
@@ -1948,6 +1985,8 @@
|
|||||||
|
|
||||||
"perfect-debounce": ["[email protected]", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
"perfect-debounce": ["[email protected]", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
||||||
|
|
||||||
|
"permessage-deflate": ["[email protected]", "", { "dependencies": { "safe-buffer": "*" } }, "sha512-EUNi/RIsyJ1P1u9QHFwMOUWMYetqlE22ZgGbad7YP856WF4BFF0B7DuNy6vEGsgNNud6c/SkdWzkne71hH8MjA=="],
|
||||||
|
|
||||||
"pickleparser": ["[email protected]", "", { "bin": { "pickletojson": "bin/pickletojson.js", "pickleparser": "bin/pickletojson.js" } }, "sha512-kMzY3uFYcR6OjOqr7nV2nkaXaBsUEOafu3zgPxeD6s/2ueMfVQH8lrymcDWBPGx0OkVxGMikxQit6jgByXjwBg=="],
|
"pickleparser": ["[email protected]", "", { "bin": { "pickletojson": "bin/pickletojson.js", "pickleparser": "bin/pickletojson.js" } }, "sha512-kMzY3uFYcR6OjOqr7nV2nkaXaBsUEOafu3zgPxeD6s/2ueMfVQH8lrymcDWBPGx0OkVxGMikxQit6jgByXjwBg=="],
|
||||||
|
|
||||||
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
@@ -1996,6 +2035,8 @@
|
|||||||
|
|
||||||
"proxy-from-env": ["[email protected]", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
"proxy-from-env": ["[email protected]", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||||
|
|
||||||
|
"psl": ["[email protected]", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="],
|
||||||
|
|
||||||
"pump": ["[email protected]", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
|
"pump": ["[email protected]", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
|
||||||
|
|
||||||
"punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
"punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
@@ -2172,7 +2213,7 @@
|
|||||||
|
|
||||||
"socks": ["[email protected]", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
|
"socks": ["[email protected]", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
|
||||||
|
|
||||||
"socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
|
"socks-proxy-agent": ["socks-proxy-agent@7.0.0", "", { "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", "socks": "^2.6.2" } }, "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww=="],
|
||||||
|
|
||||||
"source-map": ["[email protected]", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"source-map": ["[email protected]", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
@@ -2190,6 +2231,16 @@
|
|||||||
|
|
||||||
"statuses": ["[email protected]", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
"statuses": ["[email protected]", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||||
|
|
||||||
|
"steam-appticket": ["[email protected]", "", { "dependencies": { "@doctormckay/stdlib": "^1.6.0", "@doctormckay/steam-crypto": "^1.2.0", "bytebuffer": "^5.0.1", "protobufjs": "^6.8.8", "steamid": "^1.1.0" } }, "sha512-zwDwZALGv3RanE8RHNYcQU3u4Ez23EzMuQ4Lh15uIHddpDh6TI6uFGbC0HNyt6y+UJYSILe77A33VhFZKQiaqQ=="],
|
||||||
|
|
||||||
|
"steam-session": ["[email protected]", "", { "dependencies": { "@doctormckay/stdlib": "^2.9.0", "@doctormckay/user-agents": "^1.0.0", "debug": "^4.3.4", "kvparser": "^1.0.1", "node-bignumber": "^1.2.2", "protobufjs": "^7.1.0", "socks-proxy-agent": "^7.0.0", "steamid": "^2.0.0", "tiny-typed-emitter": "^2.1.0", "websocket13": "^4.0.0" } }, "sha512-MLvg1uMLEOIRHZS5LKruy1w5OqHb8EL7TeMxIY2a4mUcaVOgszz060+jo4c7s3gFeedyuoqGcfwJrR0pLKYzLw=="],
|
||||||
|
|
||||||
|
"steam-totp": ["[email protected]", "", {}, "sha512-bTKlc/NoIUQId+my+O556s55DDsNNXfVIPWFDNVu68beql7AJhV0c+GTjFxfwCDYfdc4NkAme+0WrDdnY2D2VA=="],
|
||||||
|
|
||||||
|
"steam-user": ["[email protected]", "", { "dependencies": { "@bbob/parser": "^2.2.0", "@doctormckay/stdlib": "^2.9.1", "@doctormckay/steam-crypto": "^1.2.0", "adm-zip": "^0.5.10", "binarykvparser": "^2.2.0", "bytebuffer": "^5.0.0", "file-manager": "^2.0.0", "kvparser": "^1.0.1", "lzma": "^2.3.2", "protobufjs": "^7.2.4", "socks-proxy-agent": "^7.0.0", "steam-appticket": "^1.0.1", "steam-session": "^1.8.0", "steam-totp": "^2.0.1", "steamid": "^2.0.0", "websocket13": "^4.0.0", "zstddec": "^0.1.0" } }, "sha512-6Yzaa0l7fcSRaaJzHjfuqettYqAsrW3+1AEu/cj4M/lgRYXa0DfOAjuWj0/wesfkogwuXNE4N/ZMZvNir8TVtw=="],
|
||||||
|
|
||||||
|
"steamid": ["[email protected]", "", {}, "sha512-ndt1cvuuSC+i8fcxVsmeyRlgGsR1QsoAuIXz+eabj8/Y4GIWE2+mgHA7Hys61JDHOxttfWtXHtN2m5TNYTlORg=="],
|
||||||
|
|
||||||
"stop-iteration-iterator": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
"stop-iteration-iterator": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||||
|
|
||||||
"streamx": ["[email protected]", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="],
|
"streamx": ["[email protected]", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="],
|
||||||
@@ -2246,6 +2297,8 @@
|
|||||||
|
|
||||||
"tiny-inflate": ["[email protected]", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
"tiny-inflate": ["[email protected]", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
||||||
|
|
||||||
|
"tiny-typed-emitter": ["[email protected]", "", {}, "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="],
|
||||||
|
|
||||||
"tinyexec": ["[email protected]", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
"tinyexec": ["[email protected]", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
||||||
|
|
||||||
"tinyglobby": ["[email protected]", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
"tinyglobby": ["[email protected]", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||||
@@ -2438,6 +2491,10 @@
|
|||||||
|
|
||||||
"webidl-conversions": ["[email protected]", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
|
"webidl-conversions": ["[email protected]", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="],
|
||||||
|
|
||||||
|
"websocket-extensions": ["[email protected]", "", {}, "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="],
|
||||||
|
|
||||||
|
"websocket13": ["[email protected]", "", { "dependencies": { "@doctormckay/stdlib": "^2.7.1", "bytebuffer": "^5.0.1", "permessage-deflate": "^0.1.7", "tiny-typed-emitter": "^2.1.0", "websocket-extensions": "^0.1.4" } }, "sha512-7+hxkUVTKQlUDTzN2rJI7fJRBXCT6dvRXr1aZflxUZlpNJutHBkKiEIbZOCGs0A1s7vxAmcAXngsNUQMSUTiVQ=="],
|
||||||
|
|
||||||
"whatwg-encoding": ["[email protected]", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
|
"whatwg-encoding": ["[email protected]", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
|
||||||
|
|
||||||
"whatwg-mimetype": ["[email protected]", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
"whatwg-mimetype": ["[email protected]", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
||||||
@@ -2532,6 +2589,8 @@
|
|||||||
|
|
||||||
"zod-to-ts": ["[email protected]", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="],
|
"zod-to-ts": ["[email protected]", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="],
|
||||||
|
|
||||||
|
"zstddec": ["[email protected]", "", {}, "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg=="],
|
||||||
|
|
||||||
"zustand": ["[email protected]", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw=="],
|
"zustand": ["[email protected]", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw=="],
|
||||||
|
|
||||||
"zwitch": ["[email protected]", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
"zwitch": ["[email protected]", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||||
@@ -2554,8 +2613,6 @@
|
|||||||
|
|
||||||
"@blitzkit/bot/prisma": ["[email protected]", "", { "dependencies": { "@prisma/config": "6.17.1", "@prisma/engines": "6.17.1" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-ac6h0sM1Tg3zu8NInY+qhP/S9KhENVaw9n1BrGKQVFu05JT5yT5Qqqmb8tMRIE3ZXvVj4xcRA5yfrsy4X7Yy5g=="],
|
"@blitzkit/bot/prisma": ["[email protected]", "", { "dependencies": { "@prisma/config": "6.17.1", "@prisma/engines": "6.17.1" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-ac6h0sM1Tg3zu8NInY+qhP/S9KhENVaw9n1BrGKQVFu05JT5yT5Qqqmb8tMRIE3ZXvVj4xcRA5yfrsy4X7Yy5g=="],
|
||||||
|
|
||||||
"@blitzkit/scripts/@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
|
|
||||||
|
|
||||||
"@blitzkit/website/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="],
|
"@blitzkit/website/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="],
|
||||||
|
|
||||||
"@discordjs/rest/@discordjs/collection": ["@discordjs/[email protected]", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
"@discordjs/rest/@discordjs/collection": ["@discordjs/[email protected]", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||||
@@ -2594,8 +2651,12 @@
|
|||||||
|
|
||||||
"babel-plugin-polyfill-corejs2/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"babel-plugin-polyfill-corejs2/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
|
"binarykvparser/long": ["[email protected]", "", { "bundled": true }, "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg=="],
|
||||||
|
|
||||||
"boxen/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
"boxen/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||||
|
|
||||||
|
"bytebuffer/long": ["[email protected]", "", {}, "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg=="],
|
||||||
|
|
||||||
"c12/dotenv": ["[email protected]", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
"c12/dotenv": ["[email protected]", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||||
|
|
||||||
"cheerio/undici": ["[email protected]", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="],
|
"cheerio/undici": ["[email protected]", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="],
|
||||||
@@ -2612,6 +2673,8 @@
|
|||||||
|
|
||||||
"favilib/sharp": ["[email protected]", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
"favilib/sharp": ["[email protected]", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
||||||
|
|
||||||
|
"file-manager/@doctormckay/stdlib": ["@doctormckay/[email protected]", "", {}, "sha512-XhuUOzElz6fnNdt70IYNKqhPAEpGaL4JHOhAvklRh0hAhVPW+/wLxaWT3DWUbaG5Dta5YvIp7+cZK3GhIpAuug=="],
|
||||||
|
|
||||||
"filelist/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
"filelist/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
||||||
|
|
||||||
"get-uri/data-uri-to-buffer": ["[email protected]", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="],
|
"get-uri/data-uri-to-buffer": ["[email protected]", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="],
|
||||||
@@ -2622,6 +2685,10 @@
|
|||||||
|
|
||||||
"http-errors/statuses": ["[email protected]", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
"http-errors/statuses": ["[email protected]", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
||||||
|
|
||||||
|
"http-proxy-agent/agent-base": ["[email protected]", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
|
"https-proxy-agent/agent-base": ["[email protected]", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
"its-fine/@types/react-reconciler": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="],
|
"its-fine/@types/react-reconciler": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="],
|
||||||
|
|
||||||
"linebreak/base64-js": ["[email protected]", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="],
|
"linebreak/base64-js": ["[email protected]", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="],
|
||||||
@@ -2630,12 +2697,20 @@
|
|||||||
|
|
||||||
"micromatch/picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"micromatch/picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
|
"pac-proxy-agent/agent-base": ["[email protected]", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
|
"pac-proxy-agent/socks-proxy-agent": ["[email protected]", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
|
||||||
|
|
||||||
"parse5/entities": ["[email protected]", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
"parse5/entities": ["[email protected]", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||||
|
|
||||||
"prompts/kleur": ["[email protected]", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
"prompts/kleur": ["[email protected]", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||||
|
|
||||||
|
"proxy-agent/agent-base": ["[email protected]", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
"proxy-agent/lru-cache": ["[email protected]", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
|
"proxy-agent/lru-cache": ["[email protected]", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
|
||||||
|
|
||||||
|
"proxy-agent/socks-proxy-agent": ["[email protected]", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
|
||||||
|
|
||||||
"react-dom/scheduler": ["[email protected]", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
"react-dom/scheduler": ["[email protected]", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
"react-promise-suspense/fast-deep-equal": ["[email protected]", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="],
|
"react-promise-suspense/fast-deep-equal": ["[email protected]", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="],
|
||||||
@@ -2650,6 +2725,12 @@
|
|||||||
|
|
||||||
"stats-gl/three": ["[email protected]", "", {}, "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ=="],
|
"stats-gl/three": ["[email protected]", "", {}, "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ=="],
|
||||||
|
|
||||||
|
"steam-appticket/@doctormckay/stdlib": ["@doctormckay/[email protected]", "", {}, "sha512-XhuUOzElz6fnNdt70IYNKqhPAEpGaL4JHOhAvklRh0hAhVPW+/wLxaWT3DWUbaG5Dta5YvIp7+cZK3GhIpAuug=="],
|
||||||
|
|
||||||
|
"steam-appticket/protobufjs": ["[email protected]", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.1", "@types/node": ">=13.7.0", "long": "^4.0.0" }, "bin": { "pbjs": "bin/pbjs", "pbts": "bin/pbts" } }, "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw=="],
|
||||||
|
|
||||||
|
"steam-appticket/steamid": ["[email protected]", "", { "dependencies": { "cuint": "^0.2.1" } }, "sha512-t86YjtP1LtPt8D+TaIARm6PtC9tBnF1FhxQeLFs6ohG7vDUfQuy/M8II14rx1TTUkVuYoWHP/7DlvTtoCGULcw=="],
|
||||||
|
|
||||||
"string_decoder/safe-buffer": ["[email protected]", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
"string_decoder/safe-buffer": ["[email protected]", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||||
|
|
||||||
"tar-fs/tar-stream": ["[email protected]", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
"tar-fs/tar-stream": ["[email protected]", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
||||||
@@ -2700,8 +2781,6 @@
|
|||||||
|
|
||||||
"@blitzkit/bot/prisma/@prisma/engines": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.17.1", "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", "@prisma/fetch-engine": "6.17.1", "@prisma/get-platform": "6.17.1" } }, "sha512-D95Ik3GYZkqZ8lSR4EyFOJ/tR33FcYRP8kK61o+WMsyD10UfJwd7+YielflHfKwiGodcqKqoraWw8ElAgMDbPw=="],
|
"@blitzkit/bot/prisma/@prisma/engines": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.17.1", "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", "@prisma/fetch-engine": "6.17.1", "@prisma/get-platform": "6.17.1" } }, "sha512-D95Ik3GYZkqZ8lSR4EyFOJ/tR33FcYRP8kK61o+WMsyD10UfJwd7+YielflHfKwiGodcqKqoraWw8ElAgMDbPw=="],
|
||||||
|
|
||||||
"@blitzkit/scripts/@types/bun/bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
|
||||||
|
|
||||||
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
||||||
|
|
||||||
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
||||||
@@ -2770,6 +2849,10 @@
|
|||||||
|
|
||||||
"filelist/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"filelist/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
||||||
|
"steam-appticket/protobufjs/@types/long": ["@types/[email protected]", "", {}, "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="],
|
||||||
|
|
||||||
|
"steam-appticket/protobufjs/long": ["[email protected]", "", {}, "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="],
|
||||||
|
|
||||||
"widest-line/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="],
|
"widest-line/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="],
|
||||||
|
|
||||||
"widest-line/string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
"widest-line/string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# BlitzKit 1.29.4
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- BlitzKit now partially downloads game files instead of the entire game, allowing for faster updates
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# BlitzKit 1.29.5
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added new lighting to be a bit softer on tanks
|
||||||
|
- Added a button with the sun icon to toggle high graphics
|
||||||
|
- Added a new reveal and transition animation between tabs
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Track wheels on some tanks no longer look translucent
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# BlitzKit 1.29.6
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Simplified the ownership and test tank filters in the tank search
|
||||||
|
- Renamed "Guess the tank" to simply "Guess"
|
||||||
|
- The player count value in the Performance tool is now set to Total by default
|
||||||
|
- Added Compare-like red/green highlighting to the Performance tool
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Removed the last updated info bar in Performance for some more screen real estate
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- The remove played tanks in the Playlist tool now shows even when logged out
|
||||||
|
- The Guess game now has a page title
|
||||||
|
- The compare table now occupies the minimum width possible instead of expanding all columns excessively
|
||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkit",
|
"name": "blitzkit",
|
||||||
"version": "1.29.3",
|
"version": "1.29.6",
|
||||||
"license": "BlitzKit License",
|
"license": "BlitzKit License",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"build:website": "cd packages/website && bun run build",
|
"build:website": "cd packages/website && bun run build",
|
||||||
"build:prisma": "prisma generate",
|
"build:prisma": "prisma generate",
|
||||||
"build:proxyclient": "cd packages/scripts && bun run build:proxyclient",
|
"build:proxyclient": "cd packages/scripts && bun run build:proxyclient",
|
||||||
|
"build:catalogitemaccesssor": "cd packages/scripts && bun run build:catalogitemaccesssor",
|
||||||
"build:ratingleaderboard": "cd packages/scripts && bun run build:ratingleaderboard",
|
"build:ratingleaderboard": "cd packages/scripts && bun run build:ratingleaderboard",
|
||||||
"build:app": "cd packages/app && bun run build",
|
"build:app": "cd packages/app && bun run build",
|
||||||
"dev:bot": "bun run build:bot --dev && bun start:bot",
|
"dev:bot": "bun run build:bot --dev && bun start:bot",
|
||||||
|
|||||||
+1
-1
Submodule packages/closed updated: 33b7d22419...5b4a85d503.
@@ -17,6 +17,7 @@
|
|||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"dotenv": "^17.2.2",
|
"dotenv": "^17.2.2",
|
||||||
|
"fast-xml-parser": "^5.3.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"pickleparser": "^0.2.1",
|
"pickleparser": "^0.2.1",
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export enum BlitzVFSSourceType {
|
||||||
|
Steam,
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlitzVFSSource = {
|
||||||
|
type: BlitzVFSSourceType;
|
||||||
|
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
app: number;
|
||||||
|
os: "windows";
|
||||||
|
};
|
||||||
|
|
||||||
|
export class BlitzVFS {
|
||||||
|
constructor(private source: BlitzVFSSource) {}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
async file(path: string) {
|
||||||
|
return new Uint8Array();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +1,33 @@
|
|||||||
export * from './canSplash';
|
export * from "./canSplash";
|
||||||
export * from './clans';
|
export * from "./clans";
|
||||||
export * from './classes';
|
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 "./fetchGlossary";
|
||||||
export * from './generateStats';
|
export * from "./generateStats";
|
||||||
export * from './getAccountAchievements';
|
export * from "./getAccountAchievements";
|
||||||
export * from './getAccountInfo';
|
export * from "./getAccountInfo";
|
||||||
export * from './getClanAccountInfo';
|
export * from "./getClanAccountInfo";
|
||||||
export * from './getClanInfo';
|
export * from "./getClanInfo";
|
||||||
export * from './getLeagueFromScore';
|
export * from "./getLeagueFromScore";
|
||||||
export * from './getRatingInfo';
|
export * from "./getRatingInfo";
|
||||||
export * from './getRatingLeague';
|
export * from "./getRatingLeague";
|
||||||
export * from './getRatingNeighbors';
|
export * from "./getRatingNeighbors";
|
||||||
export * from './getTankStats';
|
export * from "./getTankStats";
|
||||||
export * from './idToRegion';
|
export * from "./idToRegion";
|
||||||
export * from './isOnGoingRatingSeason';
|
export * from "./isOnGoingRatingSeason";
|
||||||
export * from './isValidBlitzId';
|
export * from "./isValidBlitzId";
|
||||||
export * from './nations';
|
export * from "./nations";
|
||||||
export * from './normalizeExtra';
|
export * from "./normalizeExtra";
|
||||||
export * from './normalizeIds';
|
export * from "./normalizeIds";
|
||||||
export * from './permanentSkills';
|
export * from "./permanentSkills";
|
||||||
export * from './rating';
|
export * from "./rating";
|
||||||
export * from './regions';
|
export * from "./readDVPL";
|
||||||
export * from './regionSubdomainToRegion';
|
export * from "./regions";
|
||||||
export * from './regionToRegionSubdomain';
|
export * from "./regionSubdomainToRegion";
|
||||||
export * from './searchClansAcrossRegions';
|
export * from "./regionToRegionSubdomain";
|
||||||
export * from './searchCurrentRatingPlayers';
|
export * from "./searchClansAcrossRegions";
|
||||||
export * from './searchPlayersAcrossRegions';
|
export * from "./searchCurrentRatingPlayers";
|
||||||
export * from './toUniqueId';
|
export * from "./searchPlayersAcrossRegions";
|
||||||
|
export * from "./toUniqueId";
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { crc32 } from 'crc';
|
import { crc32 } from "crc";
|
||||||
import { decompressBlock } from 'lz4js';
|
import { decompressBlock } from "lz4js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thanks Maddoxkkm! Modified heavily to be modern.
|
* Thanks Maddoxkkm! Modified heavily to be modern.
|
||||||
@@ -9,10 +9,10 @@ export function readDVPL(buffer: Buffer) {
|
|||||||
const footerBuffer = buffer.subarray(buffer.length - 20, buffer.length);
|
const footerBuffer = buffer.subarray(buffer.length - 20, buffer.length);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
footerBuffer.toString('utf8', 16, 20) !== 'DVPL' ||
|
footerBuffer.toString("utf8", 16, 20) !== "DVPL" ||
|
||||||
footerBuffer.length !== 20
|
footerBuffer.length !== 20
|
||||||
) {
|
) {
|
||||||
throw new SyntaxError('Invalid DVPL footer');
|
throw new SyntaxError("Invalid DVPL footer");
|
||||||
}
|
}
|
||||||
|
|
||||||
const footerData = {
|
const footerData = {
|
||||||
@@ -24,15 +24,15 @@ export function readDVPL(buffer: Buffer) {
|
|||||||
const targetBlock = buffer.subarray(0, buffer.length - 20);
|
const targetBlock = buffer.subarray(0, buffer.length - 20);
|
||||||
|
|
||||||
if (targetBlock.length !== footerData.cSize) {
|
if (targetBlock.length !== footerData.cSize) {
|
||||||
throw new RangeError('DVPL size mismatch');
|
throw new RangeError("DVPL size mismatch");
|
||||||
}
|
}
|
||||||
if (crc32(targetBlock) !== footerData.crc32) {
|
if (crc32(targetBlock) !== footerData.crc32) {
|
||||||
throw new TypeError('DVPL CRC32 mismatch');
|
throw new TypeError("DVPL CRC32 mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (footerData.type === 0) {
|
if (footerData.type === 0) {
|
||||||
if (!(footerData.oSize === footerData.cSize && footerData.type === 0)) {
|
if (!(footerData.oSize === footerData.cSize && footerData.type === 0)) {
|
||||||
throw new RangeError('DVPL type and size mismatch');
|
throw new RangeError("DVPL type and size mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetBlock;
|
return targetBlock;
|
||||||
@@ -44,17 +44,17 @@ export function readDVPL(buffer: Buffer) {
|
|||||||
destination,
|
destination,
|
||||||
0,
|
0,
|
||||||
source.length,
|
source.length,
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
if (decompressedSize !== footerData.oSize) {
|
if (decompressedSize !== footerData.oSize) {
|
||||||
throw new RangeError(
|
throw new RangeError(
|
||||||
`Decompressed DVPL size mismatch (${decompressedSize} vs ${footerData.oSize})`,
|
`Decompressed DVPL size mismatch (${decompressedSize} vs ${footerData.oSize})`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Buffer.from(destination);
|
return Buffer.from(destination);
|
||||||
} else {
|
} else {
|
||||||
throw new SyntaxError('Unknown DVPL format');
|
throw new SyntaxError("Unknown DVPL format");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+10
-10
@@ -1,10 +1,10 @@
|
|||||||
export * from './blitz';
|
export * from "./blitz";
|
||||||
export * from './blitzkit';
|
export * from "./blitzkit";
|
||||||
export * from './imgur';
|
export * from "./imgur";
|
||||||
export * from './math';
|
export * from "./math";
|
||||||
export * from './patreon';
|
export * from "./patreon";
|
||||||
export * from './protobuf';
|
export * from "./protobuf";
|
||||||
export * from './protos';
|
export * from "./protos";
|
||||||
export * from './statistics';
|
export * from "./statistics";
|
||||||
export * from './streams';
|
export * from "./streams";
|
||||||
export * from './types';
|
export * from "./types";
|
||||||
@@ -906,8 +906,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Guess the tank",
|
"name": "Guess",
|
||||||
"button": "Guess away",
|
"button": "What could it be?",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "Randomize"
|
"randomize": "Randomize"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Guess the tank",
|
"name": "Guess",
|
||||||
"button": "Guess away",
|
"button": "What could it be?",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "Randomize"
|
"randomize": "Randomize"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Guess the tank",
|
"name": "Guess",
|
||||||
"button": "Guess away",
|
"button": "What could it be?",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "Randomize"
|
"randomize": "Randomize"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Guess the tank",
|
"name": "Guess",
|
||||||
"button": "Guess away",
|
"button": "What could it be?",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "Randomize"
|
"randomize": "Randomize"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Guess the tank",
|
"name": "Guess",
|
||||||
"button": "Guess away",
|
"button": "What could it be?",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "Randomize"
|
"randomize": "Randomize"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Guess the tank",
|
"name": "Guess",
|
||||||
"button": "Guess away",
|
"button": "What could it be?",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "Случайно"
|
"randomize": "Случайно"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Угадай танк",
|
"name": "Угадать",
|
||||||
"button": "Угадать",
|
"button": "Что это за танк?",
|
||||||
"description": "Мини-игра «Силуэт»",
|
"description": "Мини-игра «Силуэт»",
|
||||||
"seo_description": "Играйте угадывая силуэт танка из World of Tanks Blitz",
|
"seo_description": "Играйте угадывая силуэт танка из World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"test": {
|
"test": {
|
||||||
"default": "тест",
|
"default": "тест",
|
||||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
|
"long": "{fruit}",
|
||||||
"fox": "Швидка бура лисиця перестрибує через лінивого собаку."
|
"fox": "Швидка бура лисиця перестрибує через лінивого собаку."
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"name": "BlitzKit",
|
"name": "BlitzKit",
|
||||||
"units": {
|
"units": {
|
||||||
"m": "{value}m",
|
"m": "{value}м",
|
||||||
"mm": "{value}mm",
|
"mm": "{value}мм",
|
||||||
"deg": "{value}°",
|
"deg": "{value}°",
|
||||||
"hp": "{value}hp",
|
"hp": "{value}од",
|
||||||
"x": "x{value}",
|
"x": "x{value}",
|
||||||
"percentage": "{value}%",
|
"percentage": "{value}%",
|
||||||
"deg_s": "{value}°/s"
|
"deg_s": "{value}град/c"
|
||||||
},
|
},
|
||||||
"gun_types": {
|
"gun_types": {
|
||||||
"auto_loader": "Магазинна система заряджання",
|
"auto_loader": "Магазинна система заряджання",
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
"nations": {
|
"nations": {
|
||||||
"ussr": "СРСР",
|
"ussr": "СРСР",
|
||||||
"germany": "Німеччина",
|
"germany": "Німеччина",
|
||||||
"usa": "США",
|
"usa": "ЗША",
|
||||||
"china": "Китай",
|
"china": "Китай",
|
||||||
"uk": "Велика Британія",
|
"uk": "Велика Британія",
|
||||||
"france": "Франція",
|
"france": "Франція",
|
||||||
@@ -186,11 +186,11 @@
|
|||||||
"bot": {
|
"bot": {
|
||||||
"common": {
|
"common": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"clan_not_found": "# Could not find clan\nCouldn't find \"{search}\". Try selecting a clan from the search result.",
|
"clan_not_found": "# Не вдалося знайти клан\nНе вдалося знайти \"{search}\". Спробуйте обрати клан з результату пошуку.",
|
||||||
"player_not_found": "# Could not find user\nI couldn't find user \"{search}\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
"player_not_found": "# Не вдалося знайти користувача\nНе вдалося знайти користувача \"{search}\". Спробуйте вибрати користувача із результатів пошуку, ввівши правильне ім'я користувача, або скористайтеся командою `/link`.",
|
||||||
"player_not_linked": "# Використайте команду \"/link\" щоб почати\nЯ не знаю, хто ви, але ви можете сказати мені сьогодні!",
|
"player_not_linked": "# Використайте команду \"/link\" щоб почати\nЯ не знаю, хто ви, але ви можете сказати мені сьогодні!",
|
||||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"{search}\".",
|
"tank_name_not_found": "# Танк не знайдено\nНе вдалося знайти танк із назвою «{search}».",
|
||||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"{search}\". Try typing the name of the tank instead of a number.",
|
"tank_id_not_found": "# Танк не знайдено\nНе вдалося знайти танк з ID «{search}». Спробуйте ввести назву танка замість номера.",
|
||||||
"no_tank_stats": "# Немає статистики для танка\nЦей гравець не має статистики.\nМожливо, це не той гравець, якого ви шукаєте.",
|
"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).",
|
||||||
@@ -284,19 +284,19 @@
|
|||||||
"name": "рівень",
|
"name": "рівень",
|
||||||
"description": "Рівень",
|
"description": "Рівень",
|
||||||
"choices": {
|
"choices": {
|
||||||
"tier": "Tier {tier_1} - {tier_2}"
|
"tier": "Рівень {tier_1} - {tier_2}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"periods": {
|
"periods": {
|
||||||
"career": "Кар'єра",
|
"career": "Кар'єра",
|
||||||
"today": "Сьогодні",
|
"today": "Сьогодні",
|
||||||
"days": "{days} days",
|
"days": "{days} днів",
|
||||||
"custom": "{day_1} - {day_2} days"
|
"custom": "{day_1} - {day_2} днів"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"none": "Немає фільтрів",
|
"none": "Немає фільтрів",
|
||||||
"tier": "Tier {tier}"
|
"tier": "Рівень {tier}"
|
||||||
},
|
},
|
||||||
"no_data": {
|
"no_data": {
|
||||||
"battles_in_period": "За цей період боїв не було",
|
"battles_in_period": "За цей період боїв не було",
|
||||||
@@ -339,10 +339,10 @@
|
|||||||
"body": {
|
"body": {
|
||||||
"subtitle": "Дослідження",
|
"subtitle": "Дослідження",
|
||||||
"total": "Загалом",
|
"total": "Загалом",
|
||||||
"research": "Research: {cost}",
|
"research": "Дослідження: {cost}",
|
||||||
"upgrades": "Upgrades: {cost}",
|
"upgrades": "Upgrades: {cost}",
|
||||||
"purchase": "Purchase: {cost}",
|
"purchase": "Вартість: {cost}",
|
||||||
"equipment": "Equipment: {cost}",
|
"equipment": "Спорядження: {cost}",
|
||||||
"estimation": "# Це оцінка!\n\nЦей список може включати вже досліджені танки через неточну інформацію для вантажу. Спробуйте використати \"початковий танк\" з рядка за допомогою команди."
|
"estimation": "# Це оцінка!\n\nЦей список може включати вже досліджені танки через неточну інформацію для вантажу. Спробуйте використати \"початковий танк\" з рядка за допомогою команди."
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -435,7 +435,7 @@
|
|||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"subtitle": "Inactive for {days}+ days",
|
"subtitle": "Inactive for {days}+ days",
|
||||||
"listing": "{days} days"
|
"listing": "{days} днів"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"full_stats": {
|
"full_stats": {
|
||||||
@@ -549,7 +549,7 @@
|
|||||||
"top_players": "Top {count} players",
|
"top_players": "Top {count} players",
|
||||||
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
"no_participation": "# {player} didn't play rating in season {season}\n\nThis player did not participate in this season or did not get past calibration.",
|
||||||
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
|
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
|
||||||
"deleted_player": "Deleted {id}",
|
"deleted_player": "",
|
||||||
"neighbors": "Rating neighbors",
|
"neighbors": "Rating neighbors",
|
||||||
"more": "+ {count} more"
|
"more": "+ {count} more"
|
||||||
}
|
}
|
||||||
@@ -643,9 +643,9 @@
|
|||||||
"body": "This page has moved. Redirecting..."
|
"body": "This page has moved. Redirecting..."
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings",
|
"title": "Налаштування",
|
||||||
"description": "Change BlitzKit settings",
|
"description": "Змінити налаштування BlitzKit ",
|
||||||
"dev_mode": "Development mode",
|
"dev_mode": "Режим розробника",
|
||||||
"language": "Мова"
|
"language": "Мова"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
@@ -658,16 +658,16 @@
|
|||||||
"copied": "Скопійовано!"
|
"copied": "Скопійовано!"
|
||||||
},
|
},
|
||||||
"tank_search": {
|
"tank_search": {
|
||||||
"select_dialog_title": "Search for tanks",
|
"select_dialog_title": "Знайти танк",
|
||||||
"swap_dialog_title": "Swap tank",
|
"swap_dialog_title": "Swap tank",
|
||||||
"clear_filters": "Clear filters",
|
"clear_filters": "Очистити",
|
||||||
"login": "Login to use this",
|
"login": "Увійдіть, щоб скористатися",
|
||||||
"search_bar_hint": "Шукайте техніку...",
|
"search_bar_hint": "Шукайте техніку...",
|
||||||
"recent": "Недавно переглянуті",
|
"recent": "Недавно переглянуті",
|
||||||
"select_all": "Вибрати все",
|
"select_all": "Вибрати все",
|
||||||
"count_singular": "1 танк",
|
"count_singular": "1 танк",
|
||||||
"count_plural": "{count} tanks",
|
"count_plural": "Танків: {count} ",
|
||||||
"sorting_by": "Sorting by {name}",
|
"sorting_by": "Відсортувати за {name}",
|
||||||
"no_tanks_found": {
|
"no_tanks_found": {
|
||||||
"body": "Нічого не знайдено.",
|
"body": "Нічого не знайдено.",
|
||||||
"search_again": "Спробуйте шукати заново.",
|
"search_again": "Спробуйте шукати заново.",
|
||||||
@@ -731,7 +731,7 @@
|
|||||||
"home": {
|
"home": {
|
||||||
"pwa": {
|
"pwa": {
|
||||||
"title": "Install the BlitzKit App",
|
"title": "Install the BlitzKit App",
|
||||||
"new": "NEW",
|
"new": "",
|
||||||
"checking": "Checking compatibility...",
|
"checking": "Checking compatibility...",
|
||||||
"instructions": "Instructions for Apple devices.",
|
"instructions": "Instructions for Apple devices.",
|
||||||
"install": "Install",
|
"install": "Install",
|
||||||
@@ -763,20 +763,20 @@
|
|||||||
},
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"playlist": {
|
"playlist": {
|
||||||
"reset": "Reset",
|
"reset": "Скинути",
|
||||||
"shuffle": "Shuffle",
|
"shuffle": "Перемішати",
|
||||||
"remove": "Remove played",
|
"remove": "Видалити зіграні",
|
||||||
"tracking": "BlitzKit is tracking your battles",
|
"tracking": "BlitzKit is tracking your battles",
|
||||||
"sort": {
|
"sort": {
|
||||||
"button": "Сортувати за",
|
"button": "Сортувати за",
|
||||||
"last_played": "Last played",
|
"last_played": "Остання гра",
|
||||||
"least_played": "Least played"
|
"least_played": "Найменше зіграно"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"index": "#",
|
"index": "#",
|
||||||
"never": "Never",
|
"never": "Ніколи",
|
||||||
"tank": "Танк",
|
"tank": "Танк",
|
||||||
"last_played": "Last played",
|
"last_played": "Остання гра",
|
||||||
"battles": "Career battles",
|
"battles": "Career battles",
|
||||||
"unrecorded": "--",
|
"unrecorded": "--",
|
||||||
"wins": "Перемоги"
|
"wins": "Перемоги"
|
||||||
@@ -784,14 +784,14 @@
|
|||||||
"large_warning": {
|
"large_warning": {
|
||||||
"title": "That's a lot of tanks!",
|
"title": "That's a lot of tanks!",
|
||||||
"description": "Are you sure you want to create a playlist of {count} tanks? That's {time} hours of gameplay at 50% winrate!",
|
"description": "Are you sure you want to create a playlist of {count} tanks? That's {time} hours of gameplay at 50% winrate!",
|
||||||
"cancel": "Cancel",
|
"cancel": "Скасувати",
|
||||||
"proceed": "Proceed"
|
"proceed": "Продовжити"
|
||||||
},
|
},
|
||||||
"generate": "Generate playlist",
|
"generate": "Згенерувати плейлист",
|
||||||
"name": "Playlist",
|
"name": "Плейлист",
|
||||||
"description": "Tank playlist generator",
|
"description": "Tank playlist generator",
|
||||||
"seo_description": "Don't know what to play in World of Tanks Blitz? Use BlitzKit Playlist to get suggestions!",
|
"seo_description": "Don't know what to play in World of Tanks Blitz? Use BlitzKit Playlist to get suggestions!",
|
||||||
"button": "Generate",
|
"button": "Створити",
|
||||||
"pool_size": "{count} possible tanks"
|
"pool_size": "{count} possible tanks"
|
||||||
},
|
},
|
||||||
"mixer": {
|
"mixer": {
|
||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "Randomize"
|
"randomize": "Randomize"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "Guess the tank",
|
"name": "Guess",
|
||||||
"button": "Guess away",
|
"button": "What could it be?",
|
||||||
"description": "Silhouette mini-game",
|
"description": "Silhouette mini-game",
|
||||||
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -803,8 +803,8 @@
|
|||||||
"randomize": "随机搭配"
|
"randomize": "随机搭配"
|
||||||
},
|
},
|
||||||
"guess": {
|
"guess": {
|
||||||
"name": "猜坦克",
|
"name": "猜测",
|
||||||
"button": "猜猜看",
|
"button": "What could it be?",
|
||||||
"description": "坦克剪影小游戏",
|
"description": "坦克剪影小游戏",
|
||||||
"seo_description": "看剪影猜坦克",
|
"seo_description": "看剪影猜坦克",
|
||||||
"search": {
|
"search": {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/lz4js": "^0.2.1",
|
"@types/lz4js": "^0.2.1",
|
||||||
"@types/progress": "^2.0.7",
|
"@types/progress": "^2.0.7",
|
||||||
|
"@types/steam-user": "^5.1.0",
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"csv-parse": "^6.1.0",
|
"csv-parse": "^6.1.0",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"progress": "^2.0.3",
|
"progress": "^2.0.3",
|
||||||
"puppeteer": "^24.22.3",
|
"puppeteer": "^24.22.3",
|
||||||
"sharp": "^0.34.4",
|
"sharp": "^0.34.4",
|
||||||
|
"steam-user": "^5.2.3",
|
||||||
"three": "^0.180.0",
|
"three": "^0.180.0",
|
||||||
"ts-proto": "^2.7.7",
|
"ts-proto": "^2.7.7",
|
||||||
"yaml": "^2.8.1"
|
"yaml": "^2.8.1"
|
||||||
@@ -42,6 +44,7 @@
|
|||||||
"build:bot": "bun src/buildBot.ts",
|
"build:bot": "bun src/buildBot.ts",
|
||||||
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts",
|
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts",
|
||||||
"build:proxyclient": "bun src/buildProxyClient.ts",
|
"build:proxyclient": "bun src/buildProxyClient.ts",
|
||||||
|
"build:catalogitemaccesssor": "bun src/buildCatalogItemAccessor.ts",
|
||||||
"prunestrings": "bun src/pruneStrings.ts",
|
"prunestrings": "bun src/pruneStrings.ts",
|
||||||
"build:protos": "bun src/protos.ts",
|
"build:protos": "bun src/protos.ts",
|
||||||
"patchnotes": "bun src/patchNotes.ts"
|
"patchnotes": "bun src/patchNotes.ts"
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import { mkdir, writeFile } from "fs/promises";
|
|||||||
import { parse as parsePath } from "path";
|
import { parse as parsePath } from "path";
|
||||||
import ProgressBar from "progress";
|
import ProgressBar from "progress";
|
||||||
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
||||||
import { readStringDVPL } from "../src/core/blitz/readStringDVPL";
|
import { vfs } from "./buildAssets/constants";
|
||||||
import { DATA } from "./buildAssets/constants";
|
|
||||||
import { readYAMLDVPL } from "./core/blitz/readYAMLDVPL";
|
|
||||||
import { writeDVPL } from "./core/blitz/writeDVPL";
|
import { writeDVPL } from "./core/blitz/writeDVPL";
|
||||||
|
|
||||||
const versionTextFile = await readStringDVPL(`${DATA}/version.txt`);
|
const versionTextFile = await vfs.text(`Data/version.txt`);
|
||||||
const currentVersion = versionTextFile
|
const currentVersion = versionTextFile
|
||||||
.split(" ")[0]
|
.split(" ")[0]
|
||||||
.split(".")
|
.split(".")
|
||||||
@@ -51,7 +49,7 @@ while (true) {
|
|||||||
const { dir } = parsePath(path);
|
const { dir } = parsePath(path);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await mkdir(`${DATA}/${dir}`, { recursive: true });
|
await mkdir(`Data/${dir}`, { recursive: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to make directory "${dir}"`);
|
console.warn(`Failed to make directory "${dir}"`);
|
||||||
}
|
}
|
||||||
@@ -60,7 +58,7 @@ while (true) {
|
|||||||
const buffer = Buffer.from(data);
|
const buffer = Buffer.from(data);
|
||||||
|
|
||||||
await writeFile(
|
await writeFile(
|
||||||
`${DATA}/${path}${isDvpl ? "" : ".dvpl"}`,
|
`Data/${path}${isDvpl ? "" : ".dvpl"}`,
|
||||||
new Uint8Array(isDvpl ? buffer : writeDVPL(buffer))
|
new Uint8Array(isDvpl ? buffer : writeDVPL(buffer))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -76,14 +74,14 @@ while (true) {
|
|||||||
}/en.yaml`
|
}/en.yaml`
|
||||||
);
|
);
|
||||||
const newStrings = parseYaml(await localizationsResponse.text());
|
const newStrings = parseYaml(await localizationsResponse.text());
|
||||||
const oldStrings = await readYAMLDVPL<Record<string, string>>(
|
const oldStrings = await vfs.yaml<Record<string, string>>(
|
||||||
`${DATA}/Strings/en.yaml`
|
`Data/Strings/en.yaml`
|
||||||
);
|
);
|
||||||
const patchedStrings = { ...oldStrings, ...newStrings };
|
const patchedStrings = { ...oldStrings, ...newStrings };
|
||||||
const patchedContent = stringifyYaml(patchedStrings);
|
const patchedContent = stringifyYaml(patchedStrings);
|
||||||
|
|
||||||
await writeFile(
|
await writeFile(
|
||||||
`${DATA}/Strings/en.yaml`,
|
`Data/Strings/en.yaml`,
|
||||||
new Uint8Array(writeDVPL(Buffer.from(patchedContent)))
|
new Uint8Array(writeDVPL(Buffer.from(patchedContent)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import { readdir } from "fs/promises";
|
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { readStringDVPL } from "../../src/core/blitz/readStringDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
|
|
||||||
export async function boosterIcons() {
|
export async function boosterIcons() {
|
||||||
console.log("Building booster icons...");
|
console.log("Building booster icons...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("booster icons");
|
using uploader = new AssetUploader("booster icons");
|
||||||
const boosterFiles = (await readdir(`${DATA}/Gfx/Shared/boosters`)).filter(
|
const boosterFiles = (vfs.dir(`Data/Gfx/Shared/boosters`)).filter(
|
||||||
(file) => !file.includes("@2x.txt") && !file.startsWith("texture0")
|
(file) => !file.includes("@2x.txt") && !file.startsWith("texture0")
|
||||||
);
|
);
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/Gfx/Shared/boosters/texture0.packed.webp`)
|
await vfs.file(`Data/Gfx/Shared/boosters/texture0.packed.webp`)
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const file of boosterFiles) {
|
for (const file of boosterFiles) {
|
||||||
@@ -21,7 +18,7 @@ export async function boosterIcons() {
|
|||||||
|
|
||||||
if (name === undefined) continue;
|
if (name === undefined) continue;
|
||||||
|
|
||||||
const sizes = (await readStringDVPL(`${DATA}/Gfx/Shared/boosters/${file}`))
|
const sizes = (await vfs.text(`Data/Gfx/Shared/boosters/${file}`))
|
||||||
.split("\n")[4]
|
.split("\n")[4]
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map(Number);
|
.map(Number);
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
|
|
||||||
export async function camouflageIcons() {
|
export async function camouflageIcons() {
|
||||||
console.log("Building camouflage icons...");
|
console.log("Building camouflage icons...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("camo icons");
|
using uploader = new AssetUploader("camo icons");
|
||||||
const content = await readDVPLFile(
|
const content = await vfs.file(
|
||||||
`${DATA}/Gfx/UI/Hangar/IconCamouflage.packed.webp`
|
`Data/Gfx/UI/Hangar/IconCamouflage.packed.webp`
|
||||||
);
|
);
|
||||||
|
|
||||||
await uploader.add({ content, path: "icons/camo.webp" });
|
await uploader.add({ content, path: "icons/camo.webp" });
|
||||||
|
|||||||
@@ -1,32 +1,12 @@
|
|||||||
import { readdir } from "fs/promises";
|
import { assertSecret } from "@blitzkit/core";
|
||||||
import { argv } from "process";
|
import { SteamVFS } from "../core/steam/vfs";
|
||||||
|
|
||||||
const WINDOWS_APPS = "C:/Program Files/WindowsApps";
|
const APP = 444200;
|
||||||
const wotbPackageRegex = /7458BE2C\..+_\d+(\.\d+)+_x64__.+/;
|
const DEPOT = 444202;
|
||||||
|
|
||||||
const isManual = argv.includes("--manual");
|
export const vfs = await new SteamVFS(
|
||||||
|
assertSecret(import.meta.env.STEAM_USERNAME),
|
||||||
let data: string;
|
assertSecret(import.meta.env.STEAM_PASSWORD),
|
||||||
let compressed: boolean;
|
APP,
|
||||||
|
DEPOT
|
||||||
if (isManual) {
|
).init();
|
||||||
data = "../../temp/game/Data";
|
|
||||||
compressed = true;
|
|
||||||
} else {
|
|
||||||
const directories = await readdir(WINDOWS_APPS).then((files) =>
|
|
||||||
files.filter((file) => wotbPackageRegex.test(file))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (directories.length !== 1) {
|
|
||||||
throw new Error(
|
|
||||||
`Expected 1 directory for WoTB Windows App, got ${directories.length}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [windowsApp] = directories;
|
|
||||||
data = `${WINDOWS_APPS}/${windowsApp}/Data`;
|
|
||||||
compressed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DATA = data;
|
|
||||||
export const DATA_COMPRESSED = compressed;
|
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
import { existsSync } from "fs";
|
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { readStringDVPL } from "../../src/core/blitz/readStringDVPL";
|
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { readYAMLDVPL } from "../core/blitz/readYAMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import { ConsumablesCommon, ProvisionsCommon } from "./definitions";
|
import { ConsumablesCommon, ProvisionsCommon } from "./definitions";
|
||||||
|
|
||||||
interface Mappings {
|
interface Mappings {
|
||||||
@@ -33,20 +28,20 @@ export async function consumableProvisionIcons() {
|
|||||||
styles.map(async (path) =>
|
styles.map(async (path) =>
|
||||||
Object.values(
|
Object.values(
|
||||||
(
|
(
|
||||||
await readYAMLDVPL<Mappings>(`${DATA}/${path}`)
|
await vfs.yaml<Mappings>(`Data/${path}`)
|
||||||
).StyleSheets
|
).StyleSheets
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).then((array) => array.flat());
|
).then((array) => array.flat());
|
||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await vfs.text(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/consumables/list.xml`
|
`Data/XML/item_defs/vehicles/common/consumables/list.xml`
|
||||||
)
|
)
|
||||||
).matchAll(listItemsPattern)) {
|
).matchAll(listItemsPattern)) {
|
||||||
const consumablesCommon = await readXMLDVPL<{
|
const consumablesCommon = await vfs.xml<{
|
||||||
root: ConsumablesCommon;
|
root: ConsumablesCommon;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`);
|
}>(`Data/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`);
|
||||||
|
|
||||||
for (const consumable of Object.values(consumablesCommon.root)) {
|
for (const consumable of Object.values(consumablesCommon.root)) {
|
||||||
const styleSheet = styleSheets.find((styleSheet) =>
|
const styleSheet = styleSheets.find((styleSheet) =>
|
||||||
@@ -67,9 +62,9 @@ export async function consumableProvisionIcons() {
|
|||||||
.replace(".psd", "")
|
.replace(".psd", "")
|
||||||
.replace(".txt", "");
|
.replace(".txt", "");
|
||||||
|
|
||||||
if (existsSync(`${DATA}/${configPath}.packed.webp`)) {
|
if (vfs.has(`Data/${configPath}.packed.webp`)) {
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/${configPath}.packed.webp`)
|
await vfs.file(`Data/${configPath}.packed.webp`)
|
||||||
);
|
);
|
||||||
const content = await image.trim({ threshold: 100 }).toBuffer();
|
const content = await image.trim({ threshold: 100 }).toBuffer();
|
||||||
|
|
||||||
@@ -79,14 +74,14 @@ export async function consumableProvisionIcons() {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const consumablesTexture = sharp(
|
const consumablesTexture = sharp(
|
||||||
await readDVPLFile(
|
await vfs.file(
|
||||||
`${`${DATA}/${configPath}`
|
`${`Data/${configPath}`
|
||||||
.split("/")
|
.split("/")
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.join("/")}/texture0.packed.webp`
|
.join("/")}/texture0.packed.webp`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt`))
|
const sizes = (await vfs.text(`Data/${configPath}.txt`))
|
||||||
.split("\n")[4]
|
.split("\n")[4]
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map(Number);
|
.map(Number);
|
||||||
@@ -109,13 +104,13 @@ export async function consumableProvisionIcons() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await vfs.text(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/provisions/list.xml`
|
`Data/XML/item_defs/vehicles/common/provisions/list.xml`
|
||||||
)
|
)
|
||||||
).matchAll(listItemsPattern)) {
|
).matchAll(listItemsPattern)) {
|
||||||
const provisionsCommon = await readXMLDVPL<{
|
const provisionsCommon = await vfs.xml<{
|
||||||
root: ProvisionsCommon;
|
root: ProvisionsCommon;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`);
|
}>(`Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`);
|
||||||
|
|
||||||
for (const provision of Object.values(provisionsCommon.root)) {
|
for (const provision of Object.values(provisionsCommon.root)) {
|
||||||
const styleSheet = styleSheets.find((styleSheet) =>
|
const styleSheet = styleSheets.find((styleSheet) =>
|
||||||
@@ -136,9 +131,9 @@ export async function consumableProvisionIcons() {
|
|||||||
.replace(".psd", "")
|
.replace(".psd", "")
|
||||||
.replace(".txt", "");
|
.replace(".txt", "");
|
||||||
|
|
||||||
if (existsSync(`${DATA}/${configPath}.packed.webp`)) {
|
if (vfs.has(`Data/${configPath}.packed.webp`)) {
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/${configPath}.packed.webp`)
|
await vfs.file(`Data/${configPath}.packed.webp`)
|
||||||
);
|
);
|
||||||
const content = await image.trim({ threshold: 100 }).toBuffer();
|
const content = await image.trim({ threshold: 100 }).toBuffer();
|
||||||
|
|
||||||
@@ -148,14 +143,14 @@ export async function consumableProvisionIcons() {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const provisionsTexture = sharp(
|
const provisionsTexture = sharp(
|
||||||
await readDVPLFile(
|
await vfs.file(
|
||||||
`${`${DATA}/${configPath}`
|
`${`Data/${configPath}`
|
||||||
.split("/")
|
.split("/")
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.join("/")}/texture0.packed.webp`
|
.join("/")}/texture0.packed.webp`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt`))
|
const sizes = (await vfs.text(`Data/${configPath}.txt`))
|
||||||
.split("\n")[4]
|
.split("\n")[4]
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map(Number);
|
.map(Number);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { assertSecret, BlitzGlossary } from "@blitzkit/core";
|
import { assertSecret, BlitzGlossary } from "@blitzkit/core";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
|
|
||||||
const ICONS = [
|
const ICONS = [
|
||||||
["currency_silver_m.packed.webp", "silver"],
|
["currency_silver_m.packed.webp", "silver"],
|
||||||
@@ -21,7 +20,7 @@ export async function currencies() {
|
|||||||
|
|
||||||
for (const [file, name] of ICONS) {
|
for (const [file, name] of ICONS) {
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
await readDVPLFile(`${DATA}/Gfx/Lobby/currency/${file}`)
|
await vfs.file(`Data/Gfx/Lobby/currency/${file}`)
|
||||||
)
|
)
|
||||||
.trim()
|
.trim()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|||||||
@@ -32,16 +32,12 @@ import {
|
|||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n";
|
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n";
|
||||||
import locales from "@blitzkit/i18n/locales.json";
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { readdir } from "fs/promises";
|
|
||||||
import { deburr } from "lodash-es";
|
import { deburr } from "lodash-es";
|
||||||
import { parse as parsePath } from "path";
|
import { parse as parsePath } from "path";
|
||||||
import type { Vector3Tuple } from "three";
|
import type { Vector3Tuple } from "three";
|
||||||
import { parse as parseYaml } from "yaml";
|
import { parse as parseYaml } from "yaml";
|
||||||
import { readStringDVPL } from "../core/blitz/readStringDVPL";
|
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { readYAMLDVPL } from "../core/blitz/readYAMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import type { Avatar } from "./skillIcons";
|
import type { Avatar } from "./skillIcons";
|
||||||
import type { TankParameters } from "./tankIcons";
|
import type { TankParameters } from "./tankIcons";
|
||||||
|
|
||||||
@@ -540,9 +536,9 @@ export async function definitions() {
|
|||||||
|
|
||||||
using uploader = new AssetUploader("definitions");
|
using uploader = new AssetUploader("definitions");
|
||||||
const gameDefinitions: GameDefinitions = {
|
const gameDefinitions: GameDefinitions = {
|
||||||
version: (await readStringDVPL(`${DATA}/version.txt`)).split(" ")[0],
|
version: (await vfs.text('Data/version.txt')).split(" ")[0],
|
||||||
nations: (
|
nations: (
|
||||||
await readYAMLDVPL<AvailableNationsYaml>(`${DATA}/available_nations.yaml`)
|
await vfs.yaml<AvailableNationsYaml>('Data/available_nations.yaml')
|
||||||
).available_nations,
|
).available_nations,
|
||||||
gameModes: {},
|
gameModes: {},
|
||||||
roles: {},
|
roles: {},
|
||||||
@@ -565,12 +561,10 @@ export async function definitions() {
|
|||||||
[TankClass.TANK_DESTROYER]: { skills: [] },
|
[TankClass.TANK_DESTROYER]: { skills: [] },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
const nations = vfs.dir('Data/XML/item_defs/vehicles').filter((nation) => nation !== "common")
|
||||||
(nations) => nations.filter((nation) => nation !== "common")
|
|
||||||
);
|
|
||||||
const tankStringIdMap: Record<string, number> = {};
|
const tankStringIdMap: Record<string, number> = {};
|
||||||
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
|
const optionalDevices = await vfs.xml<{ root: OptionalDevices }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/optional_devices.xml`
|
'Data/XML/item_defs/vehicles/common/optional_devices.xml'
|
||||||
);
|
);
|
||||||
const stringsI18n: Record<string, Record<string, string>> = {};
|
const stringsI18n: Record<string, Record<string, string>> = {};
|
||||||
|
|
||||||
@@ -582,8 +576,8 @@ export async function definitions() {
|
|||||||
)
|
)
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
.then((string) => parseYaml(string) as BlitzStrings);
|
.then((string) => parseYaml(string) as BlitzStrings);
|
||||||
const preInstalled = await readYAMLDVPL<BlitzStrings>(
|
const preInstalled = await vfs.yaml<BlitzStrings>(
|
||||||
`${DATA}/Strings/${blitzLocale}.yaml`
|
`Data/Strings/${blitzLocale}.yaml`
|
||||||
);
|
);
|
||||||
|
|
||||||
stringsI18n[locale] = {
|
stringsI18n[locale] = {
|
||||||
@@ -616,15 +610,15 @@ export async function definitions() {
|
|||||||
return { locales: collection } satisfies I18nString;
|
return { locales: collection } satisfies I18nString;
|
||||||
}
|
}
|
||||||
|
|
||||||
const optionalDeviceSlots = await readXMLDVPL<{
|
const optionalDeviceSlots = await vfs.xml<{
|
||||||
root: OptionalDeviceSlots;
|
root: OptionalDeviceSlots;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/common/optional_device_slots.xml`);
|
}>(`Data/XML/item_defs/vehicles/common/optional_device_slots.xml`);
|
||||||
const consumables: ConsumablesCommon = {};
|
const consumables: ConsumablesCommon = {};
|
||||||
const provisions: ProvisionsCommon = {};
|
const provisions: ProvisionsCommon = {};
|
||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await vfs.text(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/consumables/list.xml`
|
`Data/XML/item_defs/vehicles/common/consumables/list.xml`
|
||||||
)
|
)
|
||||||
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
||||||
if (match[1] === "prototypes") continue;
|
if (match[1] === "prototypes") continue;
|
||||||
@@ -632,16 +626,16 @@ export async function definitions() {
|
|||||||
Object.assign(
|
Object.assign(
|
||||||
consumables,
|
consumables,
|
||||||
(
|
(
|
||||||
await readXMLDVPL<{ root: ConsumablesCommon }>(
|
await vfs.xml<{ root: ConsumablesCommon }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`
|
`Data/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`
|
||||||
)
|
)
|
||||||
).root
|
).root
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await vfs.text(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/provisions/list.xml`
|
`Data/XML/item_defs/vehicles/common/provisions/list.xml`
|
||||||
)
|
)
|
||||||
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
||||||
if (match[1] === "prototypes") continue;
|
if (match[1] === "prototypes") continue;
|
||||||
@@ -649,34 +643,34 @@ export async function definitions() {
|
|||||||
Object.assign(
|
Object.assign(
|
||||||
provisions,
|
provisions,
|
||||||
(
|
(
|
||||||
await readXMLDVPL<{ root: ConsumablesCommon }>(
|
await vfs.xml<{ root: ConsumablesCommon }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`
|
`Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`
|
||||||
)
|
)
|
||||||
).root
|
).root
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatar = await readXMLDVPL<{ root: Avatar }>(
|
const avatar = await vfs.xml<{ root: Avatar }>(
|
||||||
`${DATA}/XML/item_defs/tankmen/avatar.xml`
|
`Data/XML/item_defs/tankmen/avatar.xml`
|
||||||
);
|
);
|
||||||
const maps = await readYAMLDVPL<Maps>(`${DATA}/maps.yaml`);
|
const maps = await vfs.yaml<Maps>(`Data/maps.yaml`);
|
||||||
const tankXps = new Map<number, ResearchCost>();
|
const tankXps = new Map<number, ResearchCost>();
|
||||||
const camouflagesXml = await readXMLDVPL<{ root: CamouflagesXML }>(
|
const camouflagesXml = await vfs.xml<{ root: CamouflagesXML }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/camouflages.xml`
|
`Data/XML/item_defs/vehicles/common/camouflages.xml`
|
||||||
);
|
);
|
||||||
const camouflagesYaml = await readYAMLDVPL<CamouflagesYaml>(
|
const camouflagesYaml = await vfs.yaml<CamouflagesYaml>(
|
||||||
`${DATA}/camouflages.yaml`
|
`Data/camouflages.yaml`
|
||||||
);
|
);
|
||||||
const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages);
|
const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages);
|
||||||
const squadBattleTypeStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`
|
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`
|
||||||
);
|
);
|
||||||
const gameTypeSelectorStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`
|
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`
|
||||||
);
|
);
|
||||||
const gameModeNativeNames: Record<string, number> = {};
|
const gameModeNativeNames: Record<string, number> = {};
|
||||||
const combatRoles = await readYAMLDVPL<CombatRolesYaml>(
|
const combatRoles = await vfs.yaml<CombatRolesYaml>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/combat_roles.yaml`
|
`Data/XML/item_defs/vehicles/common/combat_roles.yaml`
|
||||||
);
|
);
|
||||||
const consumableNativeNames: Record<string, number> = {};
|
const consumableNativeNames: Record<string, number> = {};
|
||||||
const provisionNativeNames: Record<string, number> = {};
|
const provisionNativeNames: Record<string, number> = {};
|
||||||
@@ -727,8 +721,8 @@ export async function definitions() {
|
|||||||
const idToNation: Record<number, string> = {};
|
const idToNation: Record<number, string> = {};
|
||||||
|
|
||||||
for (const nation of nations) {
|
for (const nation of nations) {
|
||||||
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`
|
`Data/XML/item_defs/vehicles/${nation}/list.xml`
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const tankKey in tankList.root) {
|
for (const tankKey in tankList.root) {
|
||||||
@@ -811,24 +805,24 @@ export async function definitions() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (const nation of nations) {
|
for (const nation of nations) {
|
||||||
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`
|
`Data/XML/item_defs/vehicles/${nation}/list.xml`
|
||||||
);
|
);
|
||||||
const turretList = await readXMLDVPL<{
|
const turretList = await vfs.xml<{
|
||||||
root: TurretDefinitionsList;
|
root: TurretDefinitionsList;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/turrets.xml`);
|
}>(`Data/XML/item_defs/vehicles/${nation}/components/turrets.xml`);
|
||||||
const gunList = await readXMLDVPL<{
|
const gunList = await vfs.xml<{
|
||||||
root: GunDefinitionsList;
|
root: GunDefinitionsList;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/guns.xml`);
|
}>(`Data/XML/item_defs/vehicles/${nation}/components/guns.xml`);
|
||||||
const shellList = await readXMLDVPL<{
|
const shellList = await vfs.xml<{
|
||||||
root: ShellDefinitionsList;
|
root: ShellDefinitionsList;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/shells.xml`);
|
}>(`Data/XML/item_defs/vehicles/${nation}/components/shells.xml`);
|
||||||
const enginesList = await readXMLDVPL<{
|
const enginesList = await vfs.xml<{
|
||||||
root: EngineDefinitionsList;
|
root: EngineDefinitionsList;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/engines.xml`);
|
}>(`Data/XML/item_defs/vehicles/${nation}/components/engines.xml`);
|
||||||
const chassisList = await readXMLDVPL<{
|
const chassisList = await vfs.xml<{
|
||||||
root: ChassisDefinitionsList;
|
root: ChassisDefinitionsList;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/chassis.xml`);
|
}>(`Data/XML/item_defs/vehicles/${nation}/components/chassis.xml`);
|
||||||
|
|
||||||
function resolveUnlocks(unlocks?: BlitzModuleType) {
|
function resolveUnlocks(unlocks?: BlitzModuleType) {
|
||||||
if (!unlocks) return [];
|
if (!unlocks) return [];
|
||||||
@@ -879,11 +873,11 @@ export async function definitions() {
|
|||||||
const trackXps = new Map<number, ResearchCost>();
|
const trackXps = new Map<number, ResearchCost>();
|
||||||
const tank = tankList.root[tankKey];
|
const tank = tankList.root[tankKey];
|
||||||
let tankPrice: TankPrice;
|
let tankPrice: TankPrice;
|
||||||
const tankDefinition = await readXMLDVPL<{ root: VehicleDefinitions }>(
|
const tankDefinition = await vfs.xml<{ root: VehicleDefinitions }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/${tankKey}.xml`
|
`Data/XML/item_defs/vehicles/${nation}/${tankKey}.xml`
|
||||||
);
|
);
|
||||||
const tankParameters = await readYAMLDVPL<TankParameters>(
|
const tankParameters = await vfs.yaml<TankParameters>(
|
||||||
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
|
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
|
||||||
);
|
);
|
||||||
const turretOrigin = tankDefinition.root.hull.turretPositions.turret
|
const turretOrigin = tankDefinition.root.hull.turretPositions.turret
|
||||||
.split(" ")
|
.split(" ")
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { readStringDVPL } from "../../src/core/blitz/readStringDVPL";
|
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { readYAMLDVPL } from "../core/blitz/readYAMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import { OptionalDevices } from "./definitions";
|
import { OptionalDevices } from "./definitions";
|
||||||
|
|
||||||
interface Mappings {
|
interface Mappings {
|
||||||
@@ -20,15 +16,15 @@ export async function equipmentIcons() {
|
|||||||
console.log("Building equipment icons...");
|
console.log("Building equipment icons...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("equipment icons");
|
using uploader = new AssetUploader("equipment icons");
|
||||||
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
|
const optionalDevices = await vfs.xml<{ root: OptionalDevices }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/optional_devices.xml`
|
`Data/XML/item_defs/vehicles/common/optional_devices.xml`
|
||||||
);
|
);
|
||||||
const mappings = await readYAMLDVPL<Mappings>(
|
const mappings = await vfs.yaml<Mappings>(
|
||||||
`${DATA}/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`
|
`Data/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`
|
||||||
);
|
);
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(
|
await vfs.file(
|
||||||
`${DATA}/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`
|
`Data/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -54,7 +50,7 @@ export async function equipmentIcons() {
|
|||||||
if (configPath.startsWith("Gfx/Lobby")) {
|
if (configPath.startsWith("Gfx/Lobby")) {
|
||||||
const configPathWebp = configPath.replace(".txt", "");
|
const configPathWebp = configPath.replace(".txt", "");
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/${configPathWebp}.packed.webp`)
|
await vfs.file(`Data/${configPathWebp}.packed.webp`)
|
||||||
);
|
);
|
||||||
const content = await image.trim().toBuffer();
|
const content = await image.trim().toBuffer();
|
||||||
|
|
||||||
@@ -63,7 +59,7 @@ export async function equipmentIcons() {
|
|||||||
content,
|
content,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const config = await readStringDVPL(`${DATA}/${configPath}`);
|
const config = await vfs.text(`Data/${configPath}`);
|
||||||
const sizes = config.split("\n")[4].split(" ").map(Number);
|
const sizes = config.split("\n")[4].split(" ").map(Number);
|
||||||
const content = await image
|
const content = await image
|
||||||
.clone()
|
.clone()
|
||||||
|
|||||||
@@ -1,37 +1,28 @@
|
|||||||
import { readdir } from "fs/promises";
|
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../core/blitz/readDVPLFile";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
|
|
||||||
export async function flags() {
|
export async function flags() {
|
||||||
console.log("Building flags...");
|
console.log("Building flags...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("flags");
|
using uploader = new AssetUploader("flags");
|
||||||
const circleFiles = await readdir(`${DATA}/Gfx/Lobby/flags`).then((files) =>
|
const circleFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter(
|
||||||
files.filter(
|
|
||||||
(flag) =>
|
(flag) =>
|
||||||
flag.startsWith("flag_profile-stat_") &&
|
flag.startsWith("flag_profile-stat_") &&
|
||||||
!flag.endsWith("@2x.packed.webp")
|
!flag.endsWith("@2x.packed.webp")
|
||||||
)
|
);
|
||||||
);
|
const scratchedFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter(
|
||||||
const scratchedFiles = await readdir(`${DATA}/Gfx/Lobby/flags`).then(
|
|
||||||
(files) =>
|
|
||||||
files.filter(
|
|
||||||
(flag) =>
|
(flag) =>
|
||||||
flag.startsWith("flag_tutor-tank_") &&
|
flag.startsWith("flag_tutor-tank_") &&
|
||||||
!flag.endsWith("@2x.packed.webp")
|
!flag.endsWith("@2x.packed.webp")
|
||||||
)
|
)
|
||||||
);
|
const fadedFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter(
|
||||||
const fadedFiles = await readdir(`${DATA}/Gfx/Lobby/flags`).then((files) =>
|
|
||||||
files.filter(
|
|
||||||
(flag) =>
|
(flag) =>
|
||||||
flag.startsWith("flag_filter_") && flag.endsWith("@2x.packed.webp")
|
flag.startsWith("flag_filter_") && flag.endsWith("@2x.packed.webp")
|
||||||
)
|
)
|
||||||
);
|
|
||||||
|
|
||||||
for (const flag of circleFiles) {
|
for (const flag of circleFiles) {
|
||||||
const image = sharp(await readDVPLFile(`${DATA}/Gfx/Lobby/flags/${flag}`));
|
const image = sharp(await vfs.file(`Data/Gfx/Lobby/flags/${flag}`));
|
||||||
const content = await image.trim({ threshold: 100 }).toBuffer();
|
const content = await image.trim({ threshold: 100 }).toBuffer();
|
||||||
const name = flag.match(/flag_profile-stat_(.+)\.packed\.webp/)![1];
|
const name = flag.match(/flag_profile-stat_(.+)\.packed\.webp/)![1];
|
||||||
|
|
||||||
@@ -42,7 +33,7 @@ export async function flags() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const flag of scratchedFiles) {
|
for (const flag of scratchedFiles) {
|
||||||
const content = await readDVPLFile(`${DATA}/Gfx/Lobby/flags/${flag}`);
|
const content = await vfs.file(`Data/Gfx/Lobby/flags/${flag}`);
|
||||||
const name = flag.match(/flag_tutor-tank_(.+)\.packed\.webp/)![1];
|
const name = flag.match(/flag_tutor-tank_(.+)\.packed\.webp/)![1];
|
||||||
|
|
||||||
uploader.add({
|
uploader.add({
|
||||||
@@ -53,7 +44,7 @@ export async function flags() {
|
|||||||
|
|
||||||
for (const flag of fadedFiles) {
|
for (const flag of fadedFiles) {
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
await readDVPLFile(`${DATA}/Gfx/Lobby/flags/${flag}`)
|
await vfs.file(`Data/Gfx/Lobby/flags/${flag}`)
|
||||||
)
|
)
|
||||||
.trim({
|
.trim({
|
||||||
threshold: 100,
|
threshold: 100,
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { readYAMLDVPL } from "../core/blitz/readYAMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import { SquadBattleTypeStyles } from "./definitions";
|
import { SquadBattleTypeStyles } from "./definitions";
|
||||||
|
|
||||||
export async function gameModeBanners() {
|
export async function gameModeBanners() {
|
||||||
console.log("Building game mode banners...");
|
console.log("Building game mode banners...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("game mode banners");
|
using uploader = new AssetUploader("game mode banners");
|
||||||
const gameTypeSelectorStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`
|
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`
|
||||||
);
|
);
|
||||||
const squadBattleTypeStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`
|
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`
|
||||||
);
|
);
|
||||||
const bannerMatches: { name: string; path: string }[] = [];
|
const bannerMatches: { name: string; path: string }[] = [];
|
||||||
|
|
||||||
@@ -37,7 +35,7 @@ export async function gameModeBanners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
await readDVPLFile(`${DATA}${path}.packed.webp`)
|
await vfs.file(`Data${path}.packed.webp`)
|
||||||
)
|
)
|
||||||
.trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
.trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
import { argv } from "process";
|
import { argv } from "process";
|
||||||
|
|
||||||
config();
|
|
||||||
|
|
||||||
const allTargets = argv.includes("--all-targets");
|
|
||||||
const targets = argv
|
|
||||||
.find((argument) => argument.startsWith("--target"))
|
|
||||||
?.split("=")[1]
|
|
||||||
.split(",");
|
|
||||||
|
|
||||||
import { boosterIcons } from "./boosterIcons";
|
import { boosterIcons } from "./boosterIcons";
|
||||||
import { camouflageIcons } from "./camouflageIcons";
|
import { camouflageIcons } from "./camouflageIcons";
|
||||||
|
import { vfs } from "./constants";
|
||||||
import { consumableProvisionIcons } from "./consumableProvisionIcons";
|
import { consumableProvisionIcons } from "./consumableProvisionIcons";
|
||||||
import { currencies } from "./currencies";
|
import { currencies } from "./currencies";
|
||||||
import { definitions } from "./definitions";
|
import { definitions } from "./definitions";
|
||||||
@@ -27,6 +19,14 @@ import { tankArmors } from "./tankArmors";
|
|||||||
import { tankIcons } from "./tankIcons";
|
import { tankIcons } from "./tankIcons";
|
||||||
import { tankModels } from "./tankModels";
|
import { tankModels } from "./tankModels";
|
||||||
|
|
||||||
|
config();
|
||||||
|
|
||||||
|
const allTargets = argv.includes("--all-targets");
|
||||||
|
const targets = argv
|
||||||
|
.find((argument) => argument.startsWith("--target"))
|
||||||
|
?.split("=")[1]
|
||||||
|
.split(",");
|
||||||
|
|
||||||
if (!targets && !allTargets) throw new Error("No target(s) specified");
|
if (!targets && !allTargets) throw new Error("No target(s) specified");
|
||||||
|
|
||||||
const methods = [
|
const methods = [
|
||||||
@@ -59,3 +59,6 @@ for (const method of methods) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vfs.dispose();
|
||||||
|
process.exit(0);
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import { toUniqueId } from "@blitzkit/core";
|
import { toUniqueId } from "@blitzkit/core";
|
||||||
import { readdir } from "fs/promises";
|
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import { VehicleDefinitionList } from "./definitions";
|
import { VehicleDefinitionList } from "./definitions";
|
||||||
|
|
||||||
export async function migration() {
|
export async function migration() {
|
||||||
@@ -10,13 +8,11 @@ export async function migration() {
|
|||||||
|
|
||||||
using uploader = new AssetUploader("migration map");
|
using uploader = new AssetUploader("migration map");
|
||||||
const map: Record<string, number> = {};
|
const map: Record<string, number> = {};
|
||||||
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common");
|
||||||
(nations) => nations.filter((nation) => nation !== "common")
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const nation of nations) {
|
for (const nation of nations) {
|
||||||
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`
|
`Data/XML/item_defs/vehicles/${nation}/list.xml`
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const tankKey in tankList.root) {
|
for (const tankKey in tankList.root) {
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
import { readdir } from "fs/promises";
|
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
|
|
||||||
export async function moduleIcons() {
|
export async function moduleIcons() {
|
||||||
console.log("Building module icons...");
|
console.log("Building module icons...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("module icons");
|
using uploader = new AssetUploader("module icons");
|
||||||
const files = await readdir(`${DATA}/Gfx/UI/ModulesTechTree`).then((dir) =>
|
const files = vfs.dir(`Data/Gfx/UI/ModulesTechTree`).filter(
|
||||||
dir.filter(
|
|
||||||
(file) => !file.endsWith("@2x.packed.webp") && file.startsWith("vehicle")
|
(file) => !file.endsWith("@2x.packed.webp") && file.startsWith("vehicle")
|
||||||
)
|
)
|
||||||
);
|
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const nameRaw = file.match(/vehicle(.+)\.packed\.webp/)![1];
|
const nameRaw = file.match(/vehicle(.+)\.packed\.webp/)![1];
|
||||||
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
|
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
await readDVPLFile(`${DATA}/Gfx/UI/ModulesTechTree/${file}`)
|
await vfs.file(`Data/Gfx/UI/ModulesTechTree/${file}`)
|
||||||
)
|
)
|
||||||
.trim()
|
.trim()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
import { readdir } from "fs/promises";
|
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../../src/core/blitz/readDVPLFile";
|
|
||||||
import { readStringDVPL } from "../../src/core/blitz/readStringDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
|
|
||||||
export async function shellIcons() {
|
export async function shellIcons() {
|
||||||
console.log("Building shell icons...");
|
console.log("Building shell icons...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("shell icons");
|
using uploader = new AssetUploader("shell icons");
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(
|
await vfs.file(
|
||||||
`${DATA}/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp`
|
`Data/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const files = await readdir(
|
const files = vfs.dir(
|
||||||
`${DATA}/Gfx/Shared/tank-supply/ammunition/big`
|
`Data/Gfx/Shared/tank-supply/ammunition/big`
|
||||||
).then((files) => files.filter((file) => file.endsWith("_l.txt")));
|
).filter((file) => file.endsWith("_l.txt"))
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const name = file.match(/(.+)_l\.txt/)![1];
|
const name = file.match(/(.+)_l\.txt/)![1];
|
||||||
const sizes = (
|
const sizes = (
|
||||||
await readStringDVPL(
|
await vfs.text(
|
||||||
`${DATA}/Gfx/Shared/tank-supply/ammunition/big/${file}`
|
`Data/Gfx/Shared/tank-supply/ammunition/big/${file}`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.split("\n")[4]
|
.split("\n")[4]
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { TankClass } from "@blitzkit/core";
|
import { TankClass } from "@blitzkit/core";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { readDVPLFile } from "../core/blitz/readDVPLFile";
|
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
|
|
||||||
interface SkillIcon {
|
interface SkillIcon {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -27,8 +25,8 @@ export interface Avatar {
|
|||||||
export async function skillIcons() {
|
export async function skillIcons() {
|
||||||
console.log("Building skill icons...");
|
console.log("Building skill icons...");
|
||||||
|
|
||||||
const avatar = await readXMLDVPL<{ root: Avatar }>(
|
const avatar = await vfs.xml<{ root: Avatar }>(
|
||||||
`${DATA}/XML/item_defs/tankmen/avatar.xml`
|
`Data/XML/item_defs/tankmen/avatar.xml`
|
||||||
);
|
);
|
||||||
using uploader = new AssetUploader("skill icons");
|
using uploader = new AssetUploader("skill icons");
|
||||||
|
|
||||||
@@ -36,8 +34,8 @@ export async function skillIcons() {
|
|||||||
const skill = avatar.root.skills[key];
|
const skill = avatar.root.skills[key];
|
||||||
const icon = Array.isArray(skill.icon) ? skill.icon[0] : skill.icon;
|
const icon = Array.isArray(skill.icon) ? skill.icon[0] : skill.icon;
|
||||||
const name = icon.name.split("/").at(-1)!.replace(/_\d$/, "");
|
const name = icon.name.split("/").at(-1)!.replace(/_\d$/, "");
|
||||||
const path = `${DATA}${icon.name.replace("~res:", "")}.packed.webp`;
|
const path = `Data${icon.name.replace("~res:", "")}.packed.webp`;
|
||||||
const image = sharp(await readDVPLFile(path)).trim();
|
const image = sharp(await vfs.file(path)).trim();
|
||||||
const content = await image.toBuffer();
|
const content = await image.toBuffer();
|
||||||
|
|
||||||
await uploader.add({ content, path: `icons/skills/${name}.webp` });
|
await uploader.add({ content, path: `icons/skills/${name}.webp` });
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { toUniqueId } from "@blitzkit/core";
|
import { toUniqueId } from "@blitzkit/core";
|
||||||
import { NodeIO } from "@gltf-transform/core";
|
import { NodeIO } from "@gltf-transform/core";
|
||||||
import { readdir } from "fs/promises";
|
|
||||||
import { extractArmor } from "../core/blitz/extractArmor";
|
import { extractArmor } from "../core/blitz/extractArmor";
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import { VehicleDefinitionList } from "./definitions";
|
import { VehicleDefinitionList } from "./definitions";
|
||||||
|
|
||||||
export async function tankArmors() {
|
export async function tankArmors() {
|
||||||
@@ -12,13 +10,11 @@ export async function tankArmors() {
|
|||||||
|
|
||||||
using uploader = new AssetUploader("tank armors");
|
using uploader = new AssetUploader("tank armors");
|
||||||
const nodeIO = new NodeIO();
|
const nodeIO = new NodeIO();
|
||||||
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common");
|
||||||
(nations) => nations.filter((nation) => nation !== "common")
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const nation of nations) {
|
for (const nation of nations) {
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await vfs.xml<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`
|
`Data/XML/item_defs/vehicles/${nation}/list.xml`
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const tankKey in tanks.root) {
|
for (const tankKey in tanks.root) {
|
||||||
@@ -27,7 +23,7 @@ export async function tankArmors() {
|
|||||||
if (tankKey.includes("tutorial_bot")) continue;
|
if (tankKey.includes("tutorial_bot")) continue;
|
||||||
|
|
||||||
const id = toUniqueId(nation, tank.id);
|
const id = toUniqueId(nation, tank.id);
|
||||||
const model = await extractArmor(DATA, `${nation}-${tankKey}`);
|
const model = await extractArmor(vfs, `${nation}-${tankKey}`);
|
||||||
const content = await nodeIO.writeBinary(model);
|
const content = await nodeIO.writeBinary(model);
|
||||||
|
|
||||||
await uploader.add({ path: `3d/tanks/armor/${id}.glb`, content });
|
await uploader.add({ path: `3d/tanks/armor/${id}.glb`, content });
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { NATION_IDS } from "@blitzkit/core";
|
import { NATION_IDS } from "@blitzkit/core";
|
||||||
import { readdir } from "fs/promises";
|
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import type { Vector3Tuple } from "three";
|
import type { Vector3Tuple } from "three";
|
||||||
import { readDVPLFile } from "../core/blitz/readDVPLFile";
|
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { readYAMLDVPL } from "../core/blitz/readYAMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import type { VehicleDefinitionList } from "./definitions";
|
import type { VehicleDefinitionList } from "./definitions";
|
||||||
|
|
||||||
export interface TankParameters {
|
export interface TankParameters {
|
||||||
@@ -45,13 +41,11 @@ export async function tankIcons() {
|
|||||||
console.log("Building tank icons...");
|
console.log("Building tank icons...");
|
||||||
|
|
||||||
using uploader = new AssetUploader("tank icons");
|
using uploader = new AssetUploader("tank icons");
|
||||||
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common")
|
||||||
(nations) => nations.filter((nation) => nation !== "common")
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const nation of nations) {
|
for (const nation of nations) {
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await vfs.xml<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`
|
`Data/XML/item_defs/vehicles/${nation}/list.xml`
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const tankKey in tanks.root) {
|
for (const tankKey in tanks.root) {
|
||||||
@@ -62,19 +56,19 @@ export async function tankIcons() {
|
|||||||
const nationVehicleId = tank.id;
|
const nationVehicleId = tank.id;
|
||||||
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
||||||
|
|
||||||
const parameters = await readYAMLDVPL<TankParameters>(
|
const parameters = await vfs.yaml<TankParameters>(
|
||||||
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
|
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
|
||||||
);
|
);
|
||||||
const smallPath = `${DATA}/${parameters.resourcesPath.smallIconPath
|
const smallPath = `Data/${parameters.resourcesPath.smallIconPath
|
||||||
.replace(/~res:\//, "")
|
.replace(/~res:\//, "")
|
||||||
.replace(/\..+/, "")}.packed.webp`;
|
.replace(/\..+/, "")}.packed.webp`;
|
||||||
const bigPath = `${DATA}/${parameters.resourcesPath.bigIconPath
|
const bigPath = `Data/${parameters.resourcesPath.bigIconPath
|
||||||
.replace(/~res:\//, "")
|
.replace(/~res:\//, "")
|
||||||
.replace(/\..+/, "")}.packed.webp`;
|
.replace(/\..+/, "")}.packed.webp`;
|
||||||
const big = await sharp(await readDVPLFile(bigPath))
|
const big = await sharp(await vfs.file(bigPath))
|
||||||
.trim()
|
.trim()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
const small = await sharp(await readDVPLFile(smallPath))
|
const small = await sharp(await vfs.file(smallPath))
|
||||||
.trim()
|
.trim()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { toUniqueId } from "@blitzkit/core";
|
import { toUniqueId } from "@blitzkit/core";
|
||||||
import { NodeIO } from "@gltf-transform/core";
|
import { NodeIO } from "@gltf-transform/core";
|
||||||
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
|
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
|
||||||
import { readdir } from "fs/promises";
|
|
||||||
import { extractModel } from "../core/blitz/extractModel";
|
import { extractModel } from "../core/blitz/extractModel";
|
||||||
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
|
||||||
import { readYAMLDVPL } from "../core/blitz/readYAMLDVPL";
|
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
import { DATA } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import { VehicleDefinitionList } from "./definitions";
|
import { VehicleDefinitionList } from "./definitions";
|
||||||
import { TankParameters } from "./tankIcons";
|
import { TankParameters } from "./tankIcons";
|
||||||
|
|
||||||
@@ -15,14 +12,12 @@ export async function tankModels() {
|
|||||||
|
|
||||||
using uploader = new AssetUploader("tank models");
|
using uploader = new AssetUploader("tank models");
|
||||||
const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS);
|
const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS);
|
||||||
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common")
|
||||||
(nations) => nations.filter((nation) => nation !== "common")
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const nationIndex in nations) {
|
for (const nationIndex in nations) {
|
||||||
const nation = nations[nationIndex];
|
const nation = nations[nationIndex];
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await vfs.xml<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`
|
`Data/XML/item_defs/vehicles/${nation}/list.xml`
|
||||||
);
|
);
|
||||||
const entries = Object.entries(tanks.root);
|
const entries = Object.entries(tanks.root);
|
||||||
|
|
||||||
@@ -30,11 +25,11 @@ export async function tankModels() {
|
|||||||
if (tankKey.includes("tutorial_bot")) continue;
|
if (tankKey.includes("tutorial_bot")) continue;
|
||||||
|
|
||||||
const id = toUniqueId(nation, tank.id);
|
const id = toUniqueId(nation, tank.id);
|
||||||
const parameters = await readYAMLDVPL<TankParameters>(
|
const parameters = await vfs.yaml<TankParameters>(
|
||||||
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
|
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
|
||||||
);
|
);
|
||||||
const model = await extractModel(
|
const model = await extractModel(
|
||||||
DATA,
|
vfs,
|
||||||
parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, "")
|
parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, "")
|
||||||
);
|
);
|
||||||
const content = Buffer.from(await nodeIO.writeBinary(model));
|
const content = Buffer.from(await nodeIO.writeBinary(model));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { argv } from "process";
|
|||||||
const projectDir = "../..";
|
const projectDir = "../..";
|
||||||
const srcRoot = `${projectDir}/packages/bot`;
|
const srcRoot = `${projectDir}/packages/bot`;
|
||||||
const distRoot = `${projectDir}/dist/bot`;
|
const distRoot = `${projectDir}/dist/bot`;
|
||||||
const modulesRoot = `${projectDir}/packages/bot/node_modules`;
|
const modulesRoot = "../../node_modules";
|
||||||
const workers = await readdir(`${srcRoot}/src/workers`);
|
const workers = await readdir(`${srcRoot}/src/workers`);
|
||||||
const dev = argv.includes("--dev");
|
const dev = argv.includes("--dev");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { readdir, readFile, writeFile } from "fs/promises";
|
||||||
|
|
||||||
|
const SOURCES = "../../packages/closed/protos";
|
||||||
|
const TARGET = "../../packages/closed/src/unreal/catalogItemAccessor.ts";
|
||||||
|
|
||||||
|
const componentPattern = /^message (\w+)Component {/gm;
|
||||||
|
|
||||||
|
const files = await readdir(SOURCES).then((files) =>
|
||||||
|
files.filter(
|
||||||
|
(file) =>
|
||||||
|
file.startsWith("blitz_static_") && file.endsWith("_component.proto")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const imports: { name: string; file: string }[] = [];
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const content = await readFile(`${SOURCES}/${file}`).then((buffer) =>
|
||||||
|
buffer.toString()
|
||||||
|
);
|
||||||
|
const components = content.matchAll(componentPattern);
|
||||||
|
|
||||||
|
for (const match of components) {
|
||||||
|
const name = match[1];
|
||||||
|
const duplicate = imports.find((_import) => _import.name === name);
|
||||||
|
|
||||||
|
if (duplicate) {
|
||||||
|
console.warn(
|
||||||
|
`Duplicate component ${name} across ${duplicate.file}.proto and ${file}`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const importFile = file.replace(".proto", "");
|
||||||
|
|
||||||
|
imports.push({ name, file: importFile });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = "";
|
||||||
|
|
||||||
|
for (const _import of imports) {
|
||||||
|
content += `import { ${_import.name}Component } from "@protos/${_import.file}";\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
content +=
|
||||||
|
'import { BaseCatalogItemAccessor } from "./baseCatalogItemAccessor";\n\n';
|
||||||
|
content +=
|
||||||
|
"export class CatalogItemAccessor extends BaseCatalogItemAccessor {\n";
|
||||||
|
|
||||||
|
for (const _import of imports) {
|
||||||
|
const strippedName = _import.name.replace("BlitzStatic", "");
|
||||||
|
const defaultName = strippedName[0].toLowerCase() + strippedName.slice(1);
|
||||||
|
|
||||||
|
content += ` ${strippedName}(name = "${defaultName}Component") {\n`;
|
||||||
|
content += ` return this.component(name, ${_import.name}Component);\n`;
|
||||||
|
content += " }\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
content += "}\n";
|
||||||
|
|
||||||
|
await writeFile(TARGET, content);
|
||||||
@@ -4,21 +4,18 @@ const IMPORT = "@protos/auto_legacy_proxy_client";
|
|||||||
const SOURCE = "../../packages/closed/protos/auto_legacy_proxy_client.proto";
|
const SOURCE = "../../packages/closed/protos/auto_legacy_proxy_client.proto";
|
||||||
const TARGET = "../../packages/closed/src/unreal/methodPopulatedProxyClient.ts";
|
const TARGET = "../../packages/closed/src/unreal/methodPopulatedProxyClient.ts";
|
||||||
|
|
||||||
const requestPattern = /message (\w+)Request {/g;
|
const requestPattern = /^message (\w+)Request {/gm;
|
||||||
const source = await readFile(SOURCE).then((buffer) => buffer.toString());
|
const source = await readFile(SOURCE).then((buffer) => buffer.toString());
|
||||||
const requests = source.matchAll(requestPattern);
|
const requests = source.matchAll(requestPattern);
|
||||||
|
|
||||||
const imports: string[] = [];
|
const imports: string[] = [];
|
||||||
const methods: { name: string; method: string }[] = [];
|
const names: string[] = [];
|
||||||
|
|
||||||
for (const match of requests) {
|
for (const match of requests) {
|
||||||
const name = match[1];
|
const name = match[1];
|
||||||
// const method =
|
|
||||||
// name.charAt(0).toLowerCase() + name.slice(1).replace(/Async$/, "");
|
|
||||||
const method = name;
|
|
||||||
|
|
||||||
imports.push(`${name}Request`, `${name}Response`);
|
imports.push(`${name}Request`, `${name}Response`);
|
||||||
methods.push({ name, method });
|
names.push(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = "";
|
let content = "";
|
||||||
@@ -31,9 +28,9 @@ content += 'import { BaseProxyClient } from "./baseProxyClient";\n\n';
|
|||||||
|
|
||||||
content +=
|
content +=
|
||||||
"export abstract class MethodPopulatedProxyClient extends BaseProxyClient {\n";
|
"export abstract class MethodPopulatedProxyClient extends BaseProxyClient {\n";
|
||||||
content += methods
|
content += names
|
||||||
.map(({ name, method }) => {
|
.map((name) => {
|
||||||
return ` ${method}(data: ${name}Request) {
|
return ` ${name}(data: ${name}Request) {
|
||||||
return this.request("${name}Request", ${name}Request, data, ${name}Response);
|
return this.request("${name}Request", ${name}Request, data, ${name}Response);
|
||||||
}\n`;
|
}\n`;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,23 +3,23 @@ import {
|
|||||||
Sc2ReadStream,
|
Sc2ReadStream,
|
||||||
ScgReadStream,
|
ScgReadStream,
|
||||||
VertexAttribute,
|
VertexAttribute,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { Accessor, Document, Node, Scene } from '@gltf-transform/core';
|
import { Accessor, Document, Node, Scene } from "@gltf-transform/core";
|
||||||
import { times } from 'lodash-es';
|
import { times } from "lodash-es";
|
||||||
|
import { SteamVFS } from "../steam/vfs";
|
||||||
import {
|
import {
|
||||||
vertexAttributeGLTFName,
|
vertexAttributeGLTFName,
|
||||||
vertexAttributeGltfVectorSizes,
|
vertexAttributeGltfVectorSizes,
|
||||||
} from './extractModel/constants';
|
} from "./extractModel/constants";
|
||||||
import { readDVPLFile } from './readDVPLFile';
|
|
||||||
|
|
||||||
export async function extractArmor(data: string, fileName: string) {
|
export async function extractArmor(vfs: SteamVFS, fileName: string) {
|
||||||
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2`;
|
const sc2Path = `Data/3d/Tanks/CollisionMeshes/${fileName}.sc2`;
|
||||||
const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg`;
|
const scgPath = `Data/3d/Tanks/CollisionMeshes/${fileName}.scg`;
|
||||||
const sc2 = new Sc2ReadStream(
|
const sc2 = new Sc2ReadStream(
|
||||||
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer,
|
(await vfs.file(sc2Path)).buffer as ArrayBuffer
|
||||||
).sc2();
|
).sc2();
|
||||||
const scg = new ScgReadStream(
|
const scg = new ScgReadStream(
|
||||||
(await readDVPLFile(scgPath)).buffer as ArrayBuffer,
|
(await vfs.file(scgPath)).buffer as ArrayBuffer
|
||||||
).scg();
|
).scg();
|
||||||
const document = new Document();
|
const document = new Document();
|
||||||
const scene = document.createScene();
|
const scene = document.createScene();
|
||||||
@@ -29,21 +29,21 @@ export async function extractArmor(data: string, fileName: string) {
|
|||||||
hierarchies.forEach((hierarchy) => {
|
hierarchies.forEach((hierarchy) => {
|
||||||
const components = times(
|
const components = times(
|
||||||
hierarchy.components.count,
|
hierarchy.components.count,
|
||||||
(index) => hierarchy.components[index.toString().padStart(4, '0')],
|
(index) => hierarchy.components[index.toString().padStart(4, "0")]
|
||||||
);
|
);
|
||||||
|
|
||||||
components.forEach((component) => {
|
components.forEach((component) => {
|
||||||
switch (component['comp.typename']) {
|
switch (component["comp.typename"]) {
|
||||||
case 'TransformComponent':
|
case "TransformComponent":
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'RenderComponent': {
|
case "RenderComponent": {
|
||||||
const batch = component['rc.renderObj']['ro.batches']['0000'];
|
const batch = component["rc.renderObj"]["ro.batches"]["0000"];
|
||||||
const polygonGroup = scg.get(batch['rb.datasource']);
|
const polygonGroup = scg.get(batch["rb.datasource"]);
|
||||||
|
|
||||||
if (!polygonGroup) {
|
if (!polygonGroup) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Missing polygon group ${batch['rb.datasource']} (${hierarchy.name}); skipping...`,
|
`Missing polygon group ${batch["rb.datasource"]} (${hierarchy.name}); skipping...`
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -73,7 +73,7 @@ export async function extractArmor(data: string, fileName: string) {
|
|||||||
VertexAttribute.TANGENT,
|
VertexAttribute.TANGENT,
|
||||||
attributes
|
attributes
|
||||||
.get(VertexAttribute.TANGENT)!
|
.get(VertexAttribute.TANGENT)!
|
||||||
.map((tangent) => [...tangent, 1]),
|
.map((tangent) => [...tangent, 1])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ export async function extractArmor(data: string, fileName: string) {
|
|||||||
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
||||||
const accessor = document
|
const accessor = document
|
||||||
.createAccessor(name)
|
.createAccessor(name)
|
||||||
.setType(vertexSize === 1 ? 'SCALAR' : `VEC${vertexSize}`)
|
.setType(vertexSize === 1 ? "SCALAR" : `VEC${vertexSize}`)
|
||||||
.setArray(new Float32Array(value.flat()))
|
.setArray(new Float32Array(value.flat()))
|
||||||
.setBuffer(buffer);
|
.setBuffer(buffer);
|
||||||
|
|
||||||
@@ -94,19 +94,19 @@ export async function extractArmor(data: string, fileName: string) {
|
|||||||
|
|
||||||
hardJointIndices.forEach((hardJointIndex) => {
|
hardJointIndices.forEach((hardJointIndex) => {
|
||||||
const node = document.createNode(
|
const node = document.createNode(
|
||||||
`${hierarchy.name}_armor_${hardJointIndex}`,
|
`${hierarchy.name}_armor_${hardJointIndex}`
|
||||||
);
|
);
|
||||||
const mesh = document.createMesh(batch['##name']);
|
const mesh = document.createMesh(batch["##name"]);
|
||||||
const indicesAccessor = document
|
const indicesAccessor = document
|
||||||
.createAccessor()
|
.createAccessor()
|
||||||
.setType('SCALAR')
|
.setType("SCALAR")
|
||||||
.setArray(
|
.setArray(
|
||||||
new Uint16Array(
|
new Uint16Array(
|
||||||
polygonGroup.indices.filter(
|
polygonGroup.indices.filter(
|
||||||
(index) =>
|
(index) =>
|
||||||
vertexHardJointIndices.get(index) === hardJointIndex,
|
vertexHardJointIndices.get(index) === hardJointIndex
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setBuffer(buffer);
|
.setBuffer(buffer);
|
||||||
const primitive = document
|
const primitive = document
|
||||||
@@ -127,18 +127,18 @@ export async function extractArmor(data: string, fileName: string) {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
`Unhandled component type: ${component['comp.typename']}`,
|
`Unhandled component type: ${component["comp.typename"]}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hierarchy['#hierarchy']) {
|
if (hierarchy["#hierarchy"]) {
|
||||||
parseHierarchies(hierarchy['#hierarchy'], parent);
|
parseHierarchies(hierarchy["#hierarchy"], parent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
parseHierarchies(sc2['#hierarchy'], scene);
|
parseHierarchies(sc2["#hierarchy"], scene);
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,8 @@ import { Document, Material, Node, Scene } from "@gltf-transform/core";
|
|||||||
import { dedup, prune } from "@gltf-transform/functions";
|
import { dedup, prune } from "@gltf-transform/functions";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
|
import { SteamVFS } from "../../steam/vfs";
|
||||||
import { readBaseColor } from "../readBaseColor";
|
import { readBaseColor } from "../readBaseColor";
|
||||||
import { readDVPLFile } from "../readDVPLFile";
|
|
||||||
import { readNormal } from "../readNormal";
|
import { readNormal } from "../readNormal";
|
||||||
import { readRoughnessMetallic } from "../readRoughnessMetallic";
|
import { readRoughnessMetallic } from "../readRoughnessMetallic";
|
||||||
import {
|
import {
|
||||||
@@ -25,14 +25,14 @@ const omitMeshNames = {
|
|||||||
end: ["_POINT"],
|
end: ["_POINT"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function extractModel(data: string, path: string) {
|
export async function extractModel(vfs: SteamVFS, path: string) {
|
||||||
const sc2Path = `${data}/3d/${path}.sc2`;
|
const sc2Path = `Data/3d/${path}.sc2`;
|
||||||
const scgPath = `${data}/3d/${path}.scg`;
|
const scgPath = `Data/3d/${path}.scg`;
|
||||||
const sc2 = new Sc2ReadStream(
|
const sc2 = new Sc2ReadStream(
|
||||||
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer
|
(await vfs.file(sc2Path)).buffer as ArrayBuffer
|
||||||
).sc2();
|
).sc2();
|
||||||
const scg = new ScgReadStream(
|
const scg = new ScgReadStream(
|
||||||
(await readDVPLFile(scgPath)).buffer as ArrayBuffer
|
(await vfs.file(scgPath)).buffer as ArrayBuffer
|
||||||
).scg();
|
).scg();
|
||||||
const document = new Document();
|
const document = new Document();
|
||||||
const scene = document.createScene();
|
const scene = document.createScene();
|
||||||
@@ -67,11 +67,11 @@ export async function extractModel(data: string, path: string) {
|
|||||||
.setMimeType("image/webp")
|
.setMimeType("image/webp")
|
||||||
.setImage(
|
.setImage(
|
||||||
await readBaseColor(
|
await readBaseColor(
|
||||||
`${data}/3d/${dirname(path)}/${
|
`Data/3d/${dirname(path)}/${
|
||||||
textures.baseColorMap ?? textures.albedo
|
textures.baseColorMap ?? textures.albedo
|
||||||
}`,
|
}`,
|
||||||
textures.miscMap
|
textures.miscMap
|
||||||
? `${data}/3d/${dirname(path)}/${textures.miscMap}`
|
? `Data/3d/${dirname(path)}/${textures.miscMap}`
|
||||||
: undefined
|
: undefined
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -103,7 +103,7 @@ export async function extractModel(data: string, path: string) {
|
|||||||
.setMimeType("image/webp")
|
.setMimeType("image/webp")
|
||||||
.setImage(
|
.setImage(
|
||||||
await readRoughnessMetallic(
|
await readRoughnessMetallic(
|
||||||
`${data}/3d/${dirname(path)}/${textures.baseRMMap}`
|
`Data/3d/${dirname(path)}/${textures.baseRMMap}`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -118,7 +118,7 @@ export async function extractModel(data: string, path: string) {
|
|||||||
.setMimeType("image/webp")
|
.setMimeType("image/webp")
|
||||||
.setImage(
|
.setImage(
|
||||||
await readNormal(
|
await readNormal(
|
||||||
`${data}/3d/${dirname(path)}/${
|
`Data/3d/${dirname(path)}/${
|
||||||
textures.baseNormalMap ?? textures.normalmap
|
textures.baseNormalMap ?? textures.normalmap
|
||||||
}`,
|
}`,
|
||||||
isBase
|
isBase
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { readDVPLFile } from './readDVPLFile';
|
|
||||||
|
|
||||||
export async function readBase64DVPL(file: string) {
|
|
||||||
const buffer = await readDVPLFile(file);
|
|
||||||
return buffer.toString('base64');
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { readFile } from 'fs/promises';
|
|
||||||
import { DATA_COMPRESSED } from '../../buildAssets/constants';
|
|
||||||
import { readDVPL } from './readDVPL';
|
|
||||||
|
|
||||||
export async function readDVPLFile(file: string) {
|
|
||||||
let path: string;
|
|
||||||
|
|
||||||
if (DATA_COMPRESSED) {
|
|
||||||
path = file.endsWith('.dvpl') ? file : `${file}.dvpl`;
|
|
||||||
} else {
|
|
||||||
path = file.replace('.dvpl', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = await readFile(path);
|
|
||||||
|
|
||||||
if (!DATA_COMPRESSED) return content;
|
|
||||||
|
|
||||||
const dvpl = readDVPL(content);
|
|
||||||
|
|
||||||
return dvpl;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { readDVPLFile } from './readDVPLFile';
|
|
||||||
|
|
||||||
export async function readStringDVPL(file: string) {
|
|
||||||
const buffer = await readDVPLFile(file);
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
@@ -1,81 +1,17 @@
|
|||||||
import { DdsReadStream, PvrReadStream } from "@blitzkit/core";
|
import { DdsReadStream, PvrReadStream } from "@blitzkit/core";
|
||||||
import { existsSync } from "fs";
|
import { vfs } from "../../../buildAssets/constants";
|
||||||
import { DATA_COMPRESSED } from "../../../buildAssets/constants";
|
|
||||||
import { readDVPLFile } from "../readDVPLFile";
|
|
||||||
|
|
||||||
export async function readTexture(path: string) {
|
export async function readTexture(path: string) {
|
||||||
const ddsTexturePath = path.replace(".tex", ".dx11.dds");
|
const ddsTexturePath = path.replace(".tex", ".dx11.dds");
|
||||||
const isDds = existsSync(
|
const isDds = vfs.has(ddsTexturePath);
|
||||||
`${ddsTexturePath}${DATA_COMPRESSED ? ".dvpl" : ""}`
|
|
||||||
);
|
|
||||||
const resolvedTexturePath = isDds
|
const resolvedTexturePath = isDds
|
||||||
? ddsTexturePath
|
? ddsTexturePath
|
||||||
: ddsTexturePath.replace(".dds", ".pvr");
|
: ddsTexturePath.replace(".dds", ".pvr");
|
||||||
const decompressedDvpl = await readDVPLFile(`${resolvedTexturePath}.dvpl`);
|
const file = await vfs.file(resolvedTexturePath);
|
||||||
|
|
||||||
const raw = isDds
|
const raw = isDds
|
||||||
? await new DdsReadStream(decompressedDvpl.buffer as ArrayBuffer).dds()
|
? await new DdsReadStream(file.buffer as ArrayBuffer).dds()
|
||||||
: new PvrReadStream(decompressedDvpl.buffer as ArrayBuffer).pvr();
|
: new PvrReadStream(file.buffer as ArrayBuffer).pvr();
|
||||||
|
|
||||||
return raw;
|
return raw;
|
||||||
|
|
||||||
// switch (mutation) {
|
|
||||||
// case TextureMutation.Normal: {
|
|
||||||
// const bytes = 4 * raw.width * raw.height;
|
|
||||||
// for (let index = 0; index < bytes; index += 4) {
|
|
||||||
// /**
|
|
||||||
// * Red is always 255 and blue is always 0. Only alpha and green contain any
|
|
||||||
// * sort of information.
|
|
||||||
// */
|
|
||||||
// let x = raw.data[index + 3] * (2 / 255) - 1;
|
|
||||||
// let y = raw.data[index + 1] * (2 / 255) - 1;
|
|
||||||
// const underSqrt = 1 - x ** 2 - y ** 2;
|
|
||||||
// let z = underSqrt > 0 ? Math.sqrt(underSqrt) : -Math.sqrt(-underSqrt);
|
|
||||||
|
|
||||||
// raw.data[index] = Math.round((x + 1) * (255 / 2));
|
|
||||||
// raw.data[index + 1] = Math.round((y + 1) * (255 / 2));
|
|
||||||
// raw.data[index + 2] = Math.round((z + 1) * (255 / 2));
|
|
||||||
// raw.data[index + 3] = 255;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return await sharp(raw.data, { raw }).jpeg().toBuffer();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// case TextureMutation.RoughnessMetallicness: {
|
|
||||||
// const newImage = sharp(Buffer.alloc(raw.width * raw.height), {
|
|
||||||
// raw: { ...raw, channels: 1 },
|
|
||||||
// });
|
|
||||||
// const metallicness = sharp(raw.data, { raw })
|
|
||||||
// .extractChannel("green")
|
|
||||||
// .jpeg()
|
|
||||||
// .toBuffer();
|
|
||||||
// const roughness = sharp(raw.data, { raw })
|
|
||||||
// .extractChannel("alpha")
|
|
||||||
// .jpeg()
|
|
||||||
// .toBuffer();
|
|
||||||
|
|
||||||
// return await newImage
|
|
||||||
// .joinChannel(await Promise.all([roughness, metallicness]))
|
|
||||||
// .jpeg()
|
|
||||||
// .toBuffer();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// case TextureMutation.Miscellaneous: {
|
|
||||||
// /**
|
|
||||||
// * Alpha is ambient occlusion and green is emissive. But very few tanks
|
|
||||||
// * use emissive so I am ignoring it for now.
|
|
||||||
// */
|
|
||||||
// return await sharp(raw.data, { raw })
|
|
||||||
// .extractChannel("alpha")
|
|
||||||
// .jpeg()
|
|
||||||
// .toBuffer();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// case TextureMutation.Albedo: {
|
|
||||||
// return await sharp(raw.data, { raw }).removeAlpha().jpeg().toBuffer();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// default:
|
|
||||||
// return await sharp(raw.data, { raw }).jpeg().toBuffer();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { XMLParser } from 'fast-xml-parser';
|
|
||||||
import { readStringDVPL } from './readStringDVPL';
|
|
||||||
|
|
||||||
export async function readXMLDVPL<Type>(file: string) {
|
|
||||||
const parser = new XMLParser();
|
|
||||||
const xml = await readStringDVPL(file);
|
|
||||||
|
|
||||||
return parser.parse(xml) as Type;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { readFile } from 'fs/promises';
|
|
||||||
import { parse } from 'yaml';
|
|
||||||
|
|
||||||
export async function readYAML<Type>(file: string) {
|
|
||||||
const yaml = (await readFile(file)).toString();
|
|
||||||
return parse(yaml) as Type;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { parse } from 'yaml';
|
|
||||||
import { readStringDVPL } from './readStringDVPL';
|
|
||||||
|
|
||||||
export async function readYAMLDVPL<Type>(file: string) {
|
|
||||||
const yaml = await readStringDVPL(file);
|
|
||||||
|
|
||||||
return parse(yaml) as Type;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import { readDVPL } from "@blitzkit/core/src/blitz";
|
||||||
|
import { XMLParser } from "fast-xml-parser";
|
||||||
|
import { normalize } from "path/posix";
|
||||||
|
import SteamUser, { EConnectionProtocol } from "steam-user";
|
||||||
|
import { parse } from "yaml";
|
||||||
|
|
||||||
|
interface SteamManifestFile {
|
||||||
|
chunks: {
|
||||||
|
sha: string;
|
||||||
|
crc: number;
|
||||||
|
offset: string;
|
||||||
|
cb_original: number;
|
||||||
|
cb_compressed: number;
|
||||||
|
}[];
|
||||||
|
filename: string;
|
||||||
|
size: string;
|
||||||
|
flags: number;
|
||||||
|
sha_filename: string;
|
||||||
|
sha_content: string;
|
||||||
|
linktarget: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SteamManifest {
|
||||||
|
files: SteamManifestFile[];
|
||||||
|
depot_id: number;
|
||||||
|
gid_manifest: string;
|
||||||
|
creation_time: number;
|
||||||
|
filenames_encrypted: boolean;
|
||||||
|
cb_disk_original: string;
|
||||||
|
cb_disk_compressed: string;
|
||||||
|
unique_chunks: number;
|
||||||
|
crc_encrypted: number;
|
||||||
|
crc_clear: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SteamVFS {
|
||||||
|
private steam = new SteamUser({ protocol: EConnectionProtocol.TCP });
|
||||||
|
private textDecoder = new TextDecoder();
|
||||||
|
private xmlParser = new XMLParser();
|
||||||
|
|
||||||
|
private manifest: Map<string, SteamManifestFile> = new Map();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private username: string,
|
||||||
|
private password: string,
|
||||||
|
private app: number,
|
||||||
|
private depot: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
this.steam.logOn({
|
||||||
|
accountName: this.username,
|
||||||
|
password: this.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve) => this.steam.once("loggedOn", resolve));
|
||||||
|
|
||||||
|
const productInfo = await this.steam.getProductInfo([this.app], []);
|
||||||
|
const manifest: SteamManifest = await new Promise((resolve) => {
|
||||||
|
// @ts-expect-error
|
||||||
|
this.steam.getManifest(
|
||||||
|
this.app,
|
||||||
|
this.depot,
|
||||||
|
productInfo.apps[this.app].appinfo.depots[this.depot].manifests.public
|
||||||
|
.gid,
|
||||||
|
"public",
|
||||||
|
// @ts-expect-error
|
||||||
|
(_, response) => {
|
||||||
|
resolve(response);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const paths: string[] = [];
|
||||||
|
for (const file of manifest.files) {
|
||||||
|
const path = file.filename.replaceAll("\\", "/");
|
||||||
|
paths.push(path);
|
||||||
|
this.manifest.set(path, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
has(path: string) {
|
||||||
|
const normalized = normalize(path);
|
||||||
|
const dvplPath = `${normalized}.dvpl`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
(this.manifest.has(dvplPath) && dvplPath) ||
|
||||||
|
(this.manifest.has(normalized) && normalized)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(requested: string) {
|
||||||
|
const path = this.has(requested);
|
||||||
|
|
||||||
|
if (!path) throw new Error(`File not found: ${requested}`);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir(path: string) {
|
||||||
|
const parentSegments = path.split("/").length;
|
||||||
|
const children: string[] = [];
|
||||||
|
|
||||||
|
for (const child of this.manifest.keys()) {
|
||||||
|
const childSegments = child.split("/");
|
||||||
|
|
||||||
|
if (
|
||||||
|
child.startsWith(path) &&
|
||||||
|
parentSegments + 1 === childSegments.length
|
||||||
|
) {
|
||||||
|
children.push(childSegments.at(-1)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
async file(requested: string) {
|
||||||
|
const path = this.assert(requested);
|
||||||
|
const fileManifest = this.manifest.get(path)!;
|
||||||
|
const downloaded: { type: "complete"; file: Buffer } =
|
||||||
|
// @ts-expect-error
|
||||||
|
await this.steam.downloadFile(this.app, this.depot, fileManifest);
|
||||||
|
let buffer = downloaded.file;
|
||||||
|
|
||||||
|
if (path.endsWith(".dvpl")) buffer = readDVPL(downloaded.file);
|
||||||
|
|
||||||
|
return new Uint8Array(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
async text(path: string) {
|
||||||
|
const file = await this.file(path);
|
||||||
|
return this.textDecoder.decode(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
async yaml<Type>(path: string) {
|
||||||
|
const file = await this.text(path);
|
||||||
|
return parse(file) as Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
async xml<Type>(path: string) {
|
||||||
|
const file = await this.text(path);
|
||||||
|
return this.xmlParser.parse(file) as Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.steam.logOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.dispose]() {
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,10 @@ import { readdir, rm } from "fs/promises";
|
|||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
|
|
||||||
const MAX_COMMAND_LENGTH = 2 ** 11;
|
const MAX_COMMAND_LENGTH = 2 ** 11;
|
||||||
|
const TS_PROTO_EXECUTABLE_LOCATIONS = [
|
||||||
|
"./node_modules/.bin/protoc-gen-ts_proto",
|
||||||
|
"../../node_modules/.bin/protoc-gen-ts_proto",
|
||||||
|
];
|
||||||
|
|
||||||
const roots = [
|
const roots = [
|
||||||
// "../../packages/core/src/protos",
|
// "../../packages/core/src/protos",
|
||||||
@@ -12,12 +16,24 @@ const roots = [
|
|||||||
|
|
||||||
const exec = promisify(execSync);
|
const exec = promisify(execSync);
|
||||||
|
|
||||||
let executableFileExtension: string;
|
let tsProtoExecutableLocation: string | undefined = undefined;
|
||||||
|
|
||||||
if (existsSync("../../node_modules/.bin/protoc-gen-ts_proto.exe")) {
|
for (const location of TS_PROTO_EXECUTABLE_LOCATIONS) {
|
||||||
executableFileExtension = ".exe";
|
if (existsSync(location)) {
|
||||||
} else {
|
tsProtoExecutableLocation = location;
|
||||||
executableFileExtension = "";
|
break;
|
||||||
|
} else if (existsSync(`${location}.exe`)) {
|
||||||
|
tsProtoExecutableLocation = `${location}.exe`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tsProtoExecutableLocation) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find ts-proto executable. Checked locations: ${TS_PROTO_EXECUTABLE_LOCATIONS.join(
|
||||||
|
", "
|
||||||
|
)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const root of roots) {
|
for (const root of roots) {
|
||||||
@@ -32,7 +48,7 @@ for (const root of roots) {
|
|||||||
while (files.length > 0) {
|
while (files.length > 0) {
|
||||||
let command = [
|
let command = [
|
||||||
"protoc",
|
"protoc",
|
||||||
`--plugin=../../node_modules/.bin/protoc-gen-ts_proto${executableFileExtension}`,
|
`--plugin=${tsProtoExecutableLocation}`,
|
||||||
"--ts_proto_opt=esModuleInterop=true",
|
"--ts_proto_opt=esModuleInterop=true",
|
||||||
"--ts_proto_opt=oneof=unions-value",
|
"--ts_proto_opt=oneof=unions-value",
|
||||||
// "--ts_proto_opt=removeEnumPrefix=true",
|
// "--ts_proto_opt=removeEnumPrefix=true",
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
import decompress from 'decompress';
|
import decompress from "decompress";
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from "fs";
|
||||||
import { mkdir, readdir, writeFile } from 'fs/promises';
|
import { mkdir, readdir, writeFile } from "fs/promises";
|
||||||
|
|
||||||
if (!existsSync('../../temp')) {
|
if (!existsSync("../../temp")) {
|
||||||
console.log('Creating temp directory...');
|
console.log("Creating temp directory...");
|
||||||
await mkdir('../../temp');
|
await mkdir("../../temp");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Downloading depot downloader...');
|
console.log("Downloading depot downloader...");
|
||||||
await fetch(
|
await fetch(
|
||||||
'https://api.github.com/repos/SteamRE/DepotDownloader/releases/latest',
|
"https://api.github.com/repos/SteamRE/DepotDownloader/releases/latest"
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
(response) =>
|
(response) =>
|
||||||
response.json() as Promise<{
|
response.json() as Promise<{
|
||||||
assets: { browser_download_url: string; name: string }[];
|
assets: { browser_download_url: string; name: string }[];
|
||||||
}>,
|
}>
|
||||||
)
|
)
|
||||||
.then(({ assets }) => {
|
.then(({ assets }) => {
|
||||||
const asset = assets.find(({ name }) => name.includes('linux-x64'));
|
const asset = assets.find(({ name }) => name.includes("linux-x64"));
|
||||||
if (!asset) throw new Error('No asset found');
|
if (!asset) throw new Error("No asset found");
|
||||||
return fetch(asset.browser_download_url);
|
return fetch(asset.browser_download_url);
|
||||||
})
|
})
|
||||||
.then((response) => response.arrayBuffer())
|
.then((response) => response.arrayBuffer())
|
||||||
.then((arrayBuffer) =>
|
.then((arrayBuffer) =>
|
||||||
writeFile('../../temp/depotDownloader.zip', new Uint8Array(arrayBuffer)),
|
writeFile("../../temp/depotDownloader.zip", new Uint8Array(arrayBuffer))
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('Unzipping...');
|
console.log("Unzipping...");
|
||||||
await decompress(
|
await decompress(
|
||||||
'../../temp/depotDownloader.zip',
|
"../../temp/depotDownloader.zip",
|
||||||
'../../temp/depotDownloader',
|
"../../temp/depotDownloader"
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'Download complete; files:',
|
"Download complete; files:",
|
||||||
await readdir('../../temp/depotDownloader'),
|
await readdir("../../temp/depotDownloader")
|
||||||
);
|
);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"include": ["src/**/*"]
|
"include": ["src/**/*", "../core/src/blitz/readDVPL.ts"]
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -67,7 +67,9 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
<group ref={turretContainer}>
|
<group ref={turretContainer}>
|
||||||
{armorNodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isCurrentTurret = node.name.startsWith(
|
const isCurrentTurret = node.name.startsWith(
|
||||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`
|
`turret_${turretModelDefinition.model_id
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}`
|
||||||
);
|
);
|
||||||
const isVisible = isCurrentTurret;
|
const isVisible = isCurrentTurret;
|
||||||
const armorId = nameToArmorId(node.name);
|
const armorId = nameToArmorId(node.name);
|
||||||
|
|||||||
+12
-1
@@ -4,6 +4,7 @@ import {
|
|||||||
resolvePenetrationCoefficient,
|
resolvePenetrationCoefficient,
|
||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
import { invalidate, useFrame } from "@react-three/fiber";
|
import { invalidate, useFrame } from "@react-three/fiber";
|
||||||
|
import type { QuicklimeEvent } from "quicklime";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
@@ -21,6 +22,7 @@ import { jsxTree } from "../../../../core/blitzkit/jsxTree";
|
|||||||
import { Duel, type EquipmentMatrix } from "../../../../stores/duel";
|
import { Duel, type EquipmentMatrix } from "../../../../stores/duel";
|
||||||
import { Tankopedia } from "../../../../stores/tankopedia";
|
import { Tankopedia } from "../../../../stores/tankopedia";
|
||||||
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
|
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
|
||||||
|
import { transitionEvent } from "../../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
|
||||||
import fragmentShader from "./shaders/fragment.glsl?raw";
|
import fragmentShader from "./shaders/fragment.glsl?raw";
|
||||||
import vertexShader from "./shaders/vertex.glsl?raw";
|
import vertexShader from "./shaders/vertex.glsl?raw";
|
||||||
import { spacedArmorRenderTarget } from "./target";
|
import { spacedArmorRenderTarget } from "./target";
|
||||||
@@ -72,6 +74,8 @@ export function PrimaryArmorSceneComponent({
|
|||||||
resolution: { value: new Vector2() },
|
resolution: { value: new Vector2() },
|
||||||
spacedArmorBuffer: { value: null },
|
spacedArmorBuffer: { value: null },
|
||||||
spacedArmorDepth: { value: null },
|
spacedArmorDepth: { value: null },
|
||||||
|
|
||||||
|
opacity: { value: 0 },
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
@@ -154,6 +158,10 @@ export function PrimaryArmorSceneComponent({
|
|||||||
if (!noInvalidate) invalidate();
|
if (!noInvalidate) invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTransitionEvent(event: QuicklimeEvent<number>) {
|
||||||
|
material.uniforms.opacity.value = event.data;
|
||||||
|
}
|
||||||
|
|
||||||
handleShellChange();
|
handleShellChange();
|
||||||
handleGreenPenetrationChange(TankopediaPersistent.state.greenPenetration);
|
handleGreenPenetrationChange(TankopediaPersistent.state.greenPenetration);
|
||||||
handleAdvancedHighlightingChange(
|
handleAdvancedHighlightingChange(
|
||||||
@@ -164,6 +172,8 @@ export function PrimaryArmorSceneComponent({
|
|||||||
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
|
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
|
||||||
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
|
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
|
||||||
|
|
||||||
|
transitionEvent.on(handleTransitionEvent);
|
||||||
|
|
||||||
const unsubscribes = [
|
const unsubscribes = [
|
||||||
Duel.on((state) => state.antagonist.shell, handleShellChange),
|
Duel.on((state) => state.antagonist.shell, handleShellChange),
|
||||||
Tankopedia.on((state) => state.customShell, handleShellChange),
|
Tankopedia.on((state) => state.customShell, handleShellChange),
|
||||||
@@ -184,12 +194,13 @@ export function PrimaryArmorSceneComponent({
|
|||||||
(state) => state.antagonist.equipmentMatrix,
|
(state) => state.antagonist.equipmentMatrix,
|
||||||
(equipment) => handleAntagonistEquipmentChange(equipment)
|
(equipment) => handleAntagonistEquipmentChange(equipment)
|
||||||
),
|
),
|
||||||
|
() => transitionEvent.off(handleTransitionEvent),
|
||||||
];
|
];
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||||
};
|
};
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
useFrame(({ gl, camera }) => {
|
useFrame(({ gl, camera }) => {
|
||||||
gl.getSize(material.uniforms.resolution.value).multiplyScalar(
|
gl.getSize(material.uniforms.resolution.value).multiplyScalar(
|
||||||
|
|||||||
+2
-5
@@ -1,12 +1,9 @@
|
|||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define USE_FOG
|
|
||||||
|
|
||||||
#include <fog_pars_fragment>
|
|
||||||
|
|
||||||
varying vec3 vNormal;
|
varying vec3 vNormal;
|
||||||
varying vec3 vViewPosition;
|
varying vec3 vViewPosition;
|
||||||
|
|
||||||
|
uniform float opacity;
|
||||||
uniform float thickness;
|
uniform float thickness;
|
||||||
uniform float penetration;
|
uniform float penetration;
|
||||||
uniform float caliber;
|
uniform float caliber;
|
||||||
@@ -119,5 +116,5 @@ void main() {
|
|||||||
gl_FragColor = vec4(baseColor, (1.0 - penetrationChance) * alpha);
|
gl_FragColor = vec4(baseColor, (1.0 - penetrationChance) * alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <fog_fragment>
|
gl_FragColor.a *= opacity;
|
||||||
}
|
}
|
||||||
-6
@@ -1,9 +1,5 @@
|
|||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define USE_FOG
|
|
||||||
|
|
||||||
#include <fog_pars_vertex>
|
|
||||||
|
|
||||||
varying vec3 vNormal;
|
varying vec3 vNormal;
|
||||||
varying vec3 vViewPosition;
|
varying vec3 vViewPosition;
|
||||||
|
|
||||||
@@ -14,6 +10,4 @@ void main() {
|
|||||||
vNormal = normalMatrix * normal;
|
vNormal = normalMatrix * normal;
|
||||||
|
|
||||||
gl_Position = projectionMatrix * mvPosition;
|
gl_Position = projectionMatrix * mvPosition;
|
||||||
|
|
||||||
#include <fog_vertex>
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
} from "../../../core/blitzkit/modelTransform";
|
} from "../../../core/blitzkit/modelTransform";
|
||||||
import { discardClippingPlane } from "../../../core/three/discardClippingPlane";
|
import { discardClippingPlane } from "../../../core/three/discardClippingPlane";
|
||||||
import { Tankopedia } from "../../../stores/tankopedia";
|
import { Tankopedia } from "../../../stores/tankopedia";
|
||||||
|
import { transitionEvent } from "../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
|
||||||
import { ArmorType } from "./SpacedArmorScene";
|
import { ArmorType } from "./SpacedArmorScene";
|
||||||
import type {
|
import type {
|
||||||
ArmorUserData,
|
ArmorUserData,
|
||||||
@@ -108,10 +109,11 @@ export function StaticArmorSceneComponent({
|
|||||||
() =>
|
() =>
|
||||||
new MeshBasicMaterial({
|
new MeshBasicMaterial({
|
||||||
color,
|
color,
|
||||||
opacity,
|
transparent: true,
|
||||||
transparent: opacity < 1,
|
|
||||||
depthWrite,
|
depthWrite,
|
||||||
...(props.clip ? { clippingPlanes: [props.clip] } : {}),
|
...(props.clip ? { clippingPlanes: [props.clip] } : {}),
|
||||||
|
opacity: 0,
|
||||||
|
userData: { opacity0: opacity },
|
||||||
}),
|
}),
|
||||||
[thickness]
|
[thickness]
|
||||||
);
|
);
|
||||||
@@ -121,6 +123,8 @@ export function StaticArmorSceneComponent({
|
|||||||
color: color
|
color: color
|
||||||
.clone()
|
.clone()
|
||||||
.multiplyScalar(props.type === ArmorType.Spaced ? 2 ** 2 : 2 ** -1),
|
.multiplyScalar(props.type === ArmorType.Spaced ? 2 ** 2 : 2 ** -1),
|
||||||
|
opacity: 0,
|
||||||
|
transparent: true,
|
||||||
}),
|
}),
|
||||||
[thickness]
|
[thickness]
|
||||||
);
|
);
|
||||||
@@ -163,11 +167,22 @@ export function StaticArmorSceneComponent({
|
|||||||
surfaceMaterial.needsUpdate = true;
|
surfaceMaterial.needsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTransitionEvent(event: QuicklimeEvent<number>) {
|
||||||
|
surfaceMaterial.opacity = surfaceMaterial.userData.opacity0 * event.data;
|
||||||
|
outlineMaterial.opacity = event.data;
|
||||||
|
|
||||||
|
surfaceMaterial.transparent = surfaceMaterial.opacity < 1;
|
||||||
|
outlineMaterial.transparent = outlineMaterial.opacity < 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
transitionEvent.on(handleTransitionEvent);
|
||||||
|
|
||||||
const unsubscribes = [
|
const unsubscribes = [
|
||||||
Tankopedia.on(
|
Tankopedia.on(
|
||||||
(state) => state.highlightArmor?.name,
|
(state) => state.highlightArmor?.name,
|
||||||
handleHighlightArmor
|
handleHighlightArmor
|
||||||
),
|
),
|
||||||
|
() => transitionEvent.off(handleTransitionEvent),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (props.clip) {
|
if (props.clip) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Flex, Heading, IconButton, Table } from "@radix-ui/themes";
|
|||||||
import type { TankCharacteristics } from "../../core/blitzkit/tankCharacteristics";
|
import type { TankCharacteristics } from "../../core/blitzkit/tankCharacteristics";
|
||||||
import { useLocale } from "../../hooks/useLocale";
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { CompareEphemeral } from "../../stores/compareEphemeral";
|
import { CompareEphemeral } from "../../stores/compareEphemeral";
|
||||||
|
import { CompareCellDirection } from "../CompareCell";
|
||||||
import { StickyColumnHeaderCell } from "../StickyColumnHeaderCell";
|
import { StickyColumnHeaderCell } from "../StickyColumnHeaderCell";
|
||||||
import { CompareRow } from "./CompareRow";
|
import { CompareRow } from "./CompareRow";
|
||||||
import { CompareSectionTitle } from "./CompareSectionTitle";
|
import { CompareSectionTitle } from "./CompareSectionTitle";
|
||||||
@@ -79,7 +80,7 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
name={strings.website.tools.tankopedia.characteristics.values.reload}
|
name={strings.website.tools.tankopedia.characteristics.values.reload}
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
value={(stats) =>
|
value={(stats) =>
|
||||||
stats.shellReload ??
|
stats.shellReload ??
|
||||||
stats.shellReloads!.reduce((a, b) => a + b, 0) /
|
stats.shellReloads!.reduce((a, b) => a + b, 0) /
|
||||||
@@ -95,7 +96,7 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<>
|
<>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
value="intraClip"
|
value="intraClip"
|
||||||
decimals={2}
|
decimals={2}
|
||||||
/>
|
/>
|
||||||
@@ -107,7 +108,7 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
value="shellRicochet"
|
value="shellRicochet"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={0}
|
decimals={0}
|
||||||
/>
|
/>
|
||||||
<CompareRow stats={stats} value="shellVelocity" decimals={0} />
|
<CompareRow stats={stats} value="shellVelocity" decimals={0} />
|
||||||
@@ -116,48 +117,48 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
value="aimTime"
|
value="aimTime"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={2}
|
decimals={2}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
value="dispersion"
|
value="dispersion"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={3}
|
decimals={3}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="dispersionMoving"
|
value="dispersionMoving"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={3}
|
decimals={3}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="dispersionHullTraversing"
|
value="dispersionHullTraversing"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={3}
|
decimals={3}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="dispersionTurretTraversing"
|
value="dispersionTurretTraversing"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={3}
|
decimals={3}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="dispersionShooting"
|
value="dispersionShooting"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={3}
|
decimals={3}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="dispersionGunDamaged"
|
value="dispersionGunDamaged"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={3}
|
decimals={3}
|
||||||
/>
|
/>
|
||||||
<CompareRow stats={stats} value="gunDepression" decimals={1} />
|
<CompareRow stats={stats} value="gunDepression" decimals={1} />
|
||||||
@@ -186,21 +187,21 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
value="hardTerrainCoefficientRaw"
|
value="hardTerrainCoefficientRaw"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={2}
|
decimals={2}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="mediumTerrainCoefficientRaw"
|
value="mediumTerrainCoefficientRaw"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={2}
|
decimals={2}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="softTerrainCoefficientRaw"
|
value="softTerrainCoefficientRaw"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={2}
|
decimals={2}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
@@ -246,7 +247,7 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
value="fireChance"
|
value="fireChance"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
display={(stats) => (stats.fireChance * 100).toFixed(0)}
|
display={(stats) => (stats.fireChance * 100).toFixed(0)}
|
||||||
deltaNominalDisplay={(delta) => (delta * 100).toFixed(0)}
|
deltaNominalDisplay={(delta) => (delta * 100).toFixed(0)}
|
||||||
/>
|
/>
|
||||||
@@ -288,28 +289,28 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
value="volume"
|
value="volume"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={1}
|
decimals={1}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="width"
|
value="width"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={1}
|
decimals={1}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="height"
|
value="height"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={1}
|
decimals={1}
|
||||||
/>
|
/>
|
||||||
<CompareRow
|
<CompareRow
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
value="length"
|
value="length"
|
||||||
deltaType="lowerIsBetter"
|
deltaType={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
decimals={1}
|
decimals={1}
|
||||||
/>
|
/>
|
||||||
</Table.Body>
|
</Table.Body>
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import {
|
|||||||
import { Flex, IconButton, Table, Text } from "@radix-ui/themes";
|
import { Flex, IconButton, Table, Text } from "@radix-ui/themes";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import type { TankCharacteristics } from "../../core/blitzkit/tankCharacteristics";
|
import type { TankCharacteristics } from "../../core/blitzkit/tankCharacteristics";
|
||||||
import { Var } from "../../core/radix/var";
|
|
||||||
import { useLocale } from "../../hooks/useLocale";
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { CompareEphemeral } from "../../stores/compareEphemeral";
|
import { CompareEphemeral } from "../../stores/compareEphemeral";
|
||||||
import { ComparePersistent } from "../../stores/comparePersistent";
|
import { ComparePersistent } from "../../stores/comparePersistent";
|
||||||
|
import { CompareCell, CompareCellDirection } from "../CompareCell";
|
||||||
import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
|
import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
|
||||||
|
|
||||||
type CompareRowProps = {
|
type CompareRowProps = {
|
||||||
display?: (
|
display?: (
|
||||||
member: Awaited<TankCharacteristics>
|
member: Awaited<TankCharacteristics>
|
||||||
) => number | string | undefined;
|
) => number | string | undefined;
|
||||||
deltaType?: "higherIsBetter" | "lowerIsBetter";
|
deltaType?: CompareCellDirection;
|
||||||
decimals?: number;
|
decimals?: number;
|
||||||
deltaNominalDisplay?: (delta: number) => string | number;
|
deltaNominalDisplay?: (delta: number) => string | number;
|
||||||
indent?: boolean;
|
indent?: boolean;
|
||||||
@@ -33,7 +33,7 @@ type CompareRowProps = {
|
|||||||
|
|
||||||
export function CompareRow({
|
export function CompareRow({
|
||||||
display,
|
display,
|
||||||
deltaType = "higherIsBetter",
|
deltaType = CompareCellDirection.HIGHER_IS_BETTER,
|
||||||
decimals,
|
decimals,
|
||||||
deltaNominalDisplay,
|
deltaNominalDisplay,
|
||||||
indent,
|
indent,
|
||||||
@@ -108,33 +108,18 @@ export function CompareRow({
|
|||||||
{values.map((value, index) => {
|
{values.map((value, index) => {
|
||||||
const delta = value - values[0];
|
const delta = value - values[0];
|
||||||
const deltaPercentage = value / values[0] - 1;
|
const deltaPercentage = value / values[0] - 1;
|
||||||
const normalizedDeltaPercentage = Math.round(
|
|
||||||
Math.min(100, Math.abs(deltaPercentage) * 2 * 100 + 25)
|
|
||||||
);
|
|
||||||
const resolvedDisplayValue = display
|
const resolvedDisplayValue = display
|
||||||
? display(stats[index])
|
? display(stats[index])
|
||||||
: decimals === undefined
|
: decimals === undefined
|
||||||
? value
|
? value
|
||||||
: value?.toFixed(decimals);
|
: value?.toFixed(decimals);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Cell
|
<CompareCell
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
truth={values[0]}
|
||||||
backgroundColor:
|
value={value}
|
||||||
index === 0 ||
|
direction={deltaType}
|
||||||
value === undefined ||
|
|
||||||
values[0] === undefined ||
|
|
||||||
value === values[0]
|
|
||||||
? undefined
|
|
||||||
: (
|
|
||||||
deltaType === "higherIsBetter"
|
|
||||||
? value > values[0]
|
|
||||||
: value < values[0]
|
|
||||||
)
|
|
||||||
? `color-mix(in srgb, ${Var("green-7")} ${normalizedDeltaPercentage}%, ${Var("green-3")})`
|
|
||||||
: `color-mix(in srgb, ${Var("red-7")} ${normalizedDeltaPercentage}%, ${Var("red-3")})`,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
align="center"
|
align="center"
|
||||||
@@ -160,8 +145,8 @@ export function CompareRow({
|
|||||||
deltaNominalDisplay
|
deltaNominalDisplay
|
||||||
? deltaNominalDisplay(delta)
|
? deltaNominalDisplay(delta)
|
||||||
: decimals === undefined
|
: decimals === undefined
|
||||||
? delta
|
? delta
|
||||||
: delta.toFixed(decimals)
|
: delta.toFixed(decimals)
|
||||||
}`}
|
}`}
|
||||||
)
|
)
|
||||||
</Text>
|
</Text>
|
||||||
@@ -179,7 +164,7 @@ export function CompareRow({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
|
|||||||
@@ -51,19 +51,17 @@ export function CompareTable({ stats }: CompareTableProps) {
|
|||||||
variant="surface"
|
variant="surface"
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
width: "100%",
|
maxWidth: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
borderRadius: 0,
|
borderBottomRightRadius: 0,
|
||||||
|
borderBottomLeftRadius: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<StickyColumnHeaderCell width="0" style={{ height: "100%" }}>
|
<StickyColumnHeaderCell width="0" style={{ height: "100%" }}>
|
||||||
<Flex
|
<Flex
|
||||||
style={{
|
style={{ width: "100%", height: "100%" }}
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
align="center"
|
align="center"
|
||||||
justify="center"
|
justify="center"
|
||||||
>
|
>
|
||||||
@@ -107,8 +105,8 @@ export function CompareTable({ stats }: CompareTableProps) {
|
|||||||
crewSkills[skill] === 0
|
crewSkills[skill] === 0
|
||||||
? Var("gray-11")
|
? Var("gray-11")
|
||||||
: permanentSkills.includes(skill)
|
: permanentSkills.includes(skill)
|
||||||
? Var("crimson-11")
|
? Var("crimson-11")
|
||||||
: Var("amber-11"),
|
: Var("amber-11"),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export function TankCard({ index, tank }: TankCardProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StickyColumnHeaderCell>
|
<StickyColumnHeaderCell width="0">
|
||||||
<Flex direction="column" align="center" justify="between" gap="2">
|
<Flex direction="column" align="center" justify="between" gap="2">
|
||||||
<TankControl index={index} key={tank.id} slug={tank.slug} />
|
<TankControl index={index} key={tank.id} slug={tank.slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Table } from "@radix-ui/themes";
|
||||||
|
import type { CellProps } from "@radix-ui/themes/components/table";
|
||||||
|
|
||||||
|
export enum CompareCellDirection {
|
||||||
|
HIGHER_IS_BETTER,
|
||||||
|
LOWER_IS_BETTER,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CompareCellProps extends CellProps {
|
||||||
|
value: number;
|
||||||
|
truth: number;
|
||||||
|
direction?: CompareCellDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CompareCell({
|
||||||
|
value,
|
||||||
|
truth,
|
||||||
|
style,
|
||||||
|
direction = CompareCellDirection.HIGHER_IS_BETTER,
|
||||||
|
...props
|
||||||
|
}: CompareCellProps) {
|
||||||
|
const percentage = value / truth - 1;
|
||||||
|
const shiftedPercentage = Math.round(
|
||||||
|
Math.min(100, Math.abs(percentage) * 2 * 100 + 25)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table.Cell
|
||||||
|
style={{
|
||||||
|
backgroundColor:
|
||||||
|
value === truth
|
||||||
|
? undefined
|
||||||
|
: (direction === CompareCellDirection.HIGHER_IS_BETTER) ===
|
||||||
|
value > truth
|
||||||
|
? `color-mix(in srgb, var(--green-7) ${shiftedPercentage}%, var(--green-3))`
|
||||||
|
: `color-mix(in srgb, var(--red-7) ${shiftedPercentage}%, var(--red-3))`,
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Box, Text } from "@radix-ui/themes";
|
||||||
|
import { Html } from "@react-three/drei";
|
||||||
import { memo, type ComponentProps } from "react";
|
import { memo, type ComponentProps } from "react";
|
||||||
|
|
||||||
interface Props extends ComponentProps<"spotLight"> {
|
interface Props extends ComponentProps<"spotLight"> {
|
||||||
@@ -8,10 +10,20 @@ export const HelpingSpotLight = memo<Props>(({ debug, ...props }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{debug && (
|
{debug && (
|
||||||
<mesh position={props.position}>
|
<group position={props.position}>
|
||||||
<icosahedronGeometry args={[0.1, 2]} />
|
<mesh>
|
||||||
<meshBasicMaterial color={props.color} />
|
<icosahedronGeometry args={[0.1, 2]} />
|
||||||
</mesh>
|
<meshBasicMaterial color={props.color} />
|
||||||
|
</mesh>
|
||||||
|
|
||||||
|
<Html center>
|
||||||
|
<Box pb="9">
|
||||||
|
<Text wrap="nowrap">
|
||||||
|
{props.intensity} @ {JSON.stringify(props.userData)}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Html>
|
||||||
|
</group>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<spotLight {...props} />
|
<spotLight {...props} />
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import { Flex } from '@radix-ui/themes';
|
import { Flex } from "@radix-ui/themes";
|
||||||
import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps';
|
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
||||||
import { StickyTableRoot } from '../StickyTableRoot';
|
import { StickyTableRoot } from "../StickyTableRoot";
|
||||||
import { Header } from './Header';
|
import { Header } from "./Header";
|
||||||
import { Tanks } from './Tanks';
|
import { Tanks } from "./Tanks";
|
||||||
|
|
||||||
export function TankTable({ skeleton }: MaybeSkeletonComponentProps) {
|
export function TankTable({ skeleton }: MaybeSkeletonComponentProps) {
|
||||||
return (
|
return (
|
||||||
<Flex justify="center" maxWidth="100%" flexGrow="1" position="relative">
|
<Flex justify="center" maxWidth="100%" flexGrow="1" position="relative">
|
||||||
<StickyTableRoot
|
<StickyTableRoot
|
||||||
size={{ initial: '1', sm: '2' }}
|
size={{ initial: "1", sm: "2" }}
|
||||||
variant="surface"
|
variant="surface"
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
maxWidth: '100%',
|
maxWidth: "100%",
|
||||||
maxHeight: '100%',
|
maxHeight: "100%",
|
||||||
height: '100%',
|
height: "100%",
|
||||||
|
borderBottomRightRadius: 0,
|
||||||
|
borderBottomLeftRadius: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Header />
|
<Header />
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
import { TankDefinition, formatCompact } from '@blitzkit/core';
|
import { TankDefinition, formatCompact } from "@blitzkit/core";
|
||||||
import { Table } from '@radix-ui/themes';
|
import { Table } from "@radix-ui/themes";
|
||||||
import { memo } from 'react';
|
import { memo } from "react";
|
||||||
import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions';
|
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
||||||
import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio';
|
import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio";
|
||||||
import { useLocale } from '../../hooks/useLocale';
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { Performance } from '../../stores/performance';
|
import { Performance } from "../../stores/performance";
|
||||||
import { TankRowHeaderCell } from '../TankRowHeaderCell';
|
import { CompareCell, CompareCellDirection } from "../CompareCell";
|
||||||
|
import { TankRowHeaderCell } from "../TankRowHeaderCell";
|
||||||
|
|
||||||
interface TankRowProps {
|
interface TankRowProps {
|
||||||
tank: TankDefinition;
|
tank: TankDefinition;
|
||||||
|
average: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const averageDefinitions = await awaitableAverageDefinitions;
|
const averageDefinitions = await awaitableAverageDefinitions;
|
||||||
|
|
||||||
export const TankRow = memo<TankRowProps>(
|
export const TankRow = memo<TankRowProps>(
|
||||||
({ tank }) => {
|
({ tank, average }) => {
|
||||||
const averages = averageDefinitions.averages[tank.id];
|
const averages = averageDefinitions.averages[tank.id];
|
||||||
const ratio = useAveragesExclusionRatio();
|
const ratio = useAveragesExclusionRatio();
|
||||||
const playerCountPeriod = Performance.use(
|
const playerCountPeriod = Performance.use(
|
||||||
(state) => state.playerCountPeriod,
|
(state) => state.playerCountPeriod
|
||||||
);
|
);
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
|
|
||||||
@@ -26,59 +28,108 @@ export const TankRow = memo<TankRowProps>(
|
|||||||
<Table.Row>
|
<Table.Row>
|
||||||
<TankRowHeaderCell tank={tank} />
|
<TankRowHeaderCell tank={tank} />
|
||||||
|
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.wins / averages.mu.battles}
|
||||||
|
truth={average.winrate}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{((averages.mu.wins / averages.mu.battles) * 100).toFixed(1)}%
|
{((averages.mu.wins / averages.mu.battles) * 100).toFixed(1)}%
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<Table.Cell align="center">
|
||||||
{formatCompact(
|
{formatCompact(
|
||||||
locale,
|
locale,
|
||||||
Math.round(ratio * averages.samples[playerCountPeriod]),
|
Math.round(ratio * averages.samples[playerCountPeriod])
|
||||||
)}
|
)}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.damage_dealt / averages.mu.battles}
|
||||||
|
truth={average.damage}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(
|
{Math.round(
|
||||||
averages.mu.damage_dealt / averages.mu.battles,
|
averages.mu.damage_dealt / averages.mu.battles
|
||||||
).toLocaleString(locale)}
|
).toLocaleString(locale)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.survived_battles / averages.mu.battles}
|
||||||
|
truth={average.survival}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(
|
{Math.round(
|
||||||
(averages.mu.survived_battles / averages.mu.battles) * 100,
|
(averages.mu.survived_battles / averages.mu.battles) * 100
|
||||||
)}
|
)}
|
||||||
%
|
%
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.xp / averages.mu.battles}
|
||||||
|
truth={average.xp}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(averages.mu.xp / averages.mu.battles).toLocaleString(
|
{Math.round(averages.mu.xp / averages.mu.battles).toLocaleString(
|
||||||
locale,
|
locale
|
||||||
)}
|
)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.frags / averages.mu.battles}
|
||||||
|
truth={average.kills}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.frags / averages.mu.battles).toFixed(2)}
|
{(averages.mu.frags / averages.mu.battles).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.spotted / averages.mu.battles}
|
||||||
|
truth={average.spots}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.spotted / averages.mu.battles).toFixed(2)}
|
{(averages.mu.spotted / averages.mu.battles).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.hits / averages.mu.shots}
|
||||||
|
truth={average.accuracy}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round((averages.mu.hits / averages.mu.shots) * 100)}%
|
{Math.round((averages.mu.hits / averages.mu.shots) * 100)}%
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.shots / averages.mu.battles}
|
||||||
|
truth={average.shots}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.shots / averages.mu.battles).toFixed(1)}
|
{(averages.mu.shots / averages.mu.battles).toFixed(1)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.hits / averages.mu.battles}
|
||||||
|
truth={average.hits}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.hits / averages.mu.battles).toFixed(1)}
|
{(averages.mu.hits / averages.mu.battles).toFixed(1)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.damage_dealt / averages.mu.damage_received}
|
||||||
|
truth={average.damageRatio}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.damage_dealt / averages.mu.damage_received).toFixed(2)}
|
{(averages.mu.damage_dealt / averages.mu.damage_received).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.damage_received / averages.mu.battles}
|
||||||
|
truth={average.damageReceived}
|
||||||
|
direction={CompareCellDirection.LOWER_IS_BETTER}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{Math.round(
|
{Math.round(
|
||||||
averages.mu.damage_received / averages.mu.battles,
|
averages.mu.damage_received / averages.mu.battles
|
||||||
).toLocaleString(locale)}
|
).toLocaleString(locale)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
<Table.Cell align="center">
|
<CompareCell
|
||||||
|
value={averages.mu.capture_points / averages.mu.battles}
|
||||||
|
truth={average.capturePoints}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
{(averages.mu.capture_points / averages.mu.battles).toFixed(2)}
|
{(averages.mu.capture_points / averages.mu.battles).toFixed(2)}
|
||||||
</Table.Cell>
|
</CompareCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(prev, next) => prev.tank.id === next.tank.id,
|
(prev, next) => prev.tank.id === next.tank.id
|
||||||
);
|
);
|
||||||
@@ -1,17 +1,27 @@
|
|||||||
|
import { formatCompact, type BlitzkitStats } from "@blitzkit/core";
|
||||||
import { Table } from "@radix-ui/themes";
|
import { Table } from "@radix-ui/themes";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { Fragment, Suspense, useEffect, useMemo, useState } from "react";
|
import {
|
||||||
|
Fragment,
|
||||||
|
Suspense,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import usePromise from "react-promise-suspense";
|
import usePromise from "react-promise-suspense";
|
||||||
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
||||||
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
||||||
import { filterTanks } from "../../core/blitzkit/filterTanks";
|
import { filterTanks } from "../../core/blitzkit/filterTanks";
|
||||||
|
import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio";
|
||||||
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { Performance } from "../../stores/performance";
|
import { Performance } from "../../stores/performance";
|
||||||
import { TankFilters } from "../../stores/tankFilters";
|
import { TankFilters } from "../../stores/tankFilters";
|
||||||
import { TankPerformanceSort } from "../../stores/tankPerformanceSort";
|
import { TankPerformanceSort } from "../../stores/tankPerformanceSort";
|
||||||
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
||||||
|
import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
|
||||||
import { RowLoader } from "./RowLoader";
|
import { RowLoader } from "./RowLoader";
|
||||||
import { TankRow } from "./TankRow";
|
import { TankRow } from "./TankRow";
|
||||||
import { Total } from "./Total";
|
|
||||||
|
|
||||||
const PREVIEW_COUNT = 10;
|
const PREVIEW_COUNT = 10;
|
||||||
const DEFAULT_LOADED_ROWS = 25;
|
const DEFAULT_LOADED_ROWS = 25;
|
||||||
@@ -145,9 +155,83 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
setLoadedRows(DEFAULT_LOADED_ROWS);
|
setLoadedRows(DEFAULT_LOADED_ROWS);
|
||||||
}, [filters, sort]);
|
}, [filters, sort]);
|
||||||
|
|
||||||
|
const playerCountPeriod = Performance.use((state) => state.playerCountPeriod);
|
||||||
|
const { strings, locale } = useLocale();
|
||||||
|
const ratio = useAveragesExclusionRatio();
|
||||||
|
const sum = useCallback(
|
||||||
|
(slice: (tank: BlitzkitStats) => number) => {
|
||||||
|
return tanksSorted.reduce(
|
||||||
|
(acc, tank) => acc + tank.samples[playerCountPeriod] * slice(tank.mu),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[tanksSorted]
|
||||||
|
);
|
||||||
|
|
||||||
|
const battles = sum(({ battles }) => battles);
|
||||||
|
const winrate = sum(({ wins }) => wins) / battles;
|
||||||
|
const damage = sum(({ damage_dealt }) => damage_dealt) / battles;
|
||||||
|
const survival = sum(({ survived_battles }) => survived_battles) / battles;
|
||||||
|
const xp = sum(({ xp }) => xp) / battles;
|
||||||
|
const kills = sum(({ frags }) => frags) / battles;
|
||||||
|
const spots = sum(({ spotted }) => spotted) / battles;
|
||||||
|
const hits = sum(({ hits }) => hits) / battles;
|
||||||
|
const shots = sum(({ shots }) => shots) / battles;
|
||||||
|
const accuracy = hits / shots;
|
||||||
|
const damageReceived =
|
||||||
|
sum(({ damage_received }) => damage_received) / battles;
|
||||||
|
const damageRatio = damage / damageReceived;
|
||||||
|
const capturePoints = sum(({ capture_points }) => capture_points) / battles;
|
||||||
|
|
||||||
|
const average = {
|
||||||
|
battles,
|
||||||
|
winrate,
|
||||||
|
damage,
|
||||||
|
survival,
|
||||||
|
xp,
|
||||||
|
kills,
|
||||||
|
spots,
|
||||||
|
hits,
|
||||||
|
shots,
|
||||||
|
accuracy,
|
||||||
|
damageReceived,
|
||||||
|
damageRatio,
|
||||||
|
capturePoints,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
<Total tanks={tanksSorted} />
|
<Table.Row>
|
||||||
|
<StickyRowHeaderCell style={{ overflow: "hidden", display: "flex" }}>
|
||||||
|
{strings.website.tools.performance.table.tanks.total}
|
||||||
|
</StickyRowHeaderCell>
|
||||||
|
<Table.Cell align="center">{(winrate * 100).toFixed(1)}%</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{formatCompact(
|
||||||
|
locale,
|
||||||
|
ratio * averageDefinitions.samples[playerCountPeriod]
|
||||||
|
)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(damage).toLocaleString(locale)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(survival * 100).toFixed(1)}%
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(xp).toLocaleString(locale)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">{kills.toFixed(2)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{spots.toFixed(2)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{(accuracy * 100).toFixed(0)}%</Table.Cell>
|
||||||
|
<Table.Cell align="center">{shots.toFixed(1)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{hits.toFixed(1)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">{damageRatio.toFixed(2)}</Table.Cell>
|
||||||
|
<Table.Cell align="center">
|
||||||
|
{Math.round(damageReceived).toLocaleString(locale)}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell align="center">{capturePoints.toFixed(2)}</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
|
||||||
{tanksSorted.slice(0, loadedRows).map((averages) => {
|
{tanksSorted.slice(0, loadedRows).map((averages) => {
|
||||||
const tank = tankDefinitions.tanks[averages.id];
|
const tank = tankDefinitions.tanks[averages.id];
|
||||||
@@ -157,7 +241,7 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
return (
|
return (
|
||||||
<Fragment key={tank.id}>
|
<Fragment key={tank.id}>
|
||||||
<Suspense fallback={<RowLoader />}>
|
<Suspense fallback={<RowLoader />}>
|
||||||
<TankRow tank={tank} />
|
<TankRow average={average} tank={tank} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
import {
|
|
||||||
AverageDefinitionsEntry,
|
|
||||||
formatCompact,
|
|
||||||
type BlitzkitStats,
|
|
||||||
} from "@blitzkit/core";
|
|
||||||
import { Table } from "@radix-ui/themes";
|
|
||||||
import { memo, useCallback } from "react";
|
|
||||||
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
|
||||||
import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio";
|
|
||||||
import { useLocale } from "../../hooks/useLocale";
|
|
||||||
import { Performance } from "../../stores/performance";
|
|
||||||
import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
|
|
||||||
|
|
||||||
export interface AverageDefinitionsEntryWithId extends AverageDefinitionsEntry {
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TotalProps {
|
|
||||||
tanks: AverageDefinitionsEntryWithId[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const averageDefinitions = await awaitableAverageDefinitions;
|
|
||||||
|
|
||||||
export const Total = memo<TotalProps>(
|
|
||||||
({ tanks }) => {
|
|
||||||
const playerCountPeriod = Performance.use(
|
|
||||||
(state) => state.playerCountPeriod
|
|
||||||
);
|
|
||||||
const { strings, locale } = useLocale();
|
|
||||||
const ratio = useAveragesExclusionRatio();
|
|
||||||
const sum = useCallback(
|
|
||||||
(slice: (tank: BlitzkitStats) => number) => {
|
|
||||||
return tanks.reduce(
|
|
||||||
(acc, tank) => acc + tank.samples[playerCountPeriod] * slice(tank.mu),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[tanks]
|
|
||||||
);
|
|
||||||
|
|
||||||
const battles = sum(({ battles }) => battles);
|
|
||||||
const winrate = sum(({ wins }) => wins) / battles;
|
|
||||||
const damage = sum(({ damage_dealt }) => damage_dealt) / battles;
|
|
||||||
const survival = sum(({ survived_battles }) => survived_battles) / battles;
|
|
||||||
const xp = sum(({ xp }) => xp) / battles;
|
|
||||||
const kills = sum(({ frags }) => frags) / battles;
|
|
||||||
const spots = sum(({ spotted }) => spotted) / battles;
|
|
||||||
const hits = sum(({ hits }) => hits) / battles;
|
|
||||||
const shots = sum(({ shots }) => shots) / battles;
|
|
||||||
const accuracy = hits / shots;
|
|
||||||
const damageReceived =
|
|
||||||
sum(({ damage_received }) => damage_received) / battles;
|
|
||||||
const damageRatio = damage / damageReceived;
|
|
||||||
const capturePoints = sum(({ capture_points }) => capture_points) / battles;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Row>
|
|
||||||
<StickyRowHeaderCell style={{ overflow: "hidden", display: "flex" }}>
|
|
||||||
{strings.website.tools.performance.table.tanks.total}
|
|
||||||
</StickyRowHeaderCell>
|
|
||||||
<Table.Cell align="center">{(winrate * 100).toFixed(1)}%</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{formatCompact(
|
|
||||||
locale,
|
|
||||||
ratio * averageDefinitions.samples[playerCountPeriod]
|
|
||||||
)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(damage).toLocaleString(locale)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(survival * 100).toFixed(1)}%
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(xp).toLocaleString(locale)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">{kills.toFixed(2)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{spots.toFixed(2)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{(accuracy * 100).toFixed(0)}%</Table.Cell>
|
|
||||||
<Table.Cell align="center">{shots.toFixed(1)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{hits.toFixed(1)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">{damageRatio.toFixed(2)}</Table.Cell>
|
|
||||||
<Table.Cell align="center">
|
|
||||||
{Math.round(damageReceived).toLocaleString(locale)}
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell align="center">{capturePoints.toFixed(2)}</Table.Cell>
|
|
||||||
</Table.Row>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(prev, next) => prev.tanks === next.tanks
|
|
||||||
);
|
|
||||||
@@ -37,26 +37,26 @@ export function PlaylistActions() {
|
|||||||
{strings.website.tools.playlist.shuffle}
|
{strings.website.tools.playlist.shuffle}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
size={{ initial: "1", xs: "2" }}
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
Playlist.mutate((draft) => {
|
||||||
|
draft.list = draft.list?.filter(
|
||||||
|
(entry) =>
|
||||||
|
(!entry.now ||
|
||||||
|
!entry.then ||
|
||||||
|
entry.now.battles === entry.then.battles) &&
|
||||||
|
!entry.checked
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{strings.website.tools.playlist.remove}
|
||||||
|
</Button>
|
||||||
|
|
||||||
{wargaming && (
|
{wargaming && (
|
||||||
<>
|
<>
|
||||||
<Button
|
|
||||||
size={{ initial: "1", xs: "2" }}
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
Playlist.mutate((draft) => {
|
|
||||||
draft.list = draft.list?.filter(
|
|
||||||
(entry) =>
|
|
||||||
(!entry.now ||
|
|
||||||
!entry.then ||
|
|
||||||
entry.now.battles === entry.then.battles) &&
|
|
||||||
!entry.checked
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{strings.website.tools.playlist.remove}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
<Button size={{ initial: "1", xs: "2" }}>
|
<Button size={{ initial: "1", xs: "2" }}>
|
||||||
|
|||||||
@@ -5,11 +5,7 @@ import {
|
|||||||
TankType,
|
TankType,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
import {
|
import { LockClosedIcon, LockOpen2Icon } from "@radix-ui/react-icons";
|
||||||
ComponentBooleanIcon,
|
|
||||||
LockClosedIcon,
|
|
||||||
LockOpen2Icon,
|
|
||||||
} from "@radix-ui/react-icons";
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
@@ -71,7 +67,9 @@ function ShellFilter({ index, premium }: { index: number; premium?: boolean }) {
|
|||||||
<img
|
<img
|
||||||
style={{ width: "1em", height: "1em" }}
|
style={{ width: "1em", height: "1em" }}
|
||||||
src={asset(
|
src={asset(
|
||||||
`icons/shells/${shellTypeIcons[shells[index]]}${premium ? "_premium" : ""}.webp`
|
`icons/shells/${shellTypeIcons[shells[index]]}${
|
||||||
|
premium ? "_premium" : ""
|
||||||
|
}.webp`
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -107,7 +105,9 @@ function ShellFilter({ index, premium }: { index: number; premium?: boolean }) {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={asset(
|
src={asset(
|
||||||
`icons/shells/${shellTypeIcons[shellType]}${premium ? "_premium" : ""}.webp`
|
`icons/shells/${shellTypeIcons[shellType]}${
|
||||||
|
premium ? "_premium" : ""
|
||||||
|
}.webp`
|
||||||
)}
|
)}
|
||||||
style={{ width: "1em", height: "1em" }}
|
style={{ width: "1em", height: "1em" }}
|
||||||
/>
|
/>
|
||||||
@@ -135,29 +135,13 @@ function OwnershipFilter(props: FlexProps) {
|
|||||||
<IconButton
|
<IconButton
|
||||||
size={size}
|
size={size}
|
||||||
disabled={!wargaming}
|
disabled={!wargaming}
|
||||||
variant={tankFilters.ownership === "all" ? "solid" : "soft"}
|
variant={tankFilters.showOwned ? "solid" : "soft"}
|
||||||
radius="none"
|
radius="none"
|
||||||
color={tankFilters.ownership === "all" ? undefined : "gray"}
|
color={tankFilters.showOwned ? undefined : "gray"}
|
||||||
highContrast
|
highContrast
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
TankFilters.mutate((draft) => {
|
TankFilters.mutate((draft) => {
|
||||||
draft.ownership = "all";
|
draft.showOwned = !draft.showOwned;
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ComponentBooleanIcon />
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<IconButton
|
|
||||||
size={size}
|
|
||||||
disabled={!wargaming}
|
|
||||||
variant={tankFilters.ownership === "owned" ? "solid" : "soft"}
|
|
||||||
radius="none"
|
|
||||||
color={tankFilters.ownership === "owned" ? undefined : "gray"}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.ownership = "owned";
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -167,13 +151,13 @@ function OwnershipFilter(props: FlexProps) {
|
|||||||
<IconButton
|
<IconButton
|
||||||
size={size}
|
size={size}
|
||||||
disabled={!wargaming}
|
disabled={!wargaming}
|
||||||
variant={tankFilters.ownership === "unowned" ? "solid" : "soft"}
|
variant={tankFilters.showUnowned ? "solid" : "soft"}
|
||||||
radius="none"
|
radius="none"
|
||||||
color={tankFilters.ownership === "unowned" ? undefined : "gray"}
|
color={tankFilters.showUnowned ? undefined : "gray"}
|
||||||
highContrast
|
highContrast
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
TankFilters.mutate((draft) => {
|
TankFilters.mutate((draft) => {
|
||||||
draft.ownership = "unowned";
|
draft.showUnowned = !draft.showUnowned;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -579,27 +563,13 @@ export function FilterControl() {
|
|||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
size={size}
|
size={size}
|
||||||
variant={tankFilters.testing === "include" ? "solid" : "soft"}
|
variant={tankFilters.showNonTesting ? "solid" : "soft"}
|
||||||
radius="none"
|
radius="none"
|
||||||
color={tankFilters.testing === "include" ? undefined : "gray"}
|
color={tankFilters.showNonTesting ? undefined : "gray"}
|
||||||
highContrast
|
highContrast
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
TankFilters.mutate((draft) => {
|
TankFilters.mutate((draft) => {
|
||||||
draft.testing = "include";
|
draft.showNonTesting = !draft.showNonTesting;
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ComponentBooleanIcon />
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
size={size}
|
|
||||||
variant={tankFilters.testing === "exclude" ? "solid" : "soft"}
|
|
||||||
radius="none"
|
|
||||||
color={tankFilters.testing === "exclude" ? undefined : "gray"}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.testing = "exclude";
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -607,15 +577,16 @@ export function FilterControl() {
|
|||||||
style={{ width: "1em", height: "1em", color: "currentColor" }}
|
style={{ width: "1em", height: "1em", color: "currentColor" }}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
size={size}
|
size={size}
|
||||||
variant={tankFilters.testing === "only" ? "solid" : "soft"}
|
variant={tankFilters.showTesting ? "solid" : "soft"}
|
||||||
radius="none"
|
radius="none"
|
||||||
color={tankFilters.testing === "only" ? undefined : "gray"}
|
color={tankFilters.showTesting ? undefined : "gray"}
|
||||||
highContrast
|
highContrast
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
TankFilters.mutate((draft) => {
|
TankFilters.mutate((draft) => {
|
||||||
draft.testing = "only";
|
draft.showTesting = !draft.showTesting;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -487,7 +487,7 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{tankFilters.testing === "only" && (
|
{tankFilters.showTesting && !tankFilters.showNonTesting && (
|
||||||
<Flex justify="center" mt="4">
|
<Flex justify="center" mt="4">
|
||||||
<Callout.Root color="amber">
|
<Callout.Root color="amber">
|
||||||
<Callout.Icon>
|
<Callout.Icon>
|
||||||
|
|||||||
+24
-6
@@ -12,6 +12,7 @@ import {
|
|||||||
EyeOpenIcon,
|
EyeOpenIcon,
|
||||||
ShadowInnerIcon,
|
ShadowInnerIcon,
|
||||||
ShadowNoneIcon,
|
ShadowNoneIcon,
|
||||||
|
SunIcon,
|
||||||
} from "@radix-ui/react-icons";
|
} from "@radix-ui/react-icons";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -55,7 +56,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
const hasCustomShell = Tankopedia.use(
|
const hasCustomShell = Tankopedia.use(
|
||||||
(state) => state.customShell !== undefined
|
(state) => state.customShell !== undefined
|
||||||
);
|
);
|
||||||
const display = Tankopedia.use((state) => state.display);
|
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
||||||
const isFullScreen = useFullScreen();
|
const isFullScreen = useFullScreen();
|
||||||
const advancedHighlighting = TankopediaPersistent.use(
|
const advancedHighlighting = TankopediaPersistent.use(
|
||||||
(state) => state.advancedHighlighting
|
(state) => state.advancedHighlighting
|
||||||
@@ -73,6 +74,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
const { strings, unwrap } = useLocale();
|
const { strings, unwrap } = useLocale();
|
||||||
const revealed = Tankopedia.use((state) => state.revealed);
|
const revealed = Tankopedia.use((state) => state.revealed);
|
||||||
const disturbed = Tankopedia.use((state) => state.disturbed);
|
const disturbed = Tankopedia.use((state) => state.disturbed);
|
||||||
|
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -84,7 +86,9 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
gap="2"
|
gap="2"
|
||||||
direction="column"
|
direction="column"
|
||||||
top="50%"
|
top="50%"
|
||||||
right={display === TankopediaDisplay.DynamicArmor ? "3" : "-4rem"}
|
right={
|
||||||
|
requestedDisplay === TankopediaDisplay.DynamicArmor ? "3" : "-4rem"
|
||||||
|
}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
transform: "translateY(-50%)",
|
transform: "translateY(-50%)",
|
||||||
@@ -296,7 +300,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
>
|
>
|
||||||
{disturbed && (
|
{disturbed && (
|
||||||
<>
|
<>
|
||||||
{display === TankopediaDisplay.DynamicArmor && (
|
{requestedDisplay === TankopediaDisplay.DynamicArmor && (
|
||||||
<IconButton
|
<IconButton
|
||||||
size="2"
|
size="2"
|
||||||
highContrast
|
highContrast
|
||||||
@@ -349,6 +353,20 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size="2"
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankopediaPersistent.mutate((draft) => {
|
||||||
|
draft.highGraphics = !draft.highGraphics;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
color={highGraphics ? undefined : "gray"}
|
||||||
|
variant={highGraphics ? "solid" : "surface"}
|
||||||
|
>
|
||||||
|
<SunIcon />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
<ScreenshotButton
|
<ScreenshotButton
|
||||||
color="gray"
|
color="gray"
|
||||||
size="2"
|
size="2"
|
||||||
@@ -384,11 +402,11 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
<Flex gap="3" align="center" mt="2">
|
<Flex gap="3" align="center" mt="2">
|
||||||
<SegmentedControl.Root
|
<SegmentedControl.Root
|
||||||
variant="classic"
|
variant="classic"
|
||||||
value={`${disturbed ? display : -1}`}
|
value={`${disturbed ? requestedDisplay : -1}`}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
Tankopedia.mutate((draft) => {
|
Tankopedia.mutate((draft) => {
|
||||||
draft.disturbed = true;
|
draft.disturbed = true;
|
||||||
draft.display = Number(value);
|
draft.requestedDisplay = Number(value);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -430,7 +448,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
</SegmentedControl.Item>
|
</SegmentedControl.Item>
|
||||||
</SegmentedControl.Root>
|
</SegmentedControl.Root>
|
||||||
|
|
||||||
{display === TankopediaDisplay.DynamicArmor && (
|
{requestedDisplay === TankopediaDisplay.DynamicArmor && (
|
||||||
<Dialog.Root
|
<Dialog.Root
|
||||||
open={antagonistSelectorOpen}
|
open={antagonistSelectorOpen}
|
||||||
onOpenChange={setAntagonistSelectorOpen}
|
onOpenChange={setAntagonistSelectorOpen}
|
||||||
|
|||||||
+3
-13
@@ -22,14 +22,12 @@ const poseDistances: Record<Pose, number> = {
|
|||||||
const modelDefinitions = await awaitableModelDefinitions;
|
const modelDefinitions = await awaitableModelDefinitions;
|
||||||
|
|
||||||
interface ControlsProps {
|
interface ControlsProps {
|
||||||
naked?: boolean;
|
|
||||||
autoRotate?: boolean;
|
autoRotate?: boolean;
|
||||||
zoomable?: boolean;
|
zoomable?: boolean;
|
||||||
enableRotate?: boolean;
|
enableRotate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Controls({
|
export function Controls({
|
||||||
naked,
|
|
||||||
autoRotate = true,
|
autoRotate = true,
|
||||||
zoomable = true,
|
zoomable = true,
|
||||||
enableRotate = true,
|
enableRotate = true,
|
||||||
@@ -79,11 +77,7 @@ export function Controls({
|
|||||||
protagonistHullOrigin.y +
|
protagonistHullOrigin.y +
|
||||||
protagonistTurretOrigin.y +
|
protagonistTurretOrigin.y +
|
||||||
protagonistGunOrigin.y;
|
protagonistGunOrigin.y;
|
||||||
const inspectModeInitialPosition = new Vector3(
|
const inspectModeInitialPosition = new Vector3(-8, 2, -13);
|
||||||
-8,
|
|
||||||
naked ? gunHeight + 10 : 2,
|
|
||||||
-13
|
|
||||||
);
|
|
||||||
|
|
||||||
const t0 = useRef(Date.now() / 1000);
|
const t0 = useRef(Date.now() / 1000);
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
@@ -236,14 +230,10 @@ export function Controls({
|
|||||||
orbitControls.current.rotateSpeed = 0.25;
|
orbitControls.current.rotateSpeed = 0.25;
|
||||||
|
|
||||||
camera.position.copy(inspectModeInitialPosition);
|
camera.position.copy(inspectModeInitialPosition);
|
||||||
orbitControls.current.target.set(
|
orbitControls.current.target.set(0, gunHeight * 0.6, 0);
|
||||||
0,
|
|
||||||
gunHeight * (naked ? 1 / 4 : 3 / 4),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
orbitControls.current.enablePan = true;
|
orbitControls.current.enablePan = true;
|
||||||
orbitControls.current.enableZoom = true;
|
orbitControls.current.enableZoom = true;
|
||||||
camera.fov = naked ? 20 : 25;
|
camera.fov = 25;
|
||||||
|
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
}
|
}
|
||||||
|
|||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
import { invalidate, useThree } from "@react-three/fiber";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import type { Fog } from "three";
|
|
||||||
import { fogAnimationTime, fogFar0, fogFar1, forNear0, forNear1 } from "..";
|
|
||||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
|
||||||
|
|
||||||
export function InitialFogReveal() {
|
|
||||||
const fog = useThree((state) => state.scene.fog as Fog | null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
Tankopedia.mutate((draft) => {
|
|
||||||
draft.revealed = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!fog) return;
|
|
||||||
|
|
||||||
const t0 = Date.now();
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
const t = (Date.now() - t0) / 1000;
|
|
||||||
const x = t / fogAnimationTime;
|
|
||||||
const near = forNear1 + (forNear0 - forNear1) * x;
|
|
||||||
const far = fogFar1 + (fogFar0 - fogFar1) * x;
|
|
||||||
|
|
||||||
fog.near = near;
|
|
||||||
fog.far = far;
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
if (x >= 1) {
|
|
||||||
fog.near = forNear0;
|
|
||||||
fog.far = fogFar0;
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
}, 1000 / 60);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
+77
-41
@@ -1,82 +1,118 @@
|
|||||||
import { useHelper } from "@react-three/drei";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { useFrame } from "@react-three/fiber";
|
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { useRef, useState } from "react";
|
import { Quicklime } from "quicklime";
|
||||||
import { Color, SpotLight, SpotLightHelper } from "three";
|
import { useEffect, useRef } from "react";
|
||||||
import { degToRad } from "three/src/math/MathUtils.js";
|
import { HemisphereLight, SpotLight, type Group } from "three";
|
||||||
import { isHalloween } from "../../../../../../core/blitzkit/isHalloween";
|
import { clamp, degToRad, lerp } from "three/src/math/MathUtils.js";
|
||||||
|
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||||
|
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
|
||||||
import { HelpingSpotLight } from "../../../../../HelpingSpotLight";
|
import { HelpingSpotLight } from "../../../../../HelpingSpotLight";
|
||||||
|
|
||||||
|
const ANGLE = Math.PI * 2 ** -2;
|
||||||
|
const REVEAL_ANIMATION_TIME = 3;
|
||||||
|
const TRANSITION_ANIMATION_TIME = 0.5;
|
||||||
|
|
||||||
const LIGHTS_COUNT = 5;
|
const LIGHTS_COUNT = 5;
|
||||||
|
const THETA_OFFSET = degToRad(-152);
|
||||||
const LIGHT_DISTANCE = 13;
|
const LIGHT_DISTANCE = 13;
|
||||||
const LIGHT_HEIGHT_0 = 4;
|
const LIGHT_HEIGHT_0 = 4;
|
||||||
const LIGHT_HEIGHT_1 = 2;
|
const LIGHT_HEIGHT_1 = 2;
|
||||||
const INTENSITY = 50;
|
const INTENSITY_0 = 3.5;
|
||||||
|
const INTENSITY_1 = 1.5;
|
||||||
|
const HEMISPHERE_INTENSITY = 2;
|
||||||
|
|
||||||
const N = 128;
|
export const transitionEvent = new Quicklime<number>(0);
|
||||||
const TARGET_FRAME_RATE = 30;
|
|
||||||
|
|
||||||
export function Lighting() {
|
export function Lighting() {
|
||||||
const [cast, setCast] = useState(true);
|
const wrapper = useRef<Group>(null!);
|
||||||
|
|
||||||
const t0 = useRef(0);
|
const t0 = useRef(0);
|
||||||
const topLight = useRef<SpotLight>(null!);
|
const transition = useRef(false);
|
||||||
const frontLight = useRef<SpotLight>(null!);
|
const clock = useThree((state) => state.clock);
|
||||||
|
const display = Tankopedia.use((state) => state.display);
|
||||||
|
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
||||||
|
const animationTime = useRef(REVEAL_ANIMATION_TIME);
|
||||||
|
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
|
||||||
|
|
||||||
useHelper(topLight, SpotLightHelper, "red");
|
useEffect(() => {
|
||||||
useHelper(frontLight, SpotLightHelper, "green");
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.revealed = true;
|
||||||
|
});
|
||||||
|
|
||||||
const last = useRef<number>(0);
|
t0.current = clock.elapsedTime - REVEAL_ANIMATION_TIME;
|
||||||
const samples = useRef<number[]>([]);
|
}, []);
|
||||||
|
|
||||||
useFrame(({ clock, frameloop }) => {
|
useFrame(({ invalidate }) => {
|
||||||
if (frameloop === "never") {
|
if (transition.current) {
|
||||||
|
transition.current = false;
|
||||||
t0.current = clock.elapsedTime;
|
t0.current = clock.elapsedTime;
|
||||||
samples.current = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!cast || clock.elapsedTime - t0.current < TARGET_FRAME_RATE * N) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dt = clock.elapsedTime - last.current;
|
const x = clamp(
|
||||||
last.current = clock.elapsedTime;
|
(clock.elapsedTime - t0.current) / animationTime.current,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
const t = (0.5 * Math.sin(Math.PI * (x + 0.5)) + 0.5) ** 2;
|
||||||
|
|
||||||
const fps = 1 / dt;
|
if (t !== transitionEvent.last) {
|
||||||
samples.current.push(fps);
|
transitionEvent.dispatch(t);
|
||||||
|
}
|
||||||
|
|
||||||
if (samples.current.length > N) samples.current.shift();
|
for (const child of wrapper.current.children) {
|
||||||
|
if (child instanceof SpotLight) {
|
||||||
|
child.angle = ANGLE * t;
|
||||||
|
} else if (child instanceof HemisphereLight) {
|
||||||
|
child.intensity = HEMISPHERE_INTENSITY * t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const avg =
|
if (x < 2) invalidate();
|
||||||
samples.current.reduce((a, b) => a + b, 0) / samples.current.length;
|
|
||||||
|
|
||||||
if (avg < TARGET_FRAME_RATE) setCast(false);
|
if (
|
||||||
|
x > 1 &&
|
||||||
|
Tankopedia.state.display !== Tankopedia.state.requestedDisplay
|
||||||
|
) {
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.display = requestedDisplay;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (requestedDisplay === display) return;
|
||||||
|
|
||||||
|
transition.current = true;
|
||||||
|
animationTime.current = TRANSITION_ANIMATION_TIME;
|
||||||
|
}, [requestedDisplay]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<group ref={wrapper}>
|
||||||
|
<hemisphereLight intensity={0} color="#ffffff" groundColor="#afafaf" />
|
||||||
|
|
||||||
{times(LIGHTS_COUNT, (index) => {
|
{times(LIGHTS_COUNT, (index) => {
|
||||||
const x = index / (LIGHTS_COUNT - 1);
|
const x = index / (LIGHTS_COUNT - 1);
|
||||||
const theta = 2 * Math.PI * (index / LIGHTS_COUNT) - degToRad(152);
|
const theta = 2 * Math.PI * (index / LIGHTS_COUNT) + THETA_OFFSET;
|
||||||
const position = [
|
const position = [
|
||||||
LIGHT_DISTANCE * Math.sin(theta),
|
LIGHT_DISTANCE * Math.sin(theta),
|
||||||
LIGHT_HEIGHT_0 * (1 - x) + LIGHT_HEIGHT_1 * x,
|
lerp(LIGHT_HEIGHT_0, LIGHT_HEIGHT_1, x),
|
||||||
LIGHT_DISTANCE * Math.cos(theta),
|
LIGHT_DISTANCE * Math.cos(theta),
|
||||||
] as const;
|
] as const;
|
||||||
const color = new Color();
|
const intensity = lerp(INTENSITY_0, INTENSITY_1, x);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HelpingSpotLight
|
<HelpingSpotLight
|
||||||
|
userData={{ index }}
|
||||||
key={index}
|
key={index}
|
||||||
position={position}
|
position={position}
|
||||||
intensity={INTENSITY}
|
intensity={intensity}
|
||||||
penumbra={1}
|
penumbra={1}
|
||||||
castShadow={cast}
|
castShadow={highGraphics}
|
||||||
decay={1}
|
decay={0}
|
||||||
color={color}
|
color="#ffffff"
|
||||||
|
angle={0}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+3
-2
@@ -4,7 +4,7 @@ import { Quicklime, type QuicklimeEvent } from "quicklime";
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Mesh, MeshStandardMaterial } from "three";
|
import { Mesh, MeshStandardMaterial } from "three";
|
||||||
|
|
||||||
const SIZE = 2 ** 5;
|
const SIZE = 2 ** 6;
|
||||||
|
|
||||||
export const screenshotReadyEvent = new Quicklime(false);
|
export const screenshotReadyEvent = new Quicklime(false);
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ export function SceneProps() {
|
|||||||
receiveShadow
|
receiveShadow
|
||||||
visible={!screenshotReadyEvent.last!}
|
visible={!screenshotReadyEvent.last!}
|
||||||
ref={mesh}
|
ref={mesh}
|
||||||
|
renderOrder={-1}
|
||||||
>
|
>
|
||||||
<planeGeometry args={[SIZE, SIZE]} />
|
<planeGeometry args={[SIZE, SIZE]} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
@@ -43,7 +44,7 @@ export function SceneProps() {
|
|||||||
ref={material}
|
ref={material}
|
||||||
color="black"
|
color="black"
|
||||||
roughness={1}
|
roughness={1}
|
||||||
metalness={1}
|
metalness={0}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -106,7 +106,9 @@ export function TankModel() {
|
|||||||
const isTurret = node.name.startsWith("turret_");
|
const isTurret = node.name.startsWith("turret_");
|
||||||
const isCurrentTurret =
|
const isCurrentTurret =
|
||||||
node.name ===
|
node.name ===
|
||||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`;
|
`turret_${turretModelDefinition.model_id
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}`;
|
||||||
const isVisible = isCurrentTurret;
|
const isVisible = isCurrentTurret;
|
||||||
const position = new Vector2();
|
const position = new Vector2();
|
||||||
const delta = new Vector2();
|
const delta = new Vector2();
|
||||||
|
|||||||
+12
-53
@@ -1,12 +1,9 @@
|
|||||||
import { BLITZKIT_TANK_ICON_SIZE } from "@blitzkit/core";
|
|
||||||
import { invalidate } from "@react-three/fiber";
|
|
||||||
import {
|
import {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
Suspense,
|
Suspense,
|
||||||
useEffect,
|
useEffect,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
|
||||||
} from "react";
|
} from "react";
|
||||||
import { Fog } from "three";
|
import { Fog } from "three";
|
||||||
import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits";
|
import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits";
|
||||||
@@ -29,26 +26,16 @@ import { SmartCanvas } from "../../../../SmartCanvas";
|
|||||||
import { AutoClear } from "./components/AutoClear";
|
import { AutoClear } from "./components/AutoClear";
|
||||||
import { Controls } from "./components/Control";
|
import { Controls } from "./components/Control";
|
||||||
import { InitialAligner } from "./components/InitialAligner";
|
import { InitialAligner } from "./components/InitialAligner";
|
||||||
import { InitialFogReveal } from "./components/InitialFogReveal";
|
|
||||||
import { Lighting } from "./components/Lighting";
|
import { Lighting } from "./components/Lighting";
|
||||||
import { SceneProps } from "./components/SceneProps";
|
import { SceneProps } from "./components/SceneProps";
|
||||||
import { TankModel } from "./components/TankModel";
|
import { TankModel } from "./components/TankModel";
|
||||||
import { TransitionSkeleton } from "./components/TransitionSkeleton";
|
|
||||||
|
|
||||||
interface TankSandboxProps {
|
interface TankSandboxProps {
|
||||||
thicknessRange: ThicknessRange;
|
thicknessRange: ThicknessRange;
|
||||||
naked?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const forNear0 = 30;
|
|
||||||
export const fogFar0 = 50;
|
|
||||||
export const forNear1 = 0;
|
|
||||||
export const fogFar1 = 0;
|
|
||||||
export const fogAnimationTime = 2.5;
|
|
||||||
|
|
||||||
export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||||
({ thicknessRange, naked }, ref) => {
|
({ thicknessRange }, ref) => {
|
||||||
const fog = useRef(new Fog("black", forNear1, fogFar1));
|
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const canvas = useRef<HTMLCanvasElement>(null);
|
||||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||||
@@ -58,40 +45,11 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonistTurret.id];
|
tankModelDefinition.turrets[protagonistTurret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
|
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
|
||||||
const rawDisplay = Tankopedia.use((state) => state.display);
|
const display = Tankopedia.use((state) => state.display);
|
||||||
const [display, setDisplay] = useState(rawDisplay);
|
|
||||||
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
||||||
(state) => state.hideTankModelUnderArmor
|
(state) => state.hideTankModelUnderArmor
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (rawDisplay === display) return;
|
|
||||||
|
|
||||||
const t0 = Date.now();
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
const t = (Date.now() - t0) / 1000;
|
|
||||||
const x = t / fogAnimationTime;
|
|
||||||
const y = Math.sin(Math.PI * x);
|
|
||||||
|
|
||||||
const near = forNear0 + (forNear1 - forNear0) * y;
|
|
||||||
const far = fogFar0 + (fogFar1 - fogFar0) * y;
|
|
||||||
|
|
||||||
fog.current.near = near;
|
|
||||||
fog.current.far = far;
|
|
||||||
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
if (rawDisplay !== display && x >= 0.5) setDisplay(rawDisplay);
|
|
||||||
if (x >= 1) {
|
|
||||||
fog.current.near = forNear0;
|
|
||||||
fog.current.far = fogFar0;
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
}, 1000 / 60);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, [rawDisplay]);
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => canvas.current!, []);
|
useImperativeHandle(ref, () => canvas.current!, []);
|
||||||
|
|
||||||
function handlePointerDown() {
|
function handlePointerDown() {
|
||||||
@@ -188,10 +146,12 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
}
|
}
|
||||||
}, [display]);
|
}, [display]);
|
||||||
|
|
||||||
|
console.log(TankopediaDisplay[display]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SmartCanvas
|
<SmartCanvas
|
||||||
ref={canvas}
|
ref={canvas}
|
||||||
scene={{ fog: naked ? undefined : fog.current }}
|
scene={{ fog: new Fog("black", 30, 50) }}
|
||||||
gl={{
|
gl={{
|
||||||
clippingPlanes: [],
|
clippingPlanes: [],
|
||||||
localClippingEnabled: true,
|
localClippingEnabled: true,
|
||||||
@@ -207,30 +167,29 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
width: naked ? BLITZKIT_TANK_ICON_SIZE.width : "100%",
|
width: "100%",
|
||||||
height: naked ? BLITZKIT_TANK_ICON_SIZE.height : "100%",
|
height: "100%",
|
||||||
outline: naked ? "1rem red solid" : undefined,
|
outline: undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!naked && <SceneProps />}
|
<Lighting />
|
||||||
|
|
||||||
|
<SceneProps />
|
||||||
{(display === TankopediaDisplay.Model ||
|
{(display === TankopediaDisplay.Model ||
|
||||||
(display === TankopediaDisplay.DynamicArmor &&
|
(display === TankopediaDisplay.DynamicArmor &&
|
||||||
!hideTankModelUnderArmor)) && <TankModel />}
|
!hideTankModelUnderArmor)) && <TankModel />}
|
||||||
<TransitionSkeleton />
|
|
||||||
<ShotDisplay />
|
<ShotDisplay />
|
||||||
<ArmorPlateDisplay />
|
<ArmorPlateDisplay />
|
||||||
<AutoClear />
|
<AutoClear />
|
||||||
|
|
||||||
<Suspense>
|
<Suspense>
|
||||||
{/* Controls within Suspense to allow for frame-perfect start of camera auto-rotate */}
|
{/* Controls within Suspense to allow for frame-perfect start of camera auto-rotate */}
|
||||||
<Controls naked={naked} />
|
<Controls />
|
||||||
<InitialFogReveal />
|
|
||||||
|
|
||||||
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
||||||
{display === TankopediaDisplay.StaticArmor && (
|
{display === TankopediaDisplay.StaticArmor && (
|
||||||
<StaticArmor thicknessRange={thicknessRange} />
|
<StaticArmor thicknessRange={thicknessRange} />
|
||||||
)}
|
)}
|
||||||
<Lighting />
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
<InitialAligner />
|
<InitialAligner />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Box, Flex } from "@radix-ui/themes";
|
import { Box, Flex } from "@radix-ui/themes";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { Suspense, useEffect, useMemo, useRef } from "react";
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
import { awaitableTankDefinitions } from "../../../core/awaitables/tankDefinitions";
|
import { awaitableTankDefinitions } from "../../../core/awaitables/tankDefinitions";
|
||||||
import { useFullScreen } from "../../../hooks/useFullScreen";
|
import { useFullScreen } from "../../../hooks/useFullScreen";
|
||||||
import { Duel } from "../../../stores/duel";
|
import { Duel } from "../../../stores/duel";
|
||||||
@@ -99,11 +99,11 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
isFullScreen
|
isFullScreen
|
||||||
? "100vh"
|
? "100vh"
|
||||||
: disturbed
|
: disturbed
|
||||||
? "calc(100svh - 8rem)"
|
? "calc(100svh - 8rem)"
|
||||||
: {
|
: {
|
||||||
initial: "28rem",
|
initial: "28rem",
|
||||||
md: "calc(100svh - 14rem)",
|
md: "calc(100svh - 14rem)",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maxWidth={isFullScreen ? undefined : "120rem"}
|
maxWidth={isFullScreen ? undefined : "120rem"}
|
||||||
flexGrow="1"
|
flexGrow="1"
|
||||||
@@ -120,30 +120,17 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
position="relative"
|
position="relative"
|
||||||
>
|
>
|
||||||
<Box position="absolute" width="100%" height="100%" overflow="hidden">
|
<Box position="absolute" width="100%" height="100%" overflow="hidden">
|
||||||
<Box
|
<Box width="100%" height="100%" position="relative">
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
position="relative"
|
|
||||||
style={{
|
|
||||||
transitionDuration: "1s",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
<Box
|
||||||
style={{
|
position="absolute"
|
||||||
width: "100%",
|
width="100%"
|
||||||
height: "100%",
|
height="100%"
|
||||||
transitionDuration: "2s",
|
top="0"
|
||||||
opacity: revealed ? 1 : 0,
|
left="0"
|
||||||
filter: disturbed
|
|
||||||
? undefined
|
|
||||||
: `drop-shadow(0 1rem 1rem black)`,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Suspense>
|
{!skeleton && (
|
||||||
{!skeleton && (
|
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
||||||
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
)}
|
||||||
)}
|
|
||||||
</Suspense>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,14 @@ export async function filterTank(
|
|||||||
owned: number[] = []
|
owned: number[] = []
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
(filters.ownership === "all" ||
|
((filters.showOwned && owned.includes(tank.id)) ||
|
||||||
(filters.ownership === "owned" && owned.includes(tank.id)) ||
|
(filters.showUnowned && !owned.includes(tank.id))) &&
|
||||||
(filters.ownership === "unowned" && !owned.includes(tank.id))) &&
|
|
||||||
(filters.tiers.length === 0 || filters.tiers.includes(tank.tier)) &&
|
(filters.tiers.length === 0 || filters.tiers.includes(tank.tier)) &&
|
||||||
(filters.nations.length === 0 || filters.nations.includes(tank.nation)) &&
|
(filters.nations.length === 0 || filters.nations.includes(tank.nation)) &&
|
||||||
(filters.classes.length === 0 || filters.classes.includes(tank.class)) &&
|
(filters.classes.length === 0 || filters.classes.includes(tank.class)) &&
|
||||||
(filters.types.length === 0 || filters.types.includes(tank.type)) &&
|
(filters.types.length === 0 || filters.types.includes(tank.type)) &&
|
||||||
(filters.testing === "include" ||
|
((filters.showTesting && tank.testing) ||
|
||||||
(filters.testing === "only" && tank.testing) ||
|
(filters.showNonTesting && !tank.testing)) &&
|
||||||
(filters.testing === "exclude" && !tank.testing)) &&
|
|
||||||
(filters.gunType.length === 0 ||
|
(filters.gunType.length === 0 ||
|
||||||
(filters.gunType.includes("regular") &&
|
(filters.gunType.includes("regular") &&
|
||||||
tank.turrets.some((turret) =>
|
tank.turrets.some((turret) =>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
import ToolOpenGraph from "../../../components/ToolOpenGraph.astro";
|
||||||
|
import { tools } from "../../../constants/tools";
|
||||||
import BaseLayout from "../../../layouts/BaseLayout.astro";
|
import BaseLayout from "../../../layouts/BaseLayout.astro";
|
||||||
import { Page } from "./_index";
|
import { Page } from "./_index";
|
||||||
|
|
||||||
@@ -7,6 +9,8 @@ export { getStaticPaths } from "../index.astro";
|
|||||||
const { locale } = Astro.props;
|
const { locale } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<ToolOpenGraph {locale} tool={tools.guess} />
|
||||||
|
|
||||||
<BaseLayout {locale} noFooter>
|
<BaseLayout {locale} noFooter>
|
||||||
<Page {locale} client:only="react">
|
<Page {locale} client:only="react">
|
||||||
<Page {locale} skeleton slot="fallback" />
|
<Page {locale} skeleton slot="fallback" />
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
TankSearch,
|
TankSearch,
|
||||||
type TankSearchProps,
|
type TankSearchProps,
|
||||||
} from "../../../components/TankSearch";
|
} from "../../../components/TankSearch";
|
||||||
|
import { awaitableModelDefinitions } from "../../../core/awaitables/modelDefinitions";
|
||||||
import { curateMixer } from "../../../core/blitzkit/curateMixer";
|
import { curateMixer } from "../../../core/blitzkit/curateMixer";
|
||||||
import {
|
import {
|
||||||
LocaleProvider,
|
LocaleProvider,
|
||||||
@@ -24,8 +25,13 @@ import {
|
|||||||
type LocaleAcceptorProps,
|
type LocaleAcceptorProps,
|
||||||
} from "../../../hooks/useLocale";
|
} from "../../../hooks/useLocale";
|
||||||
import { Mixer } from "../../../stores/mixer";
|
import { Mixer } from "../../../stores/mixer";
|
||||||
|
import { Tankopedia } from "../../../stores/tankopedia";
|
||||||
import type { MaybeSkeletonComponentProps } from "../../../types/maybeSkeletonComponentProps";
|
import type { MaybeSkeletonComponentProps } from "../../../types/maybeSkeletonComponentProps";
|
||||||
|
|
||||||
|
const modelDefinition = await awaitableModelDefinitions.then(
|
||||||
|
({ models }) => models[1]
|
||||||
|
);
|
||||||
|
|
||||||
export function Page({
|
export function Page({
|
||||||
locale,
|
locale,
|
||||||
skeleton,
|
skeleton,
|
||||||
@@ -33,6 +39,7 @@ export function Page({
|
|||||||
const initial = useRef(curateMixer());
|
const initial = useRef(curateMixer());
|
||||||
|
|
||||||
Mixer.useInitialization(initial.current);
|
Mixer.useInitialization(initial.current);
|
||||||
|
Tankopedia.useInitialization(modelDefinition);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="gray" p="0" maxWidth="unset">
|
<PageWrapper color="gray" p="0" maxWidth="unset">
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { PageWrapper } from "../../../components/PageWrapper";
|
import { PageWrapper } from "../../../components/PageWrapper";
|
||||||
import { PerformanceInfo } from "../../../components/Performance/PerformanceInfo";
|
|
||||||
import { PlayerCountControl } from "../../../components/Performance/PlayerCountControl";
|
import { PlayerCountControl } from "../../../components/Performance/PlayerCountControl";
|
||||||
import { TankTable } from "../../../components/Performance/Table";
|
import { TankTable } from "../../../components/Performance/Table";
|
||||||
import { FilterControl } from "../../../components/TankSearch/components/FilterControl";
|
import { FilterControl } from "../../../components/TankSearch/components/FilterControl";
|
||||||
@@ -21,8 +20,7 @@ export function Page({ skeleton, locale }: PageProps) {
|
|||||||
|
|
||||||
function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="jade" maxWidth="100%">
|
<PageWrapper pb="0" px="0" color="jade" maxWidth="100%">
|
||||||
<PerformanceInfo skeleton={skeleton} />
|
|
||||||
<FilterControl />
|
<FilterControl />
|
||||||
<PlayerCountControl />
|
<PlayerCountControl />
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ export function Page({
|
|||||||
}: Props & MaybeSkeletonComponentProps) {
|
}: Props & MaybeSkeletonComponentProps) {
|
||||||
const generated = Playlist.use((state) => state.list !== undefined);
|
const generated = Playlist.use((state) => state.list !== undefined);
|
||||||
|
|
||||||
if (App.state.logins.wargaming && TankFilters.state.ownership !== "owned") {
|
if (App.state.logins.wargaming) TankFilters.state.showUnowned = false;
|
||||||
TankFilters.state.ownership = "owned";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LocaleProvider locale={locale}>
|
<LocaleProvider locale={locale}>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Samples } from '@blitzkit/core';
|
import type { Samples } from "@blitzkit/core";
|
||||||
import { Varuna } from 'varuna';
|
import { Varuna } from "varuna";
|
||||||
|
|
||||||
export interface Performance {
|
export interface Performance {
|
||||||
playerCountPeriod: PlayerCountPeriod;
|
playerCountPeriod: PlayerCountPeriod;
|
||||||
@@ -8,5 +8,5 @@ export interface Performance {
|
|||||||
export type PlayerCountPeriod = keyof Samples;
|
export type PlayerCountPeriod = keyof Samples;
|
||||||
|
|
||||||
export const Performance = new Varuna<Performance>({
|
export const Performance = new Varuna<Performance>({
|
||||||
playerCountPeriod: 'd_30',
|
playerCountPeriod: "total",
|
||||||
});
|
});
|
||||||
@@ -17,12 +17,15 @@ export interface TankFilters {
|
|||||||
nations: string[];
|
nations: string[];
|
||||||
classes: TankClass[];
|
classes: TankClass[];
|
||||||
types: TankType[];
|
types: TankType[];
|
||||||
testing: "include" | "exclude" | "only";
|
|
||||||
search: string | null;
|
search: string | null;
|
||||||
searching: boolean;
|
searching: boolean;
|
||||||
gunType: CaseType<GunDefinition>[];
|
gunType: CaseType<GunDefinition>[];
|
||||||
shells: [ShellType | null, ShellType | null, ShellType | null];
|
shells: [ShellType | null, ShellType | null, ShellType | null];
|
||||||
ownership: "all" | "owned" | "unowned";
|
|
||||||
|
showTesting: boolean;
|
||||||
|
showNonTesting: boolean;
|
||||||
|
showOwned: boolean;
|
||||||
|
showUnowned: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TankFilters = new Varuna<TankFilters>({
|
export const TankFilters = new Varuna<TankFilters>({
|
||||||
@@ -30,10 +33,13 @@ export const TankFilters = new Varuna<TankFilters>({
|
|||||||
nations: [],
|
nations: [],
|
||||||
classes: [],
|
classes: [],
|
||||||
types: [],
|
types: [],
|
||||||
testing: "include",
|
|
||||||
search: null,
|
search: null,
|
||||||
searching: false,
|
searching: false,
|
||||||
gunType: [],
|
gunType: [],
|
||||||
shells: [null, null, null],
|
shells: [null, null, null],
|
||||||
ownership: "all",
|
|
||||||
|
showTesting: true,
|
||||||
|
showNonTesting: true,
|
||||||
|
showOwned: true,
|
||||||
|
showUnowned: true,
|
||||||
});
|
});
|
||||||
@@ -100,6 +100,7 @@ interface Tankopedia {
|
|||||||
);
|
);
|
||||||
xpMultiplier: (typeof XP_MULTIPLIERS)[number];
|
xpMultiplier: (typeof XP_MULTIPLIERS)[number];
|
||||||
customShell?: ShellDefinition;
|
customShell?: ShellDefinition;
|
||||||
|
requestedDisplay: TankopediaDisplay;
|
||||||
display: TankopediaDisplay;
|
display: TankopediaDisplay;
|
||||||
statSearch?: string;
|
statSearch?: string;
|
||||||
}
|
}
|
||||||
@@ -114,5 +115,6 @@ export const Tankopedia = new Varuna<Tankopedia, ModelDefinition>((model) => ({
|
|||||||
skills: createDefaultSkills(skillDefinitions),
|
skills: createDefaultSkills(skillDefinitions),
|
||||||
model,
|
model,
|
||||||
xpMultiplier: 1,
|
xpMultiplier: 1,
|
||||||
|
requestedDisplay: TankopediaDisplay.Model,
|
||||||
display: TankopediaDisplay.Model,
|
display: TankopediaDisplay.Model,
|
||||||
}));
|
}));
|
||||||
@@ -15,6 +15,7 @@ export interface TankopediaPersistent {
|
|||||||
showPrimaryArmor: boolean;
|
showPrimaryArmor: boolean;
|
||||||
recentlyViewed: number[];
|
recentlyViewed: number[];
|
||||||
hideTankModelUnderArmor: boolean;
|
hideTankModelUnderArmor: boolean;
|
||||||
|
highGraphics: boolean;
|
||||||
|
|
||||||
sort: {
|
sort: {
|
||||||
by: TankopediaSortBy;
|
by: TankopediaSortBy;
|
||||||
@@ -33,6 +34,7 @@ export const TankopediaPersistent = new Varuna<TankopediaPersistent>(
|
|||||||
showExternalModules: true,
|
showExternalModules: true,
|
||||||
showPrimaryArmor: true,
|
showPrimaryArmor: true,
|
||||||
recentlyViewed: [],
|
recentlyViewed: [],
|
||||||
|
highGraphics: true,
|
||||||
sort: {
|
sort: {
|
||||||
by: "meta.none",
|
by: "meta.none",
|
||||||
direction: "ascending",
|
direction: "ascending",
|
||||||
|
|||||||
Reference in new issue
Block a user