feat: persistant tankopedia

This commit is contained in:
TrèsAbhi committed 2024-01-21 10:34:52 -06:00
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;
});
}}
@@ -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';
@@ -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';
@@ -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 -2
View File
@@ -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>
+13 -23
View File
@@ -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
View File
@@ -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));
}