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