From af08df1526f8158012db47b067a3194150eca859 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Thu, 15 May 2025 23:02:15 -0500 Subject: [PATCH] feat: ricochet visualizer shell switcher --- packages/i18n/strings/en.json | 3 +- .../Characteristics/components/Firepower.tsx | 75 ++--- .../components/RicochetVisualizer.tsx | 237 --------------- .../components/ShellSwitcher.tsx | 52 ++++ .../components/RicochetVisualizer/index.tsx | 270 ++++++++++++++++++ 5 files changed, 365 insertions(+), 272 deletions(-) delete mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer.tsx create mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/components/ShellSwitcher.tsx create mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 7a9cbdb4b..4090ac1a9 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -892,7 +892,8 @@ "angle": "Angle", "normalization": "Normalization", "effective_angle": "Effective angle", - "armor": "Armor" + "armor": "Armor", + "no_ricochet": "Explosive shells don't ricochet" }, "view_range": { 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 e96e9f438..02bb96e51 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 @@ -65,39 +65,44 @@ export function Firepower({ stats }: StatsAcceptorProps) { - {gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => ( - - { - mutateDuel((draft) => { - draft.protagonist.shell = thisShell; - }); - }} - > - {unwrap(thisShell.name)} - - - ))} + {gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => { + const selected = thisShell.id === shell.id; + + return ( + + { + mutateDuel((draft) => { + draft.protagonist.shell = thisShell; + }); + }} + > + {unwrap(thisShell.name)} + + + ); + })} @@ -265,9 +270,11 @@ export function Firepower({ stats }: StatsAcceptorProps) { deltaType="lowerIsBetter" value="shellRicochet" /> - )} + + + diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer.tsx deleted file mode 100644 index 7bd3d32cf..000000000 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import { asset } from '@blitzkit/core'; -import { literals } from '@blitzkit/i18n'; -import { Box, Card, Code, Flex, Text } from '@radix-ui/themes'; -import { useRef, useState } from 'react'; -import { clamp, degToRad, radToDeg } from 'three/src/math/MathUtils.js'; -import { Var } from '../../../../../../core/radix/var'; -import { useLocale } from '../../../../../../hooks/useLocale'; -import { Duel } from '../../../../../../stores/duel'; -import type { StatsAcceptorProps } from './HullTraverseVisualizer'; - -export function RicochetVisualizer({ stats }: StatsAcceptorProps) { - const shell = Duel.use((state) => state.protagonist.shell); - const container = useRef(null); - const normalization = degToRad(stats.shellNormalization); - const ricochet = degToRad(stats.shellRicochet ?? 90); - const [angle, setAngle] = useState(degToRad(-25)); - const doesRicochet = Math.abs(angle) >= ricochet; - const effectiveAngle = doesRicochet - ? -angle + Math.PI - : Math.sign(angle) * Math.max(0, Math.abs(angle) - normalization); - const { strings } = useLocale(); - - return ( - { - if (!container.current) return; - - const rect = container.current.getBoundingClientRect(); - const x = event.clientX - rect.left - rect.width / 3; - const y = event.clientY - rect.top - rect.height / 2; - const angle = clamp(Math.atan2(-y, x), -Math.PI / 2, Math.PI / 2); - - setAngle(angle); - }} - > - - - - - - - - - - - - - - - - - - - - - {strings.website.tools.tankopedia.visualizers.ricochet.angle} - - - {literals(strings.common.units.deg, [ - radToDeg(Math.abs(angle)).toFixed(0), - ])} - - - - - - { - strings.website.tools.tankopedia.visualizers.ricochet - .normalization - } - - - {doesRicochet - ? '-' - : literals(strings.common.units.deg, [ - radToDeg(Math.min(normalization, Math.abs(angle))).toFixed(0), - ])} - - - - - - - - { - strings.website.tools.tankopedia.visualizers.ricochet - .effective_angle - } - - - {doesRicochet - ? '-' - : literals(strings.common.units.deg, [ - radToDeg(Math.abs(effectiveAngle)).toFixed(0), - ])} - - - - - - {strings.website.tools.tankopedia.visualizers.ricochet.armor} - - - {doesRicochet - ? '-' - : literals(strings.common.units.percentage, [ - (100 / Math.cos(effectiveAngle)).toFixed(0), - ])} - - - - - ); -} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/components/ShellSwitcher.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/components/ShellSwitcher.tsx new file mode 100644 index 000000000..63ed31738 --- /dev/null +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/components/ShellSwitcher.tsx @@ -0,0 +1,52 @@ +import { asset } from '@blitzkit/core'; +import { Flex, IconButton, Tooltip } from '@radix-ui/themes'; +import { useLocale } from '../../../../../../../../hooks/useLocale'; +import { Duel } from '../../../../../../../../stores/duel'; + +export function ShellSwitcher() { + const gun = Duel.use((state) => state.protagonist.gun); + const shell = Duel.use((state) => state.protagonist.shell); + const mutateDuel = Duel.useMutation(); + const { unwrap } = useLocale(); + + return ( + + {gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => { + const selected = thisShell.id === shell.id; + + return ( + + { + mutateDuel((draft) => { + draft.protagonist.shell = thisShell; + }); + }} + > + {unwrap(thisShell.name)} + + + ); + })} + + ); +} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx new file mode 100644 index 000000000..45c30a373 --- /dev/null +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx @@ -0,0 +1,270 @@ +import { asset, isExplosive } from '@blitzkit/core'; +import { literals } from '@blitzkit/i18n'; +import { Box, Card, Code, Flex, Text } from '@radix-ui/themes'; +import { useRef, useState } from 'react'; +import { clamp, degToRad, radToDeg } from 'three/src/math/MathUtils.js'; +import { Var } from '../../../../../../../core/radix/var'; +import { useLocale } from '../../../../../../../hooks/useLocale'; +import { Duel } from '../../../../../../../stores/duel'; +import type { StatsAcceptorProps } from '../HullTraverseVisualizer'; +import { ShellSwitcher } from './components/ShellSwitcher'; + +export function RicochetVisualizer({ stats }: StatsAcceptorProps) { + const shell = Duel.use((state) => state.protagonist.shell); + const container = useRef(null); + const normalization = degToRad(stats.shellNormalization); + const ricochet = degToRad(stats.shellRicochet ?? 90); + const [rawAngle, setAngle] = useState(degToRad(-25)); + const explodes = isExplosive(shell.type); + const angle = explodes ? degToRad(-70) : rawAngle; + const doesRicochet = Math.abs(angle) >= ricochet; + const effectiveAngle = doesRicochet + ? -angle + Math.PI + : Math.sign(angle) * Math.max(0, Math.abs(angle) - normalization); + const { strings } = useLocale(); + const armorPercentage = Math.min(999, 100 / Math.cos(effectiveAngle)); + + return ( + + { + if (!container.current || explodes) return; + + const rect = container.current.getBoundingClientRect(); + const x = event.clientX - rect.left - rect.width / 3; + const y = event.clientY - rect.top - rect.height / 2; + const angle = clamp(Math.atan2(-y, x), -Math.PI / 2, Math.PI / 2); + + setAngle(angle); + }} + > + + + + + + + + + + + + + + + + + + + + + {strings.website.tools.tankopedia.visualizers.ricochet.angle} + + + {literals(strings.common.units.deg, [ + radToDeg(Math.abs(angle)).toFixed(0), + ])} + + + + + + { + strings.website.tools.tankopedia.visualizers.ricochet + .normalization + } + + + {doesRicochet + ? '-' + : literals(strings.common.units.deg, [ + radToDeg(Math.min(normalization, Math.abs(angle))).toFixed( + 0, + ), + ])} + + + + + + + + { + strings.website.tools.tankopedia.visualizers.ricochet + .effective_angle + } + + + {doesRicochet + ? '-' + : literals(strings.common.units.deg, [ + radToDeg(Math.abs(effectiveAngle)).toFixed(0), + ])} + + + + + + {strings.website.tools.tankopedia.visualizers.ricochet.armor} + + + {doesRicochet + ? '-' + : literals(strings.common.units.percentage, [ + armorPercentage.toFixed(0), + ])} + + + + + {explodes && ( + + + { + strings.website.tools.tankopedia.visualizers.ricochet + .no_ricochet + } + + + )} + + + + + ); +}