diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 90113a9c2..fa2fae0e7 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -11,7 +11,8 @@ "mm": "%s1mm", "deg": "%s1°", "hp": "%s1hp", - "x": "x%s1" + "x": "x%s1", + "percentage": "%s1%" }, "gun_types": { @@ -871,8 +872,10 @@ }, "ricochet": { - "safe": "Safe <%s1°", - "unsafe": "Unsafe ≥%s1°" + "angle": "Angle", + "normalization": "Normalization", + "effective_angle": "Effective angle", + "armor": "Armor" }, "view_range": { diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/HullTraverseVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/HullTraverseVisualizer/index.tsx index 4cf5843ab..08774a8eb 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/HullTraverseVisualizer/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/HullTraverseVisualizer/index.tsx @@ -31,7 +31,7 @@ export function HullTraverseVisualizer({ stats }: VisualizerProps) { left: '50%', transform: 'translate(-50%, -50%)', animationDuration: `${ - rotateHull ? 1 / (stats[`hullTraverse${terrain}Terrain`] / 360) : 0 + rotateHull ? 360 / stats[`hullTraverse${terrain}Terrain`] : 0 }s`, }} > @@ -59,7 +59,7 @@ export function HullTraverseVisualizer({ stats }: VisualizerProps) { filter: 'drop-shadow(0px 0px 4px black)', transform: 'translate(-50%, -50%)', animationDuration: `${ - rotateTurret ? 1 / (stats.turretTraverseSpeed / 360) : 0 + rotateTurret ? 360 / stats.turretTraverseSpeed : 0 }s`, }} src="/assets/images/tankopedia/visualizers/hull-traverse/turret.png" 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 new file mode 100644 index 000000000..8f9c67f77 --- /dev/null +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer.tsx @@ -0,0 +1,236 @@ +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 { VisualizerProps } from './HullTraverseVisualizer'; + +export function RicochetVisualizer({ stats }: VisualizerProps) { + 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(45)); + 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(2), + ])} + + + + + ); +} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.css b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.css deleted file mode 100644 index 5a8def587..000000000 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.css +++ /dev/null @@ -1,134 +0,0 @@ -@keyframes penetrating-shell-slide { - 0% { - margin-top: -9rem; - } - 28%, - 100% { - margin-top: 8rem; - } -} - -.ricochet-visualizer-penetrating-shell { - animation: penetrating-shell-slide infinite; -} - -@keyframes ricocheting-shell-slide { - 0% { - margin-top: -18rem; - } - 38% { - margin-top: -18rem; - } - 63%, - 69% { - margin-top: 0rem; - } - 100% { - margin-top: 8rem; - } -} - -@keyframes ricocheting-shell-slide-container { - 0% { - transform: translateX(-50%) rotate(80deg); - } - 63% { - transform: translateX(-50%) rotate(80deg); - } - 69% { - transform: translateX(-50%) rotate(100deg); - } - 100% { - transform: translateX(-50%) rotate(100deg); - } -} - -.ricochet-visualizer-ricocheting-shell { - animation: ricocheting-shell-slide infinite; -} - -.ricochet-visualizer-ricocheting-shell-container { - animation: ricocheting-shell-slide-container infinite; -} - -@keyframes surface-impact { - 0%, - 60% { - opacity: 1; - width: 0; - height: 0; - } - 66% { - opacity: 1; - width: 1.5rem; - height: 1.5rem; - } - 72%, - 100% { - opacity: 0; - width: 3rem; - height: 3rem; - } -} - -.ricochet-visualizer-surface-impact { - animation: surface-impact linear infinite; -} - -@keyframes puncture { - 0%, - 10% { - opacity: 1; - margin-top: -6rem; - } - 15%, - 30% { - opacity: 1; - margin-top: -1rem; - } - 45%, - 100% { - opacity: 0; - margin-top: -1rem; - } -} - -.ricochet-visualizer-puncture { - animation: puncture infinite; -} - -@keyframes safe { - 0% { - opacity: 0; - } - 5%, - 28% { - opacity: 1; - } - 33%, - 100% { - opacity: 0; - } -} - -.ricochet-visualizer-safe { - animation: safe infinite; -} - -@keyframes unsafe { - 0%, - 33% { - opacity: 0; - } - 38%, - 95% { - opacity: 1; - } - 100% { - opacity: 0; - } -} - -.ricochet-visualizer-unsafe { - animation: unsafe infinite; -} 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 deleted file mode 100644 index 0cb147972..000000000 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx +++ /dev/null @@ -1,229 +0,0 @@ -import { asset } from '@blitzkit/core'; -import { literals } from '@blitzkit/i18n'; -import { Box, Card, Text } from '@radix-ui/themes'; -import { Var } from '../../../../../../../core/radix/var'; -import { useLocale } from '../../../../../../../hooks/useLocale'; -import { Duel } from '../../../../../../../stores/duel'; -import type { VisualizerProps } from '../HullTraverseVisualizer'; -import './index.css'; - -export function RicochetVisualizer({ stats }: VisualizerProps) { - const ricochet = stats.shellRicochet ?? 90; - const shell = Duel.use((state) => state.protagonist.shell); - const { strings } = useLocale(); - const animationDuration = '6s'; - - return ( - - - - - - - - - - - - - {/* */} - - - - - - - - - - - - - - - - - {literals( - strings.website.tools.tankopedia.visualizers.ricochet.safe, - [ricochet.toFixed(0)], - )} - - - - {literals( - strings.website.tools.tankopedia.visualizers.ricochet.unsafe, - [ricochet.toFixed(0)], - )} - - - - ); -} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer2/index.css b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer2/index.css deleted file mode 100644 index 5a8def587..000000000 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer2/index.css +++ /dev/null @@ -1,134 +0,0 @@ -@keyframes penetrating-shell-slide { - 0% { - margin-top: -9rem; - } - 28%, - 100% { - margin-top: 8rem; - } -} - -.ricochet-visualizer-penetrating-shell { - animation: penetrating-shell-slide infinite; -} - -@keyframes ricocheting-shell-slide { - 0% { - margin-top: -18rem; - } - 38% { - margin-top: -18rem; - } - 63%, - 69% { - margin-top: 0rem; - } - 100% { - margin-top: 8rem; - } -} - -@keyframes ricocheting-shell-slide-container { - 0% { - transform: translateX(-50%) rotate(80deg); - } - 63% { - transform: translateX(-50%) rotate(80deg); - } - 69% { - transform: translateX(-50%) rotate(100deg); - } - 100% { - transform: translateX(-50%) rotate(100deg); - } -} - -.ricochet-visualizer-ricocheting-shell { - animation: ricocheting-shell-slide infinite; -} - -.ricochet-visualizer-ricocheting-shell-container { - animation: ricocheting-shell-slide-container infinite; -} - -@keyframes surface-impact { - 0%, - 60% { - opacity: 1; - width: 0; - height: 0; - } - 66% { - opacity: 1; - width: 1.5rem; - height: 1.5rem; - } - 72%, - 100% { - opacity: 0; - width: 3rem; - height: 3rem; - } -} - -.ricochet-visualizer-surface-impact { - animation: surface-impact linear infinite; -} - -@keyframes puncture { - 0%, - 10% { - opacity: 1; - margin-top: -6rem; - } - 15%, - 30% { - opacity: 1; - margin-top: -1rem; - } - 45%, - 100% { - opacity: 0; - margin-top: -1rem; - } -} - -.ricochet-visualizer-puncture { - animation: puncture infinite; -} - -@keyframes safe { - 0% { - opacity: 0; - } - 5%, - 28% { - opacity: 1; - } - 33%, - 100% { - opacity: 0; - } -} - -.ricochet-visualizer-safe { - animation: safe infinite; -} - -@keyframes unsafe { - 0%, - 33% { - opacity: 0; - } - 38%, - 95% { - opacity: 1; - } - 100% { - opacity: 0; - } -} - -.ricochet-visualizer-unsafe { - animation: unsafe infinite; -} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer2/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer2/index.tsx deleted file mode 100644 index 6ab71462f..000000000 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer2/index.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { asset } from '@blitzkit/core'; -import { Box, Card, Text } from '@radix-ui/themes'; -import { clamp } from 'lodash-es'; -import { useRef } from 'react'; -import { degToRad } from 'three/src/math/MathUtils.js'; -import { Var } from '../../../../../../../core/radix/var'; -import { Duel } from '../../../../../../../stores/duel'; -import type { VisualizerProps } from '../HullTraverseVisualizer'; -import './index.css'; - -const MAX_THICKNESS = 300; -const EXAGGERATION = 4; - -export function RicochetVisualizer2({ stats }: VisualizerProps) { - const thickness = 150; - const shell = Duel.use((state) => state.protagonist.shell); - const container = useRef(null); - const incomingTracer = useRef(null); - const normalizedTracer = useRef(null); - const normalization = EXAGGERATION * degToRad(stats.shellNormalization); - - return ( - { - if ( - !container.current || - !incomingTracer.current || - !normalizedTracer.current - ) - return; - - const rect = container.current.getBoundingClientRect(); - const x = event.clientX - rect.left - rect.width / 2; - const y = event.clientY - rect.top - rect.height / 2; - const angle = clamp(Math.atan2(y, x), -Math.PI / 2, Math.PI / 2); - const normalizedAngle = - Math.sign(angle) * Math.max(0, Math.abs(angle) - normalization); - - incomingTracer.current.style.transform = `rotate(${-Math.PI / 2 + angle}rad)`; - normalizedTracer.current.style.transform = `rotate(${-Math.PI / 2 + normalizedAngle}rad)`; - }} - > - - - - - - - - - - - - - - - - - - *Not to scale - - - - ); -} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx index b9e538c3b..72010177e 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx @@ -35,7 +35,6 @@ import { HullTraverseVisualizer } from './components/HullTraverseVisualizer'; import { Info } from './components/Info'; import { InfoWithDelta } from './components/InfoWithDelta'; import { RicochetVisualizer } from './components/RicochetVisualizer'; -import { RicochetVisualizer2 } from './components/RicochetVisualizer2'; import { ViewRangeVisualizer } from './components/ViewRangeVisualizer'; const [equipmentDefinitions, provisionDefinitions] = await Promise.all([ @@ -390,7 +389,6 @@ export function Characteristics() { value="shellRicochet" /> - )}