diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Info.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Info.tsx index a364447fa..aa13d7507 100644 --- a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Info.tsx +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Info.tsx @@ -1,13 +1,17 @@ +import { CaretDownIcon, CaretUpIcon } from '@radix-ui/react-icons'; import { Flex, Text } from '@radix-ui/themes'; import { ReactNode } from 'react'; import { theme } from '../../../../../../../stitches.config'; -interface InfoProps { +export interface InfoProps { name: ReactNode; children?: ReactNode; unit?: string; indent?: boolean; highlight?: boolean; + delta?: number; + decimals?: number; + prefix?: string; } export function Info({ @@ -16,6 +20,9 @@ export function Info({ unit, indent = false, highlight, + delta, + decimals, + prefix, }: InfoProps) { return ( {children} + + {delta !== undefined && delta !== 0 && ( + <> + 0 ? 'green' : 'tomato'}> + {decimals !== undefined + ? Math.abs(delta).toFixed(decimals) + : Math.abs(delta)} + + 0 ? 'green' : 'tomato'}> + {delta > 0 ? : } + + + )} + + {prefix} + {decimals !== undefined && typeof children === 'number' + ? children.toFixed(decimals) + : children} + + )} ); diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/components/InfoWithDelta.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/components/InfoWithDelta.tsx new file mode 100644 index 000000000..acbc8fc3a --- /dev/null +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/components/InfoWithDelta.tsx @@ -0,0 +1,20 @@ +import { useEffect, useRef } from 'react'; +import { Info, InfoProps } from './Info'; + +interface InfoWithDeltaProps extends InfoProps { + children: number; +} + +export function InfoWithDelta({ children, ...props }: InfoWithDeltaProps) { + const lastValue = useRef(children); + + useEffect(() => { + lastValue.current = children; + }); + + return ( + + {children} + + ); +} diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx index 17da50b50..d578be26c 100644 --- a/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx @@ -12,6 +12,7 @@ import { unionBoundingBox } from '../../../../../../core/blitzkrieg/unionBoundin import { useEquipment } from '../../../../../../core/blitzkrieg/useEquipmentEquipped'; import { useDuel } from '../../../../../../stores/duel'; import { Info } from './components/Info'; +import { InfoWithDelta } from './components/InfoWithDelta'; export function Characteristics() { const awaitedModelDefinitions = use(modelDefinitions); @@ -47,68 +48,65 @@ export function Characteristics() { Survivability - + {tank.health + turret.health} - - + + {Math.round(engine.fireChance * 100)} - - + + {turret.viewRange} - + - - {(tank.camouflage.still * 100).toFixed(2)} - - - {(tank.camouflage.moving * 100).toFixed(2)} - - - {(tank.camouflage.still * gun.camouflageLoss * 100).toFixed(2)} - - - {(tank.camouflage.moving * gun.camouflageLoss * 100).toFixed(2)} - - - {(tank.camouflage.onFire * tank.camouflage.still * 100).toFixed(2)} - + + {tank.camouflage.still * 100} + + + {tank.camouflage.moving * 100} + + + {tank.camouflage.still * gun.camouflageLoss * 100} + + + {tank.camouflage.moving * gun.camouflageLoss * 100} + + + {tank.camouflage.onFire * tank.camouflage.still * 100} + - {size.map((component) => component.toFixed(2)).join(' x ')} + {size[0].toFixed(2)} x {size[2].toFixed(2)} x {size[1].toFixed(2)} Fire {GUN_TYPE_NAMES[gun.type]} - - {resolveDpm(gun, shell, hasRammer).toFixed(0)} - + + {resolveDpm(gun, shell, hasRammer)} + {gun.type === 'autoReloader' && ( <> - + {gun.reload.at(-1)! < gun.reload.at(-2)! - ? ( - (shell.damage.armor / - (gun.reload.at(-1)! + gun.interClip)) * - 60 - ).toFixed(0) - : ((shell.damage.armor / gun.reload[0]) * 60).toFixed(0)} - - + ? (shell.damage.armor / (gun.reload.at(-1)! + gun.interClip)) * + 60 + : (shell.damage.armor / gun.reload[0]) * 60} + + {gun.reload.at(-1)! < gun.reload.at(-2)! - ? ( - (shell.damage.armor / - (gun.reload.at(-1)! + gun.interClip)) * - (60 - - (gun.reload.slice(0, -1).length - 1) * gun.interClip) + - shell.damage.armor * gun.reload.slice(0, -1).length - ).toFixed(0) - : ( - (shell.damage.armor / (gun.reload[0] + gun.interClip)) * - (60 - (gun.reload.slice(1).length - 1) * gun.interClip) + - shell.damage.armor * gun.reload.slice(1).length - ).toFixed(0)} - + ? (shell.damage.armor / (gun.reload.at(-1)! + gun.interClip)) * + (60 - + (gun.reload.slice(0, -1).length - 1) * gun.interClip) + + shell.damage.armor * gun.reload.slice(0, -1).length + : (shell.damage.armor / (gun.reload[0] + gun.interClip)) * + (60 - (gun.reload.slice(1).length - 1) * gun.interClip) + + shell.damage.armor * gun.reload.slice(1).length} + ( - 0} name={index > 0 ? `Shell ${index + 1}` : 'Reload on shell 1'} unit="s" + decimals={2} > - {reload.toFixed(2)} - + {reload} + )) ) : ( - - {(gun.reload * (hasRammer ? 0.93 : 1)).toFixed(2)} - + + {gun.reload * (hasRammer ? 0.93 : 1)} + )} - + {shell.caliber} - + {gun.shells.map((shell) => ( - + {resolveNearPenetration(shell.penetration)} - + ))} {gun.shells.map((shell) => ( - + {shell.damage.armor} - + ))} {gun.shells.map((shell, index) => ( - + {shell.damage.module} - + ))} - {gun.shells.map((shell, index) => ( - + {gun.shells.map((shell) => ( + {shell.speed} - + ))} - - {gun.aimTime.toFixed(2)} - + + {gun.aimTime} + - - {gun.dispersion.base.toFixed(3)} - - - + {track.dispersion.move.toFixed(3)} - - - + {track.dispersion.traverse.toFixed(3)} - - - + {gun.dispersion.traverse.toFixed(3)} - - - + {gun.dispersion.shot.toFixed(3)} - - - x {gun.dispersion.damaged.toFixed(3)} - + + {gun.dispersion.base} + + + {track.dispersion.move} + + + {track.dispersion.traverse} + + + {gun.dispersion.traverse} + + + {gun.dispersion.shot} + + + {gun.dispersion.damaged} + - - {gunModelDefinition.pitch.max.toFixed(1)} - - - {(-gunModelDefinition.pitch.min).toFixed(1)} - + + {gunModelDefinition.pitch.max} + + + {-gunModelDefinition.pitch.min} + {gunModelDefinition.pitch.front && ( <> - + {gunModelDefinition.pitch.front.max} - - + + {-gunModelDefinition.pitch.front.min} - + )} {gunModelDefinition.pitch.back && ( <> - + {gunModelDefinition.pitch.back.max} - - + + {-gunModelDefinition.pitch.back.min} - + )} {turretModelDefinition.yaw && ( - - {-turretModelDefinition.yaw.min}, {turretModelDefinition.yaw.max} - + <> + + {-turretModelDefinition.yaw.min} + + + {turretModelDefinition.yaw.max} + + )} Maneuverability - - {tank.speed.forwards.toFixed(0)} - - - {tank.speed.backwards.toFixed(0)} - - + + {tank.speed.forwards} + + + {tank.speed.backwards} + + {engine.power} - + - - {(engine.power / weightMt / track.resistance.hard).toFixed(1)} - - - {(engine.power / weightMt / track.resistance.medium).toFixed(1)} - - - {(engine.power / weightMt / track.resistance.soft).toFixed(1)} - - - {weightMt.toFixed(1)} - + + {engine.power / weightMt / track.resistance.hard} + + + {engine.power / weightMt / track.resistance.medium} + + + {engine.power / weightMt / track.resistance.soft} + + + {weightMt} + - - {( - (engine.power / stockEngine.power) * + + {(engine.power / stockEngine.power) * track.traverseSpeed * (track.resistance.hard / track.resistance.hard) * - (stockWeight / weight) - ).toFixed(1)} - - - {( - (engine.power / stockEngine.power) * + (stockWeight / weight)} + + + {(engine.power / stockEngine.power) * track.traverseSpeed * (track.resistance.hard / track.resistance.medium) * - (stockWeight / weight) - ).toFixed(1)} - - - {( - (engine.power / stockEngine.power) * + (stockWeight / weight)} + + + {(engine.power / stockEngine.power) * track.traverseSpeed * (track.resistance.hard / track.resistance.soft) * - (stockWeight / weight) - ).toFixed(1)} - + (stockWeight / weight)} + );