diff --git a/packages/website/public/assets/images/tankopedia/visualizers/reload/caret.png b/packages/website/public/assets/images/tankopedia/visualizers/reload/caret.png new file mode 100644 index 000000000..660853971 Binary files /dev/null and b/packages/website/public/assets/images/tankopedia/visualizers/reload/caret.png differ diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Firepower.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Firepower.tsx index fe806b2b7..c2eb22cb2 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Firepower.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Firepower.tsx @@ -24,6 +24,7 @@ import { Duel } from '../../../../../../stores/duel'; import type { StatsAcceptorProps } from './HullTraverseVisualizer'; import { Info } from './Info'; import { InfoWithDelta } from './InfoWithDelta'; +import { ReloadVisualizer } from './ReloadVisualizer'; import { RicochetVisualizer } from './RicochetVisualizer'; import { StatsTableWrapper } from './StatsTableWrapper'; @@ -165,6 +166,8 @@ export function Firepower({ stats }: StatsAcceptorProps) { value="intraClip" /> )} + + {typeof shell.penetration !== 'number' && ( <> diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/components/Shell.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/components/Shell.tsx new file mode 100644 index 000000000..e345c3f89 --- /dev/null +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/components/Shell.tsx @@ -0,0 +1,61 @@ +import { Box, Flex } from '@radix-ui/themes'; +import type { QuicklimeCallback } from 'quicklime'; +import { useEffect, useRef } from 'react'; +import { reloadUpdate, type ReloadUpdateData } from '..'; +import { Var } from '../../../../../../../../core/radix/var'; +import type { StatsAcceptorProps } from '../../HullTraverseVisualizer'; + +interface ShellProps { + index: number; +} + +export function Shell({ index, stats }: ShellProps & StatsAcceptorProps) { + const bar = useRef(null!); + + useEffect(() => { + const handleReloadUpdate: QuicklimeCallback = ({ + data, + }) => { + bar.current.style.width = `${data.shells[index] * 100}%`; + }; + + reloadUpdate.on(handleReloadUpdate); + + return () => { + reloadUpdate.off(handleReloadUpdate); + }; + }, []); + + return ( + + + + + + ); +} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/index.tsx new file mode 100644 index 000000000..001cb7ba3 --- /dev/null +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/index.tsx @@ -0,0 +1,135 @@ +import { Box, Card } from '@radix-ui/themes'; +import { times } from 'lodash-es'; +import { Quicklime, type QuicklimeCallback } from 'quicklime'; +import { useEffect, useRef } from 'react'; +import { Var } from '../../../../../../../core/radix/var'; +import type { StatsAcceptorProps } from '../HullTraverseVisualizer'; +import { Shell } from './components/Shell'; + +export interface ReloadUpdateData { + reload: number; + shells: number[]; +} + +export const reloadUpdate = new Quicklime(); + +export function ReloadVisualizer({ stats }: StatsAcceptorProps) { + const reloadCircle = useRef(null!); + const reloadCore = useRef(null!); + const reloadGlow = useRef(null!); + + const size = `${stats.shells === 1 ? 66 : 58}%`; + + useEffect(() => { + let cancel = false; + + function frame() { + const now = Date.now() / 1000; + + const t = now % stats.shellReload!; + const x = t / stats.shellReload!; + + reloadUpdate.dispatch({ + reload: x, + shells: Array(stats.shells).fill(x), + }); + + if (!cancel) requestAnimationFrame(frame); + } + + frame(); + + return () => { + cancel = true; + }; + }, [stats]); + + useEffect(() => { + const handleReloadUpdate: QuicklimeCallback = ({ + data, + }) => { + const reloadAngle = data.reload * 2 * Math.PI; + + reloadCircle.current.style.background = `conic-gradient(${Var('amber-9')} 0 ${reloadAngle}rad, ${Var('red-7')} ${reloadAngle}rad)`; + reloadCore.current.style.background = `conic-gradient(${Var('amber-3')} 0 ${reloadAngle}rad, ${Var('red-2')} ${reloadAngle}rad)`; + reloadGlow.current.style.background = `conic-gradient(${Var('amber-8')} 0 ${reloadAngle}rad, transparent ${reloadAngle}rad)`; + }; + + reloadUpdate.on(handleReloadUpdate); + + return () => { + reloadUpdate.off(handleReloadUpdate); + }; + }, []); + + return ( + + + + + + + + + + + + {times(stats.shells, (index) => ( + + ))} + + ); +}