From 98b555fa7ee1b74219d33ebec53046a0bb21becb Mon Sep 17 00:00:00 2001 From: Mark Greene Date: Sun, 4 Oct 2026 13:38:20 +1300 Subject: [PATCH] Fix: gun depression visualizer ignoring turret mount tilt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gun flexibility visualizer at the bottom of the Tankopedia Firepower section computed depression/elevation purely from the raw gun `pitch.min`/`pitch.max` values, ignoring `initial_turret_rotation.pitch` (the tank's baked-in turret mount tilt). Every other place that reports gun depression/elevation already accounts for this offset; the visualizer was the outlier. On tanks with a level turret mount the offset is zero, so it was invisible almost everywhere. Minotauro's turret sits pitched 3° forward, which made the visualizer's min/max/pitch stats and wedge shape disagree with the Firepower stats directly above it (e.g. 7°/-18° instead of 10°/15°). **Changes:** - `GunFlexibilityVisualizer/index.tsx`: fold the turret's initial pitch offset into `minPitch`, `maxPitch` and the displayed `pitch` stat, and into the drawn limit shape, the pitch dot and the pointer mapping, so the dashed neutrality circle sits at the displayed 0°. - `GunFlexibilityVisualizer/components/CanvasContent.tsx`: nest the gun mesh inside a turret group carrying the same static tilt as its base rotation, so the mini 3D preview's live drag-pitch composes on top of it, mirroring `useTankTransform.ts`. --- .../components/CanvasContent.tsx | 27 +++++++++++-- .../GunFlexibilityVisualizer/index.tsx | 40 +++++++++++++------ 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx index 2f1d7342c..1fdc38bb1 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx @@ -1,6 +1,7 @@ import type { QuicklimeEvent } from "quicklime"; import { useEffect, useRef } from "react"; import type { Group } from "three"; +import { degToRad } from "three/src/math/MathUtils.js"; import { api } from "../../../../../../../../core/blitzkit/api"; import { modelTransformEvent, @@ -21,6 +22,13 @@ export function CanvasContent() { const tankModel = modelDefinitions.models[tank.id]; const turretModel = tankModel.turrets[turret.id]; + // baked-in turret mount tilt (e.g. Minotauro sits pitched 3deg forward); + // applied as a static base rotation so the live gun pitch composes on top + // of it, mirroring useTankTransform.ts's turretContainer/gunContainer nesting + const initialTurretPitch = -degToRad( + tankModel.initial_turret_rotation?.pitch ?? 0, + ); + useEffect(() => { function handleModelTransformEvent( event: QuicklimeEvent, @@ -45,10 +53,23 @@ export function CanvasContent() { - + + + - - + + + + ); 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 index 171c1a73d..93d7a5e1a 100644 --- 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 @@ -48,13 +48,27 @@ export function GunFlexibilityVisualizer({ const turretModel = tankModel.turrets[turret.id]; const gunModel = turretModel.guns[gun.id]; + // turret's own baked-in mount tilt (e.g. Minotauro's turret sits pitched + // 3deg forward on the hull); must be folded into displayed pitch values + // the same way tankCharacteristics.ts and QuickInputs do + const initialTurretPitch = degToRad( + tankModel.initial_turret_rotation?.pitch ?? 0, + ); + + // radial map is in the tilt-compensated frame so the neutrality circle sits + // at the displayed 0deg + const tiltDeg = radToDeg(initialTurretPitch); + const magTilted = (deg: number) => mag(deg + tiltDeg); + const [yaw, setYaw] = useState(0); const [pitch, setPitch] = useState(0); const [minPitch, setMinPitch] = useState( - -degToRad(gunModel.pitch!.front?.max ?? gunModel.pitch!.max), + -degToRad(gunModel.pitch!.front?.max ?? gunModel.pitch!.max) - + initialTurretPitch, ); const [maxPitch, setMaxPitch] = useState( - -degToRad(gunModel.pitch!.front?.min ?? gunModel.pitch!.min), + -degToRad(gunModel.pitch!.front?.min ?? gunModel.pitch!.min) - + initialTurretPitch, ); const container = useRef(null); @@ -70,7 +84,7 @@ export function GunFlexibilityVisualizer({ let d = ""; // right - const mo = mag(p.max); + const mo = magTilted(p.max); if (y) { d += `M 0 0`; d += `L ${c(90 - y.max, mo)}`; @@ -87,7 +101,7 @@ export function GunFlexibilityVisualizer({ // front if (f) { - const m = mag(f.max); + const m = magTilted(f.max); d += `L ${c(90 - f.range / 2, m)}`; d += `A ${m} ${m} 0 0 0 ${c(90 + f.range / 2, m)}`; } @@ -108,7 +122,7 @@ export function GunFlexibilityVisualizer({ // back if (!y && b) { - const m = mag(b.max); + const m = magTilted(b.max); d += `L ${c(-90 - b.range / 2, m)}`; d += `A ${m} ${m} 0 0 0 ${c(-90 + b.range / 2, m)}`; } @@ -116,7 +130,7 @@ export function GunFlexibilityVisualizer({ d += `Z`; // right - const mi = mag(p.min); + const mi = magTilted(p.min); if (y) { d += `M 0 0`; d += `L ${c(90 - y.max, mi)}`; @@ -133,7 +147,7 @@ export function GunFlexibilityVisualizer({ // front if (f) { - const m = mag(f.min); + const m = magTilted(f.min); d += `L ${c(90 - f.range / 2, m)}`; d += `A ${m} ${m} 0 0 0 ${c(90 + f.range / 2, m)}`; } @@ -154,14 +168,14 @@ export function GunFlexibilityVisualizer({ // back if (!y && b) { - const m = mag(b.min); + const m = magTilted(b.min); d += `L ${c(-90 - b.range / 2, m)}`; d += `A ${m} ${m} 0 0 0 ${c(-90 + b.range / 2, m)}`; } d += `Z`; - const r = mag(radToDeg(-pitch)); + const r = mag(radToDeg(-(pitch - initialTurretPitch))); const dotX = r * Math.sin(yaw); const dotY = r * Math.cos(yaw); @@ -251,13 +265,13 @@ export function GunFlexibilityVisualizer({ yaw = min[1]; let t = 2 * Math.sqrt((u / rect.width) ** 2 + (v / rect.height) ** 2); t = clamp(t, 0, 1); - let pitch = degToRad(magInverse(-t)); + let pitch = degToRad(magInverse(-t)) + initialTurretPitch; pitch = clamp(pitch, min[0], max[0]); setPitch(pitch); setYaw(yaw); - setMinPitch(min[0]); - setMaxPitch(max[0]); + setMinPitch(min[0] - initialTurretPitch); + setMaxPitch(max[0] - initialTurretPitch); modelTransformEvent.dispatch({ pitch, yaw }); }} @@ -376,7 +390,7 @@ export function GunFlexibilityVisualizer({