From 42e860b171b53e827e49bf3893bef9ce9780c640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Fri, 8 Aug 2025 12:00:33 -0500 Subject: [PATCH] feat: start of gun depression visualzier --- .../Characteristics/components/Firepower.tsx | 2 + .../GunFlexibilityVisualizer/index.tsx | 94 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx 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 350a28177..f8e6de584 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 @@ -22,6 +22,7 @@ import { useLocale } from '../../../../../../hooks/useLocale'; import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition'; import { Duel } from '../../../../../../stores/duel'; import { AssaultRangesVisualizer } from './AssaultRangesVisualizer'; +import { GunFlexibilityVisualizer } from './GunFlexibilityVisualizer'; import { Info } from './Info'; import { InfoWithDelta } from './InfoWithDelta'; import { ReloadVisualizer } from './ReloadVisualizer'; @@ -404,6 +405,7 @@ export function Firepower({ stats }: StatsAcceptorProps) { /> )} + ); } diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx new file mode 100644 index 000000000..2b52173b0 --- /dev/null +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx @@ -0,0 +1,94 @@ +import { Card } from '@radix-ui/themes'; +import { awaitableModelDefinitions } from '../../../../../../../core/awaitables/modelDefinitions'; +import { Duel } from '../../../../../../../stores/duel'; + +const MAX_MAG = 20; + +const modelDefinition = await awaitableModelDefinitions; + +function mag(x: number) { + return (x / MAX_MAG + 1) / 2; +} + +function c(thetaDeg: number, m = 1) { + const theta = thetaDeg * (Math.PI / 180); + return `${m * Math.cos(theta)} ${-m * Math.sin(theta)}`; +} + +export function GunFlexibilityVisualizer() { + const tank = Duel.use((state) => state.protagonist.tank); + const turret = Duel.use((state) => state.protagonist.turret); + const gun = Duel.use((state) => state.protagonist.gun); + + const tankModel = modelDefinition.models[tank.id]; + const turretModel = tankModel.turrets[turret.id]; + const gunModel = turretModel.guns[gun.gun_type!.value.base.id]; + + const p = gunModel.pitch; + const f = p.front; + const b = p.back; + + let d = ''; + + d += `M 0 0`; + const ms = mag(p.max); + if (b) { + d += `L ${c(-90 + b.range / 2, ms)}`; + } else { + d += `L ${c(-90 / 2, ms)}`; + } + if (f) { + d += `A ${ms} ${ms} 0 0 0 ${c(90 + f.range / 2, ms)}`; + } else { + d += `A ${ms} ${ms} 0 0 0 ${c(90 / 2, ms)}`; + } + + d += `M 0 0`; + if (f) { + d += `L ${c(90 + f.range / 2, ms)}`; + } else { + d += `L ${c(90 / 2, ms)}`; + } + if (b) { + d += `A ${ms} ${ms} 0 0 0 ${c(-90 - b.range / 2, ms)}`; + } else { + d += `A ${ms} ${ms} 0 0 0 ${c(-90 / 2, ms)}`; + } + + if (f) { + const m = mag(f.max); + d += `M 0 0`; + d += `L ${c(90 - f.range / 2, m)}`; + d += `A ${m} ${m} 0 0 0 ${c(90 + f.range / 2, m)}`; + d += `Z`; + } + + if (b) { + const m = mag(b.max); + d += `M 0 0`; + d += `L ${c(-90 - b.range / 2, m)}`; + d += `A ${m} ${m} 0 0 0 ${c(-90 + b.range / 2, m)}`; + d += `Z`; + } + + return ( + + + + + + + ); +}