From e1b5b21fae1edcb019fc2fdd7ef17c646bcd82a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com>
Date: Fri, 1 Aug 2025 12:25:09 -0500
Subject: [PATCH] feat: cursor
---
packages/i18n/strings/en.json | 2 +-
.../components/Cursor.tsx | 66 -----------
.../components/Cursor/index.css | 7 ++
.../components/Cursor/index.tsx | 107 ++++++++++++++++++
.../AssaultRangesVisualizer/index.tsx | 80 +++++++------
.../components/InfoWithDelta.tsx | 2 +-
.../components/Characteristics/index.tsx | 5 +-
packages/website/src/core/blitzkit/fakeLog.ts | 7 ++
.../src/core/blitzkit/tankCharacteristics.ts | 13 +--
.../src/core/blitzkit/tankToDuelMember.ts | 1 +
packages/website/src/stores/duel/index.ts | 1 +
11 files changed, 181 insertions(+), 110 deletions(-)
delete mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor.tsx
create mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.css
create mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.tsx
create mode 100644 packages/website/src/core/blitzkit/fakeLog.ts
diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json
index b752e8717..539136d13 100644
--- a/packages/i18n/strings/en.json
+++ b/packages/i18n/strings/en.json
@@ -1083,7 +1083,6 @@
"hull_traverse_speed": "Hull traverse speed",
"camouflage": "Camouflage",
"reload": "Reload",
-
"crewCount": "Crew count",
"shellNormalization": "Normalization",
"shellRicochet": "Ricochet",
@@ -1097,6 +1096,7 @@
"caliber": "Caliber",
"penetration": "Penetration",
"damage": "Damage",
+ "damageWithoutAssault": "Damage without assault shells",
"clipDamage": "Clipping potential",
"moduleDamage": "Module damage",
"explosionRadius": "Explosion radius",
diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor.tsx
deleted file mode 100644
index 3ed0c304a..000000000
--- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import { DragHandleDots2Icon } from '@radix-ui/react-icons';
-import { Box, Flex, Text } from '@radix-ui/themes';
-import { useState } from 'react';
-import { Var } from '../../../../../../../../core/radix/var';
-
-export function AssaultCursor() {
- const [x, setX] = useState(0.5);
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.css b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.css
new file mode 100644
index 000000000..bf7e4950c
--- /dev/null
+++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.css
@@ -0,0 +1,7 @@
+#assault-cursor {
+ cursor: grab;
+}
+
+#assault-cursor:active {
+ cursor: grabbing;
+}
diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.tsx
new file mode 100644
index 000000000..50f7a9ff6
--- /dev/null
+++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/components/Cursor/index.tsx
@@ -0,0 +1,107 @@
+import { DragHandleDots2Icon } from '@radix-ui/react-icons';
+import { Box, Flex, Text } from '@radix-ui/themes';
+import { useEffect, useRef, useState } from 'react';
+import { clamp } from 'three/src/math/MathUtils.js';
+import { fakeLogInverse } from '../../../../../../../../../core/blitzkit/fakeLog';
+import { Var } from '../../../../../../../../../core/radix/var';
+import { Duel } from '../../../../../../../../../stores/duel';
+import './index.css';
+
+interface Props {
+ maxDistance: number;
+ maxFakeDistance: number;
+}
+
+export function AssaultCursor({ maxDistance, maxFakeDistance }: Props) {
+ const [x, setX] = useState(0.5);
+ const cursor = useRef(null);
+ const mutateDuel = Duel.useMutation();
+
+ useEffect(() => {
+ let localX = x;
+
+ function handlePointerDown(event: PointerEvent) {
+ event.preventDefault();
+
+ window.addEventListener('pointermove', handlePointerMove);
+ window.addEventListener('pointerup', handlePointerUp);
+ }
+
+ function handlePointerMove(event: PointerEvent) {
+ const parent = cursor.current?.parentElement;
+
+ if (!parent) return;
+
+ const width = parent.clientWidth;
+
+ setX((state) => {
+ localX = clamp(state + event.movementX / width, 0, 1);
+ return localX;
+ });
+ }
+
+ function handlePointerUp() {
+ mutateDuel((draft) => {
+ draft.protagonist.assaultDistance =
+ fakeLogInverse(localX * maxFakeDistance, 2) * maxDistance;
+ });
+
+ window.removeEventListener('pointermove', handlePointerMove);
+ window.removeEventListener('pointerup', handlePointerUp);
+ }
+
+ cursor.current?.addEventListener('pointerdown', handlePointerDown);
+
+ return () => {
+ cursor.current?.removeEventListener('pointerdown', handlePointerDown);
+ };
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx
index b0602c2e6..0b2d8ca13 100644
--- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx
+++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx
@@ -2,6 +2,7 @@ import type { AssaultRanges } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Box, Card, Flex, Text } from '@radix-ui/themes';
import { clamp } from 'three/src/math/MathUtils.js';
+import { fakeLog } from '../../../../../../../core/blitzkit/fakeLog';
import type { TankCharacteristics } from '../../../../../../../core/blitzkit/tankCharacteristics';
import { Var } from '../../../../../../../core/radix/var';
import { useLocale } from '../../../../../../../hooks/useLocale';
@@ -13,18 +14,14 @@ interface Props {
}
export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) {
- const maxDistance = Math.max(
- ..._ranges.ranges.map((range) => range.distance),
- );
+ const maxDistance = _ranges.ranges.at(-1)!.distance;
const ranges = _ranges.ranges.map((range) => ({
factor: range.factor,
distance: range.distance,
- fakeDistance: (2 / Math.PI) * Math.atan((4 * range.distance) / maxDistance),
+ fakeDistance: fakeLog(range.distance / maxDistance, 2),
}));
- const sumFakeDistances = ranges.reduce(
- (total, range) => total + range.fakeDistance,
- 0,
- );
+ const maxFakeDistance = ranges.at(-1)!.fakeDistance;
+
const { strings } = useLocale();
return (
@@ -51,8 +48,9 @@ export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) {
>
- {ranges.map((range) => (
+ {ranges.map((range, index) => (
))}
-
+
- {ranges.map((range, index) => (
-
- {
+ const last = ranges[index - 1];
+ const lastFakeDistance = last?.fakeDistance ?? 0;
+ const fakeWidth = range.fakeDistance - lastFakeDistance;
+ const fraction = fakeWidth / ranges.at(-1)!.fakeDistance;
+
+ return (
+
- {literals(strings.common.units.m, [
- `${Math.round(range.distance)}`,
- ])}
-
-
- ))}
+
+ {literals(strings.common.units.m, [
+ `${Math.round(range.distance)}`,
+ ])}
+
+
+ );
+ })}
diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx
index 8e7f53658..f83ba4375 100644
--- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx
+++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx
@@ -118,7 +118,7 @@ export function InfoWithDelta({
tank,
track: member.track,
turret: member.turret,
- assaultDistance: 0,
+ assaultDistance: member.assaultDistance,
},
{
equipmentDefinitions,
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 ceeb805a6..5e5de9e70 100644
--- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx
+++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx
@@ -24,6 +24,9 @@ export function Characteristics() {
const provisions = Duel.use((state) => state.protagonist.provisions);
const consumables = Duel.use((state) => state.protagonist.consumables);
const camouflage = Duel.use((state) => state.protagonist.camouflage);
+ const assaultDistance = Duel.use(
+ (state) => state.protagonist.assaultDistance,
+ );
const equipmentMatrix = Duel.use(
(state) => state.protagonist.equipmentMatrix,
);
@@ -58,7 +61,7 @@ export function Characteristics() {
applyReactiveArmor: false,
applyDynamicArmor: false,
applySpallLiner: false,
- assaultDistance: 0,
+ assaultDistance,
},
{
tankModelDefinition,
diff --git a/packages/website/src/core/blitzkit/fakeLog.ts b/packages/website/src/core/blitzkit/fakeLog.ts
new file mode 100644
index 000000000..93d44bfd0
--- /dev/null
+++ b/packages/website/src/core/blitzkit/fakeLog.ts
@@ -0,0 +1,7 @@
+export function fakeLog(x: number, a = 1) {
+ return (2 / Math.PI) * Math.atan((Math.PI * a * x) / 2);
+}
+
+export function fakeLogInverse(y: number, a = 1) {
+ return (2 / (Math.PI * a)) * Math.tan((Math.PI * y) / 2);
+}
diff --git a/packages/website/src/core/blitzkit/tankCharacteristics.ts b/packages/website/src/core/blitzkit/tankCharacteristics.ts
index e8e021203..0c152cedf 100644
--- a/packages/website/src/core/blitzkit/tankCharacteristics.ts
+++ b/packages/website/src/core/blitzkit/tankCharacteristics.ts
@@ -159,13 +159,12 @@ export function tankCharacteristics(
[applyDynamicArmor, -0.1],
[applySpallLiner && shell.type === ShellType.HE, -0.2],
);
- const assaultDamageCoefficient =
- gun.gun_type!.value.base.assault_ranges &&
- gun.gun_type!.value.base.assault_ranges.types.includes(shell.type)
- ? (gun.gun_type!.value.base.assault_ranges.ranges.find(
- ({ distance }) => distance >= assaultDistance,
- )?.factor ?? 0)
- : 1;
+ const assaultDamageCoefficient = gun.gun_type!.value.base.assault_ranges
+ ? // && gun.gun_type!.value.base.assault_ranges.types.includes(shell.type)
+ (gun.gun_type!.value.base.assault_ranges.ranges.find(
+ ({ distance }) => distance >= assaultDistance,
+ )?.factor ?? 0)
+ : 1;
const moduleDamageCoefficient =
coefficient([hasTungsten, 0.15]) * assaultDamageCoefficient;
const reloadCoefficient =
diff --git a/packages/website/src/core/blitzkit/tankToDuelMember.ts b/packages/website/src/core/blitzkit/tankToDuelMember.ts
index ab0d96a92..9e6c9dc3e 100644
--- a/packages/website/src/core/blitzkit/tankToDuelMember.ts
+++ b/packages/website/src/core/blitzkit/tankToDuelMember.ts
@@ -40,5 +40,6 @@ export function tankToDuelMember(
yaw,
pitch,
turret,
+ assaultDistance: 0,
} satisfies DuelMember;
}
diff --git a/packages/website/src/stores/duel/index.ts b/packages/website/src/stores/duel/index.ts
index 132c5d789..310e5c703 100644
--- a/packages/website/src/stores/duel/index.ts
+++ b/packages/website/src/stores/duel/index.ts
@@ -39,6 +39,7 @@ export interface DuelMember {
provisions: number[];
camouflage: boolean;
cooldownBooster: number;
+ assaultDistance: number;
}
export interface DuelStore {