mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
Merge branch 'main' into stable-tank-sort-order
This commit is contained in:
19 files changed
+136
-42
No files matched your search
+3
-2
@@ -4,12 +4,13 @@
|
|||||||
"license": "BlitzKit License",
|
"license": "BlitzKit License",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cd packages/website && bun run dev",
|
"dev": "cd packages/website && bun --bun run dev",
|
||||||
"build": "cd packages/website && bun run build",
|
"build": "cd packages/website && bun run build",
|
||||||
"discover-ids": "cd packages/scripts && bun run discover-ids",
|
"discover-ids": "cd packages/scripts && bun run discover-ids",
|
||||||
"build-protos": "cd packages/scripts && bun run build-protos",
|
"build-protos": "cd packages/scripts && bun run build-protos",
|
||||||
"record-performance": "cd packages/scripts && bun run record-performance",
|
"record-performance": "cd packages/scripts && bun run record-performance",
|
||||||
"download-client": "cd packages/scripts && bun run download-client"
|
"download-client": "cd packages/scripts && bun run download-client",
|
||||||
|
"clean-astro": "rm -rf packages/website/.astro packages/website/node_modules/.astro packages/website/node_modules/.vite"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import {
|
|||||||
VehicleDefinitionList,
|
VehicleDefinitionList,
|
||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
import { AbstractVFS } from "@blitzkit/core/src/blitzkit/vfs/abstract";
|
import { AbstractVFS } from "@blitzkit/core/src/blitzkit/vfs/abstract";
|
||||||
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n";
|
|
||||||
import locales from "@blitzkit/i18n/locales.json";
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
|
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n/src/strings";
|
||||||
import { parse as parseYaml } from "yaml";
|
import { parse as parseYaml } from "yaml";
|
||||||
import { BlitzKitAPI } from "../base";
|
import { BlitzKitAPI } from "../base";
|
||||||
|
|
||||||
@@ -113,6 +113,9 @@ export abstract class ServerBlitzKitAPI0 extends BlitzKitAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
const { existsSync } = await import("fs");
|
||||||
|
const { readFile, writeFile, mkdir } = await import("fs/promises");
|
||||||
|
|
||||||
console.log("Initializing virtual file system...");
|
console.log("Initializing virtual file system...");
|
||||||
await this.vfs.init();
|
await this.vfs.init();
|
||||||
|
|
||||||
@@ -155,31 +158,37 @@ export abstract class ServerBlitzKitAPI0 extends BlitzKitAPI {
|
|||||||
|
|
||||||
console.log("Fetching game localizations...");
|
console.log("Fetching game localizations...");
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
locales.supported = [locales.supported[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
let fetchedLocalizations = 0;
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
locales.supported.map(async ({ locale }) => {
|
locales.supported.map(async ({ locale }) => {
|
||||||
|
const root = "../../temp/game-strings";
|
||||||
|
const cachePath = `${root}/${locale}.json`;
|
||||||
|
|
||||||
|
if (existsSync(cachePath)) {
|
||||||
|
console.log(` Cache hit for ${locale}`);
|
||||||
|
|
||||||
|
const content = await readFile(cachePath, "utf-8");
|
||||||
|
this.stringsI18n[locale] = JSON.parse(content) as BlitzStrings;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(` Cache miss for ${locale} (this will take a while...)`);
|
||||||
|
|
||||||
const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale];
|
const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale];
|
||||||
const cache = await fetch(
|
const networkStrings = (await fetch(
|
||||||
`https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`,
|
`https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`,
|
||||||
)
|
)
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
.then((string) => parseYaml(string) as BlitzStrings);
|
.then(parseYaml)) as BlitzStrings;
|
||||||
const preInstalled = await this.vfs.yaml<BlitzStrings>(
|
const localStrings = await this.vfs.yaml<BlitzStrings>(
|
||||||
`Data/Strings/${blitzLocale}.yaml`,
|
`Data/Strings/${blitzLocale}.yaml`,
|
||||||
);
|
);
|
||||||
|
const combined = { ...networkStrings, ...localStrings };
|
||||||
|
|
||||||
this.stringsI18n[locale] = {
|
await mkdir(root, { recursive: true });
|
||||||
...cache,
|
await writeFile(cachePath, JSON.stringify(combined));
|
||||||
...preInstalled,
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(
|
this.stringsI18n[locale] = combined;
|
||||||
`Fetched localizations for ${locale} (${++fetchedLocalizations}/${locales.supported.length})`,
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export const TIER_ROMAN_NUMERALS: Record<number, string> = {
|
|||||||
8: "VIII",
|
8: "VIII",
|
||||||
9: "IX",
|
9: "IX",
|
||||||
10: "X",
|
10: "X",
|
||||||
|
11: "XI",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const flags: Record<string, string> = {
|
export const flags: Record<string, string> = {
|
||||||
|
|||||||
@@ -215,6 +215,7 @@
|
|||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
"description": "Change BlitzKit settings",
|
"description": "Change BlitzKit settings",
|
||||||
"dev_mode": "Development mode",
|
"dev_mode": "Development mode",
|
||||||
|
"auto_load_models": "Auto-load models",
|
||||||
"language": "Language"
|
"language": "Language"
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -458,6 +459,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"sandbox": {
|
"sandbox": {
|
||||||
|
"load_model": "Load model",
|
||||||
|
|
||||||
"poses": {
|
"poses": {
|
||||||
"hull_down": "Hull down",
|
"hull_down": "Hull down",
|
||||||
"face_hug": "Face hug",
|
"face_hug": "Face hug",
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
} from "@radix-ui/themes";
|
} from "@radix-ui/themes";
|
||||||
import fuzzysort from "fuzzysort";
|
import fuzzysort from "fuzzysort";
|
||||||
import { debounce, times } from "lodash-es";
|
import { debounce } from "lodash-es";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { awaitableTankNames } from "../core/awaitables/tankNames";
|
import { awaitableTankNames } from "../core/awaitables/tankNames";
|
||||||
|
import { awaitableTiers } from "../core/awaitables/tiers";
|
||||||
import { api } from "../core/blitzkit/api";
|
import { api } from "../core/blitzkit/api";
|
||||||
import { useLocale } from "../hooks/useLocale";
|
import { useLocale } from "../hooks/useLocale";
|
||||||
import { Guess, GuessState } from "../stores/guess";
|
import { Guess, GuessState } from "../stores/guess";
|
||||||
@@ -38,9 +39,10 @@ import { SearchResults } from "./SearchResults";
|
|||||||
|
|
||||||
const { go } = fuzzysort;
|
const { go } = fuzzysort;
|
||||||
|
|
||||||
const [tankNames, tankDefinitions] = await Promise.all([
|
const [tankNames, tankDefinitions, TIERS] = await Promise.all([
|
||||||
awaitableTankNames,
|
awaitableTankNames,
|
||||||
api.tankDefinitions(),
|
api.tankDefinitions(),
|
||||||
|
awaitableTiers,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const ids = Object.keys(tankDefinitions.tanks);
|
const ids = Object.keys(tankDefinitions.tanks);
|
||||||
@@ -193,8 +195,7 @@ export function Guesser() {
|
|||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
{times(10, (index) => {
|
{TIERS.map((tier) => {
|
||||||
const tier = 10 - index;
|
|
||||||
const isSelected = tiers.includes(tier);
|
const isSelected = tiers.includes(tier);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
type FlexProps,
|
type FlexProps,
|
||||||
} from "@radix-ui/themes";
|
} from "@radix-ui/themes";
|
||||||
import { times } from "lodash-es";
|
|
||||||
import { Fragment, type ComponentProps, type ReactNode } from "react";
|
import { Fragment, type ComponentProps, type ReactNode } from "react";
|
||||||
|
import { awaitableTiers } from "../../../core/awaitables/tiers";
|
||||||
import { api } from "../../../core/blitzkit/api";
|
import { api } from "../../../core/blitzkit/api";
|
||||||
import { useLocale } from "../../../hooks/useLocale";
|
import { useLocale } from "../../../hooks/useLocale";
|
||||||
import { App } from "../../../stores/app";
|
import { App } from "../../../stores/app";
|
||||||
@@ -171,7 +171,7 @@ const GUN_TYPES = Object.keys(
|
|||||||
|
|
||||||
const MAX_ICONS = 4;
|
const MAX_ICONS = 4;
|
||||||
|
|
||||||
const TIERS = times(10, (i) => 10 - i);
|
const TIERS = await awaitableTiers;
|
||||||
|
|
||||||
export function FilterControl() {
|
export function FilterControl() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { TIER_ROMAN_NUMERALS } from "@blitzkit/core";
|
import { TIER_ROMAN_NUMERALS } from "@blitzkit/core";
|
||||||
import { Flex, IconButton, Text } from "@radix-ui/themes";
|
import { Flex, IconButton, Text } from "@radix-ui/themes";
|
||||||
import { times } from "lodash-es";
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
|
import { awaitableTiers } from "../../../core/awaitables/tiers";
|
||||||
import { TankFilters } from "../../../stores/tankFilters";
|
import { TankFilters } from "../../../stores/tankFilters";
|
||||||
import { TankSort } from "../../../stores/tankopediaSort";
|
import { TankSort } from "../../../stores/tankopediaSort";
|
||||||
|
|
||||||
|
const TIERS = await awaitableTiers;
|
||||||
|
|
||||||
export const TierFilter = memo(() => {
|
export const TierFilter = memo(() => {
|
||||||
const by = TankSort.use((state) => state.by);
|
const by = TankSort.use((state) => state.by);
|
||||||
const search = TankFilters.use((state) => state.search);
|
const search = TankFilters.use((state) => state.search);
|
||||||
@@ -19,8 +21,7 @@ export const TierFilter = memo(() => {
|
|||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
style={{ borderRadius: "var(--radius-full)" }}
|
style={{ borderRadius: "var(--radius-full)" }}
|
||||||
>
|
>
|
||||||
{times(10, (index) => {
|
{TIERS.map((tier) => {
|
||||||
const tier = 10 - index;
|
|
||||||
const selected = tiers.includes(tier);
|
const selected = tiers.includes(tier);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+3
-1
@@ -13,6 +13,7 @@ import { modelTransformEvent } from "../../../../../../../core/blitzkit/modelTra
|
|||||||
import { Var } from "../../../../../../../core/radix/var";
|
import { Var } from "../../../../../../../core/radix/var";
|
||||||
import { useLocale } from "../../../../../../../hooks/useLocale";
|
import { useLocale } from "../../../../../../../hooks/useLocale";
|
||||||
import { Duel } from "../../../../../../../stores/duel";
|
import { Duel } from "../../../../../../../stores/duel";
|
||||||
|
import { Tankopedia } from "../../../../../../../stores/tankopedia";
|
||||||
import type { MaybeSkeletonComponentProps } from "../../../../../../../types/maybeSkeletonComponentProps";
|
import type { MaybeSkeletonComponentProps } from "../../../../../../../types/maybeSkeletonComponentProps";
|
||||||
import { VisualizerCard } from "../VisualizerCard";
|
import { VisualizerCard } from "../VisualizerCard";
|
||||||
import { VisualizerCornerStat } from "../VisualizerCornerStat";
|
import { VisualizerCornerStat } from "../VisualizerCornerStat";
|
||||||
@@ -43,6 +44,7 @@ export function GunFlexibilityVisualizer({
|
|||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
const gun = Duel.use((state) => state.protagonist.gun);
|
const gun = Duel.use((state) => state.protagonist.gun);
|
||||||
|
const modelRequested = Tankopedia.use((state) => state.modelRequested);
|
||||||
|
|
||||||
const tankModel = modelDefinition.models[tank.id];
|
const tankModel = modelDefinition.models[tank.id];
|
||||||
const turretModel = tankModel.turrets[turret.id];
|
const turretModel = tankModel.turrets[turret.id];
|
||||||
@@ -193,7 +195,7 @@ export function GunFlexibilityVisualizer({
|
|||||||
top="0"
|
top="0"
|
||||||
left="0"
|
left="0"
|
||||||
>
|
>
|
||||||
{!skeleton && <FlexibilityCanvas />}
|
{!skeleton && modelRequested && <FlexibilityCanvas />}
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
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);
|
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
|
||||||
|
const modelRequested = Tankopedia.use((state) => state.modelRequested);
|
||||||
const equalize = Duel.use((state) => state.equalize);
|
const equalize = Duel.use((state) => state.equalize);
|
||||||
const equalizer = Duel.use((state) => state.antagonist.tank.equalizer);
|
const equalizer = Duel.use((state) => state.antagonist.tank.equalizer);
|
||||||
|
|
||||||
@@ -283,7 +284,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
<SpallLinerSwitcher />
|
<SpallLinerSwitcher />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{!skeleton && (
|
{!skeleton && modelRequested && (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<DynamicArmorSwitcher />
|
<DynamicArmorSwitcher />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|||||||
+38
-4
@@ -1,11 +1,14 @@
|
|||||||
import { Flex, Heading } from "@radix-ui/themes";
|
import { UpdateIcon } from "@radix-ui/react-icons";
|
||||||
|
import { Button, Flex, Heading } from "@radix-ui/themes";
|
||||||
import { useLocale } from "../../../../../../hooks/useLocale";
|
import { useLocale } from "../../../../../../hooks/useLocale";
|
||||||
import { Duel } from "../../../../../../stores/duel";
|
import { Duel } from "../../../../../../stores/duel";
|
||||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||||
|
import type { MaybeSkeletonComponentProps } from "../../../../../../types/maybeSkeletonComponentProps";
|
||||||
import { Tracker } from "./components/Tracker";
|
import { Tracker } from "./components/Tracker";
|
||||||
|
|
||||||
export function Title() {
|
export function Title({ skeleton }: MaybeSkeletonComponentProps) {
|
||||||
const { unwrap } = useLocale();
|
const { unwrap, strings } = useLocale();
|
||||||
|
const modelRequested = Tankopedia.use((state) => state.modelRequested);
|
||||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||||
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);
|
||||||
@@ -21,6 +24,8 @@ export function Title() {
|
|||||||
onPointerDown={(event) => {
|
onPointerDown={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!modelRequested) return;
|
||||||
|
|
||||||
Tankopedia.mutate((draft) => {
|
Tankopedia.mutate((draft) => {
|
||||||
draft.revealed = true;
|
draft.revealed = true;
|
||||||
});
|
});
|
||||||
@@ -60,7 +65,36 @@ export function Title() {
|
|||||||
{name}
|
{name}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
{!revealed && <Tracker fontSize={fontSize} />}
|
{!skeleton && !revealed && modelRequested && (
|
||||||
|
<Tracker fontSize={fontSize} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!skeleton && !modelRequested && (
|
||||||
|
<Flex
|
||||||
|
position="absolute"
|
||||||
|
left="50%"
|
||||||
|
top="50%"
|
||||||
|
width="min(100%, 16rem)"
|
||||||
|
justify="center"
|
||||||
|
style={{
|
||||||
|
transform: `translate(-50%, calc(${fontSize} * 0.75 + var(--space-4) - 50%))`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="solid"
|
||||||
|
highContrast
|
||||||
|
onPointerDown={(event) => event.stopPropagation()}
|
||||||
|
onClick={() => {
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.modelRequested = true;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UpdateIcon />
|
||||||
|
{strings.website.tools.tankopedia.sandbox.load_model}
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -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 { useEffect, useMemo, useRef } from "react";
|
import { Suspense, useEffect, useMemo, useRef } from "react";
|
||||||
import { api } from "../../../core/blitzkit/api";
|
import { api } from "../../../core/blitzkit/api";
|
||||||
import { defaultEqualizer } from "../../../core/blitzkit/tankToDuelMember";
|
import { defaultEqualizer } from "../../../core/blitzkit/tankToDuelMember";
|
||||||
import { useFullScreen } from "../../../hooks/useFullScreen";
|
import { useFullScreen } from "../../../hooks/useFullScreen";
|
||||||
@@ -20,6 +20,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
const canvas = useRef<HTMLCanvasElement>(null!);
|
const canvas = useRef<HTMLCanvasElement>(null!);
|
||||||
const isFullScreen = useFullScreen();
|
const isFullScreen = useFullScreen();
|
||||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||||
|
const modelRequested = Tankopedia.use((state) => state.modelRequested);
|
||||||
const thicknessRange = useMemo(() => {
|
const thicknessRange = useMemo(() => {
|
||||||
const entries = Object.values(tankDefinitions.tanks);
|
const entries = Object.values(tankDefinitions.tanks);
|
||||||
const filtered = entries.filter(
|
const filtered = entries.filter(
|
||||||
@@ -131,13 +132,15 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
top="0"
|
top="0"
|
||||||
left="0"
|
left="0"
|
||||||
>
|
>
|
||||||
{!skeleton && (
|
{!skeleton && modelRequested && (
|
||||||
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
<Suspense>
|
||||||
|
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
||||||
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Title />
|
<Title skeleton={skeleton} />
|
||||||
|
|
||||||
<Options
|
<Options
|
||||||
skeleton={skeleton}
|
skeleton={skeleton}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{averages && nextTanks && tank.tier !== 10 && (
|
{averages && nextTanks && (
|
||||||
<Text color="gray" size="1" mt="2">
|
<Text color="gray" size="1" mt="2">
|
||||||
{skeleton && <Skeleton height="1em" width="4em" />}
|
{skeleton && <Skeleton height="1em" width="4em" />}
|
||||||
{!skeleton &&
|
{!skeleton &&
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{master.tier < 10 && (
|
{master.successors.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Arrow />
|
<Arrow />
|
||||||
{master.successors!.map((id, index) => (
|
{master.successors!.map((id, index) => (
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { times } from "lodash-es";
|
||||||
|
import { api } from "../blitzkit/api";
|
||||||
|
|
||||||
|
export const awaitableTiers = api.tankDefinitions().then((tankDefinitions) => {
|
||||||
|
let max = 0;
|
||||||
|
|
||||||
|
for (const tank of Object.values(tankDefinitions.tanks)) {
|
||||||
|
if (tank.tier > max) max = tank.tier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return times(max, (index) => max - index);
|
||||||
|
});
|
||||||
@@ -3,11 +3,13 @@ import { useRef } from "react";
|
|||||||
|
|
||||||
export function useStableProgress() {
|
export function useStableProgress() {
|
||||||
const { loaded, total } = useProgress();
|
const { loaded, total } = useProgress();
|
||||||
|
const baseline = useRef(loaded);
|
||||||
const progress = useRef(0);
|
const progress = useRef(0);
|
||||||
|
const pending = total - baseline.current;
|
||||||
|
|
||||||
progress.current = Math.max(
|
progress.current = Math.max(
|
||||||
progress.current,
|
progress.current,
|
||||||
total === 0 ? 0 : (loaded / total) * 100
|
pending <= 0 ? 0 : ((loaded - baseline.current) / pending) * 100
|
||||||
);
|
);
|
||||||
|
|
||||||
return progress.current;
|
return progress.current;
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ export function Page({ locale }: LocaleAcceptorProps) {
|
|||||||
|
|
||||||
function Content({ locale }: LocaleAcceptorProps) {
|
function Content({ locale }: LocaleAcceptorProps) {
|
||||||
const developerMode = App.useDeferred((state) => state.developerMode, false);
|
const developerMode = App.useDeferred((state) => state.developerMode, false);
|
||||||
|
const autoLoadModels = App.useDeferred(
|
||||||
|
(state) => state.autoLoadModels,
|
||||||
|
true,
|
||||||
|
);
|
||||||
const { strings } = useLocale();
|
const { strings } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -36,6 +40,19 @@ function Content({ locale }: LocaleAcceptorProps) {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
<Flex align="center" gap="2" justify="between">
|
||||||
|
<Text>{strings.website.settings.auto_load_models}</Text>
|
||||||
|
<Switch
|
||||||
|
variant="classic"
|
||||||
|
checked={autoLoadModels}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
App.mutate((draft) => {
|
||||||
|
draft.autoLoadModels = checked;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
<Flex align="center" gap="2" justify="between">
|
<Flex align="center" gap="2" justify="between">
|
||||||
<Text>{strings.website.settings.language}</Text>
|
<Text>{strings.website.settings.language}</Text>
|
||||||
<LocaleSwitcher locale={locale} />
|
<LocaleSwitcher locale={locale} />
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface WargamingLogin {
|
|||||||
|
|
||||||
export interface App {
|
export interface App {
|
||||||
developerMode: boolean;
|
developerMode: boolean;
|
||||||
|
autoLoadModels: boolean;
|
||||||
policiesAgreementIndex: number;
|
policiesAgreementIndex: number;
|
||||||
logins: {
|
logins: {
|
||||||
wargaming?: WargamingLogin;
|
wargaming?: WargamingLogin;
|
||||||
@@ -17,6 +18,7 @@ export interface App {
|
|||||||
export const App = new Varuna<App>(
|
export const App = new Varuna<App>(
|
||||||
{
|
{
|
||||||
developerMode: false,
|
developerMode: false,
|
||||||
|
autoLoadModels: true,
|
||||||
policiesAgreementIndex: -1,
|
policiesAgreementIndex: -1,
|
||||||
logins: {},
|
logins: {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { TankDefinition } from "@blitzkit/core";
|
import type { TankDefinition } from "@blitzkit/core";
|
||||||
import { times } from "lodash-es";
|
|
||||||
import { Varuna } from "varuna";
|
import { Varuna } from "varuna";
|
||||||
|
import { awaitableTiers } from "../core/awaitables/tiers";
|
||||||
|
|
||||||
|
const tiers = await awaitableTiers;
|
||||||
|
|
||||||
export enum GuessState {
|
export enum GuessState {
|
||||||
Correct,
|
Correct,
|
||||||
@@ -25,5 +27,5 @@ export const Guess = new Varuna<Guess, TankDefinition>((tank) => ({
|
|||||||
correctGuesses: 0,
|
correctGuesses: 0,
|
||||||
streak: 0,
|
streak: 0,
|
||||||
helpingReveal: false,
|
helpingReveal: false,
|
||||||
tiers: times(10, (index) => index + 1),
|
tiers,
|
||||||
}));
|
}));
|
||||||
@@ -9,6 +9,7 @@ import type { ArmorType } from "../components/Armor/components/SpacedArmorScene"
|
|||||||
import type { ExternalModuleVariant } from "../components/Armor/components/SpacedArmorSceneComponent";
|
import type { ExternalModuleVariant } from "../components/Armor/components/SpacedArmorSceneComponent";
|
||||||
import type { XP_MULTIPLIERS } from "../components/Tankopedia/TechTreeSection";
|
import type { XP_MULTIPLIERS } from "../components/Tankopedia/TechTreeSection";
|
||||||
import { api } from "../core/blitzkit/api";
|
import { api } from "../core/blitzkit/api";
|
||||||
|
import { App } from "./app";
|
||||||
import { TankopediaDisplay } from "./tankopediaPersistent/constants";
|
import { TankopediaDisplay } from "./tankopediaPersistent/constants";
|
||||||
|
|
||||||
export interface ShotLayerBase {
|
export interface ShotLayerBase {
|
||||||
@@ -79,6 +80,7 @@ export enum TankopediaRelativeAgainst {
|
|||||||
interface Tankopedia {
|
interface Tankopedia {
|
||||||
disturbed: boolean;
|
disturbed: boolean;
|
||||||
revealed: boolean;
|
revealed: boolean;
|
||||||
|
modelRequested: boolean;
|
||||||
shot?: Shot;
|
shot?: Shot;
|
||||||
skills: Record<string, number>;
|
skills: Record<string, number>;
|
||||||
relativeAgainst: TankopediaRelativeAgainst;
|
relativeAgainst: TankopediaRelativeAgainst;
|
||||||
@@ -110,6 +112,7 @@ const skillDefinitions = await api.skillDefinitions();
|
|||||||
export const Tankopedia = new Varuna<Tankopedia, ModelDefinition>((model) => ({
|
export const Tankopedia = new Varuna<Tankopedia, ModelDefinition>((model) => ({
|
||||||
disturbed: false,
|
disturbed: false,
|
||||||
revealed: false,
|
revealed: false,
|
||||||
|
modelRequested: App.state.autoLoadModels,
|
||||||
relativeAgainst: TankopediaRelativeAgainst.Class,
|
relativeAgainst: TankopediaRelativeAgainst.Class,
|
||||||
editStatic: false,
|
editStatic: false,
|
||||||
skills: createDefaultSkills(skillDefinitions),
|
skills: createDefaultSkills(skillDefinitions),
|
||||||
|
|||||||
Reference in new issue
Block a user