feat: remove reactive pitch and yaw

This commit is contained in:
TrèsAbhi committed 2025-08-12 13:43:11 -05:00
1 parent 4ddf77fc7f
commit 94cba0c9ba
12 files changed
+141 -190

No files matched your search

@@ -1,4 +1,3 @@
import { normalizeAngleRad } from '@blitzkit/core';
import { useThree, type ThreeEvent } from '@react-three/fiber'; import { useThree, type ThreeEvent } from '@react-three/fiber';
import { memo, useRef } from 'react'; import { memo, useRef } from 'react';
import { Group, Plane, Vector2, Vector3 } from 'three'; import { Group, Plane, Vector2, Vector3 } from 'three';
@@ -153,15 +152,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const position = new Vector2(); const position = new Vector2();
const delta = new Vector2(); const delta = new Vector2();
let pitch = 0;
let yaw = 0;
function onPointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
yaw = protagonist.yaw;
pitch = protagonist.pitch;
mutateTankopediaEphemeral((draft) => { mutateTankopediaEphemeral((draft) => {
draft.controlsEnabled = false; draft.controlsEnabled = false;
@@ -190,9 +185,10 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
delta.set(event.clientX, event.clientY).sub(position); delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
[pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
pitch, modelTransformEvent.last!.pitch,
yaw + delta.x * (Math.PI / boundingRect.width), modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
@@ -201,10 +197,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
} }
function handlePointerUp() { function handlePointerUp() {
mutateDuel((draft) => {
draft.protagonist.pitch = normalizeAngleRad(pitch);
draft.protagonist.yaw = normalizeAngleRad(yaw);
});
mutateTankopediaEphemeral((draft) => { mutateTankopediaEphemeral((draft) => {
draft.controlsEnabled = true; draft.controlsEnabled = true;
}); });
@@ -253,8 +245,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const position = new Vector2(); const position = new Vector2();
const delta = new Vector2(); const delta = new Vector2();
let pitch = 0;
let yaw = 0;
function onPointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
@@ -268,8 +258,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
}); });
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
pitch = protagonist.pitch;
yaw = protagonist.yaw;
window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp); window.addEventListener('pointerup', handlePointerUp);
@@ -290,9 +278,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
delta.set(event.clientX, event.clientY).sub(position); delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
[pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
pitch - delta.y * (Math.PI / boundingRect.height), modelTransformEvent.last!.pitch -
yaw + delta.x * (Math.PI / boundingRect.width), delta.y * (Math.PI / boundingRect.height),
modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
@@ -304,10 +294,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
mutateTankopediaEphemeral((draft) => { mutateTankopediaEphemeral((draft) => {
draft.controlsEnabled = true; draft.controlsEnabled = true;
}); });
mutateDuel((draft) => {
draft.protagonist.pitch = normalizeAngleRad(pitch);
draft.protagonist.yaw = normalizeAngleRad(yaw);
});
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
} }
@@ -341,8 +327,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const position = new Vector2(); const position = new Vector2();
const delta = new Vector2(); const delta = new Vector2();
let pitch = 0;
let yaw = 0;
function onPointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
@@ -356,9 +340,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
}); });
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
pitch = protagonist.pitch;
yaw = protagonist.yaw;
window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp); window.addEventListener('pointerup', handlePointerUp);
} }
@@ -378,9 +359,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
delta.set(event.clientX, event.clientY).sub(position); delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
[pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
pitch - delta.y * (Math.PI / boundingRect.height), modelTransformEvent.last!.pitch -
yaw + delta.x * (Math.PI / boundingRect.width), delta.y * (Math.PI / boundingRect.height),
modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
@@ -392,10 +375,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
mutateTankopediaEphemeral((draft) => { mutateTankopediaEphemeral((draft) => {
draft.controlsEnabled = true; draft.controlsEnabled = true;
}); });
mutateDuel((draft) => {
draft.protagonist.pitch = normalizeAngleRad(pitch);
draft.protagonist.yaw = normalizeAngleRad(yaw);
});
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
} }
@@ -1,6 +1,7 @@
import { I_HAT, J_HAT } from '@blitzkit/core'; import { I_HAT, J_HAT } from '@blitzkit/core';
import { type MeshProps, useThree } from '@react-three/fiber'; import { type MeshProps, useThree } from '@react-three/fiber';
import { clamp } from 'lodash-es'; import { clamp } from 'lodash-es';
import type { QuicklimeEvent } from 'quicklime';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { import {
Box3, Box3,
@@ -186,16 +187,18 @@ export function StaticArmorSceneComponent({
const distanceToBarrel = clip.distanceToPoint(barrelOrigin); const distanceToBarrel = clip.distanceToPoint(barrelOrigin);
const point = new Vector3(); const point = new Vector3();
function handleModelTransform(data: ModelTransformEventData) { function handleModelTransform(
event: QuicklimeEvent<ModelTransformEventData>,
) {
clip.normal clip.normal
.set(0, 0, -1) .set(0, 0, -1)
.applyAxisAngle(I_HAT, data.pitch) .applyAxisAngle(I_HAT, event.data.pitch)
.applyAxisAngle(J_HAT, data.yaw ?? 0); .applyAxisAngle(J_HAT, event.data.yaw);
clip.constant = 0; clip.constant = 0;
point point
.copy(gunOrigin) .copy(gunOrigin)
.applyAxisAngle(J_HAT, data.yaw ?? 0) .applyAxisAngle(J_HAT, event.data.yaw)
.add(clip.normal.multiplyScalar(-distanceToBarrel)) .add(clip.normal.multiplyScalar(-distanceToBarrel))
.add(neckOrigin); .add(neckOrigin);
clip.normal.normalize(); clip.normal.normalize();
@@ -3,6 +3,7 @@ import { useEffect } from 'react';
import { awaitableEquipmentDefinitions } from '../../../../../core/awaitables/equipmentDefinitions'; import { awaitableEquipmentDefinitions } from '../../../../../core/awaitables/equipmentDefinitions';
import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions'; import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions';
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
import { modelTransformEvent } from '../../../../../core/blitzkit/modelTransform';
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics'; import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
import { useEquipment } from '../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../hooks/useEquipment';
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition'; import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
@@ -75,16 +76,16 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
const hasDownImprovedVerticalStabilizer = useEquipment(124); const hasDownImprovedVerticalStabilizer = useEquipment(124);
useEffect(() => { useEffect(() => {
mutateDuel((draft) => { const [pitch, yaw] = applyPitchYawLimits(
[draft.protagonist.pitch, draft.protagonist.yaw] = applyPitchYawLimits( modelTransformEvent.last!.pitch,
draft.protagonist.pitch, modelTransformEvent.last!.yaw,
draft.protagonist.yaw, gunModelDefinition.pitch,
gunModelDefinition.pitch, turretModelDefinition.yaw,
turretModelDefinition.yaw, hasImprovedVerticalStabilizer,
hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer, );
);
}); modelTransformEvent.dispatch({ pitch, yaw });
}, [hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer]); }, [hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer]);
return ( return (
@@ -1,5 +1,6 @@
import { HeightIcon, WidthIcon } from '@radix-ui/react-icons'; import { HeightIcon, WidthIcon } from '@radix-ui/react-icons';
import { Flex, TextField } from '@radix-ui/themes'; import { Flex, TextField } from '@radix-ui/themes';
import type { QuicklimeEvent } from 'quicklime';
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { degToRad, radToDeg } from 'three/src/math/MathUtils.js'; import { degToRad, radToDeg } from 'three/src/math/MathUtils.js';
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
@@ -28,31 +29,19 @@ export function QuickInputs() {
const hasDownImprovedVerticalStabilizer = useEquipment(124); const hasDownImprovedVerticalStabilizer = useEquipment(124);
useEffect(() => { useEffect(() => {
if (!yawInput.current) return; function handleTransformEvent(
event: QuicklimeEvent<ModelTransformEventData>,
yawInput.current.value = radToDeg(protagonist.yaw).toFixed(1); ) {
}, [protagonist.yaw]);
useEffect(() => {
if (!pitchInput.current) return;
pitchInput.current.value = (
-radToDeg(protagonist.pitch) + initialGunPitch
).toFixed(1);
}, [protagonist.pitch]);
useEffect(() => {
function handleTransformEvent({ pitch, yaw }: ModelTransformEventData) {
if (!pitchInput.current || !yawInput.current) return; if (!pitchInput.current || !yawInput.current) return;
pitchInput.current.value = ( pitchInput.current.value = (
-radToDeg(pitch) + -radToDeg(event.data.pitch) +
(tankModelDefinition.initial_turret_rotation?.pitch ?? 0) (tankModelDefinition.initial_turret_rotation?.pitch ?? 0)
).toFixed(1); ).toFixed(1);
if (yaw === undefined) return; if (event.data.yaw === undefined) return;
yawInput.current.value = radToDeg(yaw).toFixed(1); yawInput.current.value = radToDeg(event.data.yaw).toFixed(1);
} }
modelTransformEvent.on(handleTransformEvent); modelTransformEvent.on(handleTransformEvent);
@@ -85,24 +74,27 @@ export function QuickInputs() {
defaultValue="0.0" defaultValue="0.0"
onBlur={() => { onBlur={() => {
const value = Number(yawInput.current!.value); const value = Number(yawInput.current!.value);
if (isNaN(value)) { if (isNaN(value)) {
yawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1); yawInput.current!.value = radToDeg(
modelTransformEvent.last!.yaw,
).toFixed(1);
return; return;
} }
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
protagonist.pitch, modelTransformEvent.last!.pitch,
degToRad(value), degToRad(value),
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer,
); );
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
mutateDuel((state) => { yawInput.current!.value = radToDeg(
state.protagonist.pitch = pitch; modelTransformEvent.last!.yaw,
state.protagonist.yaw = yaw; ).toFixed(1);
});
yawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1);
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
@@ -128,25 +120,21 @@ export function QuickInputs() {
const value = Number(pitchInput.current!.value); const value = Number(pitchInput.current!.value);
if (isNaN(value)) { if (isNaN(value)) {
pitchInput.current!.value = ( pitchInput.current!.value = (
-radToDeg(protagonist.pitch) + initialGunPitch -radToDeg(modelTransformEvent.last!.pitch) + initialGunPitch
).toFixed(1); ).toFixed(1);
return; return;
} }
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
degToRad(-value + initialGunPitch), degToRad(-value + initialGunPitch),
protagonist.yaw, modelTransformEvent.last!.yaw,
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer,
); );
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
mutateDuel((state) => {
state.protagonist.pitch = pitch;
state.protagonist.yaw = yaw;
});
pitchInput.current!.value = ( pitchInput.current!.value = (
-radToDeg(protagonist.pitch) + initialGunPitch -radToDeg(modelTransformEvent.last!.pitch) + initialGunPitch
).toFixed(1); ).toFixed(1);
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
@@ -0,0 +1,31 @@
import { useEffect } from 'react';
import { degToRad } from 'three/src/math/MathUtils.js';
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
import { Duel } from '../../../../../../stores/duel';
const modelDefinitions = await awaitableModelDefinitions;
export function InitialAligner() {
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 = modelDefinitions.models[tank.id];
const turretModel = tankModel.turrets[turret.id];
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
useEffect(() => {
const [pitch, yaw] = applyPitchYawLimits(
degToRad(8),
degToRad(25),
gunModel.pitch,
turretModel.yaw,
);
modelTransformEvent.dispatch({ pitch, yaw });
}, []);
return null;
}
@@ -39,9 +39,6 @@ export function SceneProps() {
const material = useRef<MeshStandardMaterial>(null); const material = useRef<MeshStandardMaterial>(null);
const playground = useRef<Group>(null); const playground = useRef<Group>(null);
let lastPitch = Duel.use((state) => state.protagonist.pitch);
let lastYaw = Duel.use((state) => state.protagonist.yaw);
const display = TankopediaEphemeral.use((state) => state.display);
const turret = Duel.use((state) => state.protagonist.turret); const turret = Duel.use((state) => state.protagonist.turret);
const track = Duel.use((state) => state.protagonist.track); const track = Duel.use((state) => state.protagonist.track);
const tank = Duel.use((state) => state.protagonist.tank); const tank = Duel.use((state) => state.protagonist.tank);
@@ -152,8 +149,8 @@ export function SceneProps() {
const desiredPitch = Math.asin(direction.y); const desiredPitch = Math.asin(direction.y);
const desiredYaw = Math.atan2(-direction.x, -direction.z); const desiredYaw = Math.atan2(-direction.x, -direction.z);
const deltaPitch = desiredPitch - lastPitch; const deltaPitch = desiredPitch - modelTransformEvent.last!.pitch;
const deltaYaw = desiredYaw - lastYaw; const deltaYaw = desiredYaw - modelTransformEvent.last!.yaw;
const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2); const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2);
const maxPossibleRotationInTime = gunRotationSpeed * deltaT; const maxPossibleRotationInTime = gunRotationSpeed * deltaT;
const clampedDeltaAngle = Math.min(maxPossibleRotationInTime, deltaAngle); const clampedDeltaAngle = Math.min(maxPossibleRotationInTime, deltaAngle);
@@ -163,14 +160,14 @@ export function SceneProps() {
deltaAngle === 0 ? 0 : deltaYaw * (clampedDeltaAngle / deltaAngle); deltaAngle === 0 ? 0 : deltaYaw * (clampedDeltaAngle / deltaAngle);
const [pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
lastPitch + clampedDeltaPitch, modelTransformEvent.last!.pitch + clampedDeltaPitch,
lastYaw + clampedDeltaYaw, modelTransformEvent.last!.yaw + clampedDeltaYaw,
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
); );
const actualDeltaPitch = pitch - lastPitch; const actualDeltaPitch = pitch - modelTransformEvent.last!.pitch;
const actualDeltaYaw = yaw - lastYaw; const actualDeltaYaw = yaw - modelTransformEvent.last!.yaw;
const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw); const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw);
const rotationSpeed = deltaRotation / deltaT; const rotationSpeed = deltaRotation / deltaT;
const turretTraversePenalty = const turretTraversePenalty =
@@ -190,8 +187,6 @@ export function SceneProps() {
} }
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
lastPitch = pitch;
lastYaw = yaw;
const gunDirection = new Vector3(0, 0, -1) const gunDirection = new Vector3(0, 0, -1)
.applyAxisAngle(I_HAT, pitch) .applyAxisAngle(I_HAT, pitch)
@@ -1,4 +1,3 @@
import { normalizeAngleRad } from '@blitzkit/core';
import { invalidate, useThree, type ThreeEvent } from '@react-three/fiber'; import { invalidate, useThree, type ThreeEvent } from '@react-three/fiber';
import { useRef } from 'react'; import { useRef } from 'react';
import { Group, Mesh, MeshStandardMaterial, Vector2 } from 'three'; import { Group, Mesh, MeshStandardMaterial, Vector2 } from 'three';
@@ -125,17 +124,12 @@ export function TankModel() {
if (!isVisible) return null; if (!isVisible) return null;
let pitch = 0;
let yaw = 0;
function onPointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
if (!isTurret) return; if (!isTurret) return;
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
yaw = protagonist.yaw;
pitch = protagonist.pitch;
mutateTankopediaEphemeral((draft) => { mutateTankopediaEphemeral((draft) => {
draft.controlsEnabled = false; draft.controlsEnabled = false;
@@ -164,9 +158,10 @@ export function TankModel() {
delta.set(event.clientX, event.clientY).sub(position); delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
[pitch, yaw] = applyPitchYawLimits( const [pitch, yaw] = applyPitchYawLimits(
pitch, modelTransformEvent.last!.pitch,
yaw + delta.x * (Math.PI / boundingRect.width), modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch, gunModelDefinition.pitch,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
@@ -175,10 +170,6 @@ export function TankModel() {
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
} }
function handlePointerUp() { function handlePointerUp() {
mutateDuel((draft) => {
draft.protagonist.pitch = normalizeAngleRad(pitch);
draft.protagonist.yaw = normalizeAngleRad(yaw);
});
mutateTankopediaEphemeral((draft) => { mutateTankopediaEphemeral((draft) => {
draft.controlsEnabled = true; draft.controlsEnabled = true;
}); });
@@ -231,8 +222,8 @@ export function TankModel() {
}); });
position.set(event.clientX, event.clientY); position.set(event.clientX, event.clientY);
pitch = protagonist.pitch; pitch = modelTransformEvent.last!.pitch;
yaw = protagonist.yaw; yaw = modelTransformEvent.last!.yaw;
window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp); window.addEventListener('pointerup', handlePointerUp);
@@ -267,10 +258,6 @@ export function TankModel() {
mutateTankopediaEphemeral((draft) => { mutateTankopediaEphemeral((draft) => {
draft.controlsEnabled = true; draft.controlsEnabled = true;
}); });
mutateDuel((draft) => {
draft.protagonist.pitch = normalizeAngleRad(pitch);
draft.protagonist.yaw = normalizeAngleRad(yaw);
});
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
} }
@@ -28,6 +28,7 @@ import {
import { SmartCanvas } from '../../../../SmartCanvas'; import { SmartCanvas } from '../../../../SmartCanvas';
import { AutoClear } from './components/AutoClear'; import { AutoClear } from './components/AutoClear';
import { Controls } from './components/Control'; import { Controls } from './components/Control';
import { InitialAligner } from './components/InitialAligner';
import { InitialFogReveal } from './components/InitialFogReveal'; import { InitialFogReveal } from './components/InitialFogReveal';
import { Lighting } from './components/Lighting'; import { Lighting } from './components/Lighting';
import { SceneProps } from './components/SceneProps'; import { SceneProps } from './components/SceneProps';
@@ -114,56 +115,44 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
function handlePoseEvent(pose: Pose) { function handlePoseEvent(pose: Pose) {
switch (pose) { switch (pose) {
case Pose.HullDown: { case Pose.HullDown: {
mutateDuel((draft) => { const [pitch, yaw] = applyPitchYawLimits(
const [pitch, yaw] = applyPitchYawLimits( -Infinity,
-Infinity, 0,
0, gunModelDefinition.pitch,
gunModelDefinition.pitch, turretModelDefinition.yaw,
turretModelDefinition.yaw, hasImprovedVerticalStabilizer,
hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer, );
);
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
draft.protagonist.pitch = pitch;
draft.protagonist.yaw = yaw;
});
break; break;
} }
case Pose.FaceHug: { case Pose.FaceHug: {
mutateDuel((draft) => { const [pitch, yaw] = applyPitchYawLimits(
const [pitch, yaw] = applyPitchYawLimits( Infinity,
Infinity, 0,
0, gunModelDefinition.pitch,
gunModelDefinition.pitch, turretModelDefinition.yaw,
turretModelDefinition.yaw, hasImprovedVerticalStabilizer,
hasImprovedVerticalStabilizer, );
);
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
draft.protagonist.pitch = pitch;
draft.protagonist.yaw = yaw;
});
break; break;
} }
case Pose.Default: case Pose.Default:
mutateDuel((draft) => { const [pitch, yaw] = applyPitchYawLimits(
const [pitch, yaw] = applyPitchYawLimits( 0,
0, 0,
0, gunModelDefinition.pitch,
gunModelDefinition.pitch, turretModelDefinition.yaw,
turretModelDefinition.yaw, hasImprovedVerticalStabilizer,
hasImprovedVerticalStabilizer, );
);
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
draft.protagonist.pitch = pitch;
draft.protagonist.yaw = yaw;
});
break; break;
} }
@@ -177,15 +166,15 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
}); });
useEffect(() => { useEffect(() => {
mutateDuel((draft) => { const [pitch, yaw] = applyPitchYawLimits(
[draft.protagonist.pitch, draft.protagonist.yaw] = applyPitchYawLimits( modelTransformEvent.last!.pitch,
draft.protagonist.pitch, modelTransformEvent.last!.yaw,
draft.protagonist.yaw, gunModelDefinition.pitch,
gunModelDefinition.pitch, turretModelDefinition.yaw,
turretModelDefinition.yaw, hasImprovedVerticalStabilizer,
hasImprovedVerticalStabilizer, );
);
}); modelTransformEvent.dispatch({ pitch, yaw });
}, [protagonistGun, protagonistTurret]); }, [protagonistGun, protagonistTurret]);
useEffect(() => { useEffect(() => {
@@ -246,6 +235,8 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
)} )}
<Lighting display={display} /> <Lighting display={display} />
</Suspense> </Suspense>
<InitialAligner />
</SmartCanvas> </SmartCanvas>
); );
}, },
@@ -1,8 +1,11 @@
import { Quicklime } from 'quicklime'; import { Quicklime } from 'quicklime';
export interface ModelTransformEventData { export interface ModelTransformEventData {
yaw?: number; yaw: number;
pitch: number; pitch: number;
} }
export const modelTransformEvent = new Quicklime<ModelTransformEventData>(); export const modelTransformEvent = new Quicklime<ModelTransformEventData>({
pitch: 0,
yaw: 0,
});
@@ -4,10 +4,8 @@ import {
type ProvisionDefinitions, type ProvisionDefinitions,
type TankDefinition, type TankDefinition,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { degToRad } from 'three/src/math/MathUtils.js';
import type { DuelMember } from '../../stores/duel'; import type { DuelMember } from '../../stores/duel';
import { genericDefaultEquipmentMatrix } from '../../stores/duel/constants'; import { genericDefaultEquipmentMatrix } from '../../stores/duel/constants';
import { applyPitchYawLimits } from '../blitz/applyPitchYawLimits';
export function tankToDuelMember( export function tankToDuelMember(
tank: TankDefinition, tank: TankDefinition,
@@ -15,16 +13,7 @@ export function tankToDuelMember(
provisionDefinitions: ProvisionDefinitions, provisionDefinitions: ProvisionDefinitions,
) { ) {
const turret = tank.turrets.at(-1)!; const turret = tank.turrets.at(-1)!;
const turretModel = model.turrets[turret.id];
const gun = turret.guns.at(-1)!; const gun = turret.guns.at(-1)!;
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
const [pitch, yaw] = applyPitchYawLimits(
degToRad(8),
degToRad(25),
gunModel.pitch,
turretModel.yaw,
);
return { return {
camouflage: true, camouflage: true,
@@ -37,8 +26,6 @@ export function tankToDuelMember(
shell: tank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0], shell: tank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0],
tank, tank,
track: tank.tracks.at(-1)!, track: tank.tracks.at(-1)!,
yaw,
pitch,
turret, turret,
assaultDistance: assaultDistance:
(gun.gun_type!.value.base.assault_ranges?.ranges[0].distance ?? 0) / 2, (gun.gun_type!.value.base.assault_ranges?.ranges[0].distance ?? 0) / 2,
+3 -16
View File
@@ -45,9 +45,8 @@ export function useTankTransform(
} }
function handleModelTransformInner(data?: ModelTransformEventData) { function handleModelTransformInner(data?: ModelTransformEventData) {
const duel = duelStore.getState(); const yaw = data?.yaw ?? modelTransformEvent.last!.yaw;
const yaw = data?.yaw ?? duel.protagonist.yaw; const pitch = data?.pitch ?? modelTransformEvent.last!.pitch;
const pitch = data?.pitch ?? duel.protagonist.pitch;
gunPosition gunPosition
.set(0, 0, 0) .set(0, 0, 0)
@@ -102,20 +101,8 @@ export function useTankTransform(
modelTransformEvent.on(handleModelTransform); modelTransformEvent.on(handleModelTransform);
const unsubscribes = [
duelStore.subscribe(
(state) => state.protagonist.pitch,
() => handleModelTransformInner(),
),
duelStore.subscribe(
(state) => state.protagonist.yaw,
() => handleModelTransformInner(),
),
() => modelTransformEvent.off(handleModelTransform),
];
return () => { return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe()); modelTransformEvent.off(handleModelTransform);
}; };
}, [track, turret]); }, [track, turret]);
} }
@@ -33,8 +33,6 @@ export interface DuelMember {
shell: ShellDefinition; shell: ShellDefinition;
track: TrackDefinition; track: TrackDefinition;
equipmentMatrix: EquipmentMatrix; equipmentMatrix: EquipmentMatrix;
pitch: number;
yaw: number;
consumables: number[]; consumables: number[];
provisions: number[]; provisions: number[];
camouflage: boolean; camouflage: boolean;