mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: relative stats
This commit is contained in:
1 parent
bc60b4b42c
commit
ffe6ccaed0
3 files changed
+123
-23
No files matched your search
+3
-7
@@ -1,5 +1,5 @@
|
|||||||
import { CaretDownIcon, CaretUpIcon } from '@radix-ui/react-icons';
|
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';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface InfoProps {
|
export interface InfoProps {
|
||||||
@@ -24,11 +24,7 @@ export function Info({
|
|||||||
deltaType = 'higherIsBetter',
|
deltaType = 'higherIsBetter',
|
||||||
}: InfoProps) {
|
}: InfoProps) {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex align="center" pl={indent ? '2' : '0'} width="100%" gap="4">
|
||||||
align="center"
|
|
||||||
style={{ width: '100%', paddingLeft: indent ? 24 : 0 }}
|
|
||||||
gap="4"
|
|
||||||
>
|
|
||||||
<Text color="gray">
|
<Text color="gray">
|
||||||
{name}
|
{name}
|
||||||
{unit != undefined && (
|
{unit != undefined && (
|
||||||
@@ -41,7 +37,7 @@ export function Info({
|
|||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{children !== undefined && <Separator style={{ flex: 1 }} />}
|
<Box flexGrow="1" />
|
||||||
|
|
||||||
{children !== undefined && (
|
{children !== undefined && (
|
||||||
<Flex align="center" gap="1">
|
<Flex align="center" gap="1">
|
||||||
|
|||||||
+101
-6
@@ -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 { 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 {
|
interface InfoWithDeltaProps extends InfoProps {
|
||||||
stats: TankCharacteristics;
|
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 =
|
const uhWhatDoICallThisVariable =
|
||||||
typeof value === 'function' ? value()! : (stats[value] as number);
|
typeof value === 'function' ? value(stats)! : (stats[value] as number);
|
||||||
const delta = useDelta(uhWhatDoICallThisVariable);
|
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 (
|
return (
|
||||||
<Info {...props} delta={delta}>
|
<Flex direction="column">
|
||||||
|
<Info deltaType={deltaType} indent={indent} {...props} delta={delta}>
|
||||||
{uhWhatDoICallThisVariable}
|
{uhWhatDoICallThisVariable}
|
||||||
</Info>
|
</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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+19
-10
@@ -138,7 +138,7 @@ export function Characteristics() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex direction="column" gap="8" style={{ flex: 1 }}>
|
<Flex direction="column" gap="8" style={{ flex: 1 }}>
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="4">
|
||||||
<Flex align="center" gap="4">
|
<Flex align="center" gap="4">
|
||||||
<Heading size="5">Firepower</Heading>
|
<Heading size="5">Firepower</Heading>
|
||||||
|
|
||||||
@@ -215,6 +215,7 @@ export function Characteristics() {
|
|||||||
name={`Shell ${index + 1}`}
|
name={`Shell ${index + 1}`}
|
||||||
decimals={2}
|
decimals={2}
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
|
noRanking
|
||||||
value={() => reload}
|
value={() => reload}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -255,6 +256,7 @@ export function Characteristics() {
|
|||||||
indent
|
indent
|
||||||
decimals={0}
|
decimals={0}
|
||||||
name={`At ${penetrationDistance}m`}
|
name={`At ${penetrationDistance}m`}
|
||||||
|
noRanking
|
||||||
value={() =>
|
value={() =>
|
||||||
lerp(
|
lerp(
|
||||||
shell.penetration.near,
|
shell.penetration.near,
|
||||||
@@ -331,6 +333,7 @@ export function Characteristics() {
|
|||||||
stats={stats}
|
stats={stats}
|
||||||
name="Splash radius"
|
name="Splash radius"
|
||||||
unit="m"
|
unit="m"
|
||||||
|
noRanking
|
||||||
decimals={0}
|
decimals={0}
|
||||||
value="explosionRadius"
|
value="explosionRadius"
|
||||||
/>
|
/>
|
||||||
@@ -347,12 +350,14 @@ export function Characteristics() {
|
|||||||
name="Normalization"
|
name="Normalization"
|
||||||
decimals={0}
|
decimals={0}
|
||||||
unit="°"
|
unit="°"
|
||||||
|
noRanking
|
||||||
value="shellNormalization"
|
value="shellNormalization"
|
||||||
/>
|
/>
|
||||||
{!isExplosive(shell.type) && (
|
{!isExplosive(shell.type) && (
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
stats={stats}
|
stats={stats}
|
||||||
name="Ricochet"
|
name="Ricochet"
|
||||||
|
noRanking
|
||||||
decimals={0}
|
decimals={0}
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
unit="°"
|
unit="°"
|
||||||
@@ -514,7 +519,8 @@ export function Characteristics() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction="column" gap="2">
|
|
||||||
|
<Flex direction="column" gap="4">
|
||||||
<Flex gap="2" align="center">
|
<Flex gap="2" align="center">
|
||||||
<Heading size="5">Crew training</Heading>
|
<Heading size="5">Crew training</Heading>
|
||||||
|
|
||||||
@@ -554,6 +560,7 @@ export function Characteristics() {
|
|||||||
}`}
|
}`}
|
||||||
unit="%"
|
unit="%"
|
||||||
decimals={0}
|
decimals={0}
|
||||||
|
noRanking
|
||||||
value={() =>
|
value={() =>
|
||||||
(member.type === CrewType.COMMANDER
|
(member.type === CrewType.COMMANDER
|
||||||
? commanderMastery
|
? commanderMastery
|
||||||
@@ -580,6 +587,7 @@ export function Characteristics() {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
noRanking
|
||||||
value={() => commanderMastery * 1.05 * 100}
|
value={() => commanderMastery * 1.05 * 100}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -590,7 +598,7 @@ export function Characteristics() {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex direction="column" gap="8" style={{ flex: 1 }}>
|
<Flex direction="column" gap="8" style={{ flex: 1 }}>
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="4">
|
||||||
<Heading size="5">Maneuverability</Heading>
|
<Heading size="5">Maneuverability</Heading>
|
||||||
<Info name="Speed" unit="kph" />
|
<Info name="Speed" unit="kph" />
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
@@ -726,7 +734,8 @@ export function Characteristics() {
|
|||||||
value="hullTraverseSoftTerrain"
|
value="hullTraverseSoftTerrain"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex direction="column" gap="2">
|
|
||||||
|
<Flex direction="column" gap="4">
|
||||||
<Heading size="5">Survivability</Heading>
|
<Heading size="5">Survivability</Heading>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
value="health"
|
value="health"
|
||||||
@@ -741,7 +750,7 @@ export function Characteristics() {
|
|||||||
unit="%"
|
unit="%"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
decimals={0}
|
decimals={0}
|
||||||
value={() => stats.fireChance * 100}
|
value={(stats) => stats.fireChance * 100}
|
||||||
/>
|
/>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
value="viewRange"
|
value="viewRange"
|
||||||
@@ -752,14 +761,14 @@ export function Characteristics() {
|
|||||||
/>
|
/>
|
||||||
<Info name="Camouflage" unit="%" />
|
<Info name="Camouflage" unit="%" />
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
value={() => stats.camouflageStill * 100}
|
value={(stats) => stats.camouflageStill * 100}
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
name="Still"
|
name="Still"
|
||||||
decimals={2}
|
decimals={2}
|
||||||
/>
|
/>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
value={() => stats.camouflageMoving * 100}
|
value={(stats) => stats.camouflageMoving * 100}
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
name="Moving"
|
name="Moving"
|
||||||
@@ -770,21 +779,21 @@ export function Characteristics() {
|
|||||||
indent
|
indent
|
||||||
name="Shooting still"
|
name="Shooting still"
|
||||||
decimals={2}
|
decimals={2}
|
||||||
value={() => stats.camouflageShootingStill * 100}
|
value={(stats) => stats.camouflageShootingStill * 100}
|
||||||
/>
|
/>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
name="Shooting moving"
|
name="Shooting moving"
|
||||||
decimals={2}
|
decimals={2}
|
||||||
value={() => stats.camouflageShootingMoving * 100}
|
value={(stats) => stats.camouflageShootingMoving * 100}
|
||||||
/>
|
/>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
stats={stats}
|
stats={stats}
|
||||||
indent
|
indent
|
||||||
name="Caught on fire"
|
name="Caught on fire"
|
||||||
decimals={2}
|
decimals={2}
|
||||||
value={() => stats.camouflageCaughtOnFire * 100}
|
value={(stats) => stats.camouflageCaughtOnFire * 100}
|
||||||
/>
|
/>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
name="Width"
|
name="Width"
|
||||||
|
|||||||
Reference in new issue
Block a user