mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
Compare commits
13
Commits
1c382022a7
...
5cae5d1583
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cae5d1583 | ||
|
|
5c88bda07a | ||
|
|
19dbd8ca2e | ||
|
|
f17ac2b575 | ||
|
|
5d7482b158 | ||
|
|
22aff6188a | ||
|
|
1ee6cdabe5 | ||
|
|
480d2226f7 | ||
|
|
c42b109974 | ||
|
|
9537349664 | ||
|
|
8793c9a9c9 | ||
|
|
4d57b43912 | ||
|
|
ee146b8ed9 |
No files matched your search
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "bun"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
@@ -5,36 +5,3 @@ Tools for everything and anything, World of Tanks Blitz.
|
||||
This GitHub repository contains the code for BlitzKit and all its services. If you are here, looking to use BlitzKit, go to https://blitzkit.app/.
|
||||
|
||||
If you intend to contribute to the code, note that you may need to check out sensitive, closed-source submodules to locally develop certain aspects of BlitzKit. Communicate with `@tresabhi` on Discord to get started or discuss sensitive content, such as environment variables or submodules.
|
||||
|
||||
## Packages
|
||||
|
||||
| Package | Title | Source | Memo |
|
||||
| ------------------------------------ | -------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [packages/core](packages/core) | | | Mutual internal code shared between all other packages. |
|
||||
| [packages/i18n](packages/i18n) | | [Crowdin](https://crowdin.com/project/blitzkrieg) | Translations served as JSON shared across all other packages, including some utility code. |
|
||||
| [packages/scripts](packages/scripts) | | | CLI utilities for the development and deployment of BlitzKit services. |
|
||||
| [packages/website](packages/website) | BlitzKit | https://blitzkit.app | The BlitzKit website provides a wide range of tools, APIs, and interconnects all other services. |
|
||||
|
||||
## Submodules
|
||||
|
||||
| Submodule | Title | Source | Memo |
|
||||
| ------------------------------------------------------ | --------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [submodules/blitzkit-closed](packages/blitzkit-closed) | ClosedKit | [blitzkit/closed](https://github.com/blitzkit/closed) | Closed source, sensitive code hidden from the public to prevent bad actors from abusing the game. Discuss with `@tresabhi` on Discord for **special permission** to access. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Name | Source | BlitzKit Default | Memo |
|
||||
| ------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PUBLIC_WARGAMING_APPLICATION_ID` | [Wargaming Developer's Room](https://developers.wargaming.net/) | | Application ID needed to communicate with Wargaming APIs. |
|
||||
| `PUBLIC_PROMOTE_OPENTEST` | | `false` | Optional boolean dictating whether the website advertises [the opentest subdomain of BlitzKit](https://opentest.blitzkit.app/). |
|
||||
| `WOTB_GLOSSARY`, `WOTB_GLOSSARY_DOMAIN`, `WOTB_GLOSSARY_PATH` | | | |
|
||||
| `PUBLIC_GOOGLE_TAG_MANAGER_ID` | [Google Tag Manager](https://tagmanager.google.com/) | `G-GL2JVHCGPQ` | Google Tag Manager's unique identifier for analytical purposes. |
|
||||
| `PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID` | [Google Analytics](https://analytics.google.com/) | `430711767` | Google Analytics' unique property ID for [BlitzKit's apex domain](https://blitzkit.app/) and subdomains. |
|
||||
| `GOOGLE_APPLICATION_CREDENTIALS` | [Google Cloud](https://cloud.google.com/) | | Google Cloud's API key, primarily used for ranking popular tanks and finding relevant tank review videos in Tankopedia. |
|
||||
| `PUBLIC_DISCORD_SERVER_ID` | [BlitzKit Discord Server](https://discord.gg/nDt7AjGJQH) | `734786591205359697` | BlitzKit Discord Server's id. |
|
||||
| `PUBLIC_ASSET_BASE` | [blitzkit/assets](https://github.com/blitzkit/assets) | `https://blitzkit.github.io/assets` | The root domain and path that serves the external assets like tank models and icons. |
|
||||
| `PUBLIC_GUIDE_WEBSITE` | [GuidesBlitz](https://guidesblitz.com/) | `https://guidesblitz.com` | The website that serves the encyclopedia-like content in BlitzKit. |
|
||||
| `SERVICING` | | `false` | Wether the website is down for servicing. |
|
||||
| `GH_TOKEN` | [GitHub Fine-Grained Tokens](https://github.com/settings/personal-access-tokens) | | The GitHub API key used for checking out private submodules and pushing content to the asset repository. |
|
||||
| `DATABASE_URL` | [CockroachDB](https://www.cockroachlabs.com/) | | The CockroachDB-SQL database URL used to link Wargaming and Discord users. |
|
||||
| `WOTB_DLC_CDN` | | | |
|
||||
@@ -112,8 +112,8 @@
|
||||
"@react-three/drei": "^10.7.6",
|
||||
"@react-three/fiber": "^9.4.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"@types/three": "^0.180.0",
|
||||
"@vite-pwa/astro": "^1.1.0",
|
||||
"astro": "^5.14.4",
|
||||
@@ -128,11 +128,11 @@
|
||||
"pwa-install-handler": "^2.6.3",
|
||||
"quicklime": "^1.2.0",
|
||||
"radix-ui": "^1.6.7",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0",
|
||||
"react-error-boundary": "^6.1.3",
|
||||
"react-promise-suspense": "^0.3.4",
|
||||
"soapstone": "^1.0.0",
|
||||
"soapstone": "^1.0.1",
|
||||
"three": "^0.180.0",
|
||||
"three-stdlib": "^2.36.0",
|
||||
"transliteration": "^2.6.1",
|
||||
@@ -939,9 +939,9 @@
|
||||
|
||||
"@types/progress": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-iadjw02vte8qWx7U0YM++EybBha2CQLPGu9iJ97whVgJUT5Zq9MjAPYUnbfRI2Kpehimf1QjFJYxD0t8nqzu5w=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
||||
"@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
|
||||
"@types/react-dom": ["@types/react-dom@19.3.0", "", { "peerDependencies": { "@types/react": "^19.3.0" } }, "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q=="],
|
||||
|
||||
"@types/react-reconciler": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-RsqPttsBQ+6af0nATFXJJpemYQH7kL9+xLNm1z+0MjQFDKBZDM2R6SBrjdvRmHu9i9fM6povACj57Ft+pKRNOA=="],
|
||||
|
||||
@@ -1687,7 +1687,7 @@
|
||||
|
||||
"lodash": ["[email protected]", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash-es": ["[email protected]7.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
|
||||
"lodash-es": ["[email protected]8.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="],
|
||||
|
||||
"lodash.debounce": ["[email protected]", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
|
||||
|
||||
@@ -2003,9 +2003,9 @@
|
||||
|
||||
"range-parser": ["[email protected]", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
||||
"react": ["react@19.3.0", "", {}, "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="],
|
||||
|
||||
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
|
||||
"react-dom": ["react-dom@19.3.0", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.0" } }, "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q=="],
|
||||
|
||||
"react-error-boundary": ["[email protected]", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-GnSKpCohFi2nQmJCWwP8O8wub7zexlePvpsejvQr35vS5RTouS1+utTNOmyc540yw5vyOXnSL1rBWsCQDmkyUA=="],
|
||||
|
||||
@@ -2153,7 +2153,7 @@
|
||||
|
||||
"smol-toml": ["[email protected]", "", {}, "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g=="],
|
||||
|
||||
"soapstone": ["[email protected].0", "", { "dependencies": { "immer": "^11.1.8", "lodash-es": "^4.18.1", "react": "^19.2.7" }, "peerDependencies": { "typescript": "^5" } }, "sha512-Jlf8aCY9aCvgQR3y/aGhkXyC7vAox05J+dply+0wm567UIOYlCiwSR1MhAkZIRIXtOnoUERBrVz4mDYBrQYBDA=="],
|
||||
"soapstone": ["[email protected].1", "", { "dependencies": { "immer": "^11.1.8", "lodash-es": "^4.18.1" }, "peerDependencies": { "react": "^19.3.0", "typescript": "^5" } }, "sha512-5f6iW2Hybjqcvmn+P+X1gH1NKDhry+ePk4F1rqK0yeFjNICx7tqy0X315tQD0Lhv/zUTNA9dV+8BLW9/o7Wlpw=="],
|
||||
|
||||
"socks": ["[email protected]", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
|
||||
|
||||
@@ -2563,6 +2563,8 @@
|
||||
|
||||
"@babel/preset-env/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@blitzkit/scripts/lodash-es": ["[email protected]", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
|
||||
|
||||
"@jimp/core/file-type": ["[email protected]", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
|
||||
|
||||
"@octokit/endpoint/@octokit/types": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
||||
@@ -2671,7 +2673,7 @@
|
||||
|
||||
"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]7.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
"react-dom/scheduler": ["[email protected]8.0", "", {}, "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw=="],
|
||||
|
||||
"readable-stream/safe-buffer": ["[email protected]", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
@@ -2685,10 +2687,6 @@
|
||||
|
||||
"soapstone/immer": ["[email protected]", "", {}, "sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ=="],
|
||||
|
||||
"soapstone/lodash-es": ["[email protected]", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="],
|
||||
|
||||
"soapstone/react": ["[email protected]", "", {}, "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="],
|
||||
|
||||
"source-map-support/source-map": ["[email protected]", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"stats-gl/three": ["[email protected]", "", {}, "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ=="],
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# BlitzKit 1.31.4
|
||||
|
||||
Thank you to [Mark Greene](https://github.com/Hitjack007) for your contributions this update.
|
||||
|
||||
## Fixes
|
||||
|
||||
- [Mark Greene] BlitzKit Session now properly handles tanks removed from the game
|
||||
|
||||
## Technical Fixes
|
||||
|
||||
- BlitzKit now handles the new ID system for game-modes in WoTB
|
||||
- The directory which preview versions of the game are downloaded to are now created properly
|
||||
- This was the last bug standing in the way of BlitzKit Previews
|
||||
- Welcome back, BlitzKit Previews!
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "blitzkit",
|
||||
"version": "1.31.3",
|
||||
"version": "1.31.4",
|
||||
"license": "BlitzKit License",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -33,6 +33,8 @@ import { parse as parseYaml } from "yaml";
|
||||
import { AbstractBlitzKitAPI } from "../abstract";
|
||||
|
||||
export abstract class ServerBlitzKitAPI0 extends AbstractBlitzKitAPI {
|
||||
// TODO: move most of these to their respective constructors
|
||||
|
||||
protected vfs: AbstractVFS;
|
||||
protected parsePath?: typeof parse;
|
||||
protected extname?: typeof extname;
|
||||
|
||||
@@ -5,17 +5,18 @@ import { merge } from "lodash-es";
|
||||
import { Cache } from "./0_base";
|
||||
import { ServerBlitzKitAPI11 } from "./11_scripts";
|
||||
|
||||
const globbedStrings = import.meta.glob("../../../../../i18n/strings/*.json", {
|
||||
import: "default",
|
||||
});
|
||||
|
||||
export abstract class ServerBlitzKitAPI12 extends ServerBlitzKitAPI11 {
|
||||
private globbedStrings = import.meta.glob(
|
||||
"../../../../../i18n/strings/*.json",
|
||||
{ import: "default" },
|
||||
);
|
||||
|
||||
@Cache()
|
||||
async strings(locale: string) {
|
||||
const localized = (await globbedStrings[
|
||||
const localized = (await this.globbedStrings[
|
||||
`../../../../../i18n/strings/${locale}.json`
|
||||
]()) as DeepPartial<Strings>;
|
||||
const defaults = (await globbedStrings[
|
||||
const defaults = (await this.globbedStrings[
|
||||
`../../../../../i18n/strings/${locales.default}.json`
|
||||
]()) as Strings;
|
||||
const strings = merge({}, defaults, localized);
|
||||
|
||||
@@ -78,7 +78,8 @@ if (provider === "steam") {
|
||||
|
||||
console.log(`Downloading ${urls.length} packages...`);
|
||||
|
||||
await rm(WGPKG_DIR, { recursive: true });
|
||||
await rm(WGPKG_DIR, { recursive: true, force: true });
|
||||
await mkdir(WGPKG_DIR, { recursive: true });
|
||||
|
||||
const inputContent = urls
|
||||
.map((url) => {
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
"@react-three/drei": "^10.7.6",
|
||||
"@react-three/fiber": "^9.4.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"@types/three": "^0.180.0",
|
||||
"@vite-pwa/astro": "^1.1.0",
|
||||
"astro": "^5.14.4",
|
||||
@@ -41,11 +41,11 @@
|
||||
"pwa-install-handler": "^2.6.3",
|
||||
"quicklime": "^1.2.0",
|
||||
"radix-ui": "^1.6.7",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0",
|
||||
"react-error-boundary": "^6.1.3",
|
||||
"react-promise-suspense": "^0.3.4",
|
||||
"soapstone": "^1.0.0",
|
||||
"soapstone": "^1.0.1",
|
||||
"three": "^0.180.0",
|
||||
"three-stdlib": "^2.36.0",
|
||||
"transliteration": "^2.6.1",
|
||||
|
||||
@@ -12,8 +12,8 @@ import { useRef } from "react";
|
||||
import { radToDeg } from "three/src/math/MathUtils.js";
|
||||
import { resolveArmorIndex } from "../core/blitzkit/resolveArmorIndex";
|
||||
import { LocaleProvider, useLocale } from "../hooks/useLocale";
|
||||
import { useStrings } from "../hooks/useStrings";
|
||||
import { App } from "../stores/app";
|
||||
import { Duel } from "../stores/duel";
|
||||
import { Tankopedia } from "../stores/tankopedia";
|
||||
import { layerTypeNames } from "./ShotDisplayCard";
|
||||
import { ArmorType } from "./SpacedArmorScene";
|
||||
@@ -22,7 +22,8 @@ export function ArmorPlateDisplay() {
|
||||
const highlightArmor = Tankopedia.use((state) => state.highlightArmor);
|
||||
const developerMode = App.useDeferred((state) => state.developerMode, false);
|
||||
const input = useRef<HTMLInputElement>(null);
|
||||
const { locale, strings } = useLocale();
|
||||
const locale = useLocale();
|
||||
const strings = useStrings();
|
||||
|
||||
if (highlightArmor === undefined) return null;
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ import type { QuicklimeEvent } from "quicklime";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { PerspectiveCamera, Vector3 } from "three";
|
||||
import { OrbitControls as OrbitControlsClass } from "three-stdlib";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { api } from "../api/dynamic";
|
||||
import { applyPitchYawLimits } from "../core/blitz/applyPitchYawLimits";
|
||||
import { hasEquipment } from "../core/blitzkit/hasEquipment";
|
||||
import { Pose, poseEvent } from "../core/blitzkit/pose";
|
||||
import { controlsEnabledEvent } from "../core/controlsEnabled";
|
||||
import { DuelModule, getDuel, useDuel } from "../hooks/useDuel";
|
||||
import { hasEquipment } from "../hooks/useEquipment";
|
||||
import { Tankopedia } from "../stores/tankopedia";
|
||||
|
||||
const poseDistances: Record<Pose, number> = {
|
||||
@@ -37,12 +37,12 @@ export function CanvasControl({
|
||||
const camera = useThree((state) => state.camera as PerspectiveCamera);
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const orbitControls = useRef<OrbitControlsClass>(null);
|
||||
const protagonistTurret = Duel.use((state) => state.protagonist.turret);
|
||||
const antagonistTurret = Duel.use((state) => state.antagonist.turret);
|
||||
const protagonistTrack = Duel.use((state) => state.protagonist.track);
|
||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||
const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
||||
const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
||||
const protagonistTurret = useDuel("protagonist", DuelModule.Turret);
|
||||
const antagonistTurret = useDuel("antagonist", DuelModule.Turret);
|
||||
const protagonistTrack = useDuel("protagonist", DuelModule.Track);
|
||||
const protagonistTank = useDuel("protagonist", DuelModule.Tank);
|
||||
const antagonistTank = useDuel("antagonist", DuelModule.Tank);
|
||||
const protagonistGun = useDuel("protagonist", DuelModule.Gun);
|
||||
const protagonistModelDefinition =
|
||||
modelDefinitions.models[protagonistTank.id];
|
||||
const protagonistTrackModelDefinition =
|
||||
@@ -89,15 +89,17 @@ export function CanvasControl({
|
||||
controlsEnabledEvent.on(handleControlsEnabled);
|
||||
|
||||
function handlePoseEvent(event: Pose) {
|
||||
const protagonist = getDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
|
||||
switch (event) {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useEffect } from "react";
|
||||
import { degToRad } from "three/src/math/MathUtils.js";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { api } from "../api/dynamic";
|
||||
import { applyPitchYawLimits } from "../core/blitz/applyPitchYawLimits";
|
||||
import { modelTransformEvent } from "../core/blitzkit/modelTransform";
|
||||
import { DuelModule, useDuel } from "../hooks/useDuel";
|
||||
|
||||
const modelDefinitions = await api.models();
|
||||
|
||||
export function InitialCameraAligner() {
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const turret = Duel.use((state) => state.protagonist.turret);
|
||||
const gun = Duel.use((state) => state.protagonist.gun);
|
||||
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||
const turret = useDuel("protagonist", DuelModule.Turret);
|
||||
const gun = useDuel("protagonist", DuelModule.Gun);
|
||||
|
||||
const tankModel = modelDefinitions.models[tank.id];
|
||||
const turretModel = tankModel.turrets[turret.id];
|
||||
|
||||
@@ -4,12 +4,13 @@ import { correctZYTuple } from "../core/blitz/correctZYTuple";
|
||||
import { nameToArmorId } from "../core/blitzkit/nameToArmorId";
|
||||
import { resolveArmor } from "../core/blitzkit/resolveThickness";
|
||||
import { useArmor } from "../hooks/useArmor";
|
||||
import { useConsumable } from "../hooks/useConsumable";
|
||||
import { DuelModule, useDuel } from "../hooks/useDuel";
|
||||
import { ModelTankWrapper } from "./ModelTankWrapper";
|
||||
import { useDuelModel } from "../hooks/useDuelModel";
|
||||
import { useTankTransform } from "../hooks/useTankTransform";
|
||||
import { PrimaryArmorSceneComponent } from "./PrimaryArmorSceneComponent";
|
||||
|
||||
export const PrimaryArmorScene = memo(() => {
|
||||
const wrapper = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null!);
|
||||
const gunContainer = useRef<Group>(null!);
|
||||
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||
@@ -18,21 +19,19 @@ export const PrimaryArmorScene = memo(() => {
|
||||
const gun = useDuel("protagonist", DuelModule.Gun);
|
||||
const armorGltf = useArmor(tank.id).gltf;
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const tankModelDefinition = useDuelModel("protagonist");
|
||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||
const hullOrigin = correctZYTuple(trackModelDefinition.origin!);
|
||||
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin!);
|
||||
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin!);
|
||||
const isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
const isDynamicArmorActive = useConsumable("protagonist", 73);
|
||||
|
||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
|
||||
return (
|
||||
<ModelTankWrapper ref={wrapper}>
|
||||
<>
|
||||
<group position={hullOrigin}>
|
||||
{armorNodes.map((node) => {
|
||||
const isHull = node.name.startsWith("hull_");
|
||||
@@ -132,6 +131,6 @@ export const PrimaryArmorScene = memo(() => {
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
</ModelTankWrapper>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -23,9 +23,9 @@ import { hasEquipment } from "../../hooks/useEquipment";
|
||||
import { Tankopedia } from "../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../stores/tankopediaPersistent";
|
||||
import { transitionEvent } from "../Lighting";
|
||||
import { spacedArmorRenderTarget } from "../PrimaryArmorRenderTarget";
|
||||
import fragmentShader from "./shaders/fragment.glsl?raw";
|
||||
import vertexShader from "./shaders/vertex.glsl?raw";
|
||||
import { spacedArmorRenderTarget } from "./PrimaryArmorRenderTarget";
|
||||
|
||||
interface PrimaryArmorSceneComponentProps {
|
||||
thickness: number;
|
||||
@@ -134,6 +134,8 @@ export function PrimaryArmorSceneComponent({
|
||||
if (!noInvalidate) invalidate();
|
||||
}
|
||||
function handleAntagonistEquipmentChange(noInvalidate = false) {
|
||||
const antagonist = getDuel("antagonist", DuelModule.Tank);
|
||||
|
||||
const equipment = Tankopedia.state.antagonist.equipment;
|
||||
const tankopediaEphemeral = Tankopedia.state;
|
||||
const shell =
|
||||
@@ -142,7 +144,7 @@ export function PrimaryArmorSceneComponent({
|
||||
const penetration = shell.penetration!.near;
|
||||
const hasCalibratedShells = hasEquipment(
|
||||
103,
|
||||
Duel.state.antagonist.tank.equipment_preset,
|
||||
antagonist.equipment_preset,
|
||||
equipment,
|
||||
);
|
||||
|
||||
@@ -150,9 +152,9 @@ export function PrimaryArmorSceneComponent({
|
||||
penetration *
|
||||
resolvePenetrationCoefficient(
|
||||
hasCalibratedShells,
|
||||
Duel.state.equalize,
|
||||
Tankopedia.state.equalize,
|
||||
shell.type,
|
||||
Duel.state.antagonist.tank.equalizer,
|
||||
antagonist.equalizer,
|
||||
);
|
||||
|
||||
if (!noInvalidate) invalidate();
|
||||
@@ -175,7 +177,7 @@ export function PrimaryArmorSceneComponent({
|
||||
transitionEvent.on(handleTransitionEvent);
|
||||
|
||||
const unsubscribes = [
|
||||
Duel.on((state) => state.antagonist.shell, handleShellChange),
|
||||
Tankopedia.on((state) => state.antagonist.shell, handleShellChange),
|
||||
Tankopedia.on((state) => state.customShell, handleShellChange),
|
||||
TankopediaPersistent.on(
|
||||
(state) => state.greenPenetration,
|
||||
@@ -186,15 +188,15 @@ export function PrimaryArmorSceneComponent({
|
||||
handleAdvancedHighlightingChange,
|
||||
),
|
||||
TankopediaPersistent.on((state) => state.opaque, handleOpaqueChange),
|
||||
Duel.on(
|
||||
(state) => state.protagonist.equipmentMatrix,
|
||||
Tankopedia.on(
|
||||
(state) => state.protagonist.equipment,
|
||||
() => handleProtagonistEquipmentChange(),
|
||||
),
|
||||
Duel.on(
|
||||
(state) => state.antagonist.equipmentMatrix,
|
||||
Tankopedia.on(
|
||||
(state) => state.antagonist.equipment,
|
||||
() => handleAntagonistEquipmentChange(),
|
||||
),
|
||||
Duel.on(
|
||||
Tankopedia.on(
|
||||
(state) => state.equalize,
|
||||
() => {
|
||||
handleProtagonistEquipmentChange();
|
||||
|
||||
@@ -4,11 +4,11 @@ import { correctZYTuple } from "../core/blitz/correctZYTuple";
|
||||
import { nameToArmorId } from "../core/blitzkit/nameToArmorId";
|
||||
import { resolveArmor } from "../core/blitzkit/resolveThickness";
|
||||
import { useArmor } from "../hooks/useArmor";
|
||||
import { useConsumable } from "../hooks/useConsumable";
|
||||
import { DuelModule, useDuel } from "../hooks/useDuel";
|
||||
import { useDuelModel } from "../hooks/useDuelModel";
|
||||
import { useModel } from "../hooks/useModel";
|
||||
import { useTankModelDefinition } from "../hooks/useTankModelDefinition";
|
||||
import { useTankTransform } from "../hooks/useTankTransform";
|
||||
import { Duel } from "../stores/duel";
|
||||
import { ModelTankWrapper } from "./ModelTankWrapper";
|
||||
import { SpacedArmorSceneComponent } from "./SpacedArmorSceneComponent";
|
||||
|
||||
export enum ArmorType {
|
||||
@@ -22,18 +22,17 @@ interface SpacedArmorSceneProps {
|
||||
}
|
||||
|
||||
export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
const wrapper = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null!);
|
||||
const gunContainer = useRef<Group>(null!);
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const track = Duel.use((state) => state.protagonist.track);
|
||||
const turret = Duel.use((state) => state.protagonist.turret);
|
||||
const gun = Duel.use((state) => state.protagonist.gun);
|
||||
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||
const track = useDuel("protagonist", DuelModule.Track);
|
||||
const turret = useDuel("protagonist", DuelModule.Turret);
|
||||
const gun = useDuel("protagonist", DuelModule.Gun);
|
||||
const { gltf: armorGltf } = useArmor(tank.id);
|
||||
const { gltf: modelGltf } = useModel(tank.id);
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const modelNodes = Object.values(modelGltf.nodes);
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const tankModelDefinition = useDuelModel("protagonist");
|
||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||
@@ -44,14 +43,12 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
gunModelDefinition.mask === undefined
|
||||
? undefined
|
||||
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
||||
const isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
const isDynamicArmorActive = useConsumable("protagonist", 73);
|
||||
|
||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
|
||||
return (
|
||||
<ModelTankWrapper ref={wrapper}>
|
||||
<>
|
||||
<group position={hullOrigin}>
|
||||
{armorNodes.map((node) => {
|
||||
const isHull = node.name.startsWith("hull_");
|
||||
@@ -199,6 +196,6 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
</ModelTankWrapper>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
Vector3,
|
||||
} from "three";
|
||||
import { degToRad } from "three/src/math/MathUtils.js";
|
||||
import { hasEquipment } from "../core/blitzkit/hasEquipment";
|
||||
import { defaultEqualizer } from "../config/equalizer";
|
||||
import { jsxTree } from "../core/blitzkit/jsxTree";
|
||||
import { defaultEqualizer } from "../core/blitzkit/tankToDuelMember";
|
||||
import { discardClippingPlane } from "../core/three/discardClippingPlane";
|
||||
import { Duel } from "../stores/duel";
|
||||
import { DuelModule, getDuel } from "../hooks/useDuel";
|
||||
import { hasEquipment } from "../hooks/useEquipment";
|
||||
import {
|
||||
type Shot,
|
||||
type ShotLayerBase,
|
||||
@@ -83,15 +83,16 @@ export function SpacedArmorSceneComponent({
|
||||
allowRicochet: boolean,
|
||||
remainingPenetrationInput?: number,
|
||||
) => {
|
||||
const protagonist = getDuel("protagonist", DuelModule.Tank);
|
||||
const antagonist = getDuel("antagonist", DuelModule.Tank);
|
||||
|
||||
const { customShell } = Tankopedia.state;
|
||||
const shell = customShell ?? Duel.state.antagonist.shell;
|
||||
const { equalize } = Duel.state;
|
||||
const shell = customShell ?? getDuel("protagonist", DuelModule.Shell);
|
||||
const { equalize } = Tankopedia.state;
|
||||
const protagonistEqualizer =
|
||||
(equalize ? Duel.state.protagonist.tank.equalizer : undefined) ??
|
||||
defaultEqualizer;
|
||||
(equalize ? protagonist.equalizer : undefined) ?? defaultEqualizer;
|
||||
const antagonistEqualizer =
|
||||
(equalize ? Duel.state.antagonist.tank.equalizer : undefined) ??
|
||||
defaultEqualizer;
|
||||
(equalize ? antagonist.equalizer : undefined) ?? defaultEqualizer;
|
||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||
const shot: Shot = {
|
||||
splashRadius:
|
||||
@@ -109,13 +110,13 @@ export function SpacedArmorSceneComponent({
|
||||
|
||||
const hasCalibratedShells = hasEquipment(
|
||||
103,
|
||||
Duel.state.antagonist.tank.equipment_preset,
|
||||
Duel.state.antagonist.equipmentMatrix,
|
||||
antagonist.equipment_preset,
|
||||
Tankopedia.state.antagonist.equipment,
|
||||
);
|
||||
const hasEnhancedArmor = hasEquipment(
|
||||
110,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const penetration =
|
||||
shell.penetration!.near *
|
||||
|
||||
@@ -7,12 +7,15 @@ import {
|
||||
Plane,
|
||||
ShaderMaterial,
|
||||
} from "three";
|
||||
import type { ArmorUserData, ExternalModuleVariant } from "../SpacedArmorSceneComponent";
|
||||
import { hasEquipment } from "../../core/blitzkit/hasEquipment";
|
||||
import { jsxTree } from "../../core/blitzkit/jsxTree";
|
||||
import { Duel } from "../../stores/duel";
|
||||
import { DuelModule, getDuel } from "../../hooks/useDuel";
|
||||
import { hasEquipment } from "../../hooks/useEquipment";
|
||||
import { Tankopedia } from "../../stores/tankopedia";
|
||||
import { ArmorType } from "./SpacedArmorScene";
|
||||
import { ArmorType } from "../SpacedArmorScene";
|
||||
import type {
|
||||
ArmorUserData,
|
||||
ExternalModuleVariant,
|
||||
} from "../SpacedArmorSceneComponent";
|
||||
import fragmentShader from "./shaders/fragment.glsl?raw";
|
||||
import vertexShader from "./shaders/vertex.glsl?raw";
|
||||
|
||||
@@ -49,47 +52,51 @@ export function SpacedArmorSubExternal({
|
||||
function handleShellChange() {
|
||||
const tankopediaEphemeral = Tankopedia.state;
|
||||
const shell =
|
||||
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
|
||||
tankopediaEphemeral.customShell ??
|
||||
getDuel("antagonist", DuelModule.Shell);
|
||||
material.uniforms.penetration.value = shell.penetration!.near;
|
||||
}
|
||||
function handleProtagonistEquipmentChange() {
|
||||
const equipment = Duel.state.protagonist.equipmentMatrix;
|
||||
const protagonist = getDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasEnhancedArmor = hasEquipment(
|
||||
110,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
equipment,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const equalizer =
|
||||
(Duel.state.equalize
|
||||
? Duel.state.protagonist.tank.equalizer?.armor
|
||||
(Tankopedia.state.equalize
|
||||
? protagonist.equalizer?.armor
|
||||
: undefined) ?? 1;
|
||||
|
||||
material.uniforms.thickness.value =
|
||||
thickness * (hasEnhancedArmor ? 1.04 : 1) * equalizer;
|
||||
}
|
||||
function handleAntagonistEquipmentChange() {
|
||||
const equipment = Duel.state.antagonist.equipmentMatrix;
|
||||
const antagonist = getDuel("antagonist", DuelModule.Tank);
|
||||
|
||||
const tankopediaEphemeral = Tankopedia.state;
|
||||
const shell =
|
||||
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
|
||||
tankopediaEphemeral.customShell ??
|
||||
getDuel("antagonist", DuelModule.Shell);
|
||||
const penetration = shell.penetration!.near;
|
||||
const hasCalibratedShells = hasEquipment(
|
||||
103,
|
||||
Duel.state.antagonist.tank.equipment_preset,
|
||||
equipment,
|
||||
antagonist.equipment_preset,
|
||||
Tankopedia.state.antagonist.equipment,
|
||||
);
|
||||
const equalize =
|
||||
(Duel.state.equalize
|
||||
? Duel.state.antagonist.tank.equalizer?.penetration
|
||||
(Tankopedia.state.equalize
|
||||
? antagonist.equalizer?.penetration
|
||||
: undefined) ?? 1;
|
||||
|
||||
material.uniforms.penetration.value =
|
||||
penetration *
|
||||
resolvePenetrationCoefficient(
|
||||
hasCalibratedShells,
|
||||
Duel.state.equalize,
|
||||
Tankopedia.state.equalize,
|
||||
shell.type,
|
||||
Duel.state.antagonist.tank.equalizer,
|
||||
antagonist.equalizer,
|
||||
) *
|
||||
equalize;
|
||||
}
|
||||
@@ -99,17 +106,17 @@ export function SpacedArmorSubExternal({
|
||||
handleAntagonistEquipmentChange();
|
||||
|
||||
const unsubscribes = [
|
||||
Duel.on((state) => state.antagonist.shell, handleShellChange),
|
||||
Tankopedia.on((state) => state.antagonist.shell, handleShellChange),
|
||||
Tankopedia.on((state) => state.customShell, handleShellChange),
|
||||
Duel.on(
|
||||
(state) => state.protagonist.equipmentMatrix,
|
||||
Tankopedia.on(
|
||||
(state) => state.protagonist.equipment,
|
||||
handleProtagonistEquipmentChange,
|
||||
),
|
||||
Duel.on(
|
||||
(state) => state.antagonist.equipmentMatrix,
|
||||
Tankopedia.on(
|
||||
(state) => state.antagonist.equipment,
|
||||
handleAntagonistEquipmentChange,
|
||||
),
|
||||
Duel.on(
|
||||
Tankopedia.on(
|
||||
(state) => state.equalize,
|
||||
() => {
|
||||
handleProtagonistEquipmentChange();
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
type Object3D,
|
||||
} from "three";
|
||||
import { degToRad } from "three/src/math/MathUtils.js";
|
||||
import type { ArmorUserData } from "../SpacedArmorSceneComponent";
|
||||
import { hasEquipment } from "../../core/blitzkit/hasEquipment";
|
||||
import { jsxTree } from "../../core/blitzkit/jsxTree";
|
||||
import { Duel } from "../../stores/duel";
|
||||
import { DuelModule, getDuel } from "../../hooks/useDuel";
|
||||
import { hasEquipment } from "../../hooks/useEquipment";
|
||||
import { Tankopedia } from "../../stores/tankopedia";
|
||||
import { ArmorType } from "./SpacedArmorScene";
|
||||
import { ArmorType } from "../SpacedArmorScene";
|
||||
import type { ArmorUserData } from "../SpacedArmorSceneComponent";
|
||||
import fragmentShader from "./shaders/fragment.glsl?raw";
|
||||
import vertexShader from "./shaders/vertex.glsl?raw";
|
||||
|
||||
@@ -51,7 +51,8 @@ export function SpacedArmorSubSpaced({
|
||||
function handleShellChange() {
|
||||
const tankopediaEphemeral = Tankopedia.state;
|
||||
const shell =
|
||||
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
|
||||
tankopediaEphemeral.customShell ??
|
||||
getDuel("antagonist", DuelModule.Shell);
|
||||
|
||||
material.uniforms.penetration.value = shell.penetration!.near;
|
||||
material.uniforms.caliber.value = shell.caliber;
|
||||
@@ -63,43 +64,46 @@ export function SpacedArmorSubSpaced({
|
||||
);
|
||||
}
|
||||
function handleProtagonistEquipmentChange() {
|
||||
const equipment = Duel.state.protagonist.equipmentMatrix;
|
||||
const protagonist = getDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasEnhancedArmor = hasEquipment(
|
||||
110,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
equipment,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const equalizer =
|
||||
(Duel.state.equalize
|
||||
? Duel.state.protagonist.tank.equalizer?.armor
|
||||
(Tankopedia.state.equalize
|
||||
? protagonist.equalizer?.armor
|
||||
: undefined) ?? 1;
|
||||
|
||||
material.uniforms.thickness.value =
|
||||
thickness * (hasEnhancedArmor ? 1.04 : 1) * equalizer;
|
||||
}
|
||||
function handleAntagonistEquipmentChange() {
|
||||
const equipment = Duel.state.antagonist.equipmentMatrix;
|
||||
const antagonist = getDuel("antagonist", DuelModule.Tank);
|
||||
|
||||
const tankopediaEphemeral = Tankopedia.state;
|
||||
const shell =
|
||||
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
|
||||
tankopediaEphemeral.customShell ??
|
||||
getDuel("antagonist", DuelModule.Shell);
|
||||
const penetration = shell.penetration!.near;
|
||||
const hasCalibratedShells = hasEquipment(
|
||||
103,
|
||||
Duel.state.antagonist.tank.equipment_preset,
|
||||
equipment,
|
||||
antagonist.equipment_preset,
|
||||
Tankopedia.state.antagonist.equipment,
|
||||
);
|
||||
const equalize =
|
||||
(Duel.state.equalize
|
||||
? Duel.state.antagonist.tank.equalizer?.penetration
|
||||
(Tankopedia.state.equalize
|
||||
? antagonist.equalizer?.penetration
|
||||
: undefined) ?? 1;
|
||||
|
||||
material.uniforms.penetration.value =
|
||||
penetration *
|
||||
resolvePenetrationCoefficient(
|
||||
hasCalibratedShells,
|
||||
Duel.state.equalize,
|
||||
Tankopedia.state.equalize,
|
||||
shell.type,
|
||||
Duel.state.antagonist.tank.equalizer,
|
||||
antagonist.equalizer,
|
||||
) *
|
||||
equalize;
|
||||
}
|
||||
@@ -109,17 +113,17 @@ export function SpacedArmorSubSpaced({
|
||||
handleAntagonistEquipmentChange();
|
||||
|
||||
const unsubscribes = [
|
||||
Duel.on((state) => state.antagonist.shell, handleShellChange),
|
||||
Tankopedia.on((state) => state.antagonist.shell, handleShellChange),
|
||||
Tankopedia.on((state) => state.customShell, handleShellChange),
|
||||
Duel.on(
|
||||
(state) => state.protagonist.equipmentMatrix,
|
||||
Tankopedia.on(
|
||||
(state) => state.protagonist.equipment,
|
||||
handleProtagonistEquipmentChange,
|
||||
),
|
||||
Duel.on(
|
||||
(state) => state.antagonist.equipmentMatrix,
|
||||
Tankopedia.on(
|
||||
(state) => state.antagonist.equipment,
|
||||
handleAntagonistEquipmentChange,
|
||||
),
|
||||
Duel.on(
|
||||
Tankopedia.on(
|
||||
(state) => state.equalize,
|
||||
() => {
|
||||
handleProtagonistEquipmentChange();
|
||||
|
||||
@@ -3,19 +3,19 @@ import { memo, useRef } from "react";
|
||||
import { Group, Plane, Vector2, Vector3 } from "three";
|
||||
import { applyPitchYawLimits } from "../core/blitz/applyPitchYawLimits";
|
||||
import { correctZYTuple } from "../core/blitz/correctZYTuple";
|
||||
import { hasEquipment } from "../core/blitzkit/hasEquipment";
|
||||
import { modelTransformEvent } from "../core/blitzkit/modelTransform";
|
||||
import { nameToArmorId } from "../core/blitzkit/nameToArmorId";
|
||||
import { resolveArmor } from "../core/blitzkit/resolveThickness";
|
||||
import { controlsEnabledEvent } from "../core/controlsEnabled";
|
||||
import { useArmor } from "../hooks/useArmor";
|
||||
import { useConsumable } from "../hooks/useConsumable";
|
||||
import { DuelModule, getDuel, useDuel } from "../hooks/useDuel";
|
||||
import { useDuelModel } from "../hooks/useDuelModel";
|
||||
import { hasEquipment } from "../hooks/useEquipment";
|
||||
import { useModel } from "../hooks/useModel";
|
||||
import { useTankModelDefinition } from "../hooks/useTankModelDefinition";
|
||||
import { useTankTransform } from "../hooks/useTankTransform";
|
||||
import { Duel } from "../stores/duel";
|
||||
import { Tankopedia } from "../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../stores/tankopediaPersistent";
|
||||
import { ModelTankWrapper } from "./ModelTankWrapper";
|
||||
import { ArmorType } from "./SpacedArmorScene";
|
||||
import { StaticArmorSceneComponent } from "./StaticArmorSceneComponent";
|
||||
|
||||
@@ -28,18 +28,19 @@ export interface ThicknessRange {
|
||||
}
|
||||
|
||||
export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
const wrapper = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null!);
|
||||
const gunContainer = useRef<Group>(null!);
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const track = Duel.use((state) => state.protagonist.track);
|
||||
const turret = Duel.use((state) => state.protagonist.turret);
|
||||
const gun = Duel.use((state) => state.protagonist.gun);
|
||||
|
||||
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||
const track = useDuel("protagonist", DuelModule.Track);
|
||||
const turret = useDuel("protagonist", DuelModule.Turret);
|
||||
const gun = useDuel("protagonist", DuelModule.Gun);
|
||||
|
||||
const { gltf: armorGltf } = useArmor(tank.id);
|
||||
const { gltf: modelGltf } = useModel(tank.id);
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const modelNodes = Object.values(modelGltf.nodes);
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const tankModelDefinition = useDuelModel("protagonist");
|
||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||
@@ -61,14 +62,12 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
const showExternalModules = TankopediaPersistent.use(
|
||||
(state) => state.showExternalModules,
|
||||
);
|
||||
const isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
const isDynamicArmorActive = useConsumable("protagonist", 73);
|
||||
|
||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
|
||||
return (
|
||||
<ModelTankWrapper ref={wrapper}>
|
||||
<>
|
||||
<group position={hullOrigin}>
|
||||
{armorNodes.map((node) => {
|
||||
const isHull = node.name.startsWith("hull_");
|
||||
@@ -165,15 +164,17 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
const protagonist = getDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
@@ -253,15 +254,17 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
const protagonist = getDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
delta.set(event.clientX, event.clientY).sub(position);
|
||||
@@ -329,15 +332,17 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
const protagonist = getDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
delta.set(event.clientX, event.clientY).sub(position);
|
||||
@@ -385,6 +390,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
</ModelTankWrapper>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -16,16 +16,16 @@ import {
|
||||
Quaternion,
|
||||
Vector3,
|
||||
} from "three";
|
||||
import { defaultEqualizer } from "../config/equalizer";
|
||||
import { unrotateDavaVector } from "../core/blitz/unrotateDavaVector";
|
||||
import { jsxTree } from "../core/blitzkit/jsxTree";
|
||||
import {
|
||||
modelTransformEvent,
|
||||
type ModelTransformEventData,
|
||||
} from "../core/blitzkit/modelTransform";
|
||||
import { defaultEqualizer } from "../core/blitzkit/tankToDuelMember";
|
||||
import { discardClippingPlane } from "../core/three/discardClippingPlane";
|
||||
import { DuelModule, useDuel } from "../hooks/useDuel";
|
||||
import { useEquipment } from "../hooks/useEquipment";
|
||||
import { Duel } from "../stores/duel";
|
||||
import { Tankopedia } from "../stores/tankopedia";
|
||||
import { transitionEvent } from "./Lighting";
|
||||
import { ArmorType } from "./SpacedArmorScene";
|
||||
@@ -75,8 +75,8 @@ export function StaticArmorSceneComponent({
|
||||
}: StaticArmorSceneComponentProps) {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const hasEnhancedArmor = useEquipment("protagonist", 110);
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const equalize = Duel.use((state) => state.equalize);
|
||||
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||
const equalize = Tankopedia.use((state) => state.equalize);
|
||||
const equalizer = (equalize ? tank.equalizer : undefined) ?? defaultEqualizer;
|
||||
const thicknessCoefficient = (hasEnhancedArmor ? 1.04 : 1) * equalizer.armor;
|
||||
const thickness = thicknessRaw * thicknessCoefficient;
|
||||
|
||||
@@ -1,38 +1,36 @@
|
||||
import { invalidate, useThree, type ThreeEvent } from "@react-three/fiber";
|
||||
import { useRef } from "react";
|
||||
import { Group, Mesh, MeshStandardMaterial, Vector2 } from "three";
|
||||
import { useTankModelDefinition } from "../../../../../../hooks/useTankModelDefinition";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { applyPitchYawLimits } from "../core/blitz/applyPitchYawLimits";
|
||||
import { hasEquipment } from "../core/blitzkit/hasEquipment";
|
||||
import { jsxTree } from "../core/blitzkit/jsxTree";
|
||||
import { modelTransformEvent } from "../core/blitzkit/modelTransform";
|
||||
import { controlsEnabledEvent } from "../core/controlsEnabled";
|
||||
import { DuelModule, useDuel } from "../hooks/useDuel";
|
||||
import { useDuelModel } from "../hooks/useDuelModel";
|
||||
import { hasEquipment } from "../hooks/useEquipment";
|
||||
import { useModel } from "../hooks/useModel";
|
||||
import { useTankTransform } from "../hooks/useTankTransform";
|
||||
import { Tankopedia } from "../stores/tankopedia";
|
||||
import { TankopediaDisplay } from "../stores/tankopediaPersistent/constants";
|
||||
import { ModelTankWrapper } from "./ModelTankWrapper";
|
||||
|
||||
export function TankModel() {
|
||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||
const track = Duel.use((state) => state.protagonist.track);
|
||||
const turret = Duel.use((state) => state.protagonist.turret);
|
||||
const protagonist = Tankopedia.use((state) => state.protagonist);
|
||||
const track = useDuel("protagonist", DuelModule.Track);
|
||||
const turret = useDuel("protagonist", DuelModule.Turret);
|
||||
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const hullContainer = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null!);
|
||||
const gunContainer = useRef<Group>(null!);
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[protagonist.turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||
const { gltf } = useModel(protagonist.tank.id);
|
||||
const tankModelDefinition = useDuelModel("protagonist");
|
||||
const turretModelDefinition = tankModelDefinition.turrets[protagonist.turret];
|
||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun];
|
||||
const { gltf } = useModel(protagonist.tank);
|
||||
const nodes = Object.values(gltf.nodes);
|
||||
|
||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
|
||||
return (
|
||||
<ModelTankWrapper ref={hullContainer}>
|
||||
<>
|
||||
{nodes.map((node) => {
|
||||
const isHull = node.name === "hull";
|
||||
const isWheel = node.name.startsWith("chassis_wheel_");
|
||||
@@ -131,15 +129,17 @@ export function TankModel() {
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
const protagonist = useDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
@@ -214,15 +214,17 @@ export function TankModel() {
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
const protagonist = useDuel("protagonist", DuelModule.Tank);
|
||||
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
Duel.state.protagonist.tank.equipment_preset,
|
||||
Duel.state.protagonist.equipmentMatrix,
|
||||
protagonist.equipment_preset,
|
||||
Tankopedia.state.protagonist.equipment,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
delta.set(event.clientX, event.clientY).sub(position);
|
||||
@@ -260,6 +262,6 @@ export function TankModel() {
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
</ModelTankWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export const defaultEqualizer: Equalizer = {
|
||||
armor: 1,
|
||||
damage: 1,
|
||||
health: 1,
|
||||
module_health: 1,
|
||||
penetration: 1,
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Tankopedia } from "../stores/tankopedia";
|
||||
import type { DuelSide } from "./useEquipment";
|
||||
|
||||
export function useConsumable(side: DuelSide, id: number) {
|
||||
return Tankopedia.use((state) => state[side].consumables.includes(id));
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Tankopedia } from "../stores/tankopedia";
|
||||
import type { DuelSide } from "./useEquipment";
|
||||
|
||||
export function useDuelModel(side: DuelSide) {
|
||||
return Tankopedia.use((state) => state[side].model);
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
modelTransformEvent,
|
||||
type ModelTransformEventData,
|
||||
} from "../core/blitzkit/modelTransform";
|
||||
import { useTankModelDefinition } from "./useTankModelDefinition";
|
||||
import { useDuelModel } from "./useDuelModel";
|
||||
|
||||
export function useTankTransform(
|
||||
track: TrackDefinition,
|
||||
@@ -18,7 +18,7 @@ export function useTankTransform(
|
||||
turretContainer: RefObject<Group>,
|
||||
gunContainer: RefObject<Group>,
|
||||
) {
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const tankModelDefinition = useDuelModel("protagonist");
|
||||
|
||||
useEffect(() => {
|
||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { createDefaultSkills } from "@blitzkit/core";
|
||||
import type {
|
||||
ModelDefinition,
|
||||
ShellDefinition,
|
||||
TankDefinition,
|
||||
} from "@blitzkit/protos";
|
||||
import type { Vector3 } from "three";
|
||||
import type { ShellDefinition, TankDefinition } from "@blitzkit/protos";
|
||||
import { Soapstone } from "soapstone";
|
||||
import type { Vector3 } from "three";
|
||||
import { api } from "../api/dynamic";
|
||||
import type { ArmorType } from "../components/SpacedArmorScene";
|
||||
import type { ExternalModuleVariant } from "../components/SpacedArmorSceneComponent";
|
||||
@@ -85,7 +81,6 @@ interface Tankopedia {
|
||||
|
||||
protagonist: TankState;
|
||||
antagonist: TankState;
|
||||
model: ModelDefinition;
|
||||
|
||||
equalize: boolean;
|
||||
|
||||
@@ -117,20 +112,22 @@ interface Tankopedia {
|
||||
const skills = await api.skills();
|
||||
const models = await api.models();
|
||||
|
||||
export const Tankopedia = new Soapstone<Tankopedia, [TankDefinition]>((tank) => ({
|
||||
disturbed: false,
|
||||
revealed: false,
|
||||
export const Tankopedia = new Soapstone<Tankopedia, [TankDefinition]>(
|
||||
(tank) => ({
|
||||
disturbed: false,
|
||||
revealed: false,
|
||||
|
||||
protagonist: createTankState(tank),
|
||||
antagonist: createTankState(tank),
|
||||
model: models.models[tank.id],
|
||||
protagonist: createTankState(tank),
|
||||
antagonist: createTankState(tank),
|
||||
model: models.models[tank.id],
|
||||
|
||||
equalize: false,
|
||||
equalize: false,
|
||||
|
||||
relativeAgainst: TankopediaRelativeAgainst.Class,
|
||||
editStatic: false,
|
||||
skills: createDefaultSkills(skills),
|
||||
xpMultiplier: 1,
|
||||
requestedDisplay: TankopediaDisplay.Model,
|
||||
display: TankopediaDisplay.Model,
|
||||
}));
|
||||
relativeAgainst: TankopediaRelativeAgainst.Class,
|
||||
editStatic: false,
|
||||
skills: createDefaultSkills(skills),
|
||||
xpMultiplier: 1,
|
||||
requestedDisplay: TankopediaDisplay.Model,
|
||||
display: TankopediaDisplay.Model,
|
||||
}),
|
||||
);
|
||||
Reference in new issue
Block a user