feat: relative stats

This commit is contained in:
tresabhi committed 2024-11-12 16:11:10 -06:00
1 parent bc60b4b42c
commit ffe6ccaed0
3 files changed
+125 -25

No files matched your search

@@ -1,5 +1,5 @@
import { CaretDownIcon, CaretUpIcon } from '@radix-ui/react-icons';
import { Flex, Separator, Text } from '@radix-ui/themes';
import { Box, Flex, Text } from '@radix-ui/themes';
import type { ReactNode } from 'react';
export interface InfoProps {
@@ -24,11 +24,7 @@ export function Info({
deltaType = 'higherIsBetter',
}: InfoProps) {
return (
<Flex
align="center"
style={{ width: '100%', paddingLeft: indent ? 24 : 0 }}
gap="4"
>
<Flex align="center" pl={indent ? '2' : '0'} width="100%" gap="4">
<Text color="gray">
{name}
{unit != undefined && (
@@ -41,7 +37,7 @@ export function Info({
)}
</Text>
{children !== undefined && <Separator style={{ flex: 1 }} />}
<Box flexGrow="1" />
{children !== undefined && (
<Flex align="center" gap="1">
@@ -1,20 +1,115 @@
import type { TankCharacteristics } from '../../../../../../core/blitzkit/tankCharacteristics';
import {
createDefaultSkills,
fetchEquipmentDefinitions,
fetchModelDefinitions,
fetchProvisionDefinitions,
fetchSkillDefinitions,
fetchTankDefinitions,
} from '@blitzkit/core';
import { Flex, Progress, Text } from '@radix-ui/themes';
import { useMemo } from 'react';
import {
tankCharacteristics,
type TankCharacteristics,
} from '../../../../../../core/blitzkit/tankCharacteristics';
import { tankToDuelMember } from '../../../../../../core/blitzkit/tankToDuelMember';
import { useDelta } from '../../../../../../hooks/useDelta';
import { type InfoProps, Info } from './Info';
import { Duel } from '../../../../../../stores/duel';
import { Info, type InfoProps } from './Info';
interface InfoWithDeltaProps extends InfoProps {
stats: TankCharacteristics;
value: keyof TankCharacteristics | (() => number | undefined);
value:
| keyof TankCharacteristics
| ((tank: TankCharacteristics) => number | undefined);
noRanking?: boolean;
}
export function InfoWithDelta({ value, stats, ...props }: InfoWithDeltaProps) {
const tankDefinitions = await fetchTankDefinitions();
const provisionDefinitions = await fetchProvisionDefinitions();
const equipmentDefinitions = await fetchEquipmentDefinitions();
const tankModelDefinitions = await fetchModelDefinitions();
const skillDefinitions = await fetchSkillDefinitions();
export function InfoWithDelta({
value,
stats,
indent,
noRanking,
deltaType,
...props
}: InfoWithDeltaProps) {
const uhWhatDoICallThisVariable =
typeof value === 'function' ? value()! : (stats[value] as number);
typeof value === 'function' ? value(stats)! : (stats[value] as number);
const delta = useDelta(uhWhatDoICallThisVariable);
const protagonistTank = Duel.use((state) => state.protagonist.tank);
const others = useMemo(() => {
const defaultSkills = createDefaultSkills(skillDefinitions);
return Object.values(tankDefinitions.tanks)
.filter((tank) => tank.tier === protagonistTank.tier)
.map((tank) => {
const member = tankToDuelMember(tank, provisionDefinitions);
return tankCharacteristics(
{
applyDynamicArmor: false,
applyReactiveArmor: false,
applySpallLiner: false,
camouflage: member.camouflage,
consumables: member.consumables,
crewSkills: defaultSkills,
provisions: member.provisions,
engine: member.engine,
gun: member.gun,
equipmentMatrix: member.equipmentMatrix,
shell: member.shell,
stockEngine: tank.engines[0],
stockGun: tank.turrets[0].guns[0],
stockTrack: tank.tracks[0],
stockTurret: tank.turrets[0],
tank,
track: member.track,
turret: member.turret,
},
{
equipmentDefinitions,
provisionDefinitions,
tankModelDefinition: tankModelDefinitions.models[tank.id],
},
);
})
.filter((tank) => {
const othersValue =
typeof value === 'function' ? value(tank)! : (tank[value] as number);
return othersValue !== undefined;
});
}, []);
const betterTanks = others.filter((tank) => {
const othersValue =
typeof value === 'function' ? value(tank)! : (tank[value] as number);
if (othersValue === undefined) return false;
return deltaType === 'lowerIsBetter'
? othersValue < uhWhatDoICallThisVariable
: othersValue > uhWhatDoICallThisVariable;
});
return (
<Info {...props} delta={delta}>
{uhWhatDoICallThisVariable}
</Info>
<Flex direction="column">
<Info deltaType={deltaType} indent={indent} {...props} delta={delta}>
{uhWhatDoICallThisVariable}
</Info>
{!noRanking && (
<Flex pl={indent ? '2' : '0'} align="center" gap="2">
<Progress variant="soft" size="1" value={50} color="gray" />
<Text color="gray" size="1">
{betterTanks.length + 1} / {others.length}
</Text>
</Flex>
)}
</Flex>
);
}
@@ -138,7 +138,7 @@ export function Characteristics() {
}}
>
<Flex direction="column" gap="8" style={{ flex: 1 }}>
<Flex direction="column" gap="2">
<Flex direction="column" gap="4">
<Flex align="center" gap="4">
<Heading size="5">Firepower</Heading>
@@ -215,6 +215,7 @@ export function Characteristics() {
name={`Shell ${index + 1}`}
decimals={2}
deltaType="lowerIsBetter"
noRanking
value={() => reload}
/>
))}
@@ -255,6 +256,7 @@ export function Characteristics() {
indent
decimals={0}
name={`At ${penetrationDistance}m`}
noRanking
value={() =>
lerp(
shell.penetration.near,
@@ -331,6 +333,7 @@ export function Characteristics() {
stats={stats}
name="Splash radius"
unit="m"
noRanking
decimals={0}
value="explosionRadius"
/>
@@ -347,12 +350,14 @@ export function Characteristics() {
name="Normalization"
decimals={0}
unit="°"
noRanking
value="shellNormalization"
/>
{!isExplosive(shell.type) && (
<InfoWithDelta
stats={stats}
name="Ricochet"
noRanking
decimals={0}
deltaType="lowerIsBetter"
unit="°"
@@ -514,7 +519,8 @@ export function Characteristics() {
</>
)}
</Flex>
<Flex direction="column" gap="2">
<Flex direction="column" gap="4">
<Flex gap="2" align="center">
<Heading size="5">Crew training</Heading>
@@ -554,6 +560,7 @@ export function Characteristics() {
}`}
unit="%"
decimals={0}
noRanking
value={() =>
(member.type === CrewType.COMMANDER
? commanderMastery
@@ -580,6 +587,7 @@ export function Characteristics() {
</Flex>
</>
}
noRanking
value={() => commanderMastery * 1.05 * 100}
/>
)}
@@ -590,7 +598,7 @@ export function Characteristics() {
</Flex>
<Flex direction="column" gap="8" style={{ flex: 1 }}>
<Flex direction="column" gap="2">
<Flex direction="column" gap="4">
<Heading size="5">Maneuverability</Heading>
<Info name="Speed" unit="kph" />
<InfoWithDelta
@@ -726,7 +734,8 @@ export function Characteristics() {
value="hullTraverseSoftTerrain"
/>
</Flex>
<Flex direction="column" gap="2">
<Flex direction="column" gap="4">
<Heading size="5">Survivability</Heading>
<InfoWithDelta
value="health"
@@ -741,7 +750,7 @@ export function Characteristics() {
unit="%"
deltaType="lowerIsBetter"
decimals={0}
value={() => stats.fireChance * 100}
value={(stats) => stats.fireChance * 100}
/>
<InfoWithDelta
value="viewRange"
@@ -752,14 +761,14 @@ export function Characteristics() {
/>
<Info name="Camouflage" unit="%" />
<InfoWithDelta
value={() => stats.camouflageStill * 100}
value={(stats) => stats.camouflageStill * 100}
stats={stats}
indent
name="Still"
decimals={2}
/>
<InfoWithDelta
value={() => stats.camouflageMoving * 100}
value={(stats) => stats.camouflageMoving * 100}
stats={stats}
indent
name="Moving"
@@ -770,21 +779,21 @@ export function Characteristics() {
indent
name="Shooting still"
decimals={2}
value={() => stats.camouflageShootingStill * 100}
value={(stats) => stats.camouflageShootingStill * 100}
/>
<InfoWithDelta
stats={stats}
indent
name="Shooting moving"
decimals={2}
value={() => stats.camouflageShootingMoving * 100}
value={(stats) => stats.camouflageShootingMoving * 100}
/>
<InfoWithDelta
stats={stats}
indent
name="Caught on fire"
decimals={2}
value={() => stats.camouflageCaughtOnFire * 100}
value={(stats) => stats.camouflageCaughtOnFire * 100}
/>
<InfoWithDelta
name="Width"