feat: some stats

This commit is contained in:
TrèsAbhi committed 2024-04-04 13:51:07 -05:00
1 parent bd412cdaa2
commit f696c5f3c9
15 files changed
+192 -123

No files matched your search

+74 -53
View File
@@ -2,31 +2,63 @@
import { PlusIcon } from '@radix-ui/react-icons'; import { PlusIcon } from '@radix-ui/react-icons';
import { Button, Dialog, Flex, Table, Text } from '@radix-ui/themes'; import { Button, Dialog, Flex, Table, Text } from '@radix-ui/themes';
import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import { use, useCallback, useState } from 'react'; import { use, useState } from 'react';
import PageWrapper from '../../../components/PageWrapper'; import PageWrapper from '../../../components/PageWrapper';
import { tankDefinitions } from '../../../core/blitzkrieg/tankDefinitions'; import { tankCharacteristics } from '../../../core/blitzkrieg/tankCharacteristics';
import { tankIcon } from '../../../core/blitzkrieg/tankIcon'; import { tankIcon } from '../../../core/blitzkrieg/tankIcon';
import mutateCompare, { useCompare } from '../../../stores/compare';
import { genericDefaultEquipmentMatrix } from '../../../stores/duel/constants';
import { TankSearch } from '../tankopedia/components/TankSearch'; import { TankSearch } from '../tankopedia/components/TankSearch';
export default function Page() { export default function Page() {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const tanks = searchParams.get('tanks')?.split(',').map(Number) ?? []; const tanks = useCompare((state) => state.tanks);
const awaitedTankDefinitions = use(tankDefinitions);
const [addTankDialogOpen, setAddTankDialogOpen] = useState(false); const [addTankDialogOpen, setAddTankDialogOpen] = useState(false);
const stats = use(
const createQueryString = useCallback( Promise.all(
(name: string, value: string) => { tanks.map((item) =>
const params = new URLSearchParams(searchParams.toString()); tankCharacteristics({
params.set(name, value); ...item,
stockEngine: item.tank.engines[0],
return params.toString(); stockGun: item.tank.turrets[0].guns[0],
}, stockTrack: item.tank.tracks[0],
[searchParams], stockTurret: item.tank.turrets[0],
}),
),
),
); );
function Row({
value,
decimals,
name,
}: {
name: string;
value: (member: Awaited<ReturnType<typeof tankCharacteristics>>) => number;
decimals?: number;
}) {
const values = stats.map(value);
return (
<Table.Row>
<Table.RowHeaderCell>{name}</Table.RowHeaderCell>
{values.map((value, index) => (
<Table.Cell key={index}>
<Text
color={
index === 0 ? undefined : value > values[0] ? 'green' : 'red'
}
>
{decimals === undefined ? value : value.toFixed(decimals)}
</Text>
</Table.Cell>
))}
</Table.Row>
);
}
return ( return (
<PageWrapper color="crimson" size="100%"> <PageWrapper color="crimson" size="100%">
<Flex justify="center"> <Flex justify="center">
@@ -51,12 +83,26 @@ export default function Page() {
<TankSearch <TankSearch
compact compact
onSelect={(tank) => { onSelect={(tank) => {
tanks.push(tank.id); mutateCompare((draft) => {
router.push( draft.tanks.push({
pathname + camouflage: true,
'?' + consumables: [],
createQueryString('tanks', tanks.join(',')), equipmentMatrix: genericDefaultEquipmentMatrix,
); cooldownBooster: 0,
crewMastery: 1,
provisions: [],
engine: tank.engines.at(-1)!,
gun: tank.turrets.at(-1)!.guns.at(-1)!,
shell: tank.turrets.at(-1)!.guns.at(-1)!.shells[0],
tank,
track: tank.tracks.at(-1)!,
yaw: 0,
pitch: 0,
turret: tank.turrets.at(-1)!,
crewSkills: {},
});
});
setAddTankDialogOpen(false); setAddTankDialogOpen(false);
}} }}
/> />
@@ -76,30 +122,21 @@ export default function Page() {
> >
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.ColumnHeaderCell width="0"> <Table.ColumnHeaderCell />
<Flex
align="center"
justify="center"
style={{ width: '100%', height: '100%' }}
>
Statistics
</Flex>
</Table.ColumnHeaderCell>
{tanks.map((id) => {
const tank = awaitedTankDefinitions[id];
{tanks.map(({ tank }) => {
return ( return (
<Table.ColumnHeaderCell width="0"> <Table.ColumnHeaderCell width="0" key={tank.id}>
<Flex direction="column" align="center"> <Flex direction="column" align="center">
<img <img
src={tankIcon(id)} src={tankIcon(tank.id)}
width={64} width={64}
height={64} height={64}
style={{ style={{
objectFit: 'contain', objectFit: 'contain',
}} }}
/> />
<Text>{tank.name}</Text> <Text style={{ textAlign: 'center' }}>{tank.name}</Text>
</Flex> </Flex>
</Table.ColumnHeaderCell> </Table.ColumnHeaderCell>
); );
@@ -108,23 +145,7 @@ export default function Page() {
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
<Table.Row> <Row name="DPM" value={(stats) => stats.dpm} decimals={0} />
<Table.RowHeaderCell>Stat 1</Table.RowHeaderCell>
{tanks.map((id) => {
const tank = awaitedTankDefinitions[id];
return <Table.Cell>{Math.random()}</Table.Cell>;
})}
</Table.Row>
<Table.Row>
<Table.RowHeaderCell>Stat 2</Table.RowHeaderCell>
{tanks.map((id) => {
const tank = awaitedTankDefinitions[id];
return <Table.Cell>{Math.random()}</Table.Cell>;
})}
</Table.Row>
</Table.Body> </Table.Body>
</Table.Root> </Table.Root>
</Flex> </Flex>
@@ -10,7 +10,9 @@ export function Equipment() {
const awaitedEquipmentDefinitions = use(equipmentDefinitions); const awaitedEquipmentDefinitions = use(equipmentDefinitions);
const equipmentRows = const equipmentRows =
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment]; awaitedEquipmentDefinitions.presets[protagonist.tank.equipment];
const equipmentMatrix = useDuel((state) => state.protagonist!.equipment); const equipmentMatrix = useDuel(
(state) => state.protagonist!.equipmentMatrix,
);
return ( return (
<ConfigurationChildWrapper> <ConfigurationChildWrapper>
@@ -21,7 +23,7 @@ export function Equipment() {
color="red" color="red"
onClick={() => { onClick={() => {
mutateDuel((draft) => { mutateDuel((draft) => {
draft.protagonist!.equipment.forEach((row) => { draft.protagonist!.equipmentMatrix.forEach((row) => {
row.forEach((_, index) => { row.forEach((_, index) => {
row[index] = 0; row[index] = 0;
}); });
@@ -46,10 +48,13 @@ export function Equipment() {
onClick={() => { onClick={() => {
mutateDuel((draft) => { mutateDuel((draft) => {
if (equipmentMatrix[rowIndex][columnIndex] === -1) { if (equipmentMatrix[rowIndex][columnIndex] === -1) {
draft.protagonist!.equipment[rowIndex][columnIndex] = 0; draft.protagonist!.equipmentMatrix[rowIndex][
columnIndex
] = 0;
} else { } else {
draft.protagonist!.equipment[rowIndex][columnIndex] = draft.protagonist!.equipmentMatrix[rowIndex][
-1; columnIndex
] = -1;
} }
}); });
}} }}
@@ -62,9 +67,13 @@ export function Equipment() {
onClick={() => { onClick={() => {
mutateDuel((draft) => { mutateDuel((draft) => {
if (equipmentMatrix[rowIndex][columnIndex] === 1) { if (equipmentMatrix[rowIndex][columnIndex] === 1) {
draft.protagonist!.equipment[rowIndex][columnIndex] = 0; draft.protagonist!.equipmentMatrix[rowIndex][
columnIndex
] = 0;
} else { } else {
draft.protagonist!.equipment[rowIndex][columnIndex] = 1; draft.protagonist!.equipmentMatrix[rowIndex][
columnIndex
] = 1;
} }
}); });
}} }}
@@ -17,6 +17,7 @@ import { isExplosive } from '../../../../../../core/blitz/isExplosive';
import { coefficient } from '../../../../../../core/blitzkrieg/coefficient'; import { coefficient } from '../../../../../../core/blitzkrieg/coefficient';
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions'; import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
import { provisionDefinitions } from '../../../../../../core/blitzkrieg/provisionDefinitions'; import { provisionDefinitions } from '../../../../../../core/blitzkrieg/provisionDefinitions';
import { tankCharacteristics } from '../../../../../../core/blitzkrieg/tankCharacteristics';
import { import {
CREW_MEMBER_NAMES, CREW_MEMBER_NAMES,
GUN_TYPE_NAMES, GUN_TYPE_NAMES,
@@ -24,7 +25,6 @@ import {
} from '../../../../../../core/blitzkrieg/tankDefinitions/constants'; } from '../../../../../../core/blitzkrieg/tankDefinitions/constants';
import { useEquipment } from '../../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useFullScreen } from '../../../../../../hooks/useFullScreen'; import { useFullScreen } from '../../../../../../hooks/useFullScreen';
import { useTankCharacteristics } from '../../../../../../hooks/useTankCharacteristics';
import { mutateDuel, useDuel } from '../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../stores/tankopedia'; import { useTankopediaTemporary } from '../../../../../../stores/tankopedia';
import { Info } from './components/Info'; import { Info } from './components/Info';
@@ -53,7 +53,9 @@ export function Characteristics() {
const crewMemberMastery = commanderMastery * 1.1; const crewMemberMastery = commanderMastery * 1.1;
const consumables = useDuel((state) => state.protagonist!.consumables); const consumables = useDuel((state) => state.protagonist!.consumables);
const camouflage = useDuel((state) => state.protagonist!.camouflage); const camouflage = useDuel((state) => state.protagonist!.camouflage);
const equipmentMatrix = useDuel((state) => state.protagonist!.equipment); const equipmentMatrix = useDuel(
(state) => state.protagonist!.equipmentMatrix,
);
const { tank, turret, gun, engine, track, shell } = useDuel( const { tank, turret, gun, engine, track, shell } = useDuel(
(state) => state.protagonist!, (state) => state.protagonist!,
); );
@@ -64,24 +66,26 @@ export function Characteristics() {
const tankModelDefinition = awaitedModelDefinitions[tank.id]; const tankModelDefinition = awaitedModelDefinitions[tank.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
const stats = useTankCharacteristics({ const stats = use(
tank, tankCharacteristics({
camouflage, tank,
consumables, camouflage,
crewMastery, consumables,
crewSkills, crewMastery,
engine, crewSkills,
equipmentMatrix, engine,
gun, equipmentMatrix,
provisions, gun,
shell, provisions,
turret, shell,
track, turret,
stockEngine, track,
stockGun, stockEngine,
stockTrack, stockGun,
stockTurret, stockTrack,
}); stockTurret,
}),
);
const hasSupercharger = useEquipment(107); const hasSupercharger = useEquipment(107);
const hasCalibratedShells = useEquipment(103); const hasCalibratedShells = useEquipment(103);
@@ -73,7 +73,7 @@ export function Controls() {
const hasImprovedVerticalStabilizer = await hasEquipment( const hasImprovedVerticalStabilizer = await hasEquipment(
122, 122,
duel.protagonist!.tank.equipment, duel.protagonist!.tank.equipment,
duel.protagonist!.equipment, duel.protagonist!.equipmentMatrix,
); );
switch (event) { switch (event) {
@@ -166,7 +166,7 @@ export const TankModel = memo(() => {
const hasImprovedVerticalStabilizer = await hasEquipment( const hasImprovedVerticalStabilizer = await hasEquipment(
122, 122,
duel.protagonist!.tank.equipment, duel.protagonist!.tank.equipment,
duel.protagonist!.equipment, duel.protagonist!.equipmentMatrix,
); );
[pitch, yaw] = applyPitchYawLimits( [pitch, yaw] = applyPitchYawLimits(
pitch, pitch,
@@ -232,7 +232,7 @@ export const TankModel = memo(() => {
const hasImprovedVerticalStabilizer = await hasEquipment( const hasImprovedVerticalStabilizer = await hasEquipment(
122, 122,
duel.protagonist!.tank.equipment, duel.protagonist!.tank.equipment,
duel.protagonist!.equipment, duel.protagonist!.equipmentMatrix,
); );
[pitch, yaw] = applyPitchYawLimits( [pitch, yaw] = applyPitchYawLimits(
pitch - event.movementY * (Math.PI / canvas.height), pitch - event.movementY * (Math.PI / canvas.height),
@@ -159,7 +159,9 @@ export function RotationInputs({ isFullScreen }: RotationInputsProps) {
active={hasCalibratedShells} active={hasCalibratedShells}
onClick={() => { onClick={() => {
mutateDuel((draft) => { mutateDuel((draft) => {
draft.antagonist!.equipment[0][0] = hasCalibratedShells ? 0 : 1; draft.antagonist!.equipmentMatrix[0][0] = hasCalibratedShells
? 0
: 1;
}); });
}} }}
/> />
@@ -168,7 +170,9 @@ export function RotationInputs({ isFullScreen }: RotationInputsProps) {
active={hasEnhancedArmor} active={hasEnhancedArmor}
onClick={() => { onClick={() => {
mutateDuel((draft) => { mutateDuel((draft) => {
draft.protagonist!.equipment[1][1] = hasEnhancedArmor ? 0 : -1; draft.protagonist!.equipmentMatrix[1][1] = hasEnhancedArmor
? 0
: -1;
}); });
}} }}
/> />
@@ -70,8 +70,8 @@ export function CoreArmorSceneComponent({
material.uniforms.explosionRadius.value = shell.explosionRadius; material.uniforms.explosionRadius.value = shell.explosionRadius;
const duel = useDuel.getState(); const duel = useDuel.getState();
await handleProtagonistEquipmentChange(duel.protagonist!.equipment); await handleProtagonistEquipmentChange(duel.protagonist!.equipmentMatrix);
await handleAntagonistEquipmentChange(duel.antagonist!.equipment); await handleAntagonistEquipmentChange(duel.antagonist!.equipmentMatrix);
} }
function handleVisualChange( function handleVisualChange(
visual: TankopediaPersistent['model']['visual'], visual: TankopediaPersistent['model']['visual'],
@@ -112,8 +112,12 @@ export function CoreArmorSceneComponent({
handleShellChange(useDuel.getState().antagonist!.shell); handleShellChange(useDuel.getState().antagonist!.shell);
handleVisualChange(useTankopediaPersistent.getState().model.visual); handleVisualChange(useTankopediaPersistent.getState().model.visual);
handleProtagonistEquipmentChange(useDuel.getState().protagonist!.equipment); handleProtagonistEquipmentChange(
handleAntagonistEquipmentChange(useDuel.getState().antagonist!.equipment); useDuel.getState().protagonist!.equipmentMatrix,
);
handleAntagonistEquipmentChange(
useDuel.getState().antagonist!.equipmentMatrix,
);
const unsubscribes = [ const unsubscribes = [
useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange), useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange),
@@ -122,11 +126,11 @@ export function CoreArmorSceneComponent({
handleVisualChange, handleVisualChange,
), ),
useDuel.subscribe( useDuel.subscribe(
(state) => state.protagonist!.equipment, (state) => state.protagonist!.equipmentMatrix,
handleProtagonistEquipmentChange, handleProtagonistEquipmentChange,
), ),
useDuel.subscribe( useDuel.subscribe(
(state) => state.antagonist!.equipment, (state) => state.antagonist!.equipmentMatrix,
handleAntagonistEquipmentChange, handleAntagonistEquipmentChange,
), ),
]; ];
@@ -76,17 +76,21 @@ export function SpacedArmorSubExternal({
} }
handleShellChange(useDuel.getState().antagonist!.shell); handleShellChange(useDuel.getState().antagonist!.shell);
handleProtagonistEquipmentChange(useDuel.getState().protagonist!.equipment); handleProtagonistEquipmentChange(
handleAntagonistEquipmentChange(useDuel.getState().antagonist!.equipment); useDuel.getState().protagonist!.equipmentMatrix,
);
handleAntagonistEquipmentChange(
useDuel.getState().antagonist!.equipmentMatrix,
);
const unsubscribes = [ const unsubscribes = [
useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange), useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange),
useDuel.subscribe( useDuel.subscribe(
(state) => state.protagonist!.equipment, (state) => state.protagonist!.equipmentMatrix,
handleProtagonistEquipmentChange, handleProtagonistEquipmentChange,
), ),
useDuel.subscribe( useDuel.subscribe(
(state) => state.antagonist!.equipment, (state) => state.antagonist!.equipmentMatrix,
handleAntagonistEquipmentChange, handleAntagonistEquipmentChange,
), ),
]; ];
@@ -87,17 +87,21 @@ export function SpacedArmorSubSpaced({
} }
handleShellChange(useDuel.getState().antagonist!.shell); handleShellChange(useDuel.getState().antagonist!.shell);
handleProtagonistEquipmentChange(useDuel.getState().protagonist!.equipment); handleProtagonistEquipmentChange(
handleAntagonistEquipmentChange(useDuel.getState().antagonist!.equipment); useDuel.getState().protagonist!.equipmentMatrix,
);
handleAntagonistEquipmentChange(
useDuel.getState().antagonist!.equipmentMatrix,
);
const unsubscribes = [ const unsubscribes = [
useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange), useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange),
useDuel.subscribe( useDuel.subscribe(
(state) => state.protagonist!.equipment, (state) => state.protagonist!.equipmentMatrix,
handleProtagonistEquipmentChange, handleProtagonistEquipmentChange,
), ),
useDuel.subscribe( useDuel.subscribe(
(state) => state.antagonist!.equipment, (state) => state.antagonist!.equipmentMatrix,
handleAntagonistEquipmentChange, handleAntagonistEquipmentChange,
), ),
]; ];
@@ -93,12 +93,12 @@ export function SpacedArmorSceneComponent({
const hasCalibratedShells = await hasEquipment( const hasCalibratedShells = await hasEquipment(
103, 103,
duel.antagonist!.tank.equipment, duel.antagonist!.tank.equipment,
duel.antagonist!.equipment, duel.antagonist!.equipmentMatrix,
); );
const hasEnhancedArmor = await hasEquipment( const hasEnhancedArmor = await hasEquipment(
110, 110,
duel.protagonist!.tank.equipment, duel.protagonist!.tank.equipment,
duel.protagonist!.equipment, duel.protagonist!.equipmentMatrix,
); );
const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1; const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1;
const shell = useDuel.getState().antagonist!.shell; const shell = useDuel.getState().antagonist!.shell;
+1 -1
View File
@@ -32,7 +32,7 @@ export async function assignDuelMember(
gun, gun,
shell, shell,
track, track,
equipment: genericDefaultEquipmentMatrix, equipmentMatrix: genericDefaultEquipmentMatrix,
pitch: degToRad(tankModelDefinition.turretRotation?.pitch ?? 0), pitch: degToRad(tankModelDefinition.turretRotation?.pitch ?? 0),
yaw: 0, yaw: 0,
camouflage: true, camouflage: true,
@@ -1,15 +1,15 @@
import { use } from 'react'; import { EquipmentMatrix } from '../../stores/duel';
import { isExplosive } from '../core/blitz/isExplosive'; import { isExplosive } from '../blitz/isExplosive';
import { resolveNearPenetration } from '../core/blitz/resolveNearPenetration'; import { resolveNearPenetration } from '../blitz/resolveNearPenetration';
import { coefficient } from '../core/blitzkrieg/coefficient'; import { coefficient } from './coefficient';
import { degressiveStat } from '../core/blitzkrieg/degressiveStat'; import { degressiveStat } from './degressiveStat';
import { equipmentDefinitions } from '../core/blitzkrieg/equipmentDefinitions'; import { equipmentDefinitions } from './equipmentDefinitions';
import { modelDefinitions } from '../core/blitzkrieg/modelDefinitions'; import { modelDefinitions } from './modelDefinitions';
import { normalizeBoundingBox } from '../core/blitzkrieg/normalizeBoundingBox'; import { normalizeBoundingBox } from './normalizeBoundingBox';
import { progressiveStat } from '../core/blitzkrieg/progressiveStat'; import { progressiveStat } from './progressiveStat';
import { provisionDefinitions } from '../core/blitzkrieg/provisionDefinitions'; import { provisionDefinitions } from './provisionDefinitions';
import { resolveDpm } from '../core/blitzkrieg/resolveDpm'; import { resolveDpm } from './resolveDpm';
import { sum } from '../core/blitzkrieg/sum'; import { sum } from './sum';
import { import {
EngineDefinition, EngineDefinition,
GunDefinition, GunDefinition,
@@ -17,11 +17,10 @@ import {
TankDefinition, TankDefinition,
TrackDefinition, TrackDefinition,
TurretDefinition, TurretDefinition,
} from '../core/blitzkrieg/tankDefinitions'; } from './tankDefinitions';
import { unionBoundingBox } from '../core/blitzkrieg/unionBoundingBox'; import { unionBoundingBox } from './unionBoundingBox';
import { EquipmentMatrix } from '../stores/duel';
export function useTankCharacteristics({ export async function tankCharacteristics({
tank, tank,
turret, turret,
gun, gun,
@@ -56,9 +55,9 @@ export function useTankCharacteristics({
crewSkills: Record<string, number>; crewSkills: Record<string, number>;
camouflage: boolean; camouflage: boolean;
}) { }) {
const awaitedModelDefinitions = use(modelDefinitions); const awaitedModelDefinitions = await modelDefinitions;
const awaitedEquipmentDefinitions = use(equipmentDefinitions); const awaitedEquipmentDefinitions = await equipmentDefinitions;
const awaitedProvisionDefinitions = use(provisionDefinitions); const awaitedProvisionDefinitions = await provisionDefinitions;
const presetRows = awaitedEquipmentDefinitions.presets[tank.equipment]; const presetRows = awaitedEquipmentDefinitions.presets[tank.equipment];
const tankModelDefinition = awaitedModelDefinitions[tank.id]; const tankModelDefinition = awaitedModelDefinitions[tank.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
+2 -1
View File
@@ -10,7 +10,8 @@ export function useEquipment(id: number, antagonist = false) {
const equipmentRows = const equipmentRows =
awaitedEquipmentDefinitions.presets[member.tank.equipment]; awaitedEquipmentDefinitions.presets[member.tank.equipment];
const equipmentMatrix = useDuel( const equipmentMatrix = useDuel(
(state) => state[antagonist ? 'antagonist' : 'protagonist']!.equipment, (state) =>
state[antagonist ? 'antagonist' : 'protagonist']!.equipmentMatrix,
); );
const value = useMemo( const value = useMemo(
() => () =>
+19
View File
@@ -0,0 +1,19 @@
import { produce } from 'immer';
import { create } from 'zustand';
import { DuelMember } from './duel';
export interface CompareMember extends DuelMember {
crewSkills: Record<string, number>;
}
export interface Compare {
tanks: CompareMember[];
}
export const useCompare = create<Compare>()(() => ({
tanks: [],
}));
export default function mutateCompare(recipe: (draft: Compare) => void) {
useCompare.setState(produce(recipe));
}
+1 -1
View File
@@ -29,7 +29,7 @@ export interface DuelMember {
gun: GunDefinition; gun: GunDefinition;
shell: ShellDefinition; shell: ShellDefinition;
track: TrackDefinition; track: TrackDefinition;
equipment: EquipmentMatrix; equipmentMatrix: EquipmentMatrix;
pitch: number; pitch: number;
yaw: number; yaw: number;
consumables: number[]; consumables: number[];