feat: battle count

This commit is contained in:
TrèsAbhi committed 2024-08-25 21:06:56 -05:00
1 parent 22831cc268
commit 9a686230c6
3 files changed
+89 -13

No files matched your search

@@ -2,13 +2,36 @@ import { Box, Flex, Text } from '@radix-ui/themes';
import Link from 'next/link'; import Link from 'next/link';
import { use } from 'react'; import { use } from 'react';
import { asset } from '../../../../../../../../core/blitzkit/asset'; import { asset } from '../../../../../../../../core/blitzkit/asset';
import { averageDefinitions } from '../../../../../../../../core/blitzkit/averageDefinitions';
import { tankDefinitions } from '../../../../../../../../core/blitzkit/tankDefinitions'; import { tankDefinitions } from '../../../../../../../../core/blitzkit/tankDefinitions';
import { TIER_ROMAN_NUMERALS } from '../../../../../../../../core/blitzkit/tankDefinitions/constants'; import { TIER_ROMAN_NUMERALS } from '../../../../../../../../core/blitzkit/tankDefinitions/constants';
import { formatCompact } from '../../../../../../../../core/math/formatCompact'; import { formatCompact } from '../../../../../../../../core/math/formatCompact';
import * as TankopediaEphemeral from '../../../../../../../../stores/tankopediaEphemeral';
export function Node({ id, highlight }: { id: number; highlight?: boolean }) { interface NodeProps {
id: number;
nextIds?: number[];
highlight?: boolean;
}
export function Node({ id, highlight, nextIds }: NodeProps) {
const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier);
const awaitedTankDefinitions = use(tankDefinitions); const awaitedTankDefinitions = use(tankDefinitions);
const tank = awaitedTankDefinitions[id]; const tank = awaitedTankDefinitions[id];
const nextTanks = nextIds?.map((id) => awaitedTankDefinitions[id]);
const awaitedAverageDefinitions = use(averageDefinitions);
const thisTankXp =
tank.xp === undefined || tank.tier === 1 ? 0 : tank.xp! / xpMultiplier;
const nextTanksXp = nextTanks?.reduce(
(xp, tank) =>
xp +
(tank.xp === undefined || tank.tier === 1 ? 0 : tank.xp! / xpMultiplier),
0,
);
const averages = awaitedAverageDefinitions.averages[tank.id]?.mu;
const averageXp = averages ? averages.xp / averages.battles : undefined;
const games =
averages && nextTanks ? Math.round(nextTanksXp! / averageXp!) : undefined;
return ( return (
<Link <Link
@@ -60,7 +83,6 @@ export function Node({ id, highlight }: { id: number; highlight?: boolean }) {
</Text> </Text>
<Text wrap="nowrap">{tank.name}</Text> <Text wrap="nowrap">{tank.name}</Text>
</Flex> </Flex>
<Flex gap="2" align="center"> <Flex gap="2" align="center">
<Text color="gray" size="1" wrap="nowrap"> <Text color="gray" size="1" wrap="nowrap">
<Flex gap="1" align="center"> <Flex gap="1" align="center">
@@ -74,12 +96,9 @@ export function Node({ id, highlight }: { id: number; highlight?: boolean }) {
objectPosition: 'center', objectPosition: 'center',
}} }}
/> />
{tank.xp === undefined || tank.tier === 1 {formatCompact(thisTankXp)}
? 0
: formatCompact(tank.xp!)}
</Flex> </Flex>
</Text> </Text>
<Text color="gray" size="1" wrap="nowrap"> <Text color="gray" size="1" wrap="nowrap">
<Flex gap="1" align="center"> <Flex gap="1" align="center">
<img <img
@@ -96,6 +115,12 @@ export function Node({ id, highlight }: { id: number; highlight?: boolean }) {
</Flex> </Flex>
</Text> </Text>
</Flex> </Flex>
{averages && nextTanks && (
<Text color="gray" size="1" mt="2">
{games!} {games === 1 ? 'battle' : 'battles'}
</Text>
)}
</Flex> </Flex>
</Flex> </Flex>
</Box> </Box>
@@ -2,14 +2,20 @@ import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes'; import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
import { use, useEffect, useMemo, useRef, useState } from 'react'; import { use, useEffect, useMemo, useRef, useState } from 'react';
import { asset } from '../../../../../../../core/blitzkit/asset'; import { asset } from '../../../../../../../core/blitzkit/asset';
import { imgur } from '../../../../../../../core/blitzkit/imgur';
import { tankDefinitions } from '../../../../../../../core/blitzkit/tankDefinitions'; import { tankDefinitions } from '../../../../../../../core/blitzkit/tankDefinitions';
import * as Duel from '../../../../../../../stores/duel'; import * as Duel from '../../../../../../../stores/duel';
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
import { Arrow } from './components/Arrow'; import { Arrow } from './components/Arrow';
import { Node } from './components/Node'; import { Node } from './components/Node';
type Line = number[]; type Line = number[];
export const XP_MULTIPLIERS = [1, 2, 3, 4, 5, 10];
export function TechTreeSection() { export function TechTreeSection() {
const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier);
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
const master = Duel.use((state) => state.protagonist.tank); const master = Duel.use((state) => state.protagonist.tank);
const awaitedTankDefinitions = use(tankDefinitions); const awaitedTankDefinitions = use(tankDefinitions);
const container = useRef<HTMLDivElement>(null); const container = useRef<HTMLDivElement>(null);
@@ -94,7 +100,7 @@ export function TechTreeSection() {
objectPosition: 'center', objectPosition: 'center',
}} }}
/> />
{totalXp.toLocaleString()} {Math.round(totalXp / xpMultiplier).toLocaleString()}
</Flex> </Flex>
</Text> </Text>
@@ -115,16 +121,58 @@ export function TechTreeSection() {
</Text> </Text>
</Flex> </Flex>
)} )}
<Flex gap="1" mt="2">
{XP_MULTIPLIERS.map((multiplier) => {
const selected = xpMultiplier === multiplier;
return (
<Flex
style={{
background: `url(${imgur('7hDltb4')})`,
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
opacity: selected ? 1 : 0.5,
cursor: 'pointer',
}}
onClick={() => {
mutateTankopediaEphemeral((draft) => {
draft.xpMultiplier = multiplier;
});
}}
key={multiplier}
width="2.5rem"
justify="center"
align="center"
pt="1"
pl="1"
>
<Text size="2">x{multiplier}</Text>
</Flex>
);
})}
</Flex>
</Flex> </Flex>
<ScrollArea type="hover" scrollbars="horizontal" ref={container}> <ScrollArea type="hover" scrollbars="horizontal" ref={container}>
<Flex align="center" gap="2" justify="center" p="4"> <Flex align="center" gap="2" justify="center" p="4">
{line.map((id, index) => ( {line.map((id, index) => {
<> const last = index === line.length - 1;
{index > 0 && <Arrow />} const tank = awaitedTankDefinitions[id];
<Node key={id} id={id} highlight={index === line.length - 1} />
</> return (
))} <>
{index > 0 && <Arrow />}
<Node
key={id}
id={id}
nextIds={last ? tank.successors : [line[index + 1]]}
highlight={last}
/>
</>
);
})}
{master.tier < 10 && ( {master.tier < 10 && (
<> <>
+3
View File
@@ -3,6 +3,7 @@
import { Vector3 } from 'three'; import { Vector3 } from 'three';
import { create } from 'zustand'; import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware'; import { subscribeWithSelector } from 'zustand/middleware';
import { XP_MULTIPLIERS } from '../app/tools/tankopedia/[id]/components/Model/TechTreeSection';
import { ArmorType } from '../components/Armor/components/SpacedArmorScene'; import { ArmorType } from '../components/Armor/components/SpacedArmorScene';
import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent'; import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent';
import { ModelDefinition } from '../core/blitzkit/modelDefinitions'; import { ModelDefinition } from '../core/blitzkit/modelDefinitions';
@@ -28,6 +29,7 @@ interface TankopediaEphemeral {
} }
| { type: ArmorType.External } | { type: ArmorType.External }
); );
xpMultiplier: (typeof XP_MULTIPLIERS)[number];
} }
export interface ShotLayerBase { export interface ShotLayerBase {
@@ -97,6 +99,7 @@ export const { Provider, use, useMutation, useStore } = createNextSafeStore(
skills: {}, skills: {},
model, model,
controlsEnabled: true, controlsEnabled: true,
xpMultiplier: 1,
})), })),
), ),
); );