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