mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: ea and cs
This commit is contained in:
1 parent
a0c9895122
commit
9488602f5b
17 files changed
+134
-43
No files matched your search
@@ -10,8 +10,8 @@ import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
|
||||
import { ConsumableDefinitions } from '../../src/core/blitzkrieg/consumablesDefinitions';
|
||||
import {
|
||||
EquipmentDefinitions,
|
||||
EquipmentPreset,
|
||||
EquipmentRow,
|
||||
EquipmentRows,
|
||||
} from '../../src/core/blitzkrieg/equipmentDefinitions';
|
||||
import {
|
||||
ModelArmor,
|
||||
@@ -618,7 +618,7 @@ export async function buildDefinitions(production: boolean) {
|
||||
optionalDevices.root[options.device1].id,
|
||||
];
|
||||
}) as EquipmentRow,
|
||||
) as EquipmentRows;
|
||||
) as EquipmentPreset;
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import usePromise from 'react-promise-suspense';
|
||||
import { Vector3 } from 'three';
|
||||
import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
||||
import { Pose, poseEvent } from '../../../../../core/blitzkrieg/pose';
|
||||
import { useAwait } from '../../../../../hooks/useAwait';
|
||||
import { useTankopediaTemporary } from '../../../../../stores/tankopedia';
|
||||
import { Duel } from '../page';
|
||||
|
||||
@@ -23,10 +23,7 @@ interface ControlsProps {
|
||||
export function Controls({ duel }: ControlsProps) {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const orbitControls = useRef<OrbitControlsClass>(null);
|
||||
const awaitedModelDefinitions = usePromise(
|
||||
async () => await modelDefinitions,
|
||||
[],
|
||||
);
|
||||
const awaitedModelDefinitions = useAwait(modelDefinitions);
|
||||
const protagonistModelDefinition =
|
||||
awaitedModelDefinitions[duel.protagonist.tank.id];
|
||||
const antagonistModelDefinition =
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Loader } from '../../../../components/Loader';
|
||||
import { Duel } from '../../page';
|
||||
import { Controls } from '../Control';
|
||||
import { Lighting } from '../Lighting';
|
||||
import { QuickEquipments } from '../QuickEquipments';
|
||||
import { RotationInputs } from '../RotationInputs';
|
||||
import { SceneProps } from '../SceneProps';
|
||||
import { Options } from './components/Options';
|
||||
@@ -69,8 +70,8 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
);
|
||||
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
draft.model.physical.pitch = pitch;
|
||||
draft.model.physical.yaw = yaw;
|
||||
draft.model.pose.pitch = pitch;
|
||||
draft.model.pose.yaw = yaw;
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -86,8 +87,8 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
);
|
||||
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
draft.model.physical.pitch = pitch;
|
||||
draft.model.physical.yaw = yaw;
|
||||
draft.model.pose.pitch = pitch;
|
||||
draft.model.pose.yaw = yaw;
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -111,13 +112,12 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
if (!duel.protagonist) return void 0;
|
||||
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
[draft.model.physical.pitch, draft.model.physical.yaw] =
|
||||
applyPitchYawLimits(
|
||||
draft.model.physical.pitch,
|
||||
draft.model.physical.yaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
[draft.model.pose.pitch, draft.model.pose.yaw] = applyPitchYawLimits(
|
||||
draft.model.pose.pitch,
|
||||
draft.model.pose.yaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
});
|
||||
}, [duel.protagonist.gun, duel.protagonist.turret]);
|
||||
|
||||
@@ -183,6 +183,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
|
||||
<Options canvas={canvasWrapper} isFullScreen={isFullScreen} />
|
||||
|
||||
<RotationInputs duel={duel} />
|
||||
<QuickEquipments />
|
||||
</Flex>
|
||||
</Theme>
|
||||
</Card>
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
|
||||
turretContainer.current?.rotation.copy(turretRotation);
|
||||
}
|
||||
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.physical);
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.pose);
|
||||
modelTransformEvent.on(handleModelTransform);
|
||||
|
||||
return () => {
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
|
||||
turretContainer.current?.rotation.copy(turretRotation);
|
||||
}
|
||||
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.physical);
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.pose);
|
||||
modelTransformEvent.on(handleModelTransform);
|
||||
|
||||
return () => {
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
|
||||
turretContainer.current?.rotation.copy(turretRotation);
|
||||
}
|
||||
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.physical);
|
||||
handleModelTransform(useTankopediaTemporary.getState().model.pose);
|
||||
modelTransformEvent.on(handleModelTransform);
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -38,7 +38,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
>(undefined);
|
||||
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const physical = useTankopediaTemporary((state) => state.model.physical);
|
||||
const physical = useTankopediaTemporary((state) => state.model.pose);
|
||||
const hullContainer = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
@@ -220,8 +220,8 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
function handlePointerUp() {
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.visual.controlsEnabled = true;
|
||||
state.model.physical.pitch = normalizeAnglePI(pitch);
|
||||
state.model.physical.yaw = normalizeAnglePI(yaw);
|
||||
state.model.pose.pitch = normalizeAnglePI(pitch);
|
||||
state.model.pose.yaw = normalizeAnglePI(yaw);
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
@@ -283,8 +283,8 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
|
||||
function handlePointerUp() {
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.visual.controlsEnabled = true;
|
||||
state.model.physical.pitch = normalizeAnglePI(pitch);
|
||||
state.model.physical.yaw = normalizeAnglePI(yaw);
|
||||
state.model.pose.pitch = normalizeAnglePI(pitch);
|
||||
state.model.pose.yaw = normalizeAnglePI(yaw);
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Flex } from '@radix-ui/themes';
|
||||
import { ModuleButton } from '../../../../../components/ModuleButton';
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../../../stores/tankopedia';
|
||||
|
||||
export function QuickEquipments() {
|
||||
const equipment = useTankopediaTemporary((state) => state.model.equipment);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
justify="end"
|
||||
align="center"
|
||||
style={{
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
right: 12,
|
||||
top: 62,
|
||||
}}
|
||||
>
|
||||
<ModuleButton
|
||||
selected={equipment.calibratedShells}
|
||||
first
|
||||
type="equipment"
|
||||
rowChild
|
||||
equipment={103}
|
||||
onClick={() => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.equipment.calibratedShells =
|
||||
!draft.model.equipment.calibratedShells;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ModuleButton
|
||||
selected={equipment.enhancedArmor}
|
||||
last
|
||||
type="equipment"
|
||||
rowChild
|
||||
equipment={110}
|
||||
onClick={() => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.equipment.enhancedArmor =
|
||||
!draft.model.equipment.enhancedArmor;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ interface RotationInputsProps {
|
||||
}
|
||||
|
||||
export function RotationInputs({ duel: { protagonist } }: RotationInputsProps) {
|
||||
const physical = useTankopediaTemporary((state) => state.model.physical);
|
||||
const physical = useTankopediaTemporary((state) => state.model.pose);
|
||||
const awaitedModelDefinitions = use(modelDefinitions);
|
||||
const turretYawInput = useRef<HTMLInputElement>(null);
|
||||
const gunPitchInput = useRef<HTMLInputElement>(null);
|
||||
@@ -69,8 +69,8 @@ export function RotationInputs({ duel: { protagonist } }: RotationInputsProps) {
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.physical.pitch = pitch;
|
||||
state.model.physical.yaw = yaw;
|
||||
state.model.pose.pitch = pitch;
|
||||
state.model.pose.yaw = yaw;
|
||||
});
|
||||
turretYawInput.current!.value = `${Math.round(
|
||||
yaw * (180 / Math.PI),
|
||||
@@ -105,8 +105,8 @@ export function RotationInputs({ duel: { protagonist } }: RotationInputsProps) {
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
mutateTankopediaTemporary((state) => {
|
||||
state.model.physical.pitch = pitch;
|
||||
state.model.physical.yaw = yaw;
|
||||
state.model.pose.pitch = pitch;
|
||||
state.model.pose.yaw = yaw;
|
||||
});
|
||||
gunPitchInput.current!.value = `${Math.round(
|
||||
pitch * (180 / Math.PI),
|
||||
|
||||
@@ -39,8 +39,8 @@ export default function Page({ params }: { params: { id: string } }) {
|
||||
|
||||
useEffect(() => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.model.physical.yaw = 0;
|
||||
draft.model.physical.pitch = 0;
|
||||
draft.model.pose.yaw = 0;
|
||||
draft.model.pose.pitch = 0;
|
||||
draft.mode = 'model';
|
||||
});
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export function ArmorMesh({
|
||||
duel,
|
||||
...props
|
||||
}: ArmorMeshProps) {
|
||||
const equipment = useTankopediaTemporary((draft) => draft.model.equipment);
|
||||
const camera = useThree((state) => state.camera);
|
||||
const { shell } = duel.antagonist;
|
||||
const greenPenetration = useTankopediaTemporary(
|
||||
@@ -32,6 +33,12 @@ export function ArmorMesh({
|
||||
);
|
||||
const material = useRef<ShaderMaterial>(null);
|
||||
const resolution = new Vector2();
|
||||
const explosionCapable = isExplosive(shell.type);
|
||||
let mutatedThickness = thickness;
|
||||
let penetration = resolveNearPenetration(shell.penetration);
|
||||
|
||||
if (equipment.calibratedShells) penetration *= explosionCapable ? 1.1 : 1.05;
|
||||
if (equipment.enhancedArmor) mutatedThickness *= 1.04;
|
||||
|
||||
useFrame(({ gl, camera }) => {
|
||||
if (material.current) {
|
||||
@@ -75,11 +82,11 @@ export function ArmorMesh({
|
||||
zFar: { value: camera.far },
|
||||
greenPenetration: { value: greenPenetration },
|
||||
maxThickness: { value: maxThickness },
|
||||
isExplosive: { value: isExplosive(shell.type) },
|
||||
isExplosive: { value: explosionCapable },
|
||||
canSplash: { value: canSplash(shell.type) },
|
||||
thickness: { value: thickness },
|
||||
thickness: { value: mutatedThickness },
|
||||
penetration: {
|
||||
value: resolveNearPenetration(shell.penetration),
|
||||
value: penetration,
|
||||
},
|
||||
caliber: { value: shell.caliber },
|
||||
ricochetAngle: { value: degToRad(shell.ricochet ?? 90) },
|
||||
|
||||
@@ -8,11 +8,11 @@ export type EquipmentRow = [
|
||||
EquipmentOptions,
|
||||
];
|
||||
|
||||
export type EquipmentRows = [EquipmentRow, EquipmentRow, EquipmentRow];
|
||||
export type EquipmentPreset = [EquipmentRow, EquipmentRow, EquipmentRow];
|
||||
|
||||
export interface EquipmentDefinitions {
|
||||
presets: {
|
||||
[key: string]: EquipmentRows;
|
||||
[key: string]: EquipmentPreset;
|
||||
};
|
||||
equipments: {
|
||||
[key: number]: {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { EquipmentPreset } from '../core/blitzkrieg/equipmentDefinitions';
|
||||
import { EquipmentMatrix } from '../stores/tankopedia';
|
||||
|
||||
export function useAppliedEquipments(
|
||||
matrix: EquipmentMatrix,
|
||||
preset: EquipmentPreset,
|
||||
) {
|
||||
const flatMatrix = matrix.flat();
|
||||
return preset.flat().map((options, index) => options[flatMatrix[index]]);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import usePromise from 'react-promise-suspense';
|
||||
|
||||
/**
|
||||
* A more stable alternative to `use` using `usePromise`.
|
||||
*/
|
||||
export function useAwait<Type>(promise: Promise<Type>) {
|
||||
return usePromise(async () => await promise, []);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { equipmentDefinitions } from '../core/blitzkrieg/equipmentDefinitions';
|
||||
import { useAwait } from './useAwait';
|
||||
|
||||
export function useEquipmentPreset(preset: string) {
|
||||
const awaitedEquipmentDefinitions = useAwait(equipmentDefinitions);
|
||||
console.log(awaitedEquipmentDefinitions);
|
||||
return awaitedEquipmentDefinitions.presets[preset];
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import usePromise from 'react-promise-suspense';
|
||||
import { modelDefinitions } from '../core/blitzkrieg/modelDefinitions';
|
||||
import { useAwait } from './useAwait';
|
||||
|
||||
export function useModelDefinitions() {
|
||||
return usePromise(async () => await modelDefinitions, []);
|
||||
return useAwait(modelDefinitions);
|
||||
}
|
||||
@@ -35,19 +35,25 @@ interface TankopediaPersistent {
|
||||
};
|
||||
}
|
||||
|
||||
export type EquipmentMatrix = (0 | 1)[][];
|
||||
|
||||
interface TankopediaTemporary {
|
||||
model: {
|
||||
physical: {
|
||||
pose: {
|
||||
yaw: number;
|
||||
pitch: number;
|
||||
};
|
||||
equipment: {
|
||||
enhancedArmor: boolean;
|
||||
calibratedShells: boolean;
|
||||
};
|
||||
visual: {
|
||||
controlsEnabled: boolean;
|
||||
showGrid: boolean;
|
||||
greenPenetration: boolean;
|
||||
};
|
||||
};
|
||||
equipmentMatrix: (0 | 1)[][];
|
||||
equipmentMatrix: EquipmentMatrix;
|
||||
mode: TankopediaMode;
|
||||
}
|
||||
|
||||
@@ -74,10 +80,14 @@ export const useTankopediaPersistent = create<TankopediaPersistent>()(
|
||||
export const useTankopediaTemporary = create<TankopediaTemporary>()(
|
||||
subscribeWithSelector<TankopediaTemporary>(() => ({
|
||||
model: {
|
||||
physical: {
|
||||
pose: {
|
||||
yaw: 0,
|
||||
pitch: 0,
|
||||
},
|
||||
equipment: {
|
||||
enhancedArmor: false,
|
||||
calibratedShells: false,
|
||||
},
|
||||
visual: {
|
||||
controlsEnabled: true,
|
||||
showGrid: true,
|
||||
|
||||
Reference in new issue
Block a user