feat: rotation inputs

This commit is contained in:
TrèsAbhi committed 2024-05-04 17:14:08 -05:00
1 parent cebc4392ed
commit a099df5133
4 files changed
+118 -137

No files matched your search

@@ -88,6 +88,7 @@ export function HeroSection() {
</Flex>
<div
className="tank-sandbox-container"
style={{
flex: 2,
position: isFullScreen ? 'fixed' : 'relative',
@@ -36,6 +36,7 @@ import mutateTankopediaPersistent, {
} from '../../../../../../../stores/tankopedia';
import { TankSearch } from '../../../../components/TankSearch';
import { ENVIRONMENTS } from '../../Lighting';
import { RotationInputs } from '../../QuickInputs';
export function Options() {
const mode = useTankopediaPersistent((state) => state.mode);
@@ -73,6 +74,8 @@ export function Options() {
return (
<>
<RotationInputs />
{mode === 'armor' && (
<Flex
gap="2"
@@ -0,0 +1,11 @@
import { style } from '@vanilla-extract/css';
export const container = style({
display: 'none',
selectors: {
'.tank-sandbox-container:hover &': {
display: 'flex',
},
},
});
@@ -1,3 +1,4 @@
import { CaretSortIcon } from '@radix-ui/react-icons';
import { Flex, TextField } from '@radix-ui/themes';
import { use, useEffect, useRef } from 'react';
import { degToRad, radToDeg } from 'three/src/math/MathUtils';
@@ -5,28 +6,22 @@ import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawL
import { modelDefinitions } from '../../../../../../core/blitzkit/modelDefinitions';
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
import { useTankopediaPersistent } from '../../../../../../stores/tankopedia';
import { QuickEquipmentButton } from './components/QuickEquipmentButton';
import * as styles from './index.css';
interface RotationInputsProps {
isFullScreen?: boolean;
}
export function RotationInputs({ isFullScreen }: RotationInputsProps) {
export function RotationInputs() {
const protagonist = useDuel((state) => state.protagonist!);
const awaitedModelDefinitions = use(modelDefinitions);
const turretYawInput = useRef<HTMLInputElement>(null);
const gunPitchInput = useRef<HTMLInputElement>(null);
const mode = useTankopediaPersistent((state) => state.mode);
const hasCalibratedShells = useEquipment(103, true);
const hasEnhancedArmor = useEquipment(110);
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const initialGunPitch = tankModelDefinition.turretRotation?.pitch ?? 0;
const hasImprovedVerticalStabilizer = useEquipment(122);
const isFullScreen = useFullScreen();
useEffect(() => {
turretYawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1);
@@ -39,147 +34,118 @@ export function RotationInputs({ isFullScreen }: RotationInputsProps) {
return (
<Flex
justify="center"
align="center"
className={isFullScreen ? undefined : styles.container}
gap="2"
style={{
width: 'calc(100% - 16px)',
width: 128 + 32,
pointerEvents: 'auto',
position: 'absolute',
left: 8,
bottom: isFullScreen ? 80 : 8,
pointerEvents: 'none',
left: '50%',
top: 16,
transform: 'translateX(-50%)',
}}
gap="4"
>
<Flex gap="2" style={{ width: 256, pointerEvents: 'auto' }}>
<TextField.Root
radius="full"
style={{
flex: 1,
textAlign: 'right',
}}
onBlur={() => {
const value = Number(turretYawInput.current!.value);
if (isNaN(value)) {
turretYawInput.current!.value = radToDeg(protagonist.yaw).toFixed(
1,
);
return;
}
const [pitch, yaw] = applyPitchYawLimits(
protagonist.pitch,
degToRad(value),
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
mutateDuel((state) => {
state.protagonist!.pitch = pitch;
state.protagonist!.yaw = yaw;
});
<TextField.Root
size="1"
radius="full"
style={{
flex: 1,
textAlign: 'right',
}}
onBlur={() => {
const value = Number(turretYawInput.current!.value);
if (isNaN(value)) {
turretYawInput.current!.value = radToDeg(protagonist.yaw).toFixed(
1,
);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
turretYawInput.current?.blur();
}
}}
onFocus={() => turretYawInput.current?.focus()}
ref={turretYawInput}
>
<TextField.Slot
style={{
userSelect: 'none',
}}
>
Yaw
</TextField.Slot>
<TextField.Slot style={{ userSelect: 'none' }}>°</TextField.Slot>
</TextField.Root>
<TextField.Root
radius="full"
return;
}
const [pitch, yaw] = applyPitchYawLimits(
protagonist.pitch,
degToRad(value),
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
mutateDuel((state) => {
state.protagonist!.pitch = pitch;
state.protagonist!.yaw = yaw;
});
turretYawInput.current!.value = radToDeg(protagonist.yaw).toFixed(1);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
turretYawInput.current?.blur();
}
}}
onFocus={() => turretYawInput.current?.focus()}
ref={turretYawInput}
>
<TextField.Slot
style={{
flex: 1,
textAlign: 'right',
userSelect: 'none',
}}
onBlur={() => {
const value = Number(gunPitchInput.current!.value);
if (isNaN(value)) {
gunPitchInput.current!.value = (
-radToDeg(protagonist.pitch) + initialGunPitch
).toFixed(1);
return;
}
const [pitch, yaw] = applyPitchYawLimits(
degToRad(-value + initialGunPitch),
protagonist.yaw,
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
mutateDuel((state) => {
state.protagonist!.pitch = pitch;
state.protagonist!.yaw = yaw;
});
>
<CaretSortIcon style={{ transform: 'rotate(90deg)' }} />
</TextField.Slot>
<TextField.Slot style={{ userSelect: 'none' }}>°</TextField.Slot>
</TextField.Root>
<TextField.Root
size="1"
radius="full"
style={{
flex: 1,
textAlign: 'right',
}}
onBlur={() => {
const value = Number(gunPitchInput.current!.value);
if (isNaN(value)) {
gunPitchInput.current!.value = (
-radToDeg(protagonist.pitch) + initialGunPitch
).toFixed(1);
return;
}
const [pitch, yaw] = applyPitchYawLimits(
degToRad(-value + initialGunPitch),
protagonist.yaw,
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
mutateDuel((state) => {
state.protagonist!.pitch = pitch;
state.protagonist!.yaw = yaw;
});
gunPitchInput.current!.value = (
-radToDeg(protagonist.pitch) + initialGunPitch
).toFixed(1);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
gunPitchInput.current?.blur();
}
}}
onFocus={() => gunPitchInput.current?.focus()}
ref={gunPitchInput}
>
<TextField.Slot
style={{
userSelect: 'none',
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
gunPitchInput.current?.blur();
}
}}
onFocus={() => gunPitchInput.current?.focus()}
ref={gunPitchInput}
>
<TextField.Slot
style={{
userSelect: 'none',
}}
>
Pitch
</TextField.Slot>
<TextField.Slot
style={{
userSelect: 'none',
}}
>
°
</TextField.Slot>
</TextField.Root>
</Flex>
{mode === 'armor' && (
<Flex gap="4" style={{ pointerEvents: 'auto' }}>
<QuickEquipmentButton
equipment={103}
active={hasCalibratedShells}
onClick={() => {
mutateDuel((draft) => {
draft.antagonist!.equipmentMatrix[0][0] = hasCalibratedShells
? 0
: 1;
});
}}
/>
<QuickEquipmentButton
equipment={110}
active={hasEnhancedArmor}
onClick={() => {
mutateDuel((draft) => {
draft.protagonist!.equipmentMatrix[1][1] = hasEnhancedArmor
? 0
: -1;
});
}}
/>
</Flex>
)}
<CaretSortIcon />
</TextField.Slot>
<TextField.Slot
style={{
userSelect: 'none',
}}
>
°
</TextField.Slot>
</TextField.Root>
</Flex>
);
}