From de80519f06a4d2d3185e2a0db8d338acea8f8d58 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:49:54 -0500 Subject: [PATCH] ricochet toy --- packages/i18n/strings/en.json | 10 + .../ricochet => media/random}/armor-hash.png | Bin .../GunFlexibilityVisualizer/index.tsx | 2 +- .../components/RicochetVisualizer/index.tsx | 2 +- .../TankopediaToys/index.module.css | 151 ++++++++++ .../src/components/TankopediaToys/index.tsx | 268 +++++++----------- .../[...locale]/tanks/[slug]/index.astro | 4 +- 7 files changed, 271 insertions(+), 166 deletions(-) rename packages/website/public/{assets/images/tankopedia/visualizers/ricochet => media/random}/armor-hash.png (100%) create mode 100644 packages/website/src/components/TankopediaToys/index.module.css diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 44aae9c71..0a9358313 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -28,6 +28,16 @@ "all": "All tanks" }, + "toys": { + "ricochet": { + "angle": "Angle", + "normalization": "Normalization", + "effective": "Effective", + "armor": "Armor", + "no_ricochet": "Explosive shells don't ricochet" + } + }, + "titles": { "gun": "Gun", "clip": "Clip", diff --git a/packages/website/public/assets/images/tankopedia/visualizers/ricochet/armor-hash.png b/packages/website/public/media/random/armor-hash.png similarity index 100% rename from packages/website/public/assets/images/tankopedia/visualizers/ricochet/armor-hash.png rename to packages/website/public/media/random/armor-hash.png 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 5de4fc3fe..21b7a6b6a 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 @@ -296,7 +296,7 @@ export function GunFlexibilityVisualizer({ height="100%" style={{ opacity: 2 ** -3, - backgroundImage: `url(/assets/images/tankopedia/visualizers/ricochet/armor-hash.png)`, + backgroundImage: `url(/media/random/armor-hash.png)`, boxShadow: "inset 0 0 4rem 1rem black", }} /> 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 index 4b8cf3d5c..81e66c872 100644 --- 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 @@ -60,7 +60,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) { height="100%" style={{ opacity: 2 ** -3, - backgroundImage: `url(/assets/images/tankopedia/visualizers/ricochet/armor-hash.png)`, + backgroundImage: `url(/media/random/armor-hash.png)`, }} /> diff --git a/packages/website/src/components/TankopediaToys/index.module.css b/packages/website/src/components/TankopediaToys/index.module.css new file mode 100644 index 000000000..9caaedeee --- /dev/null +++ b/packages/website/src/components/TankopediaToys/index.module.css @@ -0,0 +1,151 @@ +.visualizer { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + touch-action: none; + border-radius: inherit; + background-color: var(--gray-2); +} + +.armor { + position: absolute; + top: 0; + left: 0; + width: 33%; + height: 100%; + background: linear-gradient(to right, transparent, var(--gray-a4)); +} + +.armor-texture { + width: 100%; + height: 100%; + opacity: 0.125; + background-image: url("/media/random/armor-hash.png"); +} + +.zones { + position: absolute; + top: 0; + left: 33%; + width: 67%; + height: 100%; + background: conic-gradient( + at 0 50%, + var(--ruby-a2), + var(--ruby-a1) calc(90deg - var(--ricochet-angle)), + var(--jade-a2) calc(90deg - var(--ricochet-angle)), + var(--jade-a4) calc(90deg - var(--normalization-angle)), + var(--cyan-a5) calc(90deg - var(--normalization-angle)), + var(--cyan-a3) calc(90deg + var(--normalization-angle)), + var(--jade-a2) calc(90deg + var(--normalization-angle)), + var(--jade-a4) calc(90deg + var(--ricochet-angle)), + var(--ruby-a1) calc(90deg + var(--ricochet-angle)), + var(--ruby-a2) 180deg + ); +} + +.shell-path, +.effective-path { + position: absolute; + top: 50%; + left: 33%; + transform: translateX(-50%) rotate(var(--rotation-angle)); + transform-origin: top center; +} + +.shell-path { + width: 0; + height: 100%; +} + +.shell-path-extension { + position: absolute; + left: 50%; + bottom: 100%; + width: var(--space-0); + height: 100%; + transform: translateX(-50%); + background-color: var(--gray-a8); +} + +.shell-path-line { + position: absolute; + top: 0; + left: 50%; + width: var(--space-2); + height: 100%; + transform: translateX(-50%); + background: linear-gradient(var(--gray-a11), var(--gray-a4)); +} + +.shell { + position: absolute; + top: 6rem; + left: 0; + width: var(--space-5); + height: var(--space-5); + object-fit: contain; + transform: translate(-50%, -50%) rotate(-45deg); + filter: drop-shadow(0 0 var(--space-4) black); +} + +.effective-path { + width: 0; + height: 100%; +} + +.effective-path-line { + position: absolute; + left: 50%; + top: -100%; + width: var(--space-2); + height: 100%; + transform: translateX(-50%); + background: linear-gradient(var(--gray-a4), var(--gray-a11)); +} + +.corner-stat { + position: absolute; + display: flex; + gap: var(--space-2); + color: var(--gray-11); + font-size: var(--font-size-minor); +} + +.corner-stat-value { + color: var(--gray-12); + font-size: inherit; +} + +.corner-stat[data-position="top-left"] { + top: var(--space-3); + left: var(--space-3); +} + +.corner-stat[data-position="top-right"] { + top: var(--space-3); + right: var(--space-3); +} + +.corner-stat[data-position="bottom-left"] { + bottom: var(--space-3); + left: var(--space-3); +} + +.corner-stat[data-position="bottom-right"] { + right: var(--space-3); + bottom: var(--space-3); +} + +.explosive-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: var(--space-3); + color: var(--gray-11); + text-align: center; + background: radial-gradient(var(--black-a7), var(--black-a11)); +} diff --git a/packages/website/src/components/TankopediaToys/index.tsx b/packages/website/src/components/TankopediaToys/index.tsx index 52e548cff..a08b60e58 100644 --- a/packages/website/src/components/TankopediaToys/index.tsx +++ b/packages/website/src/components/TankopediaToys/index.tsx @@ -1,174 +1,108 @@ -import { isExplosive } from "@blitzkit/core"; -import { useRef, useState } from "react"; -import { degToRad } from "three/src/math/MathUtils.js"; +import { alias, isExplosive } from "@blitzkit/core"; +import { literals } from "@blitzkit/i18n"; +import { + useRef, + useState, + type CSSProperties, + type PointerEvent, +} from "react"; +import { clamp, degToRad, radToDeg } from "three/src/math/MathUtils.js"; import type { ToyProps } from "../../config/toys"; import { DuelModule, useDuel } from "../../hooks/useDuel"; import { useStrings } from "../../hooks/useStrings"; +import { Code } from "../Code"; +import styles from "./index.module.css"; export function RicochetToy({ characteristics }: ToyProps) { const strings = useStrings(); - const shell = useDuel("protagonist", DuelModule.Shell); - const container = useRef(null); - + const normalizationValue = characteristics.get("normalization"); + const ricochetValue = characteristics.get("ricochet"); const normalization = degToRad( - (characteristics.get("normalization") as number) ?? 0, + typeof normalizationValue === "number" ? normalizationValue : 0, + ); + const ricochet = degToRad( + typeof ricochetValue === "number" ? ricochetValue : 90, ); - const ricochet = degToRad((characteristics.get("ricochet") as number) ?? 90); const explodes = isExplosive(shell.type); - const [rawAngle, setAngle] = useState(degToRad(-25)); - 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 armorPercentage = Math.min(999, 100 / Math.cos(effectiveAngle)); + function updateAngle(event: PointerEvent) { + event.preventDefault(); + + 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; + + setAngle(clamp(Math.atan2(-y, x), -Math.PI / 2, Math.PI / 2)); + } + return ( - { - event.preventDefault(); - - 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); - }} +
- - - +
+
+
- - - - - +
+
- +
- - - +
+
- - - - {explodes && ( - - - {strings.website.tools.tankopedia.visualizers.ricochet.no_ricochet} - - +
+ {strings.tanks.characteristics.toys.ricochet.no_ricochet} +
)} -
+
+ ); +} + +enum CornerStatPosition { + TopLeft = "top-left", + TopRight = "top-right", + BottomLeft = "bottom-left", + BottomRight = "bottom-right", +} + +interface CornerStatProps { + label: string; + value: string; + position: CornerStatPosition; +} + +function CornerStat({ label, value, position }: CornerStatProps) { + return ( +
+ {label} + + {value} + +
); } diff --git a/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro b/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro index c6967f8eb..89c26f73a 100644 --- a/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro +++ b/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro @@ -21,11 +21,13 @@ const { id, locale } = Astro.props; - + + +