mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: relative stats
This commit is contained in:
1 parent
bc60b4b42c
commit
ffe6ccaed0
3 files changed
+125
-25
No files matched your search
+3
-7
@@ -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">
|
||||
|
||||
+103
-8
@@ -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>
|
||||
);
|
||||
}
|
||||
+19
-10
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user