This commit is contained in:
tresabhi committed 2026-09-23 17:25:51 -05:00
1 parent 960ca4b0f4
commit 4247004b13
20 files changed
+279 -251

No files matched your search

@@ -22,7 +22,7 @@ import { jsxTree } from "../../../../core/blitzkit/jsxTree";
import { Duel } from "../../../../stores/duel";
import { Tankopedia } from "../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
import { transitionEvent } from "../../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import { transitionEvent } from "../../../Lighting";
import fragmentShader from "./shaders/fragment.glsl?raw";
import vertexShader from "./shaders/vertex.glsl?raw";
import { spacedArmorRenderTarget } from "./target";
@@ -27,7 +27,7 @@ import { discardClippingPlane } from "../../../core/three/discardClippingPlane";
import { useEquipment } from "../../../hooks/useEquipment";
import { Duel } from "../../../stores/duel";
import { Tankopedia } from "../../../stores/tankopedia";
import { transitionEvent } from "../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import { transitionEvent } from "../../Lighting";
import { ArmorType } from "./SpacedArmorScene";
import type {
ArmorUserData,
@@ -1,6 +1,6 @@
import { useFrame } from "@react-three/fiber";
export function AutoClear() {
export function CanvasAutoClear() {
useFrame(({ gl }) => {
gl.clear();
}, 0);
@@ -5,13 +5,13 @@ import type { QuicklimeEvent } from "quicklime";
import { useEffect, useRef } from "react";
import { PerspectiveCamera, Vector3 } from "three";
import { OrbitControls as OrbitControlsClass } from "three-stdlib";
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 { Duel } from "../../../../../../stores/duel";
import { Tankopedia } from "../../../../../../stores/tankopedia";
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 { Tankopedia } from "../stores/tankopedia";
const poseDistances: Record<Pose, number> = {
[Pose.HullDown]: 15,
@@ -29,7 +29,7 @@ interface ControlsProps {
enableRotate?: boolean;
}
export function Controls({
export function CanvasControl({
autoRotate = true,
zoomable = true,
enableRotate = true,
@@ -1,9 +1,9 @@
import { useModel } from "../hooks/useModel";
import { Guess, GuessState } from "../stores/guess";
import { Controls } from "./CanvasControl";
import { Lighting } from "./Lighting";
import { SmartCanvas } from "./SmartCanvas";
import { Controls } from "./Tankopedia/HeroSection/components/TankSandbox/components/Control";
import { Lighting } from "./Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import { TankModel } from "./Tankopedia/HeroSection/components/TankSandbox/components/TankModel";
import { TankModel } from "./TankModel";
const BRIGHTNESS = 2 ** -5.5;
const CONTRAST = 2 ** 2;
@@ -1,13 +1,13 @@
import { useEffect } from "react";
import { degToRad } from "three/src/math/MathUtils.js";
import { api } from "../../../../../../api/dynamic";
import { applyPitchYawLimits } from "../../../../../../core/blitz/applyPitchYawLimits";
import { modelTransformEvent } from "../../../../../../core/blitzkit/modelTransform";
import { Duel } from "../../../../../../stores/duel";
import { api } from "../api/dynamic";
import { applyPitchYawLimits } from "../core/blitz/applyPitchYawLimits";
import { modelTransformEvent } from "../core/blitzkit/modelTransform";
const modelDefinitions = await api.models();
export function InitialAligner() {
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);
@@ -12,9 +12,9 @@ import {
} from "react";
import { HemisphereLight, SpotLight, type Group } from "three";
import { degToRad, lerp } from "three/src/math/MathUtils.js";
import { Tankopedia } from "../../../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
import { TankopediaDisplay } from "../../../../../../stores/tankopediaPersistent/constants";
import { Tankopedia } from "../stores/tankopedia";
import { TankopediaPersistent } from "../stores/tankopediaPersistent";
import { TankopediaDisplay } from "../stores/tankopediaPersistent/constants";
const ANGLE = degToRad(15);
const REVEAL_ANIMATION_TIME = 3;
@@ -4,8 +4,8 @@ import type { QuicklimeEvent } from "quicklime";
import { forwardRef, Suspense, useEffect, useRef } from "react";
import type { OrbitControls as OrbitControlsClass } from "three-stdlib";
import { controlsEnabledEvent } from "../../core/controlsEnabled";
import { Lighting } from "../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import { SceneProps } from "../Tankopedia/HeroSection/components/TankSandbox/components/SceneProps";
import { Lighting } from "../Lighting";
import { SceneProps } from "../SceneProps";
import { MixerFallback } from "./components/MixerFallback";
import { Model } from "./components/Model";
@@ -9,7 +9,7 @@ import {
import { Jimp } from "jimp";
import { useCallback, type RefObject } from "react";
import { useLocale } from "../hooks/useLocale";
import { screenshotReadyEvent } from "./Tankopedia/HeroSection/components/TankSandbox/components/SceneProps";
import { screenshotReadyEvent } from "./SceneProps";
interface Props extends ButtonProps {
canvas: RefObject<HTMLCanvasElement>;
@@ -1,18 +1,18 @@
import { invalidate, useThree, type ThreeEvent } from "@react-three/fiber";
import { useRef } from "react";
import { Group, Mesh, MeshStandardMaterial, Vector2 } from "three";
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 { 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 { TankopediaDisplay } from "../../../../../../stores/tankopediaPersistent/constants";
import { ModelTankWrapper } from "../../../../../Armor/components/ModelTankWrapper";
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 { useModel } from "../hooks/useModel";
import { useTankTransform } from "../hooks/useTankTransform";
import { Tankopedia } from "../stores/tankopedia";
import { TankopediaDisplay } from "../stores/tankopediaPersistent/constants";
import { ModelTankWrapper } from "./Armor/components/ModelTankWrapper";
export function TankModel() {
const protagonist = Duel.use((draft) => draft.protagonist);
@@ -1,200 +0,0 @@
import {
forwardRef,
Suspense,
useEffect,
useImperativeHandle,
useRef,
} from "react";
import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits";
import { modelTransformEvent } from "../../../../../core/blitzkit/modelTransform";
import { Pose, poseEvent } from "../../../../../core/blitzkit/pose";
import { useEquipment } from "../../../../../hooks/useEquipment";
import { useModel } from "../../../../../hooks/useModel";
import { useTankModelDefinition } from "../../../../../hooks/useTankModelDefinition";
import { Duel } from "../../../../../stores/duel";
import { Tankopedia } from "../../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../../stores/tankopediaPersistent";
import { TankopediaDisplay } from "../../../../../stores/tankopediaPersistent/constants";
import { Armor } from "../../../../Armor";
import { ArmorPlateDisplay } from "../../../../Armor/components/ArmorPlateDisplay";
import { ShotDisplay } from "../../../../Armor/components/ShotDisplay";
import {
StaticArmor,
type ThicknessRange,
} from "../../../../Armor/components/StaticArmor";
import { SmartCanvas } from "../../../../SmartCanvas";
import { AutoClear } from "./components/AutoClear";
import { Controls } from "./components/Control";
import { InitialAligner } from "./components/InitialAligner";
import { Lighting } from "./components/Lighting";
import { SceneProps } from "./components/SceneProps";
import { TankModel } from "./components/TankModel";
interface TankSandboxProps {
thicknessRange: ThicknessRange;
}
export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
({ thicknessRange }, ref) => {
const canvas = useRef<HTMLCanvasElement>(null);
const hasImprovedVerticalStabilizer = useEquipment(122);
const hasDownImprovedVerticalStabilizer = useEquipment(124);
const protagonistGun = Duel.use((state) => state.protagonist.gun);
const protagonistTurret = Duel.use((state) => state.protagonist.turret);
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition =
tankModelDefinition.turrets[protagonistTurret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
const display = Tankopedia.use((state) => state.display);
const hideTankModelUnderArmor = TankopediaPersistent.use(
(state) => state.hideTankModelUnderArmor,
);
const id = Duel.use((state) => state.protagonist.tank.id);
const { hasPbr } = useModel(id);
useImperativeHandle(ref, () => canvas.current!, []);
function handlePointerDown() {
window.addEventListener("pointermove", handlePointerMove);
window.addEventListener("pointerup", handlePointerUp);
}
function handlePointerMove(event: PointerEvent) {
event.preventDefault();
}
function handlePointerUp(event: PointerEvent) {
event.preventDefault();
window.removeEventListener("pointermove", handlePointerMove);
window.removeEventListener("pointerup", handlePointerUp);
}
useEffect(() => {
function handlePoseEvent(pose: Pose) {
switch (pose) {
case Pose.HullDown: {
const [pitch, yaw] = applyPitchYawLimits(
-Infinity,
0,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
break;
}
case Pose.FaceHug: {
const [pitch, yaw] = applyPitchYawLimits(
Infinity,
0,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
break;
}
case Pose.Default:
const [pitch, yaw] = applyPitchYawLimits(
0,
0,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
break;
}
}
poseEvent.on(handlePoseEvent);
return () => {
poseEvent.off(handlePoseEvent);
};
});
useEffect(() => {
const [pitch, yaw] = applyPitchYawLimits(
modelTransformEvent.last!.pitch,
modelTransformEvent.last!.yaw,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
}, [protagonistGun, protagonistTurret]);
useEffect(() => {
if (display !== TankopediaDisplay.DynamicArmor) {
Tankopedia.mutate((draft) => {
draft.shot = undefined;
});
}
if (display !== TankopediaDisplay.StaticArmor) {
Tankopedia.mutate((draft) => {
draft.highlightArmor = undefined;
});
}
}, [display]);
return (
<SmartCanvas
resize={{ debounce: 0 }}
ref={canvas}
gl={{
clippingPlanes: [],
localClippingEnabled: true,
preserveDrawingBuffer: true,
}}
shadows="soft"
onPointerDown={handlePointerDown}
onPointerMissed={() => {
Tankopedia.mutate((draft) => {
draft.shot = undefined;
draft.highlightArmor = undefined;
});
}}
style={{
userSelect: "none",
width: "100%",
height: "100%",
outline: undefined,
}}
>
<Lighting hasPbr={hasPbr} />
<SceneProps />
{(display === TankopediaDisplay.Model ||
(display === TankopediaDisplay.DynamicArmor &&
!hideTankModelUnderArmor)) && <TankModel />}
<ShotDisplay />
<ArmorPlateDisplay />
<AutoClear />
<Suspense>
{/* Controls within Suspense to allow for frame-perfect start of camera auto-rotate */}
<Controls />
{display === TankopediaDisplay.DynamicArmor && <Armor />}
{display === TankopediaDisplay.StaticArmor && (
<StaticArmor thicknessRange={thicknessRange} />
)}
</Suspense>
<InitialAligner />
</SmartCanvas>
);
},
);
@@ -1,10 +1,10 @@
import type { Consumable } from "@blitzkit/core";
import { TrashIcon } from "@radix-ui/react-icons";
import { useTankCompatibility } from "../../hooks/useTankCompatibility";
import { useConsumables } from "../../hooks/useConsumables";
import { useProtagonist } from "../../hooks/useProtagonist";
import { useProtagonistGun } from "../../hooks/useProtagonistGun";
import { useProtagonistTank } from "../../hooks/useProtagonistTank";
import { useStrings } from "../../hooks/useStrings";
import { useTankCompatibility } from "../../hooks/useTankCompatibility";
import { Tankopedia } from "../../stores/tankopedia";
import { hasUpgrades } from "../../tankopedia/hasUpgrades";
import { vehicleStatusKeys } from "../../tankopedia/tankState";
@@ -19,7 +19,7 @@ interface TankopediaLoadoutProps {
}
export function TankopediaLoadout({ characteristics }: TankopediaLoadoutProps) {
const tank = useProtagonist();
const tank = useProtagonistTank();
const showModules = hasUpgrades(tank);
return (
@@ -120,7 +120,7 @@ interface ConsumablesProps {
function Consumables({ characteristics }: ConsumablesProps) {
const strings = useStrings();
const consumables = useConsumables();
const tank = useProtagonist();
const tank = useProtagonistTank();
const gun = useProtagonistGun();
const isCompatible = useTankCompatibility(tank, gun);
@@ -165,7 +165,7 @@ function Consumable({ id, consumable }: ConsumableProps) {
const isSelected = Tankopedia.use((state) =>
state.protagonist.consumables.includes(id),
);
const tank = useProtagonist();
const tank = useProtagonistTank();
const unwrap = useUnwrap();
return (
@@ -214,7 +214,7 @@ function Consumable({ id, consumable }: ConsumableProps) {
function Equipment() {
const strings = useStrings();
const tank = useProtagonist();
const tank = useProtagonistTank();
const equipment = useEquipment(tank.tank!);
return (
@@ -273,7 +273,7 @@ interface EquipmentSlotProps {
function EquipmentSlot({ slot, rowIndex, columnIndex }: EquipmentSlotProps) {
const equipmentIndex = rowIndex * equipmentColumns + columnIndex;
const tank = useProtagonist();
const tank = useProtagonistTank();
const equipment = useEquipment(tank.tank!);
const { price } = equipment.price.unlock_slot_prices[equipmentIndex];
@@ -307,7 +307,7 @@ function EquipmentOption({
columnIndex,
}: EquipmentOptionProps) {
const equipmentIndex = rowIndex * equipmentColumns + columnIndex;
const tank = useProtagonist();
const tank = useProtagonistTank();
const equipment = useEquipment(tank.tank!);
const slot = equipment.preset.slots[equipmentIndex];
const id = slot.options_catalog_i_ds[optionIndex];
@@ -346,7 +346,7 @@ function EquipmentOption({
function Modules() {
const strings = useStrings();
const tank = useProtagonist();
const tank = useProtagonistTank();
const upgrades = Tankopedia.use((state) => state.protagonist.upgrades);
const alternates = Tankopedia.use((state) => state.protagonist.alternates);
const isStock = useMemo(() => {
@@ -496,7 +496,7 @@ interface LineElementProps {
function LineElement({ index, lineName, stage }: LineElementProps) {
const upgrades = Tankopedia.use((state) => state.protagonist.upgrades);
const alternates = Tankopedia.use((state) => state.protagonist.alternates);
const tank = useProtagonist();
const tank = useProtagonistTank();
const strings = useStrings();
const gameStrings = useGameStrings("TankEntity");
const upgradePreset = useUpgradePreset(tank.tank!.tank_upgrade_preset);
@@ -0,0 +1,16 @@
.wrapper {
height: calc(100dvh - 16rem);
position: relative;
.sandbox {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.canvas {
background-color: black;
}
}
}
@@ -0,0 +1,202 @@
import {
forwardRef,
Suspense,
useEffect,
useImperativeHandle,
useRef,
} from "react";
import { applyPitchYawLimits } from "../../core/blitz/applyPitchYawLimits";
import { modelTransformEvent } from "../../core/blitzkit/modelTransform";
import { Pose, poseEvent } from "../../core/blitzkit/pose";
import { useEquipment } from "../../hooks/useEquipment";
import { useModel } from "../../hooks/useModel";
import { useProtagonistGun } from "../../hooks/useProtagonistGun";
import { useProtagonistTank } from "../../hooks/useProtagonistTank";
import { useProtagonistTurret } from "../../hooks/useProtagonistTurret";
import { Tankopedia } from "../../stores/tankopedia";
import { TankopediaPersistent } from "../../stores/tankopediaPersistent";
import { TankopediaDisplay } from "../../stores/tankopediaPersistent/constants";
import { Armor } from "../Armor";
import { ArmorPlateDisplay } from "../Armor/components/ArmorPlateDisplay";
import { ShotDisplay } from "../Armor/components/ShotDisplay";
import {
StaticArmor,
type ThicknessRange,
} from "../Armor/components/StaticArmor";
import { CanvasAutoClear } from "../CanvasAutoClear";
import { CanvasControl } from "../CanvasControl";
import { InitialCameraAligner } from "../InitialCameraAligner";
import { Lighting } from "../Lighting";
import { SceneProps } from "../SceneProps";
import { SmartCanvas } from "../SmartCanvas";
import { TankModel } from "../TankModel";
import styles from "./index.module.css";
interface TankSandboxProps {
thicknessRange: ThicknessRange;
}
export const TankopediaSandbox = forwardRef<
HTMLCanvasElement,
TankSandboxProps
>(({ thicknessRange }, ref) => {
const canvas = useRef<HTMLCanvasElement>(null);
const hasImprovedVerticalStabilizer = useEquipment(122);
const hasDownImprovedVerticalStabilizer = useEquipment(124);
const tank = useProtagonistTank();
const gun = useProtagonistGun();
const turret = useProtagonistTurret();
const model = Tankopedia.use((state) => state.protagonist.model);
const turretModelDefinition = model.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id];
const display = Tankopedia.use((state) => state.display);
const hideTankModelUnderArmor = TankopediaPersistent.use(
(state) => state.hideTankModelUnderArmor,
);
const { hasPbr } = useModel(tank.id);
useImperativeHandle(ref, () => canvas.current!, []);
function handlePointerDown() {
window.addEventListener("pointermove", handlePointerMove);
window.addEventListener("pointerup", handlePointerUp);
}
function handlePointerMove(event: PointerEvent) {
event.preventDefault();
}
function handlePointerUp(event: PointerEvent) {
event.preventDefault();
window.removeEventListener("pointermove", handlePointerMove);
window.removeEventListener("pointerup", handlePointerUp);
}
useEffect(() => {
function handlePoseEvent(pose: Pose) {
switch (pose) {
case Pose.HullDown: {
const [pitch, yaw] = applyPitchYawLimits(
-Infinity,
0,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
break;
}
case Pose.FaceHug: {
const [pitch, yaw] = applyPitchYawLimits(
Infinity,
0,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
break;
}
case Pose.Default:
const [pitch, yaw] = applyPitchYawLimits(
0,
0,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
break;
}
}
poseEvent.on(handlePoseEvent);
return () => {
poseEvent.off(handlePoseEvent);
};
});
useEffect(() => {
const [pitch, yaw] = applyPitchYawLimits(
modelTransformEvent.last!.pitch,
modelTransformEvent.last!.yaw,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
}, [gun, turret]);
useEffect(() => {
if (display !== TankopediaDisplay.DynamicArmor) {
Tankopedia.mutate((draft) => {
draft.shot = undefined;
});
}
if (display !== TankopediaDisplay.StaticArmor) {
Tankopedia.mutate((draft) => {
draft.highlightArmor = undefined;
});
}
}, [display]);
return (
<div className={styles.wrapper}>
<SmartCanvas
className={styles.canvas}
resize={{ debounce: 0 }}
ref={canvas}
gl={{
clippingPlanes: [],
localClippingEnabled: true,
preserveDrawingBuffer: true,
}}
shadows="soft"
onPointerDown={handlePointerDown}
onPointerMissed={() => {
Tankopedia.mutate((draft) => {
draft.shot = undefined;
draft.highlightArmor = undefined;
});
}}
>
<Lighting hasPbr={hasPbr} />
<SceneProps />
{(display === TankopediaDisplay.Model ||
(display === TankopediaDisplay.DynamicArmor &&
!hideTankModelUnderArmor)) && <TankModel />}
<ShotDisplay />
<ArmorPlateDisplay />
<CanvasAutoClear />
<Suspense>
{/* Controls within Suspense to allow for frame-perfect start of camera auto-rotate */}
<CanvasControl />
{display === TankopediaDisplay.DynamicArmor && <Armor />}
{display === TankopediaDisplay.StaticArmor && (
<StaticArmor thicknessRange={thicknessRange} />
)}
</Suspense>
<InitialCameraAligner />
</SmartCanvas>
</div>
);
});
+14
View File
@@ -0,0 +1,14 @@
import { degToRad } from "three/src/math/MathUtils.js";
export const fov = 20;
export const initialCameraR = 15;
export const initialCameraPhi = degToRad(90 - 10);
export const initialCameraTheta = degToRad(90 + 15);
export const cameraSwingVerticalAmplitude = degToRad(5);
export const cameraSwingVerticalFrequency = Math.E ** -6;
export const cameraSwingHorizontalAmplitude = degToRad(30);
export const cameraSwingHorizontalFrequency = Math.E ** -5;
export const cameraCenterCorrectionAmplitude = 0.8;
@@ -2,7 +2,7 @@ import { api } from "../api/dynamic";
import { Tankopedia } from "../stores/tankopedia";
import { useAwait } from "./useAwait";
export function useProtagonist() {
export function useProtagonistTank() {
const id = Tankopedia.use((state) => state.protagonist.tank);
const tank = useAwait(() => api.tank(id), `tank-${id}`);
return tank;
@@ -1,8 +1,8 @@
import { Tankopedia } from "../stores/tankopedia";
import { useProtagonist } from "./useProtagonist";
import { useProtagonistTank } from "./useProtagonistTank";
export function useProtagonistTurret() {
const id = Tankopedia.use((state) => state.protagonist.gun);
const tank = useProtagonist();
const tank = useProtagonistTank();
return tank.turrets[id];
}
@@ -1,5 +0,0 @@
import { Tankopedia } from "../stores/tankopedia";
export function useTankModelDefinition() {
return Tankopedia.use((state) => state.model);
}
@@ -1,6 +1,7 @@
import { useMemo } from "react";
import { api } from "../../../../../api/dynamic";
import { TankopediaLoadout } from "../../../../../components/TankopediaLoadout";
import { TankopediaSandbox } from "../../../../../components/TankopediaSandbox";
import { withErrorWrapper } from "../../../../../hocs/withErrorWrapper";
import { withLocale } from "../../../../../hocs/withLocale";
import { useAwait } from "../../../../../hooks/useAwait";