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