feat: united duel data

This commit is contained in:
TrèsAbhi committed 2024-02-22 16:41:42 -06:00
1 parent da767213cb
commit 7e92f8ac50
23 files changed
+249 -249

No files matched your search

@@ -21,13 +21,13 @@ import {
import { mutateDuel, useDuel } from '../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../stores/duel';
import { import {
mutateTankopediaTemporary, mutateTankopediaTemporary,
useTankopediaTemporary, useTankopediaPersistent,
} from '../../../../../../stores/tankopedia'; } from '../../../../../../stores/tankopedia';
import { TankSearch } from '../../../components/TankSearch'; import { TankSearch } from '../../../components/TankSearch';
export function AntagonistBar() { export function AntagonistBar() {
const antagonist = useDuel((state) => state.antagonist!); const antagonist = useDuel((state) => state.antagonist!);
const mode = useTankopediaTemporary((state) => state.mode); const mode = useTankopediaPersistent((state) => state.mode);
const [tab, setTab] = useState('search'); const [tab, setTab] = useState('search');
const [antagonistSelectorOpen, setAntagonistSelectorVisible] = const [antagonistSelectorOpen, setAntagonistSelectorVisible] =
useState(false); useState(false);
@@ -4,17 +4,13 @@ import { ConsumableButton } from '../../../../../../../components/ModuleButtons/
import { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkrieg/checkConsumableProvisionInclusivity'; import { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkrieg/checkConsumableProvisionInclusivity';
import { consumableDefinitions } from '../../../../../../../core/blitzkrieg/consumableDefinitions'; import { consumableDefinitions } from '../../../../../../../core/blitzkrieg/consumableDefinitions';
import { useEquipment } from '../../../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../../../hooks/useEquipment';
import { useDuel } from '../../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
import {
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia';
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper'; import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
export function Consumables() { export function Consumables() {
const protagonist = useDuel((state) => state.protagonist!); const protagonist = useDuel((state) => state.protagonist!);
const awaitedConsumableDefinitions = use(consumableDefinitions); const awaitedConsumableDefinitions = use(consumableDefinitions);
const consumables = useTankopediaTemporary((state) => state.consumables); const consumables = useDuel((state) => state.protagonist!.consumables);
const consumablesList = Object.values(awaitedConsumableDefinitions).filter( const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
(consumable) => (consumable) =>
checkConsumableProvisionInclusivity( checkConsumableProvisionInclusivity(
@@ -23,8 +19,8 @@ export function Consumables() {
protagonist.gun, protagonist.gun,
), ),
); );
const cooldownBooster = useTankopediaTemporary( const cooldownBooster = useDuel(
(state) => state.cooldownBooster, (state) => state.protagonist!.cooldownBooster,
); );
const hasConsumableDeliverySystem = useEquipment(118); const hasConsumableDeliverySystem = useEquipment(118);
const hasHighEndConsumables = useEquipment(101); const hasHighEndConsumables = useEquipment(101);
@@ -59,13 +55,14 @@ export function Consumables() {
consumable={consumable.id} consumable={consumable.id}
selected={selected} selected={selected}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
if (selected) { if (selected) {
draft.consumables = draft.consumables.filter( draft.protagonist!.consumables =
(id) => id !== consumable.id, draft.protagonist!.consumables.filter(
); (id) => id !== consumable.id,
);
} else { } else {
draft.consumables.push(consumable.id); draft.protagonist!.consumables.push(consumable.id);
} }
}); });
}} }}
@@ -2,11 +2,7 @@ import { Flex, Heading } from '@radix-ui/themes';
import { use } from 'react'; import { use } from 'react';
import { EquipmentButton } from '../../../../../../../components/ModuleButtons/EquipmentButton'; import { EquipmentButton } from '../../../../../../../components/ModuleButtons/EquipmentButton';
import { equipmentDefinitions } from '../../../../../../../core/blitzkrieg/equipmentDefinitions'; import { equipmentDefinitions } from '../../../../../../../core/blitzkrieg/equipmentDefinitions';
import { useDuel } from '../../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
import {
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia';
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper'; import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
export function Equipments() { export function Equipments() {
@@ -14,9 +10,7 @@ export function Equipments() {
const awaitedEquipmentDefinitions = use(equipmentDefinitions); const awaitedEquipmentDefinitions = use(equipmentDefinitions);
const equipmentRows = const equipmentRows =
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment]; awaitedEquipmentDefinitions.presets[protagonist.tank.equipment];
const equipmentMatrix = useTankopediaTemporary( const equipmentMatrix = useDuel((state) => state.protagonist!.equipment);
(state) => state.equipmentMatrix,
);
return ( return (
<ConfigurationChildWrapper> <ConfigurationChildWrapper>
@@ -33,11 +27,12 @@ export function Equipments() {
rowChild rowChild
selected={equipmentMatrix[rowIndex][columnIndex] === -1} selected={equipmentMatrix[rowIndex][columnIndex] === -1}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
if (equipmentMatrix[rowIndex][columnIndex] === -1) { if (equipmentMatrix[rowIndex][columnIndex] === -1) {
draft.equipmentMatrix[rowIndex][columnIndex] = 0; draft.protagonist!.equipment[rowIndex][columnIndex] = 0;
} else { } else {
draft.equipmentMatrix[rowIndex][columnIndex] = -1; draft.protagonist!.equipment[rowIndex][columnIndex] =
-1;
} }
}); });
}} }}
@@ -48,11 +43,11 @@ export function Equipments() {
rowChild rowChild
selected={equipmentMatrix[rowIndex][columnIndex] === 1} selected={equipmentMatrix[rowIndex][columnIndex] === 1}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
if (equipmentMatrix[rowIndex][columnIndex] === 1) { if (equipmentMatrix[rowIndex][columnIndex] === 1) {
draft.equipmentMatrix[rowIndex][columnIndex] = 0; draft.protagonist!.equipment[rowIndex][columnIndex] = 0;
} else { } else {
draft.equipmentMatrix[rowIndex][columnIndex] = 1; draft.protagonist!.equipment[rowIndex][columnIndex] = 1;
} }
}); });
}} }}
@@ -3,19 +3,15 @@ import { clamp, debounce } from 'lodash';
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import { GenericTankComponentButton } from '../../../../../../../components/ModuleButtons/GenericTankComponentButton'; import { GenericTankComponentButton } from '../../../../../../../components/ModuleButtons/GenericTankComponentButton';
import { asset } from '../../../../../../../core/blitzkrieg/asset'; import { asset } from '../../../../../../../core/blitzkrieg/asset';
import { useDuel } from '../../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
import {
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia';
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper'; import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
export function Miscellaneous() { export function Miscellaneous() {
const camouflage = useTankopediaTemporary((state) => state.camouflage); const camouflage = useDuel((state) => state.protagonist!.camouflage);
const cooldownBooster = useTankopediaTemporary( const cooldownBooster = useDuel(
(state) => state.cooldownBooster, (state) => state.protagonist!.cooldownBooster,
); );
const crewMastery = useTankopediaTemporary((state) => state.crewMastery); const crewMastery = useDuel((state) => state.protagonist!.crewMastery);
const crewInput = useRef<HTMLInputElement>(null); const crewInput = useRef<HTMLInputElement>(null);
const [crewMasteryDraft, setCrewMasteryDraft] = useState(crewMastery); const [crewMasteryDraft, setCrewMasteryDraft] = useState(crewMastery);
const { tank } = useDuel((state) => state.protagonist!); const { tank } = useDuel((state) => state.protagonist!);
@@ -23,8 +19,8 @@ export function Miscellaneous() {
const debouncedApplyDraft = useMemo( const debouncedApplyDraft = useMemo(
() => () =>
debounce((value) => { debounce((value) => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.crewMastery = value; draft.protagonist!.crewMastery = value;
}); });
}, 500), }, 500),
[], [],
@@ -53,8 +49,8 @@ export function Miscellaneous() {
first first
last last
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.camouflage = !camouflage; draft.protagonist!.camouflage = !camouflage;
}); });
}} }}
/> />
@@ -77,9 +73,10 @@ export function Miscellaneous() {
transform: 'translate(-50%, -50%) scale(0.8)', transform: 'translate(-50%, -50%) scale(0.8)',
}} }}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.cooldownBooster++; draft.protagonist!.cooldownBooster++;
if (draft.cooldownBooster === 4) draft.cooldownBooster = 0; if (draft.protagonist!.cooldownBooster === 4)
draft.protagonist!.cooldownBooster = 0;
}); });
}} }}
/> />
@@ -106,8 +103,12 @@ export function Miscellaneous() {
return; return;
} }
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.crewMastery = clamp(newValue / 100, 0.5, 1); draft.protagonist!.crewMastery = clamp(
newValue / 100,
0.5,
1,
);
}); });
}} }}
onKeyUp={(event) => { onKeyUp={(event) => {
@@ -3,16 +3,12 @@ import { use } from 'react';
import { ProvisionButton } from '../../../../../../../components/ModuleButtons/ProvisionButton'; import { ProvisionButton } from '../../../../../../../components/ModuleButtons/ProvisionButton';
import { availableProvisions } from '../../../../../../../core/blitzkrieg/availableProvisions'; import { availableProvisions } from '../../../../../../../core/blitzkrieg/availableProvisions';
import { provisionDefinitions } from '../../../../../../../core/blitzkrieg/provisionDefinitions'; import { provisionDefinitions } from '../../../../../../../core/blitzkrieg/provisionDefinitions';
import { useDuel } from '../../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
import {
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia';
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper'; import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
export function Provisions() { export function Provisions() {
const { tank, gun } = useDuel((state) => state.protagonist!); const { tank, gun } = useDuel((state) => state.protagonist!);
const provisions = useTankopediaTemporary((state) => state.provisions); const provisions = useDuel((state) => state.protagonist!.provisions);
const awaitedProvisionDefinitions = use(provisionDefinitions); const awaitedProvisionDefinitions = use(provisionDefinitions);
const provisionsList = availableProvisions( const provisionsList = availableProvisions(
tank, tank,
@@ -38,13 +34,14 @@ export function Provisions() {
provision={provision.id} provision={provision.id}
selected={selected} selected={selected}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
if (selected) { if (selected) {
draft.provisions = draft.provisions.filter( draft.protagonist!.provisions =
(id) => id !== provision.id, draft.protagonist!.provisions.filter(
); (id) => id !== provision.id,
);
} else { } else {
draft.provisions.push(provision.id); draft.protagonist!.provisions.push(provision.id);
} }
}); });
}} }}
@@ -24,7 +24,6 @@ import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useProvision } from '../../../../../../hooks/useProvision'; import { useProvision } from '../../../../../../hooks/useProvision';
import { useProvisions } from '../../../../../../hooks/useProvisions'; import { useProvisions } from '../../../../../../hooks/useProvisions';
import { mutateDuel, useDuel } from '../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../stores/tankopedia';
import { Info } from './components/Info'; import { Info } from './components/Info';
import { InfoWithDelta } from './components/InfoWithDelta'; import { InfoWithDelta } from './components/InfoWithDelta';
@@ -35,7 +34,7 @@ export function Characteristics() {
); );
const hasImprovedVentilation = useEquipment(102); const hasImprovedVentilation = useEquipment(102);
const provisions = useProvisions(); const provisions = useProvisions();
const crewMastery = useTankopediaTemporary((state) => state.crewMastery); const crewMastery = useDuel((state) => state.protagonist!.crewMastery);
const provisionCrewBonus = const provisionCrewBonus =
provisions.reduce( provisions.reduce(
(total, provision) => (total, provision) =>
@@ -67,7 +66,7 @@ export function Characteristics() {
stockTurret.weight + stockTurret.weight +
stockGun.weight; stockGun.weight;
const [penetrationDistance, setPenetrationDistance] = useState(250); const [penetrationDistance, setPenetrationDistance] = useState(250);
const camouflage = useTankopediaTemporary((state) => state.camouflage); const camouflage = useDuel((state) => state.protagonist!.camouflage);
const hasRammer = useEquipment(100); const hasRammer = useEquipment(100);
const hasCalibratedShells = useEquipment(103); const hasCalibratedShells = useEquipment(103);
const hasEnhancedGunLayingDrive = useEquipment(104); const hasEnhancedGunLayingDrive = useEquipment(104);
@@ -11,11 +11,11 @@ import { modelTransformEvent } from '../../../../../../core/blitzkrieg/modelTran
import { Pose, poseEvent } from '../../../../../../core/blitzkrieg/pose'; import { Pose, poseEvent } from '../../../../../../core/blitzkrieg/pose';
import { tankIcon } from '../../../../../../core/blitzkrieg/tankIcon'; import { tankIcon } from '../../../../../../core/blitzkrieg/tankIcon';
import { useWideFormat } from '../../../../../../hooks/useWideFormat'; import { useWideFormat } from '../../../../../../hooks/useWideFormat';
import { useDuel } from '../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../stores/duel';
import { import mutateTankopediaPersistent, {
TankopediaMode, TankopediaMode,
mutateTankopediaTemporary, mutateTankopediaTemporary,
useTankopediaTemporary, useTankopediaPersistent,
} from '../../../../../../stores/tankopedia'; } from '../../../../../../stores/tankopedia';
import { Controls } from '../Control'; import { Controls } from '../Control';
import { Lighting } from '../Lighting'; import { Lighting } from '../Lighting';
@@ -33,7 +33,7 @@ export function TankSandbox() {
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 = useTankopediaTemporary((state) => state.mode); const mode = useTankopediaPersistent((state) => state.mode);
const protagonist = useDuel((state) => state.protagonist!); const protagonist = useDuel((state) => state.protagonist!);
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id]; const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
const turretModelDefinition = const turretModelDefinition =
@@ -65,7 +65,7 @@ export function TankSandbox() {
function handlePoseEvent(pose: Pose) { function handlePoseEvent(pose: Pose) {
switch (pose) { switch (pose) {
case Pose.HullDown: { case Pose.HullDown: {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
-Infinity, -Infinity,
0, 0,
@@ -74,15 +74,15 @@ export function TankSandbox() {
); );
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
draft.model.pose.pitch = pitch; draft.protagonist!.pitch = pitch;
draft.model.pose.yaw = yaw; draft.protagonist!.yaw = yaw;
}); });
break; break;
} }
case Pose.FaceHug: { case Pose.FaceHug: {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
Infinity, Infinity,
0, 0,
@@ -91,15 +91,15 @@ export function TankSandbox() {
); );
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
draft.model.pose.pitch = pitch; draft.protagonist!.pitch = pitch;
draft.model.pose.yaw = yaw; draft.protagonist!.yaw = yaw;
}); });
break; break;
} }
case Pose.Default: case Pose.Default:
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
0, 0,
0, 0,
@@ -108,8 +108,8 @@ export function TankSandbox() {
); );
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
draft.model.pose.pitch = pitch; draft.protagonist!.pitch = pitch;
draft.model.pose.yaw = yaw; draft.protagonist!.yaw = yaw;
}); });
break; break;
@@ -126,10 +126,10 @@ export function TankSandbox() {
}); });
useEffect(() => { useEffect(() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
[draft.model.pose.pitch, draft.model.pose.yaw] = applyPitchYawLimits( [draft.protagonist!.pitch, draft.protagonist!.yaw] = applyPitchYawLimits(
draft.model.pose.pitch, draft.protagonist!.pitch,
draft.model.pose.yaw, draft.protagonist!.yaw,
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
); );
@@ -157,7 +157,7 @@ export function TankSandbox() {
<Tabs.Root <Tabs.Root
value={mode} value={mode}
onValueChange={(mode) => { onValueChange={(mode) => {
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.mode = mode as TankopediaMode; draft.mode = mode as TankopediaMode;
}); });
}} }}
@@ -12,7 +12,7 @@ import { resolveArmor } from '../../../../../../../../../core/blitzkrieg/resolve
import { useArmor } from '../../../../../../../../../hooks/useArmor'; import { useArmor } from '../../../../../../../../../hooks/useArmor';
import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions'; import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions';
import { useDuel } from '../../../../../../../../../stores/duel'; import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia'; import { useTankopediaPersistent } from '../../../../../../../../../stores/tankopedia';
export function ArmorHighlighting() { export function ArmorHighlighting() {
const protagonist = useDuel((state) => state.protagonist!); const protagonist = useDuel((state) => state.protagonist!);
@@ -20,7 +20,7 @@ export function ArmorHighlighting() {
const modelDefinitions = useModelDefinitions(); const modelDefinitions = useModelDefinitions();
const gunContainer = useRef<Group>(null); const gunContainer = useRef<Group>(null);
const turretContainer = useRef<Group>(null); const turretContainer = useRef<Group>(null);
const initialTankopediaState = useTankopediaTemporary.getState(); const initialTankopediaState = useTankopediaPersistent.getState();
useEffect(() => { useEffect(() => {
const hullOrigin = new Vector3( const hullOrigin = new Vector3(
@@ -80,14 +80,14 @@ export function ArmorHighlighting() {
turretContainer.current?.position.copy(turretPosition); turretContainer.current?.position.copy(turretPosition);
turretContainer.current?.rotation.copy(turretRotation); turretContainer.current?.rotation.copy(turretRotation);
} }
const unsubscribe = useTankopediaTemporary.subscribe( const unsubscribe = useTankopediaPersistent.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';
}, },
); );
handleModelTransform(useTankopediaTemporary.getState().model.pose); handleModelTransform(useDuel.getState().protagonist!);
modelTransformEvent.on(handleModelTransform); modelTransformEvent.on(handleModelTransform);
return () => { return () => {
@@ -13,7 +13,7 @@ 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 { useDuel } from '../../../../../../../../../stores/duel'; import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia'; import { useTankopediaPersistent } from '../../../../../../../../../stores/tankopedia';
interface ExternalModuleMaskProps { interface ExternalModuleMaskProps {
ornamental?: boolean; ornamental?: boolean;
@@ -33,7 +33,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
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 = useTankopediaTemporary.getState(); const initialTankopediaState = useTankopediaPersistent.getState();
useEffect(() => { useEffect(() => {
const hullOrigin = new Vector3( const hullOrigin = new Vector3(
@@ -102,7 +102,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
turretContainer.current?.rotation.copy(turretRotation); turretContainer.current?.rotation.copy(turretRotation);
} }
handleModelTransform(useTankopediaTemporary.getState().model.pose); handleModelTransform(useDuel.getState().protagonist!);
modelTransformEvent.on(handleModelTransform); modelTransformEvent.on(handleModelTransform);
return () => { return () => {
@@ -111,7 +111,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
}); });
useEffect(() => { useEffect(() => {
const unsubscribe = useTankopediaTemporary.subscribe( const unsubscribe = useTankopediaPersistent.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';
@@ -13,7 +13,7 @@ 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 { useDuel } from '../../../../../../../../../stores/duel'; import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia'; import { useTankopediaPersistent } from '../../../../../../../../../stores/tankopedia';
interface SpacedArmorDepthProps { interface SpacedArmorDepthProps {
ornamental?: boolean; ornamental?: boolean;
@@ -37,7 +37,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(
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 = useTankopediaTemporary.getState(); const initialTankopediaState = useTankopediaPersistent.getState();
useEffect(() => { useEffect(() => {
if (!modelDefinitions) return; if (!modelDefinitions) return;
@@ -108,7 +108,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(
turretContainer.current?.rotation.copy(turretRotation); turretContainer.current?.rotation.copy(turretRotation);
} }
handleModelTransform(useTankopediaTemporary.getState().model.pose); handleModelTransform(useDuel.getState().protagonist!);
modelTransformEvent.on(handleModelTransform); modelTransformEvent.on(handleModelTransform);
return () => { return () => {
@@ -117,7 +117,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(
}); });
useEffect(() => { useEffect(() => {
const unsubscribe = useTankopediaTemporary.subscribe( const unsubscribe = useTankopediaPersistent.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';
@@ -15,10 +15,9 @@ import {
} from '../../../../../../../core/blitzkrieg/modelTransform'; } from '../../../../../../../core/blitzkrieg/modelTransform';
import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180'; import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180';
import { useModel } from '../../../../../../../hooks/useModel'; import { useModel } from '../../../../../../../hooks/useModel';
import { useDuel } from '../../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
import mutateTankopediaPersistent, { import mutateTankopediaPersistent, {
mutateTankopediaTemporary, mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia'; } from '../../../../../../../stores/tankopedia';
export const TankModel = memo(() => { export const TankModel = memo(() => {
@@ -27,7 +26,6 @@ export const TankModel = memo(() => {
>(undefined); >(undefined);
const protagonist = useDuel((draft) => draft.protagonist!); const protagonist = useDuel((draft) => draft.protagonist!);
const canvas = useThree((state) => state.gl.domElement); const canvas = useThree((state) => state.gl.domElement);
const physical = useTankopediaTemporary((state) => state.model.pose);
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);
@@ -156,8 +154,8 @@ export const TankModel = memo(() => {
event.stopPropagation(); event.stopPropagation();
if (isTurret) { if (isTurret) {
yaw = physical.yaw; yaw = protagonist.yaw;
pitch = physical.pitch; pitch = protagonist.pitch;
mutateTankopediaPersistent((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = false; draft.model.visual.controlsEnabled = false;
@@ -179,9 +177,9 @@ export const TankModel = memo(() => {
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
} }
function handlePointerUp() { function handlePointerUp() {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.model.pose.pitch = normalizeAnglePI(pitch); draft.protagonist!.pitch = normalizeAnglePI(pitch);
draft.model.pose.yaw = normalizeAnglePI(yaw); draft.protagonist!.yaw = normalizeAnglePI(yaw);
}); });
mutateTankopediaPersistent((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = true; draft.model.visual.controlsEnabled = true;
@@ -223,8 +221,8 @@ export const TankModel = memo(() => {
mutateTankopediaTemporary((draft) => { mutateTankopediaTemporary((draft) => {
draft.shot = undefined; draft.shot = undefined;
}); });
pitch = physical.pitch; pitch = protagonist.pitch;
yaw = physical.yaw; yaw = protagonist.yaw;
window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp); window.addEventListener('pointerup', handlePointerUp);
} }
@@ -241,9 +239,9 @@ export const TankModel = memo(() => {
mutateTankopediaPersistent((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = true; draft.model.visual.controlsEnabled = true;
}); });
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.model.pose.pitch = normalizeAnglePI(pitch); draft.protagonist!.pitch = normalizeAnglePI(pitch);
draft.model.pose.yaw = normalizeAnglePI(yaw); draft.protagonist!.yaw = normalizeAnglePI(yaw);
}); });
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
@@ -4,33 +4,29 @@ import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawL
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 { useEquipment } from '../../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useDuel } from '../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../stores/duel';
import { import { useTankopediaPersistent } from '../../../../../../stores/tankopedia';
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../stores/tankopedia';
import { QuickEquipmentButton } from './components/QuickEquipmentButton'; import { QuickEquipmentButton } from './components/QuickEquipmentButton';
export function RotationInputs() { export function RotationInputs() {
const protagonist = useDuel((state) => state.protagonist!); const protagonist = useDuel((state) => state.protagonist!);
const physical = useTankopediaTemporary((state) => state.model.pose);
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);
const mode = useTankopediaTemporary((state) => state.mode); const mode = useTankopediaPersistent((state) => state.mode);
const hasCalibratedShells = useEquipment(103); const hasCalibratedShells = useEquipment(103, true);
const hasEnhancedArmor = useEquipment(110); const hasEnhancedArmor = useEquipment(110);
useEffect(() => { useEffect(() => {
turretYawInput.current!.value = `${-Math.round( turretYawInput.current!.value = `${-Math.round(
physical.yaw * (180 / Math.PI), protagonist.yaw * (180 / Math.PI),
)}`; )}`;
}, [physical.yaw]); }, [protagonist.yaw]);
useEffect(() => { useEffect(() => {
gunPitchInput.current!.value = `${-Math.round( gunPitchInput.current!.value = `${-Math.round(
physical.pitch * (180 / Math.PI), protagonist.pitch * (180 / Math.PI),
)}`; )}`;
}, [physical.pitch]); }, [protagonist.pitch]);
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id]; const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
const turretModelDefinition = const turretModelDefinition =
@@ -68,18 +64,18 @@ export function RotationInputs() {
Yaw Yaw
</TextField.Slot> </TextField.Slot>
<TextField.Input <TextField.Input
defaultValue={Math.round(physical.yaw * (180 / Math.PI))} defaultValue={Math.round(protagonist.yaw * (180 / Math.PI))}
onBlur={() => { onBlur={() => {
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
physical.pitch, protagonist.pitch,
-Number(turretYawInput.current?.value) * (Math.PI / 180), -Number(turretYawInput.current?.value) * (Math.PI / 180),
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
); );
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
mutateTankopediaTemporary((state) => { useDuel((state) => {
state.model.pose.pitch = pitch; state.protagonist!.pitch = pitch;
state.model.pose.yaw = yaw; state.protagonist!.yaw = yaw;
}); });
turretYawInput.current!.value = `${Math.round( turretYawInput.current!.value = `${Math.round(
yaw * (180 / Math.PI), yaw * (180 / Math.PI),
@@ -116,18 +112,18 @@ export function RotationInputs() {
Pitch Pitch
</TextField.Slot> </TextField.Slot>
<TextField.Input <TextField.Input
defaultValue={-Math.round(physical.pitch * (180 / Math.PI))} defaultValue={-Math.round(protagonist.pitch * (180 / Math.PI))}
onBlur={() => { onBlur={() => {
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
-Number(gunPitchInput.current?.value) * (Math.PI / 180), -Number(gunPitchInput.current?.value) * (Math.PI / 180),
physical.yaw, protagonist.yaw,
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
); );
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
mutateTankopediaTemporary((state) => { mutateDuel((state) => {
state.model.pose.pitch = pitch; state.protagonist!.pitch = pitch;
state.model.pose.yaw = yaw; state.protagonist!.yaw = yaw;
}); });
gunPitchInput.current!.value = `${Math.round( gunPitchInput.current!.value = `${Math.round(
pitch * (180 / Math.PI), pitch * (180 / Math.PI),
@@ -158,8 +154,8 @@ export function RotationInputs() {
equipment={103} equipment={103}
active={hasCalibratedShells} active={hasCalibratedShells}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.equipmentMatrix[0][0] = hasCalibratedShells ? 0 : 1; draft.antagonist!.equipment[0][0] = hasCalibratedShells ? 0 : 1;
}); });
}} }}
/> />
@@ -167,8 +163,8 @@ export function RotationInputs() {
equipment={110} equipment={110}
active={hasEnhancedArmor} active={hasEnhancedArmor}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateDuel((draft) => {
draft.equipmentMatrix[1][1] = hasEnhancedArmor ? 0 : -1; draft.protagonist!.equipment[1][1] = hasEnhancedArmor ? 0 : -1;
}); });
}} }}
/> />
+5 -43
View File
@@ -1,11 +1,9 @@
'use client'; 'use client';
import { Flex } from '@radix-ui/themes'; import { Flex } from '@radix-ui/themes';
import { use, useEffect } from 'react'; import { useEffect } from 'react';
import PageWrapper from '../../../../components/PageWrapper'; import PageWrapper from '../../../../components/PageWrapper';
import { availableProvisions } from '../../../../core/blitzkrieg/availableProvisions'; import { assignDuelMember } from '../../../../core/blitzkrieg/assignDuelMember';
import { provisionDefinitions } from '../../../../core/blitzkrieg/provisionDefinitions';
import { tankDefinitions } from '../../../../core/blitzkrieg/tankDefinitions';
import { useWideFormat } from '../../../../hooks/useWideFormat'; import { useWideFormat } from '../../../../hooks/useWideFormat';
import { mutateDuel, useDuel } from '../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../stores/duel';
import { mutateTankopediaTemporary } from '../../../../stores/tankopedia'; import { mutateTankopediaTemporary } from '../../../../stores/tankopedia';
@@ -20,26 +18,12 @@ import { TankSandbox } from './components/Model/TankSandbox';
import { Title } from './components/Title'; import { Title } from './components/Title';
export default function Page({ params }: { params: { id: string } }) { export default function Page({ params }: { params: { id: string } }) {
const id = parseInt(params.id); const initialId = parseInt(params.id);
const awaitedTankDefinitions = use(tankDefinitions);
const tank = awaitedTankDefinitions[id];
const gun = tank.turrets.at(-1)!.guns.at(-1)!;
const assigned = useDuel((state) => state.assigned); const assigned = useDuel((state) => state.assigned);
const wideFormat = useWideFormat(); const wideFormat = useWideFormat();
useEffect(() => { useEffect(() => {
mutateDuel((draft) => { assignDuelMember('both', initialId);
draft.assigned = true;
draft.protagonist = {
tank,
engine: tank.engines.at(-1)!,
turret: tank.turrets.at(-1)!,
gun,
shell: tank.turrets.at(-1)!.guns.at(-1)!.shells[0],
track: tank.tracks.at(-1)!,
};
draft.antagonist = { ...draft.protagonist };
});
function handleKeyDown(event: KeyboardEvent) { function handleKeyDown(event: KeyboardEvent) {
if (document.activeElement instanceof HTMLInputElement) return; if (document.activeElement instanceof HTMLInputElement) return;
@@ -72,34 +56,12 @@ export default function Page({ params }: { params: { id: string } }) {
} }
} }
(async () => {
const provisionsList = availableProvisions(
tank,
gun,
await provisionDefinitions,
);
mutateTankopediaTemporary((draft) => {
draft.model.pose.yaw = 0;
draft.model.pose.pitch = 0;
draft.consumables = [];
draft.provisions = provisionsList
.filter((provision) => provision.crew !== undefined)
.map((provision) => provision.id);
draft.shot = undefined;
draft.equipmentMatrix = [
[1, -1, -1],
[0, 0, 0],
[0, 0, 0],
];
});
})();
window.addEventListener('keydown', handleKeyDown); window.addEventListener('keydown', handleKeyDown);
return () => { return () => {
window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keydown', handleKeyDown);
}; };
}, [id, assigned]); }, [initialId, assigned]);
return ( return (
<PageWrapper color="purple" size="double"> <PageWrapper color="purple" size="double">
@@ -22,7 +22,6 @@ import {
Shot, Shot,
mutateTankopediaTemporary, mutateTankopediaTemporary,
useTankopediaPersistent, useTankopediaPersistent,
useTankopediaTemporary,
} from '../../../../stores/tankopedia'; } from '../../../../stores/tankopedia';
import { externalModuleMaskRenderTarget } from '../ExternalModuleMask'; import { externalModuleMaskRenderTarget } from '../ExternalModuleMask';
import { spacedArmorDepthRenderTarget } from '../SpacedArmorDepth'; import { spacedArmorDepthRenderTarget } from '../SpacedArmorDepth';
@@ -52,7 +51,7 @@ export const ArmorMesh = memo<ArmorMeshProps>(
const surfaceNormal = new Vector3(); const surfaceNormal = new Vector3();
const mesh = useRef<Mesh>(null); const mesh = useRef<Mesh>(null);
const scene = useThree((state) => state.scene); const scene = useThree((state) => state.scene);
const hasCalibratedShells = useAwait(hasEquipment(103)); const hasCalibratedShells = useAwait(hasEquipment(103, true));
const hasEnhancedArmor = useAwait(hasEquipment(110)); const hasEnhancedArmor = useAwait(hasEquipment(110));
const wireframe = initialTankopedia.model.visual.wireframe; const wireframe = initialTankopedia.model.visual.wireframe;
@@ -92,8 +91,8 @@ export const ArmorMesh = memo<ArmorMeshProps>(
} }
const unsubscribes = [ const unsubscribes = [
useTankopediaTemporary.subscribe( useDuel.subscribe(
(state) => state.equipmentMatrix, (state) => state.protagonist!.equipment,
updateQuickEquipments, updateQuickEquipments,
), ),
useDuel.subscribe( useDuel.subscribe(
+57
View File
@@ -0,0 +1,57 @@
import {
DuelMember,
genericDefaultEquipmentMatrix,
mutateDuel,
} from '../../stores/duel';
import { availableProvisions } from './availableProvisions';
import { provisionDefinitions } from './provisionDefinitions';
import { tankDefinitions } from './tankDefinitions';
export async function assignDuelMember(
type: 'antagonist' | 'protagonist' | 'both',
id: number,
) {
const awaitedTankDefinitions = await tankDefinitions;
const awaitedProvisionDefinitions = await provisionDefinitions;
const tank = awaitedTankDefinitions[id];
const turret = tank.turrets.at(-1)!;
const gun = turret.guns.at(-1)!;
const shell = gun.shells[0];
const engine = tank.engines.at(-1)!;
const track = tank.tracks.at(-1)!;
const provisionsList = availableProvisions(
tank,
gun,
awaitedProvisionDefinitions,
);
const member: DuelMember = {
tank,
engine,
turret,
gun,
shell,
track,
equipment: genericDefaultEquipmentMatrix,
pitch: 0,
yaw: 0,
camouflage: true,
consumables: [],
cooldownBooster: 0,
crewMastery: 1,
provisions: provisionsList
.filter((provision) => provision.crew !== undefined)
.map((provision) => provision.id),
};
mutateDuel((draft) => {
draft.assigned = true;
if (type === 'antagonist') {
draft.antagonist = member;
} else if (type === 'protagonist') {
draft.protagonist = member;
} else {
draft.protagonist = member;
draft.antagonist = { ...member };
}
});
}
+5 -5
View File
@@ -1,13 +1,13 @@
import { useDuel } from '../../stores/duel'; import { useDuel } from '../../stores/duel';
import { useTankopediaTemporary } from '../../stores/tankopedia';
import { equipmentDefinitions } from './equipmentDefinitions'; import { equipmentDefinitions } from './equipmentDefinitions';
export async function hasEquipment(id: number) { export async function hasEquipment(id: number, antagonist = false) {
const awaitedEquipmentDefinitions = await equipmentDefinitions; const awaitedEquipmentDefinitions = await equipmentDefinitions;
const protagonist = useDuel.getState().protagonist!; const member = useDuel.getState()[antagonist ? 'antagonist' : 'protagonist']!;
const equipmentRows = const equipmentRows =
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment]; awaitedEquipmentDefinitions.presets[member.tank.equipment];
const equipmentMatrix = useTankopediaTemporary.getState().equipmentMatrix; const equipmentMatrix =
useDuel.getState()[antagonist ? 'antagonist' : 'protagonist']!.equipment;
return equipmentRows.some((equipmentRow, rowIndex) => { return equipmentRows.some((equipmentRow, rowIndex) => {
return equipmentRow.some((equipment, columnIndex) => { return equipmentRow.some((equipment, columnIndex) => {
+4 -2
View File
@@ -1,10 +1,12 @@
import { EquipmentPreset } from '../core/blitzkrieg/equipmentDefinitions'; import { EquipmentPreset } from '../core/blitzkrieg/equipmentDefinitions';
import { EquipmentMatrix } from '../stores/tankopedia'; import { EquipmentMatrix } from '../stores/duel';
export function useAppliedEquipments( export function useAppliedEquipments(
matrix: EquipmentMatrix, matrix: EquipmentMatrix,
preset: EquipmentPreset, preset: EquipmentPreset,
) { ) {
const flatMatrix = matrix.flat(); const flatMatrix = matrix.flat();
return preset.flat().map((options, index) => options[flatMatrix[index] == 1 ? 1 : 0]); return preset
.flat()
.map((options, index) => options[flatMatrix[index] == 1 ? 1 : 0]);
} }
+2 -2
View File
@@ -1,6 +1,6 @@
import { useTankopediaTemporary } from '../stores/tankopedia'; import { useDuel } from '../stores/duel';
export function useConsumable(id: number) { export function useConsumable(id: number) {
const consumables = useTankopediaTemporary((state) => state.consumables); const consumables = useDuel((state) => state.protagonist!.consumables);
return consumables.includes(id); return consumables.includes(id);
} }
+8 -7
View File
@@ -1,15 +1,16 @@
import { use, useMemo } from 'react'; import { use, useMemo } from 'react';
import { equipmentDefinitions } from '../core/blitzkrieg/equipmentDefinitions'; import { equipmentDefinitions } from '../core/blitzkrieg/equipmentDefinitions';
import { useDuel } from '../stores/duel'; import { useDuel } from '../stores/duel';
import { useTankopediaTemporary } from '../stores/tankopedia';
export function useEquipment(id: number) { export function useEquipment(id: number, antagonist = false) {
const awaitedEquipmentDefinitions = use(equipmentDefinitions); const awaitedEquipmentDefinitions = use(equipmentDefinitions);
const protagonist = useDuel((state) => state.protagonist!); const member = useDuel(
(state) => state[antagonist ? 'antagonist' : 'protagonist']!,
);
const equipmentRows = const equipmentRows =
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment]; awaitedEquipmentDefinitions.presets[member.tank.equipment];
const equipmentMatrix = useTankopediaTemporary( const equipmentMatrix = useDuel(
(state) => state.equipmentMatrix, (state) => state[antagonist ? 'antagonist' : 'protagonist']!.equipment,
); );
const value = useMemo( const value = useMemo(
() => () =>
@@ -21,7 +22,7 @@ export function useEquipment(id: number) {
return equipped === id; return equipped === id;
}); });
}), }),
[equipmentMatrix, protagonist.tank], [equipmentMatrix, member.tank],
); );
return value; return value;
+2 -2
View File
@@ -1,6 +1,6 @@
import { useTankopediaTemporary } from '../stores/tankopedia'; import { useDuel } from '../stores/duel';
export function useProvision(id: number) { export function useProvision(id: number) {
const provisions = useTankopediaTemporary((state) => state.provisions); const provisions = useDuel((state) => state.protagonist!.provisions);
return provisions.includes(id); return provisions.includes(id);
} }
+2 -2
View File
@@ -1,9 +1,9 @@
import { use } from 'react'; import { use } from 'react';
import { provisionDefinitions } from '../core/blitzkrieg/provisionDefinitions'; import { provisionDefinitions } from '../core/blitzkrieg/provisionDefinitions';
import { useTankopediaTemporary } from '../stores/tankopedia'; import { useDuel } from '../stores/duel';
export function useProvisions() { export function useProvisions() {
const provisions = useTankopediaTemporary((state) => state.provisions); const provisions = useDuel((state) => state.protagonist!.provisions);
const awaitedProvisionDefinitions = use(provisionDefinitions); const awaitedProvisionDefinitions = use(provisionDefinitions);
return provisions.map((id) => awaitedProvisionDefinitions[id]); return provisions.map((id) => awaitedProvisionDefinitions[id]);
} }
+43 -1
View File
@@ -1,7 +1,43 @@
import { produce } from 'immer'; import { produce } from 'immer';
import { create } from 'zustand'; import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware'; import { subscribeWithSelector } from 'zustand/middleware';
import { DuelMember } from './tankopedia'; import {
EngineDefinition,
GunDefinition,
ShellDefinition,
TankDefinition,
TrackDefinition,
TurretDefinition,
} from '../core/blitzkrieg/tankDefinitions';
type EquipmentMatrixItem = -1 | 0 | 1;
type EquipmentMatrixRow = [
EquipmentMatrixItem,
EquipmentMatrixItem,
EquipmentMatrixItem,
];
export type EquipmentMatrix = [
EquipmentMatrixRow,
EquipmentMatrixRow,
EquipmentMatrixRow,
];
export interface DuelMember {
tank: TankDefinition;
engine: EngineDefinition;
turret: TurretDefinition;
gun: GunDefinition;
shell: ShellDefinition;
track: TrackDefinition;
equipment: EquipmentMatrix;
pitch: number;
yaw: number;
consumables: number[];
crewMastery: number;
provisions: number[];
camouflage: boolean;
cooldownBooster: number;
}
export interface Duel { export interface Duel {
assigned: boolean; assigned: boolean;
@@ -18,3 +54,9 @@ export const useDuel = create<Duel>()(
export function mutateDuel(recipe: (draft: Duel) => void) { export function mutateDuel(recipe: (draft: Duel) => void) {
useDuel.setState(produce(recipe)); useDuel.setState(produce(recipe));
} }
export const genericDefaultEquipmentMatrix: EquipmentMatrix = [
[1, 1, -1],
[0, 0, 0],
[0, 0, 0],
];
+5 -51
View File
@@ -5,28 +5,13 @@ import { create } from 'zustand';
import { persist, subscribeWithSelector } from 'zustand/middleware'; import { persist, subscribeWithSelector } from 'zustand/middleware';
import { ENVIRONMENTS } from '../app/tools/tankopedia/[id]/components/Lighting'; import { ENVIRONMENTS } from '../app/tools/tankopedia/[id]/components/Lighting';
import { TankType, TreeType } from '../components/Tanks'; import { TankType, TreeType } from '../components/Tanks';
import { import { Tier } from '../core/blitzkrieg/tankDefinitions';
EngineDefinition,
GunDefinition,
ShellDefinition,
TankDefinition,
Tier,
TrackDefinition,
TurretDefinition,
} from '../core/blitzkrieg/tankDefinitions';
export type TankopediaSortBy = keyof typeof SORT_NAMES; export type TankopediaSortBy = keyof typeof SORT_NAMES;
export type TankopediaSortDirection = 'ascending' | 'descending'; export type TankopediaSortDirection = 'ascending' | 'descending';
export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only'; export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only';
export type TankopediaMode = 'model' | 'armor'; export type TankopediaMode = 'model' | 'armor';
export interface DuelMember {
tank: TankDefinition;
engine: EngineDefinition;
turret: TurretDefinition;
gun: GunDefinition;
shell: ShellDefinition;
track: TrackDefinition;
}
interface TankopediaPersistent { interface TankopediaPersistent {
model: { model: {
visual: { visual: {
@@ -50,24 +35,10 @@ interface TankopediaPersistent {
test: TankopediaTestTankDisplay; test: TankopediaTestTankDisplay;
page: number; page: number;
}; };
mode: TankopediaMode;
} }
export type EquipmentMatrix = (-1 | 0 | 1)[][];
interface TankopediaTemporary { interface TankopediaTemporary {
model: {
pose: {
yaw: number;
pitch: number;
};
};
equipmentMatrix: EquipmentMatrix;
mode: TankopediaMode;
consumables: number[];
crewMastery: number;
provisions: number[];
camouflage: boolean;
cooldownBooster: number;
shot?: Shot; shot?: Shot;
} }
@@ -152,31 +123,14 @@ export const useTankopediaPersistent = create<TankopediaPersistent>()(
test: 'include', test: 'include',
page: 0, page: 0,
}, },
mode: 'model',
})), })),
{ name: 'tankopedia', merge }, { name: 'tankopedia', merge },
), ),
); );
export const useTankopediaTemporary = create<TankopediaTemporary>()( export const useTankopediaTemporary = create<TankopediaTemporary>()(
subscribeWithSelector<TankopediaTemporary>(() => ({ subscribeWithSelector<TankopediaTemporary>(() => ({})),
model: {
pose: {
yaw: 0,
pitch: 0,
},
},
equipmentMatrix: [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
],
crewMastery: 1,
mode: 'model',
consumables: [],
provisions: [],
camouflage: true,
cooldownBooster: 0,
})),
); );
export default function mutateTankopediaPersistent( export default function mutateTankopediaPersistent(