mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: migrated duel store
This commit is contained in:
1 parent
eaaea114a0
commit
332c1787f4
32 files changed
+223
-191
No files matched your search
@@ -12,13 +12,14 @@ import { ConsumablesManager } from '../../../../../../../components/ConsumablesM
|
||||
import { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkit/checkConsumableProvisionInclusivity';
|
||||
import { consumableDefinitions } from '../../../../../../../core/blitzkit/consumableDefinitions';
|
||||
import { useEquipment } from '../../../../../../../hooks/useEquipment';
|
||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
export function Consumables() {
|
||||
const protagonist = useDuel((state) => state.protagonist!);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const protagonist = Duel.use((state) => state.protagonist!);
|
||||
const awaitedConsumableDefinitions = use(consumableDefinitions);
|
||||
const consumables = useDuel((state) => state.protagonist!.consumables);
|
||||
const consumables = Duel.use((state) => state.protagonist!.consumables);
|
||||
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
|
||||
(consumable) =>
|
||||
checkConsumableProvisionInclusivity(
|
||||
@@ -27,7 +28,7 @@ export function Consumables() {
|
||||
protagonist.gun,
|
||||
),
|
||||
);
|
||||
const cooldownBooster = useDuel(
|
||||
const cooldownBooster = Duel.use(
|
||||
(state) => state.protagonist!.cooldownBooster,
|
||||
);
|
||||
const hasConsumableDeliverySystem = useEquipment(118);
|
||||
|
||||
@@ -2,15 +2,16 @@ import { Button, Flex, Heading } from '@radix-ui/themes';
|
||||
import { use } from 'react';
|
||||
import { EquipmentManager } from '../../../../../../../components/EquipmentManager';
|
||||
import { equipmentDefinitions } from '../../../../../../../core/blitzkit/equipmentDefinitions';
|
||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
export function Equipment() {
|
||||
const protagonist = useDuel((state) => state.protagonist!);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const protagonist = Duel.use((state) => state.protagonist!);
|
||||
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
||||
const equipmentPreset =
|
||||
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment];
|
||||
const equipmentMatrix = useDuel(
|
||||
const equipmentMatrix = Duel.use(
|
||||
(state) => state.protagonist!.equipmentMatrix,
|
||||
);
|
||||
|
||||
|
||||
+6
-5
@@ -3,18 +3,19 @@ import { clamp, debounce } from 'lodash';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { GenericTankComponentButton } from '../../../../../../../components/ModuleButtons/GenericTankComponentButton';
|
||||
import { asset } from '../../../../../../../core/blitzkit/asset';
|
||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
export function Miscellaneous() {
|
||||
const camouflage = useDuel((state) => state.protagonist!.camouflage);
|
||||
const cooldownBooster = useDuel(
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const camouflage = Duel.use((state) => state.protagonist!.camouflage);
|
||||
const cooldownBooster = Duel.use(
|
||||
(state) => state.protagonist!.cooldownBooster,
|
||||
);
|
||||
const crewMastery = useDuel((state) => state.protagonist!.crewMastery);
|
||||
const crewMastery = Duel.use((state) => state.protagonist!.crewMastery);
|
||||
const crewInput = useRef<HTMLInputElement>(null);
|
||||
const [crewMasteryDraft, setCrewMasteryDraft] = useState(crewMastery);
|
||||
const { tank } = useDuel((state) => state.protagonist!);
|
||||
const { tank } = Duel.use((state) => state.protagonist!);
|
||||
|
||||
const debouncedApplyDraft = useMemo(
|
||||
() =>
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from '../../../../../../../core/blitzkit/tankDefinitions';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||
import { tankIcon } from '../../../../../../../core/blitzkit/tankIcon';
|
||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
function ModuleButton({
|
||||
@@ -116,17 +116,18 @@ function ModuleButton({
|
||||
}
|
||||
|
||||
export function Modules() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const awaitedTankDefinitions = use(tankDefinitions);
|
||||
const tank = useDuel((state) => state.protagonist!.tank);
|
||||
const tank = Duel.use((state) => state.protagonist!.tank);
|
||||
const hasUpgrades =
|
||||
tank.turrets.length > 1 ||
|
||||
tank.turrets[0].guns.length > 1 ||
|
||||
tank.engines.length > 1 ||
|
||||
tank.tracks.length > 1;
|
||||
const turret = useDuel((state) => state.protagonist!.turret);
|
||||
const gun = useDuel((state) => state.protagonist!.gun);
|
||||
const engine = useDuel((state) => state.protagonist!.engine);
|
||||
const track = useDuel((state) => state.protagonist!.track);
|
||||
const turret = Duel.use((state) => state.protagonist!.turret);
|
||||
const gun = Duel.use((state) => state.protagonist!.gun);
|
||||
const engine = Duel.use((state) => state.protagonist!.engine);
|
||||
const track = Duel.use((state) => state.protagonist!.track);
|
||||
const turret0 = tank.turrets[0];
|
||||
const gun0 = turret0.guns[0];
|
||||
const engine0 = tank.engines[0];
|
||||
|
||||
@@ -3,12 +3,13 @@ import { use } from 'react';
|
||||
import { ProvisionsManager } from '../../../../../../../components/ProvisionsManager';
|
||||
import { availableProvisions } from '../../../../../../../core/blitzkit/availableProvisions';
|
||||
import { provisionDefinitions } from '../../../../../../../core/blitzkit/provisionDefinitions';
|
||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
export function Provisions() {
|
||||
const { tank, gun } = useDuel((state) => state.protagonist!);
|
||||
const provisions = useDuel((state) => state.protagonist!.provisions);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const { tank, gun } = Duel.use((state) => state.protagonist!);
|
||||
const provisions = Duel.use((state) => state.protagonist!.provisions);
|
||||
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
||||
const provisionsList = availableProvisions(
|
||||
tank,
|
||||
|
||||
@@ -28,12 +28,13 @@ import {
|
||||
} from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||
import { useAdExempt } from '../../../../../../hooks/useAdExempt';
|
||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { Info } from './components/Info';
|
||||
import { InfoWithDelta } from './components/InfoWithDelta';
|
||||
|
||||
export function Characteristics() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const exempt = useAdExempt();
|
||||
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
||||
const awaitedModelDefinitions = use(modelDefinitions);
|
||||
@@ -41,10 +42,10 @@ export function Characteristics() {
|
||||
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||
const hasImprovedVentilation = useEquipment(102);
|
||||
const crewMastery = useDuel((state) => state.protagonist!.crewMastery);
|
||||
const crewMastery = Duel.use((state) => state.protagonist!.crewMastery);
|
||||
const [penetrationDistance, setPenetrationDistance] = useState(250);
|
||||
|
||||
const provisions = useDuel((state) => state.protagonist!.provisions);
|
||||
const provisions = Duel.use((state) => state.protagonist!.provisions);
|
||||
const provisionCrewBonus =
|
||||
provisions.reduce(
|
||||
(total, provision) =>
|
||||
@@ -55,12 +56,12 @@ export function Characteristics() {
|
||||
) + (hasImprovedVentilation ? 0.08 : 0);
|
||||
const commanderMastery = crewMastery + provisionCrewBonus;
|
||||
const crewMemberMastery = commanderMastery * 1.1;
|
||||
const consumables = useDuel((state) => state.protagonist!.consumables);
|
||||
const camouflage = useDuel((state) => state.protagonist!.camouflage);
|
||||
const equipmentMatrix = useDuel(
|
||||
const consumables = Duel.use((state) => state.protagonist!.consumables);
|
||||
const camouflage = Duel.use((state) => state.protagonist!.camouflage);
|
||||
const equipmentMatrix = Duel.use(
|
||||
(state) => state.protagonist!.equipmentMatrix,
|
||||
);
|
||||
const { tank, turret, gun, engine, track, shell } = useDuel(
|
||||
const { tank, turret, gun, engine, track, shell } = Duel.use(
|
||||
(state) => state.protagonist!,
|
||||
);
|
||||
const stockEngine = tank.engines[0];
|
||||
|
||||
@@ -8,7 +8,7 @@ import { hasEquipment } from '../../../../../core/blitzkit/hasEquipment';
|
||||
import { modelDefinitions } from '../../../../../core/blitzkit/modelDefinitions';
|
||||
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
|
||||
import { useAwait } from '../../../../../hooks/useAwait';
|
||||
import { useDuel } from '../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../stores/tankopediaEphemeral';
|
||||
|
||||
const poseDistances: Record<Pose, number> = {
|
||||
@@ -18,13 +18,14 @@ const poseDistances: Record<Pose, number> = {
|
||||
};
|
||||
|
||||
export function Controls() {
|
||||
const duelStore = Duel.useStore();
|
||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||
const camera = useThree((state) => state.camera);
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const orbitControls = useRef<OrbitControlsClass>(null);
|
||||
const awaitedModelDefinitions = useAwait(modelDefinitions);
|
||||
const protagonist = useDuel((state) => state.protagonist!);
|
||||
const antagonist = useDuel((state) => state.antagonist!);
|
||||
const protagonist = Duel.use((state) => state.protagonist!);
|
||||
const antagonist = Duel.use((state) => state.antagonist!);
|
||||
const protagonistModelDefinition =
|
||||
awaitedModelDefinitions[protagonist.tank.id];
|
||||
const protagonistTrackModelDefinition =
|
||||
@@ -71,7 +72,7 @@ export function Controls() {
|
||||
);
|
||||
|
||||
async function handlePoseEvent(event: Pose) {
|
||||
const duel = useDuel.getState();
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
122,
|
||||
duel.protagonist!.tank.equipment,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Heading, Text } from '@radix-ui/themes';
|
||||
import PageWrapper from '../../../../../components/PageWrapper';
|
||||
import { useDuel } from '../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../stores/duel';
|
||||
|
||||
export function HistorySection() {
|
||||
const tank = useDuel((state) => state.protagonist!.tank);
|
||||
const tank = Duel.use((state) => state.protagonist!.tank);
|
||||
|
||||
if (!tank.description) return null;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Environment } from '@react-three/drei';
|
||||
import { useModel } from '../../../../../hooks/useModel';
|
||||
import { useDuel } from '../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../stores/duel';
|
||||
import * as TankopediaPersistent from '../../../../../stores/tankopediaPersistent';
|
||||
|
||||
export const ENVIRONMENTS = [
|
||||
@@ -12,7 +12,7 @@ export const ENVIRONMENTS = [
|
||||
] as const;
|
||||
|
||||
export function Lighting() {
|
||||
const protagonist = useDuel((state) => state.protagonist!);
|
||||
const protagonist = Duel.use((state) => state.protagonist!);
|
||||
const { hasPbr } = useModel(protagonist.tank.id);
|
||||
const showEnvironment = TankopediaPersistent.use(
|
||||
(state) => state.model.visual.showEnvironment,
|
||||
|
||||
@@ -7,13 +7,13 @@ import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
||||
import { useWideFormat } from '../../../../../../hooks/useWideFormat';
|
||||
import strings from '../../../../../../lang/en-US.json';
|
||||
import { theme } from '../../../../../../stitches.config';
|
||||
import { useDuel } from '../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
import { TankSandbox } from './TankSandbox';
|
||||
import { Options } from './components/Options';
|
||||
|
||||
export function HeroSection() {
|
||||
const protagonist = useDuel((state) => state.protagonist!.tank);
|
||||
const antagonist = useDuel((state) => state.antagonist!.tank);
|
||||
const protagonist = Duel.use((state) => state.protagonist!.tank);
|
||||
const antagonist = Duel.use((state) => state.antagonist!.tank);
|
||||
const wideFormat = useWideFormat(880);
|
||||
const compareTanks =
|
||||
protagonist.id === antagonist.id
|
||||
|
||||
@@ -7,7 +7,7 @@ import { modelDefinitions } from '../../../../../../core/blitzkit/modelDefinitio
|
||||
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
|
||||
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
|
||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { Controls } from '../Control';
|
||||
import { Lighting } from '../Lighting';
|
||||
@@ -20,7 +20,8 @@ export function TankSandbox() {
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||
const awaitedModelDefinitions = use(modelDefinitions);
|
||||
const protagonist = useDuel((state) => state.protagonist!);
|
||||
const protagonist = Duel.use((state) => state.protagonist!);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[protagonist.turret.id];
|
||||
|
||||
@@ -7,7 +7,7 @@ import { asset } from '../../../../../../core/blitzkit/asset';
|
||||
import { tankDefinitions } from '../../../../../../core/blitzkit/tankDefinitions';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||
import { useWideFormat } from '../../../../../../hooks/useWideFormat';
|
||||
import { useDuel } from '../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
|
||||
export function TreeArrow({
|
||||
style,
|
||||
@@ -53,7 +53,7 @@ export function TreeArrow({
|
||||
export function TechTreeSection() {
|
||||
const wideFormat = useWideFormat(720);
|
||||
const awaitedTankDefinitions = use(tankDefinitions);
|
||||
const tank = useDuel((state) => state.protagonist!.tank);
|
||||
const tank = Duel.use((state) => state.protagonist!.tank);
|
||||
|
||||
if (tank.treeType !== 'researchable') return null;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
import { useEquipment } from '../../../../../../../hooks/useEquipment';
|
||||
import { useFullScreen } from '../../../../../../../hooks/useFullScreen';
|
||||
import * as App from '../../../../../../../stores/app';
|
||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../../../../../../stores/tankopediaPersistent';
|
||||
import { TankSearch } from '../../../../components/TankSearch';
|
||||
@@ -66,13 +66,14 @@ export function Options() {
|
||||
(state) => state.model.visual.environment,
|
||||
);
|
||||
const developerMode = App.use((state) => state.developerMode);
|
||||
const antagonistGun = useDuel((state) => state.antagonist!.gun);
|
||||
const antagonistShell = useDuel((state) => state.antagonist!.shell);
|
||||
const antagonistGun = Duel.use((state) => state.antagonist!.gun);
|
||||
const antagonistShell = Duel.use((state) => state.antagonist!.shell);
|
||||
const [antagonistSelectorOpen, setAntagonistSelectorOpen] = useState(false);
|
||||
const antagonistTank = useDuel((state) => state.antagonist!.tank);
|
||||
const antagonistTurret = useDuel((state) => state.antagonist!.turret);
|
||||
const antagonistTank = Duel.use((state) => state.antagonist!.tank);
|
||||
const antagonistTurret = Duel.use((state) => state.antagonist!.turret);
|
||||
const hasCalibratedShells = useEquipment(103, true);
|
||||
const [tab, setTab] = useState('search');
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const hasEnhancedArmor = useEquipment(110);
|
||||
|
||||
return (
|
||||
|
||||
@@ -21,12 +21,14 @@ import {
|
||||
import { normalizeAngleRad } from '../../../../../../../core/math/normalizeAngleRad';
|
||||
import { useAwait } from '../../../../../../../hooks/useAwait';
|
||||
import { useModel } from '../../../../../../../hooks/useModel';
|
||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
|
||||
|
||||
export const TankModel = memo(() => {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const duelStore = Duel.useStore();
|
||||
const awaitedModelDefinitions = useAwait(modelDefinitions);
|
||||
const protagonist = useDuel((draft) => draft.protagonist!);
|
||||
const protagonist = Duel.use((draft) => draft.protagonist!);
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const hullContainer = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null);
|
||||
@@ -216,7 +218,7 @@ export const TankModel = memo(() => {
|
||||
}
|
||||
}
|
||||
async function handlePointerMove(event: PointerEvent) {
|
||||
const duel = useDuel.getState();
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
122,
|
||||
duel.protagonist!.tank.equipment,
|
||||
@@ -296,7 +298,7 @@ export const TankModel = memo(() => {
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
async function handlePointerMove(event: PointerEvent) {
|
||||
const duel = useDuel.getState();
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
122,
|
||||
duel.protagonist!.tank.equipment,
|
||||
|
||||
@@ -10,11 +10,12 @@ import {
|
||||
} from '../../../../../../core/blitzkit/modelTransform';
|
||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
||||
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
import * as styles from './index.css';
|
||||
|
||||
export function RotationInputs() {
|
||||
const protagonist = useDuel((state) => state.protagonist!);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const protagonist = Duel.use((state) => state.protagonist!);
|
||||
const awaitedModelDefinitions = use(modelDefinitions);
|
||||
const yawInput = useRef<HTMLInputElement>(null);
|
||||
const pitchInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import { CaretRightIcon, UpdateIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Dialog, Flex, Heading } from '@radix-ui/themes';
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
import { use, useState } from 'react';
|
||||
import { TREE_TYPE_ICONS } from '../../../../../components/Tanks/components/Item/constants';
|
||||
import { assignDuelMember } from '../../../../../core/blitzkit/assignDuelMember';
|
||||
import { provisionDefinitions } from '../../../../../core/blitzkit/provisionDefinitions';
|
||||
import { pushTankopediaPath } from '../../../../../core/blitzkit/pushTankopediaPath';
|
||||
import { useDuel } from '../../../../../stores/duel';
|
||||
import { tankToDuelMember } from '../../../../../core/blitzkit/tankToDuelMember';
|
||||
import * as Duel from '../../../../../stores/duel';
|
||||
import { TankSearch } from '../../components/TankSearch';
|
||||
|
||||
export function Title() {
|
||||
const protagonist = useDuel((state) => state.protagonist!);
|
||||
const antagonist = useDuel((state) => state.antagonist!);
|
||||
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
||||
const protagonist = Duel.use((state) => state.protagonist!);
|
||||
const antagonist = Duel.use((state) => state.antagonist!);
|
||||
const compareTanks =
|
||||
protagonist.tank.id === antagonist.tank.id
|
||||
? [protagonist.tank.id]
|
||||
: [protagonist.tank.id, antagonist.tank.id];
|
||||
const [changeTankDialogOpen, setChangeTankDialogOpen] = useState(false);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -76,9 +79,15 @@ export function Title() {
|
||||
<TankSearch
|
||||
compact
|
||||
onSelect={(tank) => {
|
||||
assignDuelMember('protagonist', tank.id);
|
||||
setChangeTankDialogOpen(false);
|
||||
pushTankopediaPath(tank.id);
|
||||
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist = tankToDuelMember(
|
||||
tank,
|
||||
awaitedProvisionDefinitions,
|
||||
);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Flex, Heading, Link, Text } from '@radix-ui/themes';
|
||||
import { use } from 'react';
|
||||
import PageWrapper from '../../../../../components/PageWrapper';
|
||||
import { videoDefinitions } from '../../../../../core/blitzkit/videos';
|
||||
import { useDuel } from '../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../stores/duel';
|
||||
|
||||
export function VideoSection() {
|
||||
const tank = useDuel((state) => state.protagonist!.tank);
|
||||
const tank = Duel.use((state) => state.protagonist!.tank);
|
||||
const awaitedVideoDefinitions = use(videoDefinitions);
|
||||
const videos = awaitedVideoDefinitions[tank.id]?.videos ?? [];
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import { AdMidSectionResponsive } from '../../../../components/AdMidSectionResponsive';
|
||||
import PageWrapper from '../../../../components/PageWrapper';
|
||||
import { assignDuelMember } from '../../../../core/blitzkit/assignDuelMember';
|
||||
import { useAdExempt } from '../../../../hooks/useAdExempt';
|
||||
import { mutateDuel, useDuel } from '../../../../stores/duel';
|
||||
import * as Duel from '../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../stores/tankopediaEphemeral';
|
||||
import { HistorySection } from './components/HistorySection';
|
||||
import { CharacteristicsSection } from './components/Model/CharacteristicsSection';
|
||||
@@ -14,15 +13,14 @@ import { TechTreeSection } from './components/Model/TechTreeSection';
|
||||
import { ShotDisplaySection } from './components/ShotDisplaySection';
|
||||
import { VideoSection } from './components/VideoSection';
|
||||
|
||||
// TODO: remove this param if not needed for duel assignment
|
||||
export default function Page({ params }: { params: { id: string } }) {
|
||||
const initialId = parseInt(params.id);
|
||||
const assigned = useDuel((state) => state.assigned);
|
||||
const exempt = useAdExempt();
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const duelStore = Duel.useStore();
|
||||
|
||||
useEffect(() => {
|
||||
assignDuelMember('both', initialId);
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (document.activeElement instanceof HTMLInputElement) return;
|
||||
|
||||
@@ -59,10 +57,10 @@ export default function Page({ params }: { params: { id: string } }) {
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [initialId, assigned]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = useDuel.subscribe(
|
||||
const unsubscribe = duelStore.subscribe(
|
||||
(state) => state.protagonist?.tank.id,
|
||||
() => {
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
@@ -76,19 +74,15 @@ export default function Page({ params }: { params: { id: string } }) {
|
||||
|
||||
return (
|
||||
<PageWrapper p="0" noMaxWidth color="purple" size={1600}>
|
||||
{assigned && (
|
||||
<>
|
||||
<HeroSection />
|
||||
<ShotDisplaySection />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
<CharacteristicsSection />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
<TechTreeSection />
|
||||
<VideoSection />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
<HistorySection />
|
||||
</>
|
||||
)}
|
||||
<HeroSection />
|
||||
<ShotDisplaySection />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
<CharacteristicsSection />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
<TechTreeSection />
|
||||
<VideoSection />
|
||||
{!exempt && <AdMidSectionResponsive />}
|
||||
<HistorySection />
|
||||
</PageWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
import * as Duel from '../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||
import * as TankopediaSort from '../../../stores/tankopediaSort';
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<TankopediaPersistent.Provider>
|
||||
<TankopediaEphemeral.Provider>
|
||||
<TankopediaSort.Provider>{children}</TankopediaSort.Provider>
|
||||
</TankopediaEphemeral.Provider>
|
||||
</TankopediaPersistent.Provider>
|
||||
<Duel.Provider>
|
||||
<TankopediaPersistent.Provider>
|
||||
<TankopediaEphemeral.Provider>
|
||||
<TankopediaSort.Provider>{children}</TankopediaSort.Provider>
|
||||
</TankopediaEphemeral.Provider>
|
||||
</TankopediaPersistent.Provider>
|
||||
</Duel.Provider>
|
||||
);
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||
import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModelDefinitions } from '../../../hooks/useModelDefinitions';
|
||||
import { useDuel } from '../../../stores/duel';
|
||||
import * as Duel from '../../../stores/duel';
|
||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||
import { CoreArmorSceneComponent } from './CoreArmorSceneComponent';
|
||||
import { ShotDisplay } from './ShotDisplay';
|
||||
@@ -22,7 +22,7 @@ export const CoreArmorScene = memo(() => {
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||
const initialTankopediaState = tankopediaPersistentStore.getState();
|
||||
const protagonist = useDuel((draft) => draft.protagonist!);
|
||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||
|
||||
useEffect(() => {
|
||||
if (!modelDefinitions) return;
|
||||
@@ -110,10 +110,10 @@ export const CoreArmorScene = memo(() => {
|
||||
return unsubscribe;
|
||||
});
|
||||
|
||||
const tank = useDuel((state) => state.protagonist!.tank);
|
||||
const track = useDuel((state) => state.protagonist!.track);
|
||||
const turret = useDuel((state) => state.protagonist!.turret);
|
||||
const gun = useDuel((state) => state.protagonist!.gun);
|
||||
const tank = Duel.use((state) => state.protagonist!.tank);
|
||||
const track = Duel.use((state) => state.protagonist!.track);
|
||||
const turret = Duel.use((state) => state.protagonist!.turret);
|
||||
const gun = Duel.use((state) => state.protagonist!.gun);
|
||||
const armorGltf = useArmor(tank.id);
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const tankModelDefinition = modelDefinitions[tank.id];
|
||||
|
||||
@@ -9,7 +9,7 @@ import { resolvePenetrationCoefficient } from '../../../../core/blitz/resolvePen
|
||||
import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
||||
import { ShellDefinition } from '../../../../core/blitzkit/tankDefinitions';
|
||||
import { EquipmentMatrix, useDuel } from '../../../../stores/duel';
|
||||
import * as Duel from '../../../../stores/duel';
|
||||
import * as TankopediaPersistent from '../../../../stores/tankopediaPersistent';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
@@ -29,6 +29,7 @@ export function CoreArmorSceneComponent({
|
||||
thickness,
|
||||
}: CoreArmorSceneComponentProps) {
|
||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||
const duelStore = Duel.useStore();
|
||||
const material = new ShaderMaterial({
|
||||
fragmentShader,
|
||||
vertexShader,
|
||||
@@ -70,7 +71,7 @@ export function CoreArmorSceneComponent({
|
||||
material.uniforms.damage.value = shell.damage.armor;
|
||||
material.uniforms.explosionRadius.value = shell.explosionRadius;
|
||||
|
||||
const duel = useDuel.getState();
|
||||
const duel = duelStore.getState();
|
||||
await handleProtagonistEquipmentChange(duel.protagonist!.equipmentMatrix);
|
||||
await handleAntagonistEquipmentChange(duel.antagonist!.equipmentMatrix);
|
||||
}
|
||||
@@ -83,9 +84,9 @@ export function CoreArmorSceneComponent({
|
||||
material.wireframe = visual.wireframe;
|
||||
}
|
||||
async function handleProtagonistEquipmentChange(
|
||||
equipment: EquipmentMatrix,
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
const duel = useDuel.getState();
|
||||
const duel = duelStore.getState();
|
||||
const hasEnhancedArmor = await hasEquipment(
|
||||
110,
|
||||
duel.protagonist!.tank.equipment,
|
||||
@@ -95,8 +96,10 @@ export function CoreArmorSceneComponent({
|
||||
? thickness * 1.03
|
||||
: thickness;
|
||||
}
|
||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||
const duel = useDuel.getState();
|
||||
async function handleAntagonistEquipmentChange(
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
const duel = duelStore.getState();
|
||||
const shell = duel.antagonist!.shell;
|
||||
const penetration = resolveNearPenetration(shell.penetration);
|
||||
const hasCalibratedShells = await hasEquipment(
|
||||
@@ -110,26 +113,29 @@ export function CoreArmorSceneComponent({
|
||||
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
|
||||
}
|
||||
|
||||
handleShellChange(useDuel.getState().antagonist!.shell);
|
||||
handleShellChange(duelStore.getState().antagonist!.shell);
|
||||
handleVisualChange(tankopediaPersistentStore.getState().model.visual);
|
||||
handleProtagonistEquipmentChange(
|
||||
useDuel.getState().protagonist!.equipmentMatrix,
|
||||
duelStore.getState().protagonist!.equipmentMatrix,
|
||||
);
|
||||
handleAntagonistEquipmentChange(
|
||||
useDuel.getState().antagonist!.equipmentMatrix,
|
||||
duelStore.getState().antagonist!.equipmentMatrix,
|
||||
);
|
||||
|
||||
const unsubscribes = [
|
||||
useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange),
|
||||
duelStore.subscribe(
|
||||
(state) => state.antagonist!.shell,
|
||||
handleShellChange,
|
||||
),
|
||||
tankopediaPersistentStore.subscribe(
|
||||
(state) => state.model.visual,
|
||||
handleVisualChange,
|
||||
),
|
||||
useDuel.subscribe(
|
||||
duelStore.subscribe(
|
||||
(state) => state.protagonist!.equipmentMatrix,
|
||||
handleProtagonistEquipmentChange,
|
||||
),
|
||||
useDuel.subscribe(
|
||||
duelStore.subscribe(
|
||||
(state) => state.antagonist!.equipmentMatrix,
|
||||
handleAntagonistEquipmentChange,
|
||||
),
|
||||
|
||||
@@ -11,7 +11,7 @@ import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||
import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModel } from '../../../hooks/useModel';
|
||||
import { useModelDefinitions } from '../../../hooks/useModelDefinitions';
|
||||
import { useDuel } from '../../../stores/duel';
|
||||
import * as Duel from '../../../stores/duel';
|
||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||
import { SpacedArmorSceneComponent } from './SpacedArmorSceneComponent';
|
||||
|
||||
@@ -32,7 +32,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||
const initialTankopediaState = tankopediaPersistentStore.getState();
|
||||
const protagonist = useDuel((draft) => draft.protagonist!);
|
||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||
|
||||
useEffect(() => {
|
||||
if (!modelDefinitions) return;
|
||||
@@ -120,10 +120,10 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
return unsubscribe;
|
||||
});
|
||||
|
||||
const tank = useDuel((state) => state.protagonist!.tank);
|
||||
const track = useDuel((state) => state.protagonist!.track);
|
||||
const turret = useDuel((state) => state.protagonist!.turret);
|
||||
const gun = useDuel((state) => state.protagonist!.gun);
|
||||
const tank = Duel.use((state) => state.protagonist!.tank);
|
||||
const track = Duel.use((state) => state.protagonist!.track);
|
||||
const turret = Duel.use((state) => state.protagonist!.turret);
|
||||
const gun = Duel.use((state) => state.protagonist!.gun);
|
||||
const armorGltf = useArmor(tank.id);
|
||||
const { gltf: modelGltf } = useModel(tank.id);
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
|
||||
+17
-11
@@ -12,7 +12,7 @@ import { resolvePenetrationCoefficient } from '../../../../../../core/blitz/reso
|
||||
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../../../core/blitzkit/jsxTree';
|
||||
import { ShellDefinition } from '../../../../../../core/blitzkit/tankDefinitions';
|
||||
import { EquipmentMatrix, useDuel } from '../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
import { ArmorType } from '../../../SpacedArmorScene';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
@@ -30,6 +30,7 @@ export function SpacedArmorSubExternal({
|
||||
variant,
|
||||
clip,
|
||||
}: SpacedArmorSubExternalProps) {
|
||||
const duelStore = Duel.useStore();
|
||||
const material = new ShaderMaterial({
|
||||
fragmentShader,
|
||||
vertexShader,
|
||||
@@ -53,9 +54,9 @@ export function SpacedArmorSubExternal({
|
||||
);
|
||||
}
|
||||
async function handleProtagonistEquipmentChange(
|
||||
equipment: EquipmentMatrix,
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
const duel = useDuel.getState();
|
||||
const duel = duelStore.getState();
|
||||
const hasEnhancedArmor = await hasEquipment(
|
||||
110,
|
||||
duel.protagonist!.tank.equipment,
|
||||
@@ -65,8 +66,10 @@ export function SpacedArmorSubExternal({
|
||||
? thickness * 1.03
|
||||
: thickness;
|
||||
}
|
||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||
const duel = useDuel.getState();
|
||||
async function handleAntagonistEquipmentChange(
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
const duel = duelStore.getState();
|
||||
const shell = duel.antagonist!.shell;
|
||||
const penetration = resolveNearPenetration(shell.penetration);
|
||||
const hasCalibratedShells = await hasEquipment(
|
||||
@@ -80,21 +83,24 @@ export function SpacedArmorSubExternal({
|
||||
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
|
||||
}
|
||||
|
||||
handleShellChange(useDuel.getState().antagonist!.shell);
|
||||
handleShellChange(duelStore.getState().antagonist!.shell);
|
||||
handleProtagonistEquipmentChange(
|
||||
useDuel.getState().protagonist!.equipmentMatrix,
|
||||
duelStore.getState().protagonist!.equipmentMatrix,
|
||||
);
|
||||
handleAntagonistEquipmentChange(
|
||||
useDuel.getState().antagonist!.equipmentMatrix,
|
||||
duelStore.getState().antagonist!.equipmentMatrix,
|
||||
);
|
||||
|
||||
const unsubscribes = [
|
||||
useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange),
|
||||
useDuel.subscribe(
|
||||
duelStore.subscribe(
|
||||
(state) => state.antagonist!.shell,
|
||||
handleShellChange,
|
||||
),
|
||||
duelStore.subscribe(
|
||||
(state) => state.protagonist!.equipmentMatrix,
|
||||
handleProtagonistEquipmentChange,
|
||||
),
|
||||
useDuel.subscribe(
|
||||
duelStore.subscribe(
|
||||
(state) => state.antagonist!.equipmentMatrix,
|
||||
handleAntagonistEquipmentChange,
|
||||
),
|
||||
|
||||
+18
-12
@@ -13,7 +13,7 @@ import { resolvePenetrationCoefficient } from '../../../../../../core/blitz/reso
|
||||
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../../../core/blitzkit/jsxTree';
|
||||
import { ShellDefinition } from '../../../../../../core/blitzkit/tankDefinitions';
|
||||
import { EquipmentMatrix, useDuel } from '../../../../../../stores/duel';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
import { ArmorType } from '../../../SpacedArmorScene';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
@@ -32,6 +32,7 @@ export function SpacedArmorSubSpaced({
|
||||
node,
|
||||
thickness,
|
||||
}: SpacedArmorSubSpacedProps) {
|
||||
const duelStore = Duel.useStore();
|
||||
const material = new ShaderMaterial({
|
||||
fragmentShader,
|
||||
vertexShader,
|
||||
@@ -63,9 +64,9 @@ export function SpacedArmorSubSpaced({
|
||||
);
|
||||
}
|
||||
async function handleProtagonistEquipmentChange(
|
||||
equipment: EquipmentMatrix,
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
const duel = useDuel.getState();
|
||||
const duel = duelStore.getState();
|
||||
const hasEnhancedArmor = await hasEquipment(
|
||||
110,
|
||||
duel.protagonist!.tank.equipment,
|
||||
@@ -75,9 +76,11 @@ export function SpacedArmorSubSpaced({
|
||||
? thickness * 1.03
|
||||
: thickness;
|
||||
}
|
||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||
const duel = useDuel.getState();
|
||||
const shell = useDuel.getState().antagonist!.shell;
|
||||
async function handleAntagonistEquipmentChange(
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
const duel = duelStore.getState();
|
||||
const shell = duelStore.getState().antagonist!.shell;
|
||||
const penetration = resolveNearPenetration(shell.penetration);
|
||||
const hasCalibratedShells = await hasEquipment(
|
||||
103,
|
||||
@@ -90,21 +93,24 @@ export function SpacedArmorSubSpaced({
|
||||
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
|
||||
}
|
||||
|
||||
handleShellChange(useDuel.getState().antagonist!.shell);
|
||||
handleShellChange(duelStore.getState().antagonist!.shell);
|
||||
handleProtagonistEquipmentChange(
|
||||
useDuel.getState().protagonist!.equipmentMatrix,
|
||||
duelStore.getState().protagonist!.equipmentMatrix,
|
||||
);
|
||||
handleAntagonistEquipmentChange(
|
||||
useDuel.getState().antagonist!.equipmentMatrix,
|
||||
duelStore.getState().antagonist!.equipmentMatrix,
|
||||
);
|
||||
|
||||
const unsubscribes = [
|
||||
useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange),
|
||||
useDuel.subscribe(
|
||||
duelStore.subscribe(
|
||||
(state) => state.antagonist!.shell,
|
||||
handleShellChange,
|
||||
),
|
||||
duelStore.subscribe(
|
||||
(state) => state.protagonist!.equipmentMatrix,
|
||||
handleProtagonistEquipmentChange,
|
||||
),
|
||||
useDuel.subscribe(
|
||||
duelStore.subscribe(
|
||||
(state) => state.antagonist!.equipmentMatrix,
|
||||
handleAntagonistEquipmentChange,
|
||||
),
|
||||
|
||||
@@ -16,7 +16,7 @@ import { resolvePenetrationCoefficient } from '../../../../core/blitz/resolvePen
|
||||
import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
||||
import { ShellType } from '../../../../core/blitzkit/tankDefinitions';
|
||||
import { useDuel } from '../../../../stores/duel';
|
||||
import * as Duel from '../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../stores/tankopediaEphemeral';
|
||||
import { ArmorType } from '../SpacedArmorScene';
|
||||
import { SpacedArmorSubExternal } from './components/SpacedArmorSubExternal';
|
||||
@@ -74,6 +74,7 @@ export function SpacedArmorSceneComponent({
|
||||
}: SpacedArmorSceneComponentProps) {
|
||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||
const camera = useThree((state) => state.camera);
|
||||
const duelStore = Duel.useStore();
|
||||
|
||||
async function shoot(
|
||||
point: Vector3,
|
||||
@@ -81,7 +82,7 @@ export function SpacedArmorSceneComponent({
|
||||
allowRicochet: boolean,
|
||||
remainingPenetrationInput?: number,
|
||||
) {
|
||||
const { antagonist, protagonist } = useDuel.getState();
|
||||
const { antagonist, protagonist } = duelStore.getState();
|
||||
const shell = antagonist!.shell;
|
||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||
const shot: TankopediaEphemeral.Shot = {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { mutateDuel } from '../../stores/duel';
|
||||
import { provisionDefinitions } from './provisionDefinitions';
|
||||
import { tankDefinitions } from './tankDefinitions';
|
||||
import { tankToDuelMember } from './tankToDuelMember';
|
||||
|
||||
export async function assignDuelMember(
|
||||
type: 'antagonist' | 'protagonist' | 'both',
|
||||
id: number,
|
||||
) {
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const tank = awaitedTankDefinitions[id];
|
||||
const member = tankToDuelMember(tank, awaitedProvisionDefinitions);
|
||||
|
||||
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 };
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
import { Draft, produce } from 'immer';
|
||||
import { createContext, ReactNode, useContext, useRef } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useMemo,
|
||||
use as useReact,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { StoreApi, useStore as useStoreZustand } from 'zustand';
|
||||
|
||||
interface StoreProviderProps {
|
||||
@@ -13,16 +21,24 @@ type ExtractState<S> = S extends {
|
||||
? T
|
||||
: never;
|
||||
|
||||
interface InitialContext {
|
||||
pathName: string;
|
||||
}
|
||||
|
||||
export function createNextSafeStore<Store extends StoreApi<unknown>>(
|
||||
initial: () => Store,
|
||||
initial: (context: InitialContext) => Store | Promise<Store>,
|
||||
) {
|
||||
type Type = Store extends StoreApi<infer T> ? T : never;
|
||||
|
||||
const Context = createContext<Store | undefined>(undefined);
|
||||
|
||||
function Provider({ children }: StoreProviderProps) {
|
||||
const pathName = usePathname();
|
||||
const storeRef = useRef<Store>();
|
||||
if (!storeRef.current) storeRef.current = initial() as Store;
|
||||
const context = { pathName };
|
||||
const storePromise = useMemo(async () => await initial(context), []);
|
||||
const awaitedStore = useReact(storePromise);
|
||||
if (!storeRef.current) storeRef.current = awaitedStore;
|
||||
|
||||
return (
|
||||
<Context.Provider value={storeRef.current}>{children}</Context.Provider>
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
TankDefinition,
|
||||
tankDefinitions,
|
||||
} from '../core/blitzkit/tankDefinitions';
|
||||
import { useDuel } from '../stores/duel';
|
||||
import * as Duel from '../stores/duel';
|
||||
|
||||
export enum UseEccentricityMode {
|
||||
Tier,
|
||||
@@ -13,7 +13,7 @@ export enum UseEccentricityMode {
|
||||
|
||||
export function useEccentricity(mode: UseEccentricityMode) {
|
||||
const awaitedTankDefinitions = use(tankDefinitions);
|
||||
const tank = useDuel((state) => state.protagonist!.tank);
|
||||
const tank = Duel.use((state) => state.protagonist!.tank);
|
||||
const similarTanks = useMemo(
|
||||
() =>
|
||||
Object.values(awaitedTankDefinitions).filter(
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { use, useMemo } from 'react';
|
||||
import { equipmentDefinitions } from '../core/blitzkit/equipmentDefinitions';
|
||||
import { useDuel } from '../stores/duel';
|
||||
import * as Duel from '../stores/duel';
|
||||
|
||||
export function useEquipment(id: number, antagonist = false) {
|
||||
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
||||
const member = useDuel(
|
||||
const member = Duel.use(
|
||||
(state) => state[antagonist ? 'antagonist' : 'protagonist']!,
|
||||
);
|
||||
const equipmentRows =
|
||||
awaitedEquipmentDefinitions.presets[member.tank.equipment];
|
||||
const equipmentMatrix = useDuel(
|
||||
const equipmentMatrix = Duel.use(
|
||||
(state) =>
|
||||
state[antagonist ? 'antagonist' : 'protagonist']!.equipmentMatrix,
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useDuel } from '../stores/duel';
|
||||
import * as Duel from '../stores/duel';
|
||||
|
||||
export function useProvision(id: number) {
|
||||
const provisions = useDuel((state) => state.protagonist!.provisions);
|
||||
const provisions = Duel.use((state) => state.protagonist!.provisions);
|
||||
return provisions.includes(id);
|
||||
}
|
||||
+23
-12
@@ -1,14 +1,19 @@
|
||||
import { produce } from 'immer';
|
||||
'use client';
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { subscribeWithSelector } from 'zustand/middleware';
|
||||
import { provisionDefinitions } from '../../core/blitzkit/provisionDefinitions';
|
||||
import {
|
||||
EngineDefinition,
|
||||
GunDefinition,
|
||||
ShellDefinition,
|
||||
TankDefinition,
|
||||
tankDefinitions,
|
||||
TrackDefinition,
|
||||
TurretDefinition,
|
||||
} from '../../core/blitzkit/tankDefinitions';
|
||||
import { tankToDuelMember } from '../../core/blitzkit/tankToDuelMember';
|
||||
import { createNextSafeStore } from '../../core/zustand/createNextSafeStore';
|
||||
|
||||
type EquipmentMatrixItem = -1 | 0 | 1;
|
||||
type EquipmentMatrixRow = [
|
||||
@@ -40,17 +45,23 @@ export interface DuelMember {
|
||||
}
|
||||
|
||||
export interface Duel {
|
||||
assigned: boolean;
|
||||
protagonist?: DuelMember;
|
||||
antagonist?: DuelMember;
|
||||
protagonist: DuelMember;
|
||||
antagonist: DuelMember;
|
||||
}
|
||||
|
||||
export const useDuel = create<Duel>()(
|
||||
subscribeWithSelector<Duel>(() => ({
|
||||
assigned: false,
|
||||
})),
|
||||
export const { Provider, use, useMutation, useStore } = createNextSafeStore(
|
||||
async ({ pathName }) => {
|
||||
const id = Number(pathName.split('/')[3]);
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const tank = awaitedTankDefinitions[id];
|
||||
const protagonist = tankToDuelMember(tank, awaitedProvisionDefinitions);
|
||||
|
||||
return create<Duel>()(
|
||||
subscribeWithSelector<Duel>(() => ({
|
||||
protagonist,
|
||||
antagonist: protagonist,
|
||||
})),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export function mutateDuel(recipe: (draft: Duel) => void) {
|
||||
useDuel.setState(produce(recipe));
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { mintDark } from '@radix-ui/colors';
|
||||
import { createColors } from 'bepaint';
|
||||
|
||||
export const mintTheme = {
|
||||
colors: createColors(mintDark),
|
||||
};
|
||||
Reference in new issue
Block a user