mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
fix: update errors
This commit is contained in:
1 parent
4ff9a2ec7f
commit
c14efe86cc
34 files changed
+217
-280
No files matched your search
@@ -1,12 +1,12 @@
|
|||||||
import { I18nString } from '@blitzkit/core';
|
import { I18nString } from "@blitzkit/core";
|
||||||
import { DEFAULT_LOCALE } from './strings';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
|
|
||||||
export function unwrapper(locale: string) {
|
export function unwrapper(locale = locales.default) {
|
||||||
return function (i18nString: I18nString) {
|
return function (i18nString: I18nString) {
|
||||||
if (locale in i18nString.locales) {
|
if (locale in i18nString.locales) {
|
||||||
return i18nString.locales[locale];
|
return i18nString.locales[locale];
|
||||||
}
|
}
|
||||||
|
|
||||||
return i18nString.locales[DEFAULT_LOCALE];
|
return i18nString.locales[locales.default];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -5,16 +5,19 @@
|
|||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
|
"lint": "astro check",
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/react": "^4.4.0",
|
"@astrojs/react": "^4.4.0",
|
||||||
"@radix-ui/colors": "^3.0.0",
|
"@radix-ui/colors": "^3.0.0",
|
||||||
|
"@react-three/fiber": "^9.3.0",
|
||||||
"@types/react": "^19.1.14",
|
"@types/react": "^19.1.14",
|
||||||
"@types/react-dom": "^19.1.9",
|
"@types/react-dom": "^19.1.9",
|
||||||
"@vite-pwa/astro": "^1.1.0",
|
"@vite-pwa/astro": "^1.1.0",
|
||||||
"astro": "^5.14.1",
|
"astro": "^5.14.1",
|
||||||
"astro-favicons": "^3.1.5",
|
"astro-favicons": "^3.1.5",
|
||||||
|
"fuzzysort": "^3.1.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1"
|
"react-dom": "^19.1.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { GroupProps } from '@react-three/fiber';
|
import { forwardRef, type ComponentProps } from "react";
|
||||||
import { forwardRef } from 'react';
|
import { Group } from "three";
|
||||||
import { Group } from 'three';
|
|
||||||
|
|
||||||
export const ModelTankWrapper = forwardRef<Group, GroupProps>((props, ref) => {
|
export const ModelTankWrapper = forwardRef<Group, ComponentProps<"group">>(
|
||||||
return <group rotation={[-Math.PI / 2, 0, 0]} ref={ref} {...props} />;
|
(props, ref) => {
|
||||||
});
|
return <group rotation={[-Math.PI / 2, 0, 0]} ref={ref} {...props} />;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from "react";
|
||||||
import { Group } from 'three';
|
import { Group } from "three";
|
||||||
import { correctZYTuple } from '../../../core/blitz/correctZYTuple';
|
import { correctZYTuple } from "../../../core/blitz/correctZYTuple";
|
||||||
import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
import { nameToArmorId } from "../../../core/blitzkit/nameToArmorId";
|
||||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
import { resolveArmor } from "../../../core/blitzkit/resolveThickness";
|
||||||
import { useArmor } from '../../../hooks/useArmor';
|
import { useArmor } from "../../../hooks/useArmor";
|
||||||
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
import { useTankModelDefinition } from "../../../hooks/useTankModelDefinition";
|
||||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
import { useTankTransform } from "../../../hooks/useTankTransform";
|
||||||
import { Duel } from '../../../stores/duel';
|
import { Duel } from "../../../stores/duel";
|
||||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
import { ModelTankWrapper } from "./ModelTankWrapper";
|
||||||
import { PrimaryArmorSceneComponent } from './PrimaryArmorSceneComponent';
|
import { PrimaryArmorSceneComponent } from "./PrimaryArmorSceneComponent";
|
||||||
|
|
||||||
export const PrimaryArmorScene = memo(() => {
|
export const PrimaryArmorScene = memo(() => {
|
||||||
const wrapper = useRef<Group>(null);
|
const wrapper = useRef<Group>(null);
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null!);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null!);
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
@@ -28,7 +28,7 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
||||||
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
||||||
const isDynamicArmorActive = Duel.use((state) =>
|
const isDynamicArmorActive = Duel.use((state) =>
|
||||||
state.protagonist.consumables.includes(73),
|
state.protagonist.consumables.includes(73)
|
||||||
);
|
);
|
||||||
|
|
||||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||||
@@ -37,20 +37,20 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
<ModelTankWrapper ref={wrapper}>
|
<ModelTankWrapper ref={wrapper}>
|
||||||
<group position={hullOrigin}>
|
<group position={hullOrigin}>
|
||||||
{armorNodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isHull = node.name.startsWith('hull_');
|
const isHull = node.name.startsWith("hull_");
|
||||||
const isVisible = isHull;
|
const isVisible = isHull;
|
||||||
const armorId = nameToArmorId(node.name);
|
const armorId = nameToArmorId(node.name);
|
||||||
const { spaced, thickness } = resolveArmor(
|
const { spaced, thickness } = resolveArmor(
|
||||||
tankModelDefinition.armor,
|
tankModelDefinition.armor,
|
||||||
armorId,
|
armorId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
spaced ||
|
spaced ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes("state_01")) ||
|
||||||
(!isDynamicArmorActive && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes("state_00"))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -67,21 +67,21 @@ 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);
|
||||||
const { spaced, thickness } = resolveArmor(
|
const { spaced, thickness } = resolveArmor(
|
||||||
turretModelDefinition.armor,
|
turretModelDefinition.armor,
|
||||||
armorId,
|
armorId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
spaced ||
|
spaced ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes("state_01")) ||
|
||||||
(!isDynamicArmorActive && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes("state_00"))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -101,21 +101,21 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
<group ref={gunContainer}>
|
<group ref={gunContainer}>
|
||||||
{armorNodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isCurrentGun = node.name.startsWith(
|
const isCurrentGun = node.name.startsWith(
|
||||||
`gun_${gunModelDefinition.model_id.toString().padStart(2, '0')}`,
|
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`
|
||||||
);
|
);
|
||||||
const isVisible = isCurrentGun;
|
const isVisible = isCurrentGun;
|
||||||
const armorId = nameToArmorId(node.name);
|
const armorId = nameToArmorId(node.name);
|
||||||
const { spaced, thickness } = resolveArmor(
|
const { spaced, thickness } = resolveArmor(
|
||||||
gunModelDefinition.armor,
|
gunModelDefinition.armor,
|
||||||
armorId,
|
armorId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
spaced ||
|
spaced ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes("state_01")) ||
|
||||||
(!isDynamicArmorActive && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes("state_00"))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from "react";
|
||||||
import { Group, Plane, Scene, Vector3 } from 'three';
|
import { Group, Plane, Scene, Vector3 } from "three";
|
||||||
import { correctZYTuple } from '../../../core/blitz/correctZYTuple';
|
import { correctZYTuple } from "../../../core/blitz/correctZYTuple";
|
||||||
import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
import { nameToArmorId } from "../../../core/blitzkit/nameToArmorId";
|
||||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
import { resolveArmor } from "../../../core/blitzkit/resolveThickness";
|
||||||
import { useArmor } from '../../../hooks/useArmor';
|
import { useArmor } from "../../../hooks/useArmor";
|
||||||
import { useModel } from '../../../hooks/useModel';
|
import { useModel } from "../../../hooks/useModel";
|
||||||
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
import { useTankModelDefinition } from "../../../hooks/useTankModelDefinition";
|
||||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
import { useTankTransform } from "../../../hooks/useTankTransform";
|
||||||
import { Duel } from '../../../stores/duel';
|
import { Duel } from "../../../stores/duel";
|
||||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
import { ModelTankWrapper } from "./ModelTankWrapper";
|
||||||
import { SpacedArmorSceneComponent } from './SpacedArmorSceneComponent';
|
import { SpacedArmorSceneComponent } from "./SpacedArmorSceneComponent";
|
||||||
|
|
||||||
export enum ArmorType {
|
export enum ArmorType {
|
||||||
Primary,
|
Primary,
|
||||||
@@ -23,8 +23,8 @@ interface SpacedArmorSceneProps {
|
|||||||
|
|
||||||
export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||||
const wrapper = useRef<Group>(null);
|
const wrapper = useRef<Group>(null);
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null!);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null!);
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
@@ -45,7 +45,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
? undefined
|
? undefined
|
||||||
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
||||||
const isDynamicArmorActive = Duel.use((state) =>
|
const isDynamicArmorActive = Duel.use((state) =>
|
||||||
state.protagonist.consumables.includes(73),
|
state.protagonist.consumables.includes(73)
|
||||||
);
|
);
|
||||||
|
|
||||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||||
@@ -54,19 +54,19 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
<ModelTankWrapper ref={wrapper}>
|
<ModelTankWrapper ref={wrapper}>
|
||||||
<group position={hullOrigin}>
|
<group position={hullOrigin}>
|
||||||
{armorNodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isHull = node.name.startsWith('hull_');
|
const isHull = node.name.startsWith("hull_");
|
||||||
const isVisible = isHull;
|
const isVisible = isHull;
|
||||||
const armorId = nameToArmorId(node.name);
|
const armorId = nameToArmorId(node.name);
|
||||||
const { spaced, thickness } = resolveArmor(
|
const { spaced, thickness } = resolveArmor(
|
||||||
tankModelDefinition.armor,
|
tankModelDefinition.armor,
|
||||||
armorId,
|
armorId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
(isDynamicArmorActive && node.name.includes("state_00")) ||
|
||||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
(!isDynamicArmorActive && node.name.includes("state_01"))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -83,8 +83,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
</group>
|
</group>
|
||||||
|
|
||||||
{modelNodes.map((node) => {
|
{modelNodes.map((node) => {
|
||||||
const isWheel = node.name.startsWith('chassis_wheel_');
|
const isWheel = node.name.startsWith("chassis_wheel_");
|
||||||
const isTrack = node.name.startsWith('chassis_track_');
|
const isTrack = node.name.startsWith("chassis_track_");
|
||||||
const isVisible = isWheel || isTrack;
|
const isVisible = isWheel || isTrack;
|
||||||
|
|
||||||
if (!isVisible) return null;
|
if (!isVisible) return null;
|
||||||
@@ -104,20 +104,20 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
<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);
|
||||||
const { spaced, thickness } = resolveArmor(
|
const { spaced, thickness } = resolveArmor(
|
||||||
turretModelDefinition.armor,
|
turretModelDefinition.armor,
|
||||||
armorId,
|
armorId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
(isDynamicArmorActive && node.name.includes("state_00")) ||
|
||||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
(!isDynamicArmorActive && node.name.includes("state_01"))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -139,20 +139,20 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
<group ref={gunContainer}>
|
<group ref={gunContainer}>
|
||||||
{armorNodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isCurrentGun = node.name.startsWith(
|
const isCurrentGun = node.name.startsWith(
|
||||||
`gun_${gunModelDefinition.model_id.toString().padStart(2, '0')}`,
|
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`
|
||||||
);
|
);
|
||||||
const isVisible = isCurrentGun;
|
const isVisible = isCurrentGun;
|
||||||
const armorId = nameToArmorId(node.name);
|
const armorId = nameToArmorId(node.name);
|
||||||
const { spaced, thickness } = resolveArmor(
|
const { spaced, thickness } = resolveArmor(
|
||||||
gunModelDefinition.armor,
|
gunModelDefinition.armor,
|
||||||
armorId,
|
armorId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
(isDynamicArmorActive && node.name.includes("state_00")) ||
|
||||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
(!isDynamicArmorActive && node.name.includes("state_01"))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
{modelNodes.map((node) => {
|
{modelNodes.map((node) => {
|
||||||
const gunString = `gun_${gunModelDefinition.model_id
|
const gunString = `gun_${gunModelDefinition.model_id
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, '0')}`;
|
.padStart(2, "0")}`;
|
||||||
const isCurrentGun = gunModelDefinition.mask
|
const isCurrentGun = gunModelDefinition.mask
|
||||||
? node.name.startsWith(gunString)
|
? node.name.startsWith(gunString)
|
||||||
: node.name === gunString;
|
: node.name === gunString;
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ export interface ThicknessRange {
|
|||||||
|
|
||||||
export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||||
const wrapper = useRef<Group>(null);
|
const wrapper = useRef<Group>(null);
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null!);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null!);
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { I_HAT, J_HAT } from "@blitzkit/core";
|
import { I_HAT, J_HAT } from "@blitzkit/core";
|
||||||
import { type MeshProps, useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { clamp } from "lodash-es";
|
import { clamp } from "lodash-es";
|
||||||
import type { QuicklimeEvent } from "quicklime";
|
import type { QuicklimeEvent } from "quicklime";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo, type ComponentProps } from "react";
|
||||||
import {
|
import {
|
||||||
Box3,
|
Box3,
|
||||||
Color,
|
Color,
|
||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
import { unrotateDavaVector } from "../../../core/blitz/unrotateDavaVector";
|
import { unrotateDavaVector } from "../../../core/blitz/unrotateDavaVector";
|
||||||
import { jsxTree } from "../../../core/blitzkit/jsxTree";
|
import { jsxTree } from "../../../core/blitzkit/jsxTree";
|
||||||
import {
|
import {
|
||||||
type ModelTransformEventData,
|
|
||||||
modelTransformEvent,
|
modelTransformEvent,
|
||||||
|
type ModelTransformEventData,
|
||||||
} 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";
|
||||||
@@ -36,7 +36,7 @@ type StaticArmorSceneComponentProps = {
|
|||||||
thickness: number;
|
thickness: number;
|
||||||
thicknessRange: ThicknessRange;
|
thicknessRange: ThicknessRange;
|
||||||
node: Object3D;
|
node: Object3D;
|
||||||
onPointerDown?: MeshProps["onPointerDown"];
|
onPointerDown?: ComponentProps<"mesh">["onPointerDown"];
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
type: Exclude<ArmorType, ArmorType.External>;
|
type: Exclude<ArmorType, ArmorType.External>;
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
import { assertSecret, fetchGameDefinitions } from '@blitzkit/core';
|
import { assertSecret, fetchGameDefinitions } from "@blitzkit/core";
|
||||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { Box, Code, Flex, Heading, Link, Text } from '@radix-ui/themes';
|
import { Box, Code, Flex, Heading, Link, Text } from "@radix-ui/themes";
|
||||||
import packageJson from '../../../../package.json';
|
import packageJson from "../../../../package.json";
|
||||||
import { getStrings } from '../core/i18n/getStrings';
|
import { getStrings } from "../core/i18n/getStrings";
|
||||||
import { LinkI18n } from './LinkI18n';
|
import { LinkI18n } from "./LinkI18n";
|
||||||
import { LocaleSwitcherThemeWrapper } from './LocaleSwitcher';
|
import { LocaleSwitcherThemeWrapper } from "./LocaleSwitcher";
|
||||||
|
|
||||||
const gameDefinitions = await fetchGameDefinitions();
|
const gameDefinitions = await fetchGameDefinitions();
|
||||||
const PUBLIC_ASSET_BRANCH = assertSecret(import.meta.env.PUBLIC_BRANCH);
|
const PUBLIC_ASSET_BRANCH = assertSecret(import.meta.env.PUBLIC_BRANCH);
|
||||||
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
|
const locale = Astro.params.locale ?? locales.default;
|
||||||
const strings = getStrings(locale);
|
const strings = getStrings(locale);
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -28,13 +28,13 @@ const strings = getStrings(locale);
|
|||||||
flexGrow="1"
|
flexGrow="1"
|
||||||
gap="5"
|
gap="5"
|
||||||
position="relative"
|
position="relative"
|
||||||
direction={{ initial: 'column', sm: 'row' }}
|
direction={{ initial: "column", sm: "row" }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
align={{
|
align={{
|
||||||
initial: 'center',
|
initial: "center",
|
||||||
sm: 'start',
|
sm: "start",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Heading>BlitzKit</Heading>
|
<Heading>BlitzKit</Heading>
|
||||||
@@ -51,32 +51,32 @@ const strings = getStrings(locale);
|
|||||||
<Flex
|
<Flex
|
||||||
gap="6"
|
gap="6"
|
||||||
width={{
|
width={{
|
||||||
initial: '100%',
|
initial: "100%",
|
||||||
sm: 'auto',
|
sm: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
gap="1"
|
gap="1"
|
||||||
align={{
|
align={{
|
||||||
initial: 'end',
|
initial: "end",
|
||||||
sm: 'start',
|
sm: "start",
|
||||||
}}
|
}}
|
||||||
flexGrow={{
|
flexGrow={{
|
||||||
initial: '1',
|
initial: "1",
|
||||||
sm: '0',
|
sm: "0",
|
||||||
}}
|
}}
|
||||||
flexBasis={{
|
flexBasis={{
|
||||||
initial: '0',
|
initial: "0",
|
||||||
sm: 'auto',
|
sm: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Heading size="3">{strings.website.footer.about.title}</Heading>
|
<Heading size="3">{strings.website.footer.about.title}</Heading>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
align={{
|
align={{
|
||||||
initial: 'end',
|
initial: "end",
|
||||||
sm: 'start',
|
sm: "start",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LinkI18n {locale} href="/docs/about/credits" color="gray">
|
<LinkI18n {locale} href="/docs/about/credits" color="gray">
|
||||||
@@ -106,12 +106,12 @@ const strings = getStrings(locale);
|
|||||||
direction="column"
|
direction="column"
|
||||||
gap="1"
|
gap="1"
|
||||||
flexGrow={{
|
flexGrow={{
|
||||||
initial: '1',
|
initial: "1",
|
||||||
sm: '0',
|
sm: "0",
|
||||||
}}
|
}}
|
||||||
flexBasis={{
|
flexBasis={{
|
||||||
initial: '0',
|
initial: "0",
|
||||||
sm: 'auto',
|
sm: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Heading size="3">{strings.website.footer.legal.title}</Heading>
|
<Heading size="3">{strings.website.footer.legal.title}</Heading>
|
||||||
@@ -127,11 +127,11 @@ const strings = getStrings(locale);
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex gap="5" display={{ initial: 'flex', sm: 'none' }} width="100%">
|
<Flex gap="5" display={{ initial: "flex", sm: "none" }} width="100%">
|
||||||
<Flex direction="column" gap="1" flexGrow="1" flexBasis="0" align="end">
|
<Flex direction="column" gap="1" flexGrow="1" flexBasis="0" align="end">
|
||||||
<Text size="2" color="gray">BlitzKit</Text>
|
<Text size="2" color="gray">BlitzKit</Text>
|
||||||
{
|
{
|
||||||
PUBLIC_ASSET_BRANCH !== 'preview' && (
|
PUBLIC_ASSET_BRANCH !== "preview" && (
|
||||||
<Text size="2" color="gray">
|
<Text size="2" color="gray">
|
||||||
WoTB
|
WoTB
|
||||||
</Text>
|
</Text>
|
||||||
@@ -145,7 +145,7 @@ const strings = getStrings(locale);
|
|||||||
{packageJson.version}
|
{packageJson.version}
|
||||||
</Code>
|
</Code>
|
||||||
{
|
{
|
||||||
PUBLIC_ASSET_BRANCH !== 'preview' && (
|
PUBLIC_ASSET_BRANCH !== "preview" && (
|
||||||
<Code size="2" color="gray">
|
<Code size="2" color="gray">
|
||||||
{gameDefinitions.version}
|
{gameDefinitions.version}
|
||||||
</Code>
|
</Code>
|
||||||
@@ -157,14 +157,14 @@ const strings = getStrings(locale);
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex gap="2" align="center" display={{ initial: 'none', sm: 'flex' }}>
|
<Flex gap="2" align="center" display={{ initial: "none", sm: "flex" }}>
|
||||||
<Text size="2" color="gray">BlitzKit</Text>
|
<Text size="2" color="gray">BlitzKit</Text>
|
||||||
<Code size="2" color="gray">
|
<Code size="2" color="gray">
|
||||||
{packageJson.version}
|
{packageJson.version}
|
||||||
</Code>
|
</Code>
|
||||||
|
|
||||||
{
|
{
|
||||||
PUBLIC_ASSET_BRANCH !== 'preview' && (
|
PUBLIC_ASSET_BRANCH !== "preview" && (
|
||||||
<>
|
<>
|
||||||
<Text size="2" color="gray">
|
<Text size="2" color="gray">
|
||||||
WoTB
|
WoTB
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { asset, Avatar } from '@blitzkit/core';
|
import { asset, Avatar } from "@blitzkit/core";
|
||||||
import { Cross1Icon, DownloadIcon } from '@radix-ui/react-icons';
|
import { Cross1Icon, DownloadIcon } from "@radix-ui/react-icons";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
Flex,
|
Flex,
|
||||||
@@ -7,11 +7,11 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Text,
|
Text,
|
||||||
} from '@radix-ui/themes';
|
} from "@radix-ui/themes";
|
||||||
import { useRef } from 'react';
|
import { useRef } from "react";
|
||||||
import { useIntersection } from '../../hooks/useIntersection';
|
import { useIntersection } from "../../hooks/useIntersection";
|
||||||
import { useLocale } from '../../hooks/useLocale';
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps';
|
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
|
||||||
|
|
||||||
type GalleryCardProps = MaybeSkeletonComponentProps<{
|
type GalleryCardProps = MaybeSkeletonComponentProps<{
|
||||||
avatar: Avatar;
|
avatar: Avatar;
|
||||||
@@ -19,9 +19,9 @@ type GalleryCardProps = MaybeSkeletonComponentProps<{
|
|||||||
|
|
||||||
export function GalleryCard(props: GalleryCardProps) {
|
export function GalleryCard(props: GalleryCardProps) {
|
||||||
const src = asset(
|
const src = asset(
|
||||||
`gallery/avatars/${!props.skeleton && `${props.avatar.id}${props.avatar.extension}`}`,
|
`gallery/avatars/${!props.skeleton && `${props.avatar.id}${props.avatar.extension}`}`
|
||||||
);
|
);
|
||||||
const card = useRef<HTMLDivElement>(null);
|
const card = useRef<HTMLDivElement>(null!);
|
||||||
const { unwrap } = useLocale();
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
useIntersection(() => props.skeleton && props.onIntersection?.(), card, {
|
useIntersection(() => props.skeleton && props.onIntersection?.(), card, {
|
||||||
@@ -35,7 +35,7 @@ export function GalleryCard(props: GalleryCardProps) {
|
|||||||
width="6rem"
|
width="6rem"
|
||||||
gap="1"
|
gap="1"
|
||||||
ref={card}
|
ref={card}
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: "pointer" }}
|
||||||
>
|
>
|
||||||
{props.skeleton && (
|
{props.skeleton && (
|
||||||
<>
|
<>
|
||||||
@@ -48,16 +48,16 @@ export function GalleryCard(props: GalleryCardProps) {
|
|||||||
<>
|
<>
|
||||||
<img
|
<img
|
||||||
style={{
|
style={{
|
||||||
width: '4rem',
|
width: "4rem",
|
||||||
height: '4.57rem',
|
height: "4.57rem",
|
||||||
// aspect ratio 7 / 8
|
// aspect ratio 7 / 8
|
||||||
objectFit: 'cover',
|
objectFit: "cover",
|
||||||
}}
|
}}
|
||||||
src={src}
|
src={src}
|
||||||
alt={unwrap(props.avatar.name)}
|
alt={unwrap(props.avatar.name)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text size="1" align="center" style={{ maxWidth: '100%' }}>
|
<Text size="1" align="center" style={{ maxWidth: "100%" }}>
|
||||||
{unwrap(props.avatar.name)}
|
{unwrap(props.avatar.name)}
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
@@ -94,7 +94,7 @@ export function GalleryCard(props: GalleryCardProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex justify="center" mt="4">
|
<Flex justify="center" mt="4">
|
||||||
<Dialog.Title align="center" style={{ maxWidth: '10rem' }}>
|
<Dialog.Title align="center" style={{ maxWidth: "10rem" }}>
|
||||||
{unwrap(props.avatar.name)}
|
{unwrap(props.avatar.name)}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { SpotLightProps } from '@react-three/fiber';
|
import { memo, type ComponentProps } from "react";
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
interface Props extends SpotLightProps {
|
interface Props extends ComponentProps<"spotLight"> {
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { GlobeIcon } from '@radix-ui/react-icons';
|
import { GlobeIcon } from "@radix-ui/react-icons";
|
||||||
import { Flex, Text } from '@radix-ui/themes';
|
import { Flex, Text } from "@radix-ui/themes";
|
||||||
import type { LocaleAcceptorProps } from '../../hooks/useLocale';
|
import type { LocaleAcceptorProps } from "../../hooks/useLocale";
|
||||||
import { BlitzKitTheme } from '../BlitzKitTheme';
|
import { BlitzKitTheme } from "../BlitzKitTheme";
|
||||||
import { LocaleSwitcher } from '../LocaleSwitcher';
|
import { LocaleSwitcher } from "../LocaleSwitcher";
|
||||||
|
|
||||||
export function HomeLocaleSwitcher({
|
export function HomeLocaleSwitcher({
|
||||||
locale = DEFAULT_LOCALE,
|
locale = locales.default,
|
||||||
}: LocaleAcceptorProps) {
|
}: LocaleAcceptorProps) {
|
||||||
return (
|
return (
|
||||||
<BlitzKitTheme>
|
<BlitzKitTheme>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { Link, type LinkProps } from '@radix-ui/themes';
|
import { Link, type LinkProps } from "@radix-ui/themes";
|
||||||
import type { LocaleAcceptorProps } from '../hooks/useLocale';
|
import type { LocaleAcceptorProps } from "../hooks/useLocale";
|
||||||
|
|
||||||
type LinkI18nProps = LinkProps & LocaleAcceptorProps;
|
type LinkI18nProps = LinkProps & LocaleAcceptorProps;
|
||||||
|
|
||||||
export function LinkI18n({ locale, href, ...props }: LinkI18nProps) {
|
export function LinkI18n({ locale, href, ...props }: LinkI18nProps) {
|
||||||
const prefix =
|
const prefix =
|
||||||
locale === undefined || locale === DEFAULT_LOCALE ? '' : `/${locale}`;
|
locale === undefined || locale === locales.default ? "" : `/${locale}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={href?.startsWith('/') ? `${prefix}${href}` : href} {...props} />
|
<Link href={href?.startsWith("/") ? `${prefix}${href}` : href} {...props} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import { SUPPORTED_LOCALES } from "@blitzkit/i18n";
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { getBestLocale } from "../core/i18n/getBestLocale";
|
import { getBestLocale } from "../core/i18n/getBestLocale";
|
||||||
|
|
||||||
const isAPI = window.location.pathname.startsWith("/api");
|
const isAPI = window.location.pathname.startsWith("/api");
|
||||||
const isI18nPath = SUPPORTED_LOCALES.some((locale) =>
|
const isI18nPath = locales.supported.some(({ locale }) =>
|
||||||
window.location.pathname.startsWith(`/${locale}`)
|
window.location.pathname.startsWith(`/${locale}`)
|
||||||
);
|
);
|
||||||
const bestLocale = getBestLocale();
|
const bestLocale = getBestLocale();
|
||||||
const currentPathLocale = SUPPORTED_LOCALES.find((locale) =>
|
const currentPathLocale = locales.supported.find(({ locale }) =>
|
||||||
window.location.pathname.startsWith(`/${locale}`)
|
window.location.pathname.startsWith(`/${locale}`)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import {
|
|
||||||
ResponsiveBar,
|
|
||||||
type BarDatum,
|
|
||||||
type ResponsiveBarSvgProps,
|
|
||||||
} from '@nivo/bar';
|
|
||||||
import { nivoTheme } from '../../core/nivo/theme';
|
|
||||||
|
|
||||||
export function ThemedBar<RawDatum extends BarDatum>(
|
|
||||||
props: ResponsiveBarSvgProps<RawDatum>,
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<ResponsiveBar
|
|
||||||
colors={{ scheme: 'yellow_green_blue' }}
|
|
||||||
theme={nivoTheme}
|
|
||||||
borderRadius={4}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { ResponsiveLine, type LineSvgProps } from '@nivo/line';
|
|
||||||
import { nivoTheme } from '../../core/nivo/theme';
|
|
||||||
|
|
||||||
export function ThemedLine(props: LineSvgProps) {
|
|
||||||
return (
|
|
||||||
<ResponsiveLine
|
|
||||||
colors={{ scheme: 'yellow_green_blue' }}
|
|
||||||
theme={nivoTheme}
|
|
||||||
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
|
|
||||||
enablePoints={true}
|
|
||||||
useMesh
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Skeleton, Table } from '@radix-ui/themes';
|
import { Skeleton, Table } from "@radix-ui/themes";
|
||||||
import { times } from 'lodash-es';
|
import { times } from "lodash-es";
|
||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from "react";
|
||||||
import { useIntersection } from '../../hooks/useIntersection';
|
import { useIntersection } from "../../hooks/useIntersection";
|
||||||
import { TankPerformanceSortTypeNamesArray } from '../../stores/tankPerformanceSort/constants';
|
import { TankPerformanceSortTypeNamesArray } from "../../stores/tankPerformanceSort/constants";
|
||||||
|
|
||||||
interface RowLoaderProps {
|
interface RowLoaderProps {
|
||||||
onIntersection?: () => void;
|
onIntersection?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RowLoader = memo<RowLoaderProps>(({ onIntersection }) => {
|
export const RowLoader = memo<RowLoaderProps>(({ onIntersection }) => {
|
||||||
const row = useRef<HTMLTableRowElement>(null);
|
const row = useRef<HTMLTableRowElement>(null!);
|
||||||
|
|
||||||
useIntersection(() => onIntersection?.(), row, {
|
useIntersection(() => onIntersection?.(), row, {
|
||||||
disabled: onIntersection === undefined,
|
disabled: onIntersection === undefined,
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { Canvas, type CanvasProps } from '@react-three/fiber';
|
import { Canvas, type CanvasProps } from "@react-three/fiber";
|
||||||
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
import { forwardRef, useImperativeHandle, useRef } from "react";
|
||||||
import { useOnScreen } from '../hooks/useOnScreen';
|
import { useOnScreen } from "../hooks/useOnScreen";
|
||||||
|
|
||||||
export const SmartCanvas = forwardRef<HTMLCanvasElement, CanvasProps>(
|
export const SmartCanvas = forwardRef<HTMLCanvasElement, CanvasProps>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const canvas = useRef<HTMLCanvasElement>(null!);
|
||||||
const onScreen = useOnScreen(canvas);
|
const onScreen = useOnScreen(canvas);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => canvas.current!, []);
|
useImperativeHandle(ref, () => canvas.current!, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Canvas
|
<Canvas
|
||||||
frameloop={onScreen ? 'demand' : 'never'}
|
frameloop={onScreen ? "demand" : "never"}
|
||||||
ref={canvas}
|
ref={canvas}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Flex, Skeleton } from '@radix-ui/themes';
|
import { Flex, Skeleton } from "@radix-ui/themes";
|
||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from "react";
|
||||||
import { useIntersection } from '../../../hooks/useIntersection';
|
import { useIntersection } from "../../../hooks/useIntersection";
|
||||||
|
|
||||||
interface SkeletonTankCardProps {
|
interface SkeletonTankCardProps {
|
||||||
onIntersection?: () => void;
|
onIntersection?: () => void;
|
||||||
@@ -8,7 +8,7 @@ interface SkeletonTankCardProps {
|
|||||||
|
|
||||||
export const SkeletonTankCard = memo(
|
export const SkeletonTankCard = memo(
|
||||||
({ onIntersection }: SkeletonTankCardProps) => {
|
({ onIntersection }: SkeletonTankCardProps) => {
|
||||||
const card = useRef<HTMLDivElement>(null);
|
const card = useRef<HTMLDivElement>(null!);
|
||||||
|
|
||||||
useIntersection(() => onIntersection?.(), card, {
|
useIntersection(() => onIntersection?.(), card, {
|
||||||
disabled: onIntersection === undefined,
|
disabled: onIntersection === undefined,
|
||||||
@@ -21,5 +21,5 @@ export const SkeletonTankCard = memo(
|
|||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
() => true,
|
() => true
|
||||||
);
|
);
|
||||||
+1
-1
@@ -38,7 +38,7 @@ export function Info({
|
|||||||
const result = fuzzysort.single(statSearch, name);
|
const result = fuzzysort.single(statSearch, name);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
label = fuzzysort.highlight(result, (match, index) => (
|
label = result.highlight((match, index) => (
|
||||||
<Mark key={index}>{match}</Mark>
|
<Mark key={index}>{match}</Mark>
|
||||||
));
|
));
|
||||||
highlighted = true;
|
highlighted = true;
|
||||||
|
|||||||
+2
-2
@@ -19,8 +19,8 @@ export function TankModel() {
|
|||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
const canvas = useThree((state) => state.gl.domElement);
|
const canvas = useThree((state) => state.gl.domElement);
|
||||||
const hullContainer = useRef<Group>(null);
|
const hullContainer = useRef<Group>(null);
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null!);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null!);
|
||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonist.turret.id];
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
|
|||||||
+2
-2
@@ -26,8 +26,8 @@ export function TransitionSkeleton() {
|
|||||||
const gun = Duel.use((state) => state.protagonist.gun);
|
const gun = Duel.use((state) => state.protagonist.gun);
|
||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null!);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null!);
|
||||||
const { gltf: armorGltf } = useArmor(tank.id);
|
const { gltf: armorGltf } = useArmor(tank.id);
|
||||||
const { gltf: modelGltf } = useModel(tank.id);
|
const { gltf: modelGltf } = useModel(tank.id);
|
||||||
const modelNodes = Object.values(modelGltf.nodes);
|
const modelNodes = Object.values(modelGltf.nodes);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const tankDefinitions = await awaitableTankDefinitions;
|
|||||||
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||||
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 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 thicknessRange = useMemo(() => {
|
const thicknessRange = useMemo(() => {
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
import type { GroupProps, MeshProps } from '@react-three/fiber';
|
import type { ComponentProps, ReactNode } from "react";
|
||||||
import type { ReactNode } from 'react';
|
import { Mesh, Object3D } from "three";
|
||||||
import { Mesh, Object3D } from 'three';
|
|
||||||
|
|
||||||
export function jsxTree(
|
export function jsxTree(
|
||||||
node: Object3D,
|
node: Object3D,
|
||||||
mergers?: {
|
mergers?: {
|
||||||
mesh?: (mesh: Mesh, props: MeshProps, key: string) => ReactNode;
|
mesh?: (
|
||||||
group?: (object3d: Object3D, props: GroupProps, key: string) => ReactNode;
|
mesh: Mesh,
|
||||||
},
|
props: ComponentProps<"mesh">,
|
||||||
|
key: string
|
||||||
|
) => ReactNode;
|
||||||
|
group?: (
|
||||||
|
object3d: Object3D,
|
||||||
|
props: ComponentProps<"group">,
|
||||||
|
key: string
|
||||||
|
) => ReactNode;
|
||||||
|
}
|
||||||
): ReactNode {
|
): ReactNode {
|
||||||
if (node instanceof Mesh) {
|
if (node instanceof Mesh) {
|
||||||
const props = {
|
const props = {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
|
|
||||||
export function getBestLocale() {
|
export function getBestLocale() {
|
||||||
const desiredLocale =
|
const desiredLocale =
|
||||||
localStorage.getItem('preferred-locale') ??
|
localStorage.getItem("preferred-locale") ??
|
||||||
navigator.language.split('-')[0];
|
navigator.language.split("-")[0];
|
||||||
const isSupported = (SUPPORTED_LOCALES as unknown as string[]).includes(
|
const isSupported = locales.supported.some(
|
||||||
desiredLocale,
|
({ locale }) => locale === desiredLocale
|
||||||
);
|
);
|
||||||
const isDefault = desiredLocale === DEFAULT_LOCALE;
|
const isDefault = desiredLocale === locales.default;
|
||||||
|
|
||||||
if (!isSupported || isDefault) return undefined;
|
if (!isSupported || isDefault) return undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import type { Theme } from '@nivo/core';
|
|
||||||
import { Var } from '../radix/var';
|
|
||||||
|
|
||||||
export const nivoTheme = {
|
|
||||||
background: Var('color-background'),
|
|
||||||
text: {
|
|
||||||
fill: Var('gray-contrast'),
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
ticks: {
|
|
||||||
text: {
|
|
||||||
fill: Var('gray-indicator'),
|
|
||||||
},
|
|
||||||
line: {
|
|
||||||
stroke: Var('gray-indicator'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
line: {
|
|
||||||
stroke: Var('gray-indicator'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
container: {
|
|
||||||
background: Var('color-overlay'),
|
|
||||||
backdropFilter: 'blur(4rem)',
|
|
||||||
WebkitBackdropFilter: 'blur(4rem)',
|
|
||||||
color: Var('gray-contrast'),
|
|
||||||
borderRadius: Var('radius-2'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
text: {
|
|
||||||
fill: Var('gray-1'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies Theme;
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type RefObject, useEffect, useRef } from 'react';
|
import { type RefObject, useEffect, useRef } from "react";
|
||||||
|
|
||||||
interface UseIntersectionOptions extends IntersectionObserverInit {
|
interface UseIntersectionOptions extends IntersectionObserverInit {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
@@ -7,9 +7,9 @@ interface UseIntersectionOptions extends IntersectionObserverInit {
|
|||||||
export function useIntersection(
|
export function useIntersection(
|
||||||
callback: () => void,
|
callback: () => void,
|
||||||
ref: RefObject<HTMLElement>,
|
ref: RefObject<HTMLElement>,
|
||||||
options?: UseIntersectionOptions,
|
options?: UseIntersectionOptions
|
||||||
) {
|
) {
|
||||||
const observer = useRef<IntersectionObserver>();
|
const observer = useRef<IntersectionObserver>(null!);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (options?.disabled || !ref.current) return;
|
if (options?.disabled || !ref.current) return;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
|
import { unwrapper } from "@blitzkit/i18n";
|
||||||
import { createContext, useContext, type ReactNode } from 'react';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { getStrings } from '../core/i18n/getStrings';
|
import { createContext, useContext, type ReactNode } from "react";
|
||||||
|
import { getStrings } from "../core/i18n/getStrings";
|
||||||
|
|
||||||
const LocaleContext = createContext<{
|
const LocaleContext = createContext<{
|
||||||
locale: string;
|
locale: string;
|
||||||
@@ -14,7 +15,7 @@ interface LocaleProviderProps extends LocaleAcceptorProps {
|
|||||||
export function LocaleProvider({ locale, children }: LocaleProviderProps) {
|
export function LocaleProvider({ locale, children }: LocaleProviderProps) {
|
||||||
return (
|
return (
|
||||||
<LocaleContext.Provider
|
<LocaleContext.Provider
|
||||||
value={{ locale: locale ?? DEFAULT_LOCALE, localeRaw: locale }}
|
value={{ locale: locale ?? locales.default, localeRaw: locale }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</LocaleContext.Provider>
|
</LocaleContext.Provider>
|
||||||
@@ -25,7 +26,7 @@ export function useLocale() {
|
|||||||
const context = useContext(LocaleContext);
|
const context = useContext(LocaleContext);
|
||||||
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('useLocale must be used within a LocaleProvider');
|
throw new Error("useLocale must be used within a LocaleProvider");
|
||||||
}
|
}
|
||||||
|
|
||||||
const strings = getStrings(context.locale);
|
const strings = getStrings(context.locale);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { type RefObject, useEffect, useRef, useState } from 'react';
|
import { type RefObject, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thanks GuCier!
|
* Thanks GuCire!
|
||||||
*
|
*
|
||||||
* https://stackoverflow.com/a/65008608/12294756
|
* https://stackoverflow.com/a/65008608/12294756
|
||||||
*/
|
*/
|
||||||
export function useOnScreen(ref: RefObject<HTMLElement>) {
|
export function useOnScreen(ref: RefObject<HTMLElement>) {
|
||||||
const [isIntersecting, setIntersecting] = useState(false);
|
const [isIntersecting, setIntersecting] = useState(false);
|
||||||
const observer = useRef<IntersectionObserver>();
|
const observer = useRef<IntersectionObserver>(null!);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
observer.current = new IntersectionObserver(([entry]) =>
|
observer.current = new IntersectionObserver(([entry]) =>
|
||||||
setIntersecting(entry.isIntersecting),
|
setIntersecting(entry.isIntersecting)
|
||||||
);
|
);
|
||||||
}, [ref]);
|
}, [ref]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
---
|
---
|
||||||
import { DEFAULT_LOCALE } from "@blitzkit/i18n";
|
|
||||||
import strings from "@blitzkit/i18n/strings/en.json";
|
import strings from "@blitzkit/i18n/strings/en.json";
|
||||||
import "@fontsource/inter";
|
import "@fontsource/inter";
|
||||||
import { Flex } from "@radix-ui/themes";
|
import { Flex } from "@radix-ui/themes";
|
||||||
@@ -22,7 +21,7 @@ export interface Props {
|
|||||||
|
|
||||||
const servicing = import.meta.env.SERVICING;
|
const servicing = import.meta.env.SERVICING;
|
||||||
const { noFooter, noNavbar, integrated } = Astro.props;
|
const { noFooter, noNavbar, integrated } = Astro.props;
|
||||||
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
|
const locale = Astro.params.locale;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang={locale}>
|
<html lang={locale}>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ function Content({ embed }: PageProps) {
|
|||||||
const [backgroundImage, setBackgroundImage] = useState(
|
const [backgroundImage, setBackgroundImage] = useState(
|
||||||
"/assets/images/backgrounds/embed-default.webp"
|
"/assets/images/backgrounds/embed-default.webp"
|
||||||
);
|
);
|
||||||
const fileInput = useRef<HTMLInputElement>();
|
const fileInput = useRef<HTMLInputElement>(null!);
|
||||||
const { strings } = useLocale();
|
const { strings } = useLocale();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
const hull = Mixer.use((state) => state.hull);
|
const hull = Mixer.use((state) => state.hull);
|
||||||
const turretId = Mixer.use((state) => state.turret.tank.id);
|
const turretId = Mixer.use((state) => state.turret.tank.id);
|
||||||
const gunId = Mixer.use((state) => state.gun.tank.id);
|
const gunId = Mixer.use((state) => state.gun.tank.id);
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const canvas = useRef<HTMLCanvasElement>(null!);
|
||||||
const { strings } = useLocale();
|
const { strings } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DEFAULT_LOCALE } from "@blitzkit/i18n";
|
|
||||||
import { Flex, Switch, Text } from "@radix-ui/themes";
|
import { Flex, Switch, Text } from "@radix-ui/themes";
|
||||||
import { LocaleSwitcher } from "../../../components/LocaleSwitcher";
|
import { LocaleSwitcher } from "../../../components/LocaleSwitcher";
|
||||||
import { PageWrapper } from "../../../components/PageWrapper";
|
import { PageWrapper } from "../../../components/PageWrapper";
|
||||||
@@ -9,7 +8,7 @@ import {
|
|||||||
} from "../../../hooks/useLocale";
|
} from "../../../hooks/useLocale";
|
||||||
import { App } from "../../../stores/app";
|
import { App } from "../../../stores/app";
|
||||||
|
|
||||||
export function Page({ locale = DEFAULT_LOCALE }: LocaleAcceptorProps) {
|
export function Page({ locale }: LocaleAcceptorProps) {
|
||||||
return (
|
return (
|
||||||
<LocaleProvider locale={locale}>
|
<LocaleProvider locale={locale}>
|
||||||
<Content locale={locale} />
|
<Content locale={locale} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { AstroSeo } from '@astrolib/seo';
|
import { AstroSeo } from "@astrolib/seo";
|
||||||
import {
|
import {
|
||||||
assertSecret,
|
assertSecret,
|
||||||
asset,
|
asset,
|
||||||
@@ -11,17 +11,17 @@ import {
|
|||||||
fetchSkillDefinitions,
|
fetchSkillDefinitions,
|
||||||
fetchTankDefinitions,
|
fetchTankDefinitions,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
|
import { unwrapper } from "@blitzkit/i18n";
|
||||||
import { literals } from '@blitzkit/i18n/src/literals';
|
import { literals } from "@blitzkit/i18n/src/literals";
|
||||||
import type { GetStaticPaths } from 'astro';
|
import type { GetStaticPaths } from "astro";
|
||||||
import { load } from 'cheerio';
|
import { load } from "cheerio";
|
||||||
import { tankCharacteristics } from '../../../../core/blitzkit/tankCharacteristics';
|
import { tankCharacteristics } from "../../../../core/blitzkit/tankCharacteristics";
|
||||||
import { getStrings } from '../../../../core/i18n/getStrings';
|
import { getStrings } from "../../../../core/i18n/getStrings";
|
||||||
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
import BaseLayout from "../../../../layouts/BaseLayout.astro";
|
||||||
import { genericDefaultEquipmentMatrix } from '../../../../stores/duel/constants';
|
import { genericDefaultEquipmentMatrix } from "../../../../stores/duel/constants";
|
||||||
import { getStaticPaths as parentGetStaticPaths } from '../index.astro';
|
import { getStaticPaths as parentGetStaticPaths } from "../index.astro";
|
||||||
import { Page } from './_index';
|
import { Page } from "./_index";
|
||||||
|
|
||||||
export const getStaticPaths = (async () => {
|
export const getStaticPaths = (async () => {
|
||||||
const tankDefinitions = await fetchTankDefinitions();
|
const tankDefinitions = await fetchTankDefinitions();
|
||||||
@@ -32,13 +32,13 @@ export const getStaticPaths = (async () => {
|
|||||||
parentPaths.map((parent) => ({
|
parentPaths.map((parent) => ({
|
||||||
params: { ...parent.params, slug: tank.slug },
|
params: { ...parent.params, slug: tank.slug },
|
||||||
props: { id: tank.id },
|
props: { id: tank.id },
|
||||||
})),
|
}))
|
||||||
)
|
)
|
||||||
.flat();
|
.flat();
|
||||||
}) satisfies GetStaticPaths;
|
}) satisfies GetStaticPaths;
|
||||||
|
|
||||||
const { id } = Astro.props;
|
const { id } = Astro.props;
|
||||||
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
|
const locale = Astro.params.locale;
|
||||||
const strings = getStrings(locale);
|
const strings = getStrings(locale);
|
||||||
const tankDefinitions = await fetchTankDefinitions();
|
const tankDefinitions = await fetchTankDefinitions();
|
||||||
const skillDefinitions = await fetchSkillDefinitions();
|
const skillDefinitions = await fetchSkillDefinitions();
|
||||||
@@ -56,7 +56,7 @@ const title = literals(strings.website.tools.tankopedia.tank_seo_title, [
|
|||||||
tank.nation as keyof typeof strings.common.nations_adjectives
|
tank.nation as keyof typeof strings.common.nations_adjectives
|
||||||
],
|
],
|
||||||
strings.common.tank_class_short[tank.class],
|
strings.common.tank_class_short[tank.class],
|
||||||
'BlitzKit',
|
"BlitzKit",
|
||||||
strings.common.nations[tank.nation as keyof typeof strings.common.nations],
|
strings.common.nations[tank.nation as keyof typeof strings.common.nations],
|
||||||
]);
|
]);
|
||||||
const url = import.meta.env.SITE + Astro.url.pathname;
|
const url = import.meta.env.SITE + Astro.url.pathname;
|
||||||
@@ -87,7 +87,7 @@ const characteristics = tankCharacteristics(
|
|||||||
track: tank.tracks.at(-1)!,
|
track: tank.tracks.at(-1)!,
|
||||||
assaultDistance: 0,
|
assaultDistance: 0,
|
||||||
},
|
},
|
||||||
{ equipmentDefinitions, provisionDefinitions, tankModelDefinition },
|
{ equipmentDefinitions, provisionDefinitions, tankModelDefinition }
|
||||||
);
|
);
|
||||||
const description = literals(
|
const description = literals(
|
||||||
strings.website.tools.tankopedia.tank_seo_description,
|
strings.website.tools.tankopedia.tank_seo_description,
|
||||||
@@ -98,7 +98,7 @@ const description = literals(
|
|||||||
Math.round(characteristics.penetration).toLocaleString(locale),
|
Math.round(characteristics.penetration).toLocaleString(locale),
|
||||||
Math.round(characteristics.speedForwards).toLocaleString(locale),
|
Math.round(characteristics.speedForwards).toLocaleString(locale),
|
||||||
Math.round(characteristics.health).toLocaleString(locale),
|
Math.round(characteristics.health).toLocaleString(locale),
|
||||||
],
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface TankGuide {
|
export interface TankGuide {
|
||||||
@@ -113,8 +113,8 @@ const guideResponse = await fetch(guideUrl);
|
|||||||
|
|
||||||
if (guideResponse.ok) {
|
if (guideResponse.ok) {
|
||||||
const $ = await guideResponse.text().then(load);
|
const $ = await guideResponse.text().then(load);
|
||||||
const description = $('.entry-content p').first().text();
|
const description = $(".entry-content p").first().text();
|
||||||
const image = $('.entry-content img').first().attr('src')?.toString();
|
const image = $(".entry-content img").first().attr("src")?.toString();
|
||||||
|
|
||||||
if (description !== undefined || image !== undefined) {
|
if (description !== undefined || image !== undefined) {
|
||||||
guide = {
|
guide = {
|
||||||
|
|||||||
Reference in new issue
Block a user