mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
feat: persistant tankopedia
This commit is contained in:
1 parent
f51e365ec4
commit
a70100c835
17 files changed
+156
-126
No files matched your search
@@ -25,7 +25,7 @@ import {
|
||||
SHELL_NAMES,
|
||||
tankNamesDiacritics,
|
||||
} from '../../../../../../core/blitzkrieg/tankDefinitions';
|
||||
import { useTankopedia } from '../../../../../../stores/tankopedia';
|
||||
import { useTankopediaTemporary } from '../../../../../../stores/tankopedia';
|
||||
import { Duel } from '../../page';
|
||||
import { SearchResults } from './components/SearchResults';
|
||||
|
||||
@@ -38,7 +38,7 @@ export function AntagonistBar({
|
||||
duel: { antagonist },
|
||||
setDuel,
|
||||
}: AntagonistBarProps) {
|
||||
const mode = useTankopedia((state) => state.mode);
|
||||
const mode = useTankopediaTemporary((state) => state.mode);
|
||||
const awaitedTankNamesDiacritics = use(tankNamesDiacritics);
|
||||
const [tab, setTab] = useState('search');
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -3,8 +3,9 @@ import { produce } from 'immer';
|
||||
import { Dispatch, SetStateAction, use } from 'react';
|
||||
import { ModuleButton } from '../../../../../components/ModuleButton';
|
||||
import { equipmentDefinitions } from '../../../../../core/blitzkrieg/equipmentDefinitions';
|
||||
import mutateTankopedia, {
|
||||
useTankopedia,
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../../../stores/tankopedia';
|
||||
import { Duel } from '../page';
|
||||
|
||||
@@ -17,7 +18,9 @@ export function Configure({ duel, setDuel }: ConfigureProps) {
|
||||
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
||||
const equipmentRows =
|
||||
awaitedEquipmentDefinitions.presets[duel.protagonist.tank.equipment];
|
||||
const equipmentMatrix = useTankopedia((state) => state.equipmentMatrix);
|
||||
const equipmentMatrix = useTankopediaTemporary(
|
||||
(state) => state.equipmentMatrix,
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex gap="4" direction="column">
|
||||
@@ -97,7 +100,7 @@ export function Configure({ duel, setDuel }: ConfigureProps) {
|
||||
rowChild
|
||||
selected={equipmentMatrix[rowIndex][columnIndex] === 0}
|
||||
onClick={() => {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.equipmentMatrix[rowIndex][columnIndex] = 0;
|
||||
});
|
||||
}}
|
||||
@@ -109,7 +112,7 @@ export function Configure({ duel, setDuel }: ConfigureProps) {
|
||||
rowChild
|
||||
selected={equipmentMatrix[rowIndex][columnIndex] === 1}
|
||||
onClick={() => {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.equipmentMatrix[rowIndex][columnIndex] = 1;
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
||||
import { Pose, poseEvent } from '../../../../../core/blitzkrieg/pose';
|
||||
import { useTankopedia } from '../../../../../stores/tankopedia';
|
||||
import { useTankopediaTemporary } from '../../../../../stores/tankopedia';
|
||||
import { Duel } from '../page';
|
||||
|
||||
const poseDistances: Record<Pose, number> = {
|
||||
@@ -55,7 +55,7 @@ export function Controls({ duel }: ControlsProps) {
|
||||
camera.position.set(-4, 4, -16);
|
||||
orbitControls.current?.target.set(0, antagonistGunHeight / 2, 0);
|
||||
|
||||
const unsubscribeTankopedia = useTankopedia.subscribe(
|
||||
const unsubscribeTankopedia = useTankopediaTemporary.subscribe(
|
||||
(state) => state.model.visual.controlsEnabled,
|
||||
(enabled) => {
|
||||
if (orbitControls.current) orbitControls.current.enabled = enabled;
|
||||
@@ -144,7 +144,7 @@ export function Controls({ duel }: ControlsProps) {
|
||||
return (
|
||||
<OrbitControls
|
||||
ref={orbitControls}
|
||||
enabled={useTankopedia.getState().model.visual.controlsEnabled}
|
||||
enabled={useTankopediaTemporary.getState().model.visual.controlsEnabled}
|
||||
rotateSpeed={0.25}
|
||||
enableDamping={false}
|
||||
/>
|
||||
|
||||
@@ -7,9 +7,10 @@ import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinit
|
||||
import { modelTransformEvent } from '../../../../../../core/blitzkrieg/modelTransform';
|
||||
import { Pose, poseEvent } from '../../../../../../core/blitzkrieg/pose';
|
||||
import { theme } from '../../../../../../stitches.config';
|
||||
import mutateTankopedia, {
|
||||
import {
|
||||
TankopediaMode,
|
||||
useTankopedia,
|
||||
mutateTankopediaTemporary,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../../../../stores/tankopedia';
|
||||
import { Loader } from '../../../../components/Loader';
|
||||
import { Duel } from '../../page';
|
||||
@@ -30,7 +31,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
const awaitedModelDefinitions = use(modelDefinitions);
|
||||
const canvasWrapper = useRef<HTMLDivElement>(null);
|
||||
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||
const mode = useTankopedia((state) => state.mode);
|
||||
const mode = useTankopediaTemporary((state) => state.mode);
|
||||
const tankModelDefinition = awaitedModelDefinitions[duel.protagonist.tank.id];
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[duel.protagonist.turret.id];
|
||||
@@ -59,7 +60,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
function handlePoseEvent(pose: Pose) {
|
||||
switch (pose) {
|
||||
case Pose.HullDown: {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
-Infinity,
|
||||
0,
|
||||
@@ -76,7 +77,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
}
|
||||
|
||||
case Pose.FaceHug: {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
Infinity,
|
||||
0,
|
||||
@@ -109,7 +110,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
useEffect(() => {
|
||||
if (!duel.protagonist) return void 0;
|
||||
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
[draft.model.physical.pitch, draft.model.physical.yaw] =
|
||||
applyPitchYawLimits(
|
||||
draft.model.physical.pitch,
|
||||
@@ -141,7 +142,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
<Tabs.Root
|
||||
value={mode}
|
||||
onValueChange={(mode) => {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.mode = mode as TankopediaMode;
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
import { Button, DropdownMenu, Flex } from '@radix-ui/themes';
|
||||
import { RefObject } from 'react';
|
||||
import { Pose, poseEvent } from '../../../../../../../core/blitzkrieg/pose';
|
||||
import mutateTankopedia, {
|
||||
useTankopedia,
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../../../../../stores/tankopedia';
|
||||
|
||||
interface OptionsProps {
|
||||
@@ -17,8 +18,10 @@ interface OptionsProps {
|
||||
}
|
||||
|
||||
export function Options({ isFullScreen, canvas }: OptionsProps) {
|
||||
const showGrid = useTankopedia((state) => state.model.visual.showGrid);
|
||||
const greenPenetration = useTankopedia(
|
||||
const showGrid = useTankopediaTemporary(
|
||||
(state) => state.model.visual.showGrid,
|
||||
);
|
||||
const greenPenetration = useTankopediaTemporary(
|
||||
(state) => state.model.visual.greenPenetration,
|
||||
);
|
||||
const fullScreenAvailable = document.fullscreenEnabled;
|
||||
@@ -65,7 +68,7 @@ export function Options({ isFullScreen, canvas }: OptionsProps) {
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={showGrid}
|
||||
onCheckedChange={(checked) => {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.visual.showGrid = checked;
|
||||
});
|
||||
}}
|
||||
@@ -76,7 +79,7 @@ export function Options({ isFullScreen, canvas }: OptionsProps) {
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={greenPenetration}
|
||||
onCheckedChange={(checked) => {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.visual.greenPenetration = checked;
|
||||
});
|
||||
}}
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@ import { nameToArmorId } from '../../../../../../../../../core/blitzkrieg/nameTo
|
||||
import { resolveArmor } from '../../../../../../../../../core/blitzkrieg/resolveThickness';
|
||||
import { useArmor } from '../../../../../../../../../hooks/useArmor';
|
||||
import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions';
|
||||
import { useTankopedia } from '../../../../../../../../../stores/tankopedia';
|
||||
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
|
||||
import { Duel } from '../../../../../page';
|
||||
|
||||
interface ArmorHighlightingProps {
|
||||
@@ -27,7 +27,7 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
|
||||
const modelDefinitions = useModelDefinitions();
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const initialTankopediaState = useTankopedia.getState();
|
||||
const initialTankopediaState = useTankopediaTemporary.getState();
|
||||
|
||||
useEffect(() => {
|
||||
const turretOrigin = new Vector3(
|
||||
@@ -86,7 +86,7 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
|
||||
turretContainer.current?.rotation.copy(turretRotation);
|
||||
}
|
||||
|
||||
handleModelTransform(useTankopedia.getState().model.physical);
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.physical);
|
||||
modelTransformEvent.on(handleModelTransform);
|
||||
|
||||
return () => {
|
||||
@@ -95,7 +95,7 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = useTankopedia.subscribe(
|
||||
const unsubscribe = useTankopediaTemporary.subscribe(
|
||||
(state) => state.mode,
|
||||
(mode) => {
|
||||
if (wrapper.current) wrapper.current.visible = mode === 'armor';
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ import { resolveArmor } from '../../../../../../../../../core/blitzkrieg/resolve
|
||||
import { useArmor } from '../../../../../../../../../hooks/useArmor';
|
||||
import { useModel } from '../../../../../../../../../hooks/useModel';
|
||||
import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions';
|
||||
import { useTankopedia } from '../../../../../../../../../stores/tankopedia';
|
||||
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
|
||||
import { Duel } from '../../../../../page';
|
||||
|
||||
interface ExternalModuleMaskProps {
|
||||
@@ -35,7 +35,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
|
||||
const modelDefinitions = useModelDefinitions();
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const initialTankopediaState = useTankopedia.getState();
|
||||
const initialTankopediaState = useTankopediaTemporary.getState();
|
||||
|
||||
useEffect(() => {
|
||||
const turretOrigin = new Vector3(
|
||||
@@ -94,7 +94,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
|
||||
turretContainer.current?.rotation.copy(turretRotation);
|
||||
}
|
||||
|
||||
handleModelTransform(useTankopedia.getState().model.physical);
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.physical);
|
||||
modelTransformEvent.on(handleModelTransform);
|
||||
|
||||
return () => {
|
||||
@@ -103,7 +103,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = useTankopedia.subscribe(
|
||||
const unsubscribe = useTankopediaTemporary.subscribe(
|
||||
(state) => state.mode,
|
||||
(mode) => {
|
||||
if (wrapper.current) wrapper.current.visible = mode === 'armor';
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ import { resolveArmor } from '../../../../../../../../../core/blitzkrieg/resolve
|
||||
import { useArmor } from '../../../../../../../../../hooks/useArmor';
|
||||
import { useModel } from '../../../../../../../../../hooks/useModel';
|
||||
import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions';
|
||||
import { useTankopedia } from '../../../../../../../../../stores/tankopedia';
|
||||
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
|
||||
import { Duel } from '../../../../../page';
|
||||
|
||||
interface SpacedArmorDepthProps {
|
||||
@@ -39,7 +39,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
|
||||
const modelDefinitions = useModelDefinitions();
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const initialTankopediaState = useTankopedia.getState();
|
||||
const initialTankopediaState = useTankopediaTemporary.getState();
|
||||
|
||||
useEffect(() => {
|
||||
if (!modelDefinitions) return;
|
||||
@@ -100,7 +100,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
|
||||
turretContainer.current?.rotation.copy(turretRotation);
|
||||
}
|
||||
|
||||
handleModelTransform(useTankopedia.getState().model.physical);
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.physical);
|
||||
modelTransformEvent.on(handleModelTransform);
|
||||
|
||||
return () => {
|
||||
@@ -109,7 +109,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = useTankopedia.subscribe(
|
||||
const unsubscribe = useTankopediaTemporary.subscribe(
|
||||
(state) => state.mode,
|
||||
(mode) => {
|
||||
if (wrapper.current) wrapper.current.visible = mode === 'armor';
|
||||
|
||||
@@ -22,8 +22,9 @@ import {
|
||||
import { resolveJsxTree } from '../../../../../../../core/blitzkrieg/resolveJsxTree';
|
||||
import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180';
|
||||
import { useModel } from '../../../../../../../hooks/useModel';
|
||||
import mutateTankopedia, {
|
||||
useTankopedia,
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../../../../../stores/tankopedia';
|
||||
import { Duel } from '../../../page';
|
||||
|
||||
@@ -37,7 +38,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
>(undefined);
|
||||
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const physical = useTankopedia((state) => state.model.physical);
|
||||
const physical = useTankopediaTemporary((state) => state.model.physical);
|
||||
const hullContainer = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
@@ -136,7 +137,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
event.stopPropagation();
|
||||
|
||||
if (isTrack) {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.visual.controlsEnabled = false;
|
||||
});
|
||||
|
||||
@@ -158,7 +159,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
material.normalMap?.offset.add(offset);
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.visual.controlsEnabled = true;
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
@@ -200,7 +201,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
yaw = physical.yaw;
|
||||
pitch = physical.pitch;
|
||||
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.visual.controlsEnabled = false;
|
||||
});
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
@@ -217,7 +218,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateTankopedia((state) => {
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.visual.controlsEnabled = true;
|
||||
state.model.physical.pitch = normalizeAnglePI(pitch);
|
||||
state.model.physical.yaw = normalizeAnglePI(yaw);
|
||||
@@ -262,7 +263,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
|
||||
event.stopPropagation();
|
||||
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.visual.controlsEnabled = false;
|
||||
});
|
||||
pitch = physical.pitch;
|
||||
@@ -280,7 +281,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateTankopedia((state) => {
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.visual.controlsEnabled = true;
|
||||
state.model.physical.pitch = normalizeAnglePI(pitch);
|
||||
state.model.physical.yaw = normalizeAnglePI(yaw);
|
||||
|
||||
@@ -3,8 +3,9 @@ import { use, useEffect, useRef } from 'react';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
||||
import { modelTransformEvent } from '../../../../../core/blitzkrieg/modelTransform';
|
||||
import mutateTankopedia, {
|
||||
useTankopedia,
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../../../stores/tankopedia';
|
||||
import { Duel } from '../page';
|
||||
|
||||
@@ -13,7 +14,7 @@ interface RotationInputsProps {
|
||||
}
|
||||
|
||||
export function RotationInputs({ duel: { protagonist } }: RotationInputsProps) {
|
||||
const physical = useTankopedia((state) => state.model.physical);
|
||||
const physical = useTankopediaTemporary((state) => state.model.physical);
|
||||
const awaitedModelDefinitions = use(modelDefinitions);
|
||||
const turretYawInput = useRef<HTMLInputElement>(null);
|
||||
const gunPitchInput = useRef<HTMLInputElement>(null);
|
||||
@@ -67,7 +68,7 @@ export function RotationInputs({ duel: { protagonist } }: RotationInputsProps) {
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateTankopedia((state) => {
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.physical.pitch = pitch;
|
||||
state.model.physical.yaw = yaw;
|
||||
});
|
||||
@@ -103,7 +104,7 @@ export function RotationInputs({ duel: { protagonist } }: RotationInputsProps) {
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateTankopedia((state) => {
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.physical.pitch = pitch;
|
||||
state.model.physical.yaw = yaw;
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Color } from 'three';
|
||||
import InfiniteGridHelper from '../../../../../components/InfiniteGridHelper';
|
||||
import { useTankopedia } from '../../../../../stores/tankopedia';
|
||||
import { useTankopediaTemporary } from '../../../../../stores/tankopedia';
|
||||
|
||||
export function SceneProps() {
|
||||
const showGrid = useTankopedia((state) => state.model.visual.showGrid);
|
||||
const showGrid = useTankopediaTemporary(
|
||||
(state) => state.model.visual.showGrid,
|
||||
);
|
||||
|
||||
if (!showGrid) return null;
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ import { produce } from 'immer';
|
||||
import { use, useEffect, useState } from 'react';
|
||||
import PageWrapper from '../../../../components/PageWrapper';
|
||||
import { tankDefinitions } from '../../../../core/blitzkrieg/tankDefinitions';
|
||||
import mutateTankopedia, { DuelMember } from '../../../../stores/tankopedia';
|
||||
import {
|
||||
DuelMember,
|
||||
mutateTankopediaTemporary,
|
||||
} from '../../../../stores/tankopedia';
|
||||
import { AntagonistBar } from './components/AntagonistBar';
|
||||
import { Configure } from './components/Configure';
|
||||
import { TankSandbox } from './components/Model/TankSandbox';
|
||||
@@ -35,7 +38,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.physical.yaw = 0;
|
||||
draft.model.physical.pitch = 0;
|
||||
draft.mode = 'model';
|
||||
|
||||
@@ -16,15 +16,15 @@ import {
|
||||
TIERS,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
||||
import mutateTankopedia, {
|
||||
import mutateTankopediaPersistent, {
|
||||
TankopediaTestTankDisplay,
|
||||
useTankopedia,
|
||||
useTankopediaPersistent,
|
||||
} from '../../../../../stores/tankopedia';
|
||||
import * as styles from './index.css';
|
||||
|
||||
export function Options() {
|
||||
const nations = use(NATIONS);
|
||||
const filters = useTankopedia((state) => state.filters);
|
||||
const filters = useTankopediaPersistent((state) => state.filters);
|
||||
|
||||
return (
|
||||
<Flex justify="center" direction="column" gap="2">
|
||||
@@ -32,13 +32,14 @@ export function Options() {
|
||||
<Button
|
||||
color="red"
|
||||
onClick={() =>
|
||||
useTankopedia.setState({
|
||||
useTankopediaPersistent.setState({
|
||||
filters: {
|
||||
nations: [],
|
||||
tiers: [],
|
||||
treeTypes: [],
|
||||
types: [],
|
||||
test: 'include',
|
||||
page: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -56,7 +57,7 @@ export function Options() {
|
||||
<DropdownMenu.RadioGroup
|
||||
value={filters.test}
|
||||
onValueChange={(value) =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.filters.test = value as TankopediaTestTankDisplay;
|
||||
})
|
||||
}
|
||||
@@ -88,7 +89,7 @@ export function Options() {
|
||||
index === TREE_TYPES.length - 1 ? undefined : 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.treeTypes.includes(type)) {
|
||||
draft.filters.treeTypes = draft.filters.treeTypes.filter(
|
||||
(preexistingType) => preexistingType !== type,
|
||||
@@ -123,7 +124,7 @@ export function Options() {
|
||||
index === TANK_TYPES.length - 1 ? undefined : 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.types.includes(type)) {
|
||||
draft.filters.types = draft.filters.types.filter(
|
||||
(preexistingType) => preexistingType !== type,
|
||||
@@ -167,7 +168,7 @@ export function Options() {
|
||||
borderBottomRightRadius: 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.tiers.includes(tier)) {
|
||||
draft.filters.tiers = draft.filters.tiers.filter(
|
||||
(preexistingTier) => preexistingTier !== tier,
|
||||
@@ -203,7 +204,7 @@ export function Options() {
|
||||
: 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.tiers.includes(tier)) {
|
||||
draft.filters.tiers = draft.filters.tiers.filter(
|
||||
(preexistingTier) => preexistingTier !== tier,
|
||||
@@ -243,7 +244,7 @@ export function Options() {
|
||||
index === nations.length - 1 ? undefined : 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.nations.includes(nation)) {
|
||||
draft.filters.nations = draft.filters.nations.filter(
|
||||
(preexistingType) => preexistingType !== nation,
|
||||
@@ -279,7 +280,7 @@ export function Options() {
|
||||
: 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.nations.includes(nation)) {
|
||||
draft.filters.nations = draft.filters.nations.filter(
|
||||
(preexistingType) => preexistingType !== nation,
|
||||
@@ -315,7 +316,7 @@ export function Options() {
|
||||
index === TIERS.length - 1 ? undefined : 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.tiers.includes(tier)) {
|
||||
draft.filters.tiers = draft.filters.tiers.filter(
|
||||
(preexistingTier) => preexistingTier !== tier,
|
||||
@@ -347,7 +348,7 @@ export function Options() {
|
||||
index === nations.length - 1 ? undefined : 0,
|
||||
}}
|
||||
onClick={() =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
if (draft.filters.nations.includes(nation)) {
|
||||
draft.filters.nations = draft.filters.nations.filter(
|
||||
(preexistingType) => preexistingType !== nation,
|
||||
|
||||
@@ -2,20 +2,17 @@ import { CaretLeftIcon, CaretRightIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Flex, TextField } from '@radix-ui/themes';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { TankDefinition } from '../../../../core/blitzkrieg/tankDefinitions';
|
||||
import mutateTankopediaPersistent, {
|
||||
useTankopediaPersistent,
|
||||
} from '../../../../stores/tankopedia';
|
||||
|
||||
interface PageTurnerProps {
|
||||
page: number;
|
||||
setPage: (page: number) => void;
|
||||
searchedList: TankDefinition[];
|
||||
tanksPerPage: number;
|
||||
}
|
||||
|
||||
export function PageTurner({
|
||||
page,
|
||||
setPage,
|
||||
searchedList,
|
||||
tanksPerPage,
|
||||
}: PageTurnerProps) {
|
||||
export function PageTurner({ searchedList, tanksPerPage }: PageTurnerProps) {
|
||||
const page = useTankopediaPersistent((state) => state.filters.page);
|
||||
const pageInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -27,7 +24,11 @@ export function PageTurner({
|
||||
<Button
|
||||
variant="soft"
|
||||
disabled={page === 0}
|
||||
onClick={() => setPage(Math.max(0, page - 1))}
|
||||
onClick={() => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.filters.page = Math.max(0, page - 1);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<CaretLeftIcon />
|
||||
</Button>
|
||||
@@ -41,15 +42,15 @@ export function PageTurner({
|
||||
max={Math.floor(searchedList.length / tanksPerPage) + 1}
|
||||
style={{ width: 64, textAlign: 'center' }}
|
||||
onBlur={(event) => {
|
||||
setPage(
|
||||
Math.max(
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.filters.page = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
Math.floor(searchedList.length / tanksPerPage),
|
||||
event.target.valueAsNumber - 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
});
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
@@ -64,11 +65,14 @@ export function PageTurner({
|
||||
<Button
|
||||
variant="soft"
|
||||
disabled={Math.floor(searchedList.length / tanksPerPage) === page}
|
||||
onClick={() =>
|
||||
setPage(
|
||||
Math.min(Math.floor(searchedList.length / tanksPerPage), page + 1),
|
||||
)
|
||||
}
|
||||
onClick={() => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.filters.page = Math.min(
|
||||
Math.floor(searchedList.length / tanksPerPage),
|
||||
page + 1,
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<CaretRightIcon />
|
||||
</Button>
|
||||
|
||||
@@ -26,10 +26,10 @@ import {
|
||||
} from '../../../core/blitzkrieg/tankDefinitions';
|
||||
import { tankIcon } from '../../../core/blitzkrieg/tankIcon';
|
||||
import { theme } from '../../../stitches.config';
|
||||
import mutateTankopedia, {
|
||||
import mutateTankopediaPersistent, {
|
||||
TankopediaSortBy,
|
||||
TankopediaSortDirection,
|
||||
useTankopedia,
|
||||
useTankopediaPersistent,
|
||||
} from '../../../stores/tankopedia';
|
||||
import { Options } from './components/Options';
|
||||
import { PageTurner } from './components/PageTurner';
|
||||
@@ -38,8 +38,8 @@ import * as styles from './page.css';
|
||||
const TANKS_PER_PAGE = 3 * 2 ** 3;
|
||||
|
||||
export default function Page() {
|
||||
const filters = useTankopedia((state) => state.filters);
|
||||
const sort = useTankopedia((state) => state.sort);
|
||||
const filters = useTankopediaPersistent((state) => state.filters);
|
||||
const sort = useTankopediaPersistent((state) => state.sort);
|
||||
const awaitedTanks = use(tanksDefinitionsArray);
|
||||
const input = useRef<HTMLInputElement>(null);
|
||||
const searchableTanks = useMemo(
|
||||
@@ -80,7 +80,7 @@ export default function Page() {
|
||||
[filters, sort],
|
||||
);
|
||||
const [searchedList, setSearchedList] = useState(searchableTanks);
|
||||
const [page, setPage] = useState(0);
|
||||
const page = useTankopediaPersistent((state) => state.filters.page);
|
||||
|
||||
useEffect(() => {
|
||||
setSearchedList(searchableTanks);
|
||||
@@ -88,12 +88,12 @@ export default function Page() {
|
||||
}, [searchableTanks]);
|
||||
|
||||
useEffect(() => {
|
||||
setPage(
|
||||
Math.min(
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.filters.page = Math.min(
|
||||
Math.max(0, page),
|
||||
Math.ceil(searchedList.length / TANKS_PER_PAGE) - 1,
|
||||
),
|
||||
);
|
||||
);
|
||||
});
|
||||
}, [searchedList]);
|
||||
|
||||
return (
|
||||
@@ -134,7 +134,7 @@ export default function Page() {
|
||||
<DropdownMenu.RadioGroup
|
||||
value={sort.by}
|
||||
onValueChange={(value) =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.sort.by = value as TankopediaSortBy;
|
||||
})
|
||||
}
|
||||
@@ -153,7 +153,7 @@ export default function Page() {
|
||||
<DropdownMenu.RadioGroup
|
||||
value={sort.direction}
|
||||
onValueChange={(value) =>
|
||||
mutateTankopedia((draft) => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.sort.direction = value as TankopediaSortDirection;
|
||||
})
|
||||
}
|
||||
@@ -176,12 +176,7 @@ export default function Page() {
|
||||
</Card>
|
||||
</Flex>
|
||||
|
||||
<PageTurner
|
||||
page={page}
|
||||
setPage={setPage}
|
||||
tanksPerPage={TANKS_PER_PAGE}
|
||||
searchedList={searchedList}
|
||||
/>
|
||||
<PageTurner tanksPerPage={TANKS_PER_PAGE} searchedList={searchedList} />
|
||||
|
||||
<Flex wrap="wrap" gap="3" justify="center">
|
||||
{searchedList
|
||||
@@ -302,12 +297,7 @@ export default function Page() {
|
||||
.slice(page * TANKS_PER_PAGE, (page + 1) * TANKS_PER_PAGE)}
|
||||
</Flex>
|
||||
|
||||
<PageTurner
|
||||
page={page}
|
||||
setPage={setPage}
|
||||
tanksPerPage={TANKS_PER_PAGE}
|
||||
searchedList={searchedList}
|
||||
/>
|
||||
<PageTurner tanksPerPage={TANKS_PER_PAGE} searchedList={searchedList} />
|
||||
</Suspense>
|
||||
</PageWrapper>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Duel } from '../../../../app/tools/tankopedia/[id]/page';
|
||||
import { canSplash } from '../../../../core/blitz/canSplash';
|
||||
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
||||
import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
|
||||
import { useTankopedia } from '../../../../stores/tankopedia';
|
||||
import { useTankopediaTemporary } from '../../../../stores/tankopedia';
|
||||
import { externalModuleMaskRenderTarget } from '../ExternalModuleMask';
|
||||
import { spacedArmorDepthRenderTarget } from '../SpacedArmorDepth';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
@@ -27,7 +27,7 @@ export function ArmorMesh({
|
||||
}: ArmorMeshProps) {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const { shell } = duel.antagonist;
|
||||
const greenPenetration = useTankopedia(
|
||||
const greenPenetration = useTankopediaTemporary(
|
||||
(state) => state.model.visual.greenPenetration,
|
||||
);
|
||||
const material = useRef<ShaderMaterial>(null);
|
||||
|
||||
+38
-17
@@ -1,6 +1,6 @@
|
||||
import { produce } from 'immer';
|
||||
import { create } from 'zustand';
|
||||
import { subscribeWithSelector } from 'zustand/middleware';
|
||||
import { persist, subscribeWithSelector } from 'zustand/middleware';
|
||||
import { TankType, TreeTypeString } from '../components/Tanks';
|
||||
import {
|
||||
GunDefinition,
|
||||
@@ -20,7 +20,7 @@ export interface DuelMember {
|
||||
gun: GunDefinition;
|
||||
shell: ShellDefinition;
|
||||
}
|
||||
interface Tankopedia {
|
||||
interface TankopediaPersistent {
|
||||
sort: {
|
||||
by: TankopediaSortBy;
|
||||
direction: TankopediaSortDirection;
|
||||
@@ -31,7 +31,11 @@ interface Tankopedia {
|
||||
treeTypes: TreeTypeString[];
|
||||
nations: string[];
|
||||
test: TankopediaTestTankDisplay;
|
||||
page: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface TankopediaTemporary {
|
||||
model: {
|
||||
physical: {
|
||||
yaw: number;
|
||||
@@ -47,19 +51,28 @@ interface Tankopedia {
|
||||
mode: TankopediaMode;
|
||||
}
|
||||
|
||||
export const useTankopedia = create<Tankopedia>()(
|
||||
subscribeWithSelector<Tankopedia>(() => ({
|
||||
sort: {
|
||||
by: 'tier',
|
||||
direction: 'descending',
|
||||
},
|
||||
filters: {
|
||||
tiers: [],
|
||||
types: [],
|
||||
treeTypes: [],
|
||||
nations: [],
|
||||
test: 'include',
|
||||
},
|
||||
export const useTankopediaPersistent = create<TankopediaPersistent>()(
|
||||
persist(
|
||||
subscribeWithSelector<TankopediaPersistent>(() => ({
|
||||
sort: {
|
||||
by: 'tier',
|
||||
direction: 'descending',
|
||||
},
|
||||
filters: {
|
||||
tiers: [],
|
||||
types: [],
|
||||
treeTypes: [],
|
||||
nations: [],
|
||||
test: 'include',
|
||||
page: 0,
|
||||
},
|
||||
})),
|
||||
{ name: 'tankopedia' },
|
||||
),
|
||||
);
|
||||
|
||||
export const useTankopediaTemporary = create<TankopediaTemporary>()(
|
||||
subscribeWithSelector<TankopediaTemporary>(() => ({
|
||||
model: {
|
||||
physical: {
|
||||
yaw: 0,
|
||||
@@ -80,6 +93,14 @@ export const useTankopedia = create<Tankopedia>()(
|
||||
})),
|
||||
);
|
||||
|
||||
export default function mutateTankopedia(recipe: (draft: Tankopedia) => void) {
|
||||
useTankopedia.setState(produce(recipe));
|
||||
export default function mutateTankopediaPersistent(
|
||||
recipe: (draft: TankopediaPersistent) => void,
|
||||
) {
|
||||
useTankopediaPersistent.setState(produce(recipe));
|
||||
}
|
||||
|
||||
export function mutateTankopediaTemporary(
|
||||
recipe: (draft: TankopediaTemporary) => void,
|
||||
) {
|
||||
useTankopediaTemporary.setState(produce(recipe));
|
||||
}
|
||||
Reference in new issue
Block a user