mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
feat: some armor rendering
This commit is contained in:
1 parent
3a32624823
commit
5b16e22750
13 files changed
+662
-1060
No files matched your search
@@ -1,23 +1,10 @@
|
||||
import { normalizeAngleRad } from '@blitzkit/core';
|
||||
import { useThree, type ThreeEvent } from '@react-three/fiber';
|
||||
import { memo, useRef } from 'react';
|
||||
import { Group, Plane, Vector2, Vector3 } from 'three';
|
||||
import { applyPitchYawLimits } from '../../../core/blitz/applyPitchYawLimits';
|
||||
import { correctZYTuple } from '../../../core/blitz/correctZYTuple';
|
||||
import { hasEquipment } from '../../../core/blitzkit/hasEquipment';
|
||||
import { modelTransformEvent } from '../../../core/blitzkit/modelTransform';
|
||||
import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||
import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModel } from '../../../hooks/useModel';
|
||||
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { useGLTF } from '@react-three/drei';
|
||||
import { memo } from 'react';
|
||||
import { Color, Material, MeshBasicMaterial } from 'three';
|
||||
import { jsxTree } from '../../../core/blitzkit/jsxTree';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
|
||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
import { StaticArmorSceneComponent } from './StaticArmorSceneComponent';
|
||||
|
||||
interface ArmorSceneProps {
|
||||
thicknessRange: ThicknessRange;
|
||||
@@ -28,401 +15,87 @@ export interface ThicknessRange {
|
||||
}
|
||||
|
||||
export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
const wrapper = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const track = Duel.use((state) => state.protagonist.track);
|
||||
const turret = Duel.use((state) => state.protagonist.turret);
|
||||
const gun = Duel.use((state) => state.protagonist.gun);
|
||||
const { gltf: armorGltf } = useArmor(tank.id);
|
||||
const { gltf: modelGltf } = useModel(tank.id);
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const modelNodes = Object.values(modelGltf.nodes);
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const gunModelDefinition =
|
||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
||||
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
||||
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
||||
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const duelStore = Duel.useStore();
|
||||
const maskOrigin =
|
||||
gunModelDefinition.mask === undefined
|
||||
? undefined
|
||||
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
||||
const showPrimaryArmor = TankopediaPersistent.use(
|
||||
(state) => state.showPrimaryArmor,
|
||||
);
|
||||
const showSpacedArmor = TankopediaPersistent.use(
|
||||
(state) => state.showSpacedArmor,
|
||||
);
|
||||
const showExternalModules = TankopediaPersistent.use(
|
||||
(state) => state.showExternalModules,
|
||||
);
|
||||
const isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
// const wrapper = useRef<Group>(null);
|
||||
// const turretContainer = useRef<Group>(null);
|
||||
// const gunContainer = useRef<Group>(null);
|
||||
// const protagonist = Duel.use((draft) => draft.protagonist);
|
||||
// const tank = Duel.use((state) => state.protagonist.tank);
|
||||
// const track = Duel.use((state) => state.protagonist.track);
|
||||
// const turret = Duel.use((state) => state.protagonist.turret);
|
||||
// const gun = Duel.use((state) => state.protagonist.gun);
|
||||
// const { gltf: armorGltf } = useArmor(tank.id);
|
||||
// const { gltf: modelGltf } = useModel(tank.id);
|
||||
// const armorNodes = Object.values(armorGltf.nodes);
|
||||
// const modelNodes = Object.values(modelGltf.nodes);
|
||||
// const tankModelDefinition = useTankModelDefinition();
|
||||
// const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||
// const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
// const gunModelDefinition =
|
||||
// turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
||||
// const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
||||
// const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
||||
// const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
||||
// const mutateDuel = Duel.useMutation();
|
||||
// const canvas = useThree((state) => state.gl.domElement);
|
||||
// const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
// const duelStore = Duel.useStore();
|
||||
// const maskOrigin =
|
||||
// gunModelDefinition.mask === undefined
|
||||
// ? undefined
|
||||
// : gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
||||
// const showPrimaryArmor = TankopediaPersistent.use(
|
||||
// (state) => state.showPrimaryArmor,
|
||||
// );
|
||||
// const showSpacedArmor = TankopediaPersistent.use(
|
||||
// (state) => state.showSpacedArmor,
|
||||
// );
|
||||
// const showExternalModules = TankopediaPersistent.use(
|
||||
// (state) => state.showExternalModules,
|
||||
// );
|
||||
// const isDynamicArmorActive = Duel.use((state) =>
|
||||
// state.protagonist.consumables.includes(73),
|
||||
// );
|
||||
|
||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
// useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const armor = TankopediaEphemeral.use((state) => state.armor);
|
||||
const hullArmor = armor.groups.hull;
|
||||
const hullGltf = useGLTF(asset(`tanks/${tank.id}/collision/hull.glb`));
|
||||
|
||||
return (
|
||||
<ModelTankWrapper ref={wrapper}>
|
||||
<group position={hullOrigin}>
|
||||
{armorNodes.map((node) => {
|
||||
const isHull = node.name.startsWith('hull_');
|
||||
const isVisible = isHull;
|
||||
const armorId = nameToArmorId(node.name);
|
||||
const { spaced, thickness } = resolveArmor(
|
||||
tankModelDefinition.armor,
|
||||
armorId,
|
||||
);
|
||||
<group>
|
||||
{jsxTree(hullGltf.scene, {
|
||||
mesh(mesh) {
|
||||
if (!(mesh.material instanceof Material)) {
|
||||
return { visible: false };
|
||||
}
|
||||
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
)
|
||||
return null;
|
||||
const armorName = mesh.material.name;
|
||||
const material = new MeshBasicMaterial({});
|
||||
const armorPlate = hullArmor.armors[armorName];
|
||||
|
||||
return (
|
||||
<StaticArmorSceneComponent
|
||||
name={node.name}
|
||||
thicknessRange={thicknessRange}
|
||||
key={node.uuid}
|
||||
type={spaced ? ArmorType.Spaced : ArmorType.Primary}
|
||||
thickness={thickness}
|
||||
node={node}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
switch (armorPlate.type) {
|
||||
case 'Armor':
|
||||
material.color = new Color('#ec6142');
|
||||
material.transparent = true;
|
||||
material.opacity = 0.5;
|
||||
break;
|
||||
|
||||
{modelNodes.map((node) => {
|
||||
const isWheel = node.name.startsWith('chassis_wheel_');
|
||||
const isTrack = node.name.startsWith('chassis_track_');
|
||||
const isVisible = isWheel || isTrack;
|
||||
case 'ArmorScreen':
|
||||
material.color = new Color('#ff8dcc');
|
||||
material.transparent = true;
|
||||
material.opacity = 0.5;
|
||||
break;
|
||||
|
||||
if (!isVisible || !showExternalModules) return null;
|
||||
default:
|
||||
material.color = new Color('#70b8ff');
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<StaticArmorSceneComponent
|
||||
name={node.name}
|
||||
thicknessRange={thicknessRange}
|
||||
key={node.uuid}
|
||||
type={ArmorType.External}
|
||||
thickness={trackModelDefinition.thickness}
|
||||
variant="track"
|
||||
node={node}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
/>
|
||||
);
|
||||
return { material };
|
||||
},
|
||||
})}
|
||||
|
||||
<group ref={turretContainer}>
|
||||
{armorNodes.map((node) => {
|
||||
const isCurrentTurret = node.name.startsWith(
|
||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, '0')}`,
|
||||
);
|
||||
const isVisible = isCurrentTurret;
|
||||
const armorId = nameToArmorId(node.name);
|
||||
const { spaced, thickness } = resolveArmor(
|
||||
turretModelDefinition.armor,
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const position = new Vector2();
|
||||
const delta = new Vector2();
|
||||
let pitch = 0;
|
||||
let yaw = 0;
|
||||
|
||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||
event.stopPropagation();
|
||||
|
||||
position.set(event.clientX, event.clientY);
|
||||
yaw = protagonist.yaw;
|
||||
pitch = protagonist.pitch;
|
||||
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.controlsEnabled = false;
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
async function handlePointerMove(event: PointerEvent) {
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
122,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = await hasEquipment(
|
||||
124,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
delta.set(event.clientX, event.clientY).sub(position);
|
||||
position.set(event.clientX, event.clientY);
|
||||
|
||||
[pitch, yaw] = applyPitchYawLimits(
|
||||
pitch,
|
||||
yaw + delta.x * (Math.PI / boundingRect.width),
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.controlsEnabled = true;
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
|
||||
return (
|
||||
<group key={node.uuid} position={hullOrigin}>
|
||||
<group position={turretOrigin}>
|
||||
<StaticArmorSceneComponent
|
||||
name={node.name}
|
||||
thicknessRange={thicknessRange}
|
||||
key={node.uuid}
|
||||
type={spaced ? ArmorType.Spaced : ArmorType.Primary}
|
||||
thickness={thickness}
|
||||
node={node}
|
||||
onPointerDown={onPointerDown}
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
})}
|
||||
|
||||
<group ref={gunContainer}>
|
||||
{armorNodes.map((node) => {
|
||||
const isCurrentGun = node.name.startsWith(
|
||||
`gun_${gunModelDefinition.model_id.toString().padStart(2, '0')}`,
|
||||
);
|
||||
const isVisible = isCurrentGun;
|
||||
const armorId = nameToArmorId(node.name);
|
||||
const { spaced, thickness } = resolveArmor(
|
||||
gunModelDefinition.armor,
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
)
|
||||
return null;
|
||||
|
||||
const position = new Vector2();
|
||||
const delta = new Vector2();
|
||||
let pitch = 0;
|
||||
let yaw = 0;
|
||||
|
||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||
event.stopPropagation();
|
||||
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.controlsEnabled = false;
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
|
||||
position.set(event.clientX, event.clientY);
|
||||
pitch = protagonist.pitch;
|
||||
yaw = protagonist.yaw;
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
async function handlePointerMove(event: PointerEvent) {
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
122,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = await hasEquipment(
|
||||
124,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
delta.set(event.clientX, event.clientY).sub(position);
|
||||
position.set(event.clientX, event.clientY);
|
||||
|
||||
[pitch, yaw] = applyPitchYawLimits(
|
||||
pitch - delta.y * (Math.PI / boundingRect.height),
|
||||
yaw + delta.x * (Math.PI / boundingRect.width),
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.controlsEnabled = true;
|
||||
});
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
|
||||
return (
|
||||
<group key={node.uuid} position={hullOrigin}>
|
||||
<group position={turretOrigin.clone().add(gunOrigin)}>
|
||||
<StaticArmorSceneComponent
|
||||
name={node.name}
|
||||
thicknessRange={thicknessRange}
|
||||
type={spaced ? ArmorType.Spaced : ArmorType.Primary}
|
||||
thickness={thickness}
|
||||
node={node}
|
||||
onPointerDown={onPointerDown}
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
})}
|
||||
|
||||
{modelNodes.map((node) => {
|
||||
const gunString = `gun_${gunModelDefinition.model_id
|
||||
.toString()
|
||||
.padStart(2, '0')}`;
|
||||
const isCurrentGun = gunModelDefinition.mask
|
||||
? node.name.startsWith(gunString)
|
||||
: node.name === gunString;
|
||||
const isVisible = isCurrentGun;
|
||||
|
||||
if (!isVisible || !showExternalModules) return null;
|
||||
|
||||
const position = new Vector2();
|
||||
const delta = new Vector2();
|
||||
let pitch = 0;
|
||||
let yaw = 0;
|
||||
|
||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||
event.stopPropagation();
|
||||
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.controlsEnabled = false;
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
|
||||
position.set(event.clientX, event.clientY);
|
||||
pitch = protagonist.pitch;
|
||||
yaw = protagonist.yaw;
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
async function handlePointerMove(event: PointerEvent) {
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
122,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = await hasEquipment(
|
||||
124,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
delta.set(event.clientX, event.clientY).sub(position);
|
||||
position.set(event.clientX, event.clientY);
|
||||
|
||||
[pitch, yaw] = applyPitchYawLimits(
|
||||
pitch - delta.y * (Math.PI / boundingRect.height),
|
||||
yaw + delta.x * (Math.PI / boundingRect.width),
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
);
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
}
|
||||
function handlePointerUp() {
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.controlsEnabled = true;
|
||||
});
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist.pitch = normalizeAngleRad(pitch);
|
||||
draft.protagonist.yaw = normalizeAngleRad(yaw);
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
|
||||
return (
|
||||
<StaticArmorSceneComponent
|
||||
name={node.name}
|
||||
thicknessRange={thicknessRange}
|
||||
key={node.uuid}
|
||||
type={ArmorType.External}
|
||||
thickness={gunModelDefinition.thickness}
|
||||
variant="gun"
|
||||
node={node}
|
||||
clip={
|
||||
maskOrigin === undefined
|
||||
? undefined
|
||||
: new Plane(new Vector3(0, 0, -1), -maskOrigin)
|
||||
}
|
||||
hullOrigin={hullOrigin}
|
||||
turretOrigin={turretOrigin}
|
||||
gunOrigin={gunOrigin}
|
||||
onPointerDown={onPointerDown}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
</ModelTankWrapper>
|
||||
</group>
|
||||
);
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
|
||||
interface SmallTankIconProps {
|
||||
id: number;
|
||||
id: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function SmallTankIcon({ id, size = HEIGHT }: SmallTankIconProps) {
|
||||
>
|
||||
<img
|
||||
alt={`Tank ${id}`}
|
||||
src={asset(`icons/tanks/small/${id}.webp`)}
|
||||
src={asset(`api/tanks/${id}/icons/small.png`)}
|
||||
style={{
|
||||
transform: `scale(${(size / HEIGHT) * 100}%)`,
|
||||
transformOrigin: 'top left',
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
normalizeBoundingBox,
|
||||
resolveDpm,
|
||||
SEARCH_KEYS,
|
||||
Tank,
|
||||
unionBoundingBox,
|
||||
type TankDefinition,
|
||||
} from '@blitzkit/core';
|
||||
@@ -35,8 +36,8 @@ import { MAX_RECENTLY_VIEWED } from './constants';
|
||||
|
||||
type TankSearchProps = Omit<FlexProps, 'onSelect'> & {
|
||||
compact?: boolean;
|
||||
onSelect?: (tank: TankDefinition) => void;
|
||||
onSelectAll?: (tanks: TankDefinition[]) => void;
|
||||
onSelect?: (tank: Tank) => void;
|
||||
onSelectAll?: (tanks: Tank[]) => void;
|
||||
};
|
||||
|
||||
const PREVIEW_COUNT = 20;
|
||||
|
||||
+14
-14
@@ -18,28 +18,28 @@ export function QuickInputs() {
|
||||
const yawInput = useRef<HTMLInputElement>(null);
|
||||
const pitchInput = useRef<HTMLInputElement>(null);
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[protagonist.turret.id];
|
||||
const gunModelDefinition =
|
||||
turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
|
||||
// const turretModelDefinition =
|
||||
// tankModelDefinition.turrets[protagonist.turret.id];
|
||||
// const gunModelDefinition =
|
||||
// turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
|
||||
const initialGunPitch =
|
||||
tankModelDefinition.initial_turret_rotation?.pitch ?? 0;
|
||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||
|
||||
useEffect(() => {
|
||||
if (!yawInput.current) return;
|
||||
// useEffect(() => {
|
||||
// if (!yawInput.current) return;
|
||||
|
||||
yawInput.current.value = radToDeg(protagonist.yaw).toFixed(1);
|
||||
}, [protagonist.yaw]);
|
||||
// yawInput.current.value = radToDeg(protagonist.yaw).toFixed(1);
|
||||
// }, [protagonist.yaw]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pitchInput.current) return;
|
||||
// useEffect(() => {
|
||||
// if (!pitchInput.current) return;
|
||||
|
||||
pitchInput.current.value = (
|
||||
-radToDeg(protagonist.pitch) + initialGunPitch
|
||||
).toFixed(1);
|
||||
}, [protagonist.pitch]);
|
||||
// pitchInput.current.value = (
|
||||
// -radToDeg(protagonist.pitch) + initialGunPitch
|
||||
// ).toFixed(1);
|
||||
// }, [protagonist.pitch]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleTransformEvent({ pitch, yaw }: ModelTransformEventData) {
|
||||
|
||||
+2
-4
@@ -1,12 +1,10 @@
|
||||
import { literals } from '@blitzkit/i18n/src/literals';
|
||||
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Checkbox, Flex, Text } from '@radix-ui/themes';
|
||||
import { Suspense } from 'react';
|
||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../../../../stores/tankopediaPersistent';
|
||||
import type { ThicknessRange } from '../../../../../Armor/components/StaticArmor';
|
||||
import { DynamicArmorSwitcher } from './DynamicArmorSwitcher';
|
||||
|
||||
interface ThicknessesProps {
|
||||
thicknessRange: ThicknessRange;
|
||||
@@ -190,9 +188,9 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Suspense>
|
||||
{/* <Suspense>
|
||||
<DynamicArmorSwitcher />
|
||||
</Suspense>
|
||||
</Suspense> */}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
+32
-65
@@ -1,37 +1,6 @@
|
||||
import {
|
||||
asset,
|
||||
imgur,
|
||||
resolvePenetrationCoefficient,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
uniqueGuns,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n/src/literals';
|
||||
import {
|
||||
CameraIcon,
|
||||
CopyIcon,
|
||||
DownloadIcon,
|
||||
EnterFullScreenIcon,
|
||||
ExitFullScreenIcon,
|
||||
EyeOpenIcon,
|
||||
GearIcon,
|
||||
} from '@radix-ui/react-icons';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Dialog,
|
||||
DropdownMenu,
|
||||
Flex,
|
||||
IconButton,
|
||||
Popover,
|
||||
SegmentedControl,
|
||||
Tabs,
|
||||
Text,
|
||||
Tooltip,
|
||||
} from '@radix-ui/themes';
|
||||
import { Suspense, useState, type RefObject } from 'react';
|
||||
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
|
||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||
import { imgur } from '@blitzkit/core';
|
||||
import { Box, Flex, SegmentedControl, Tooltip } from '@radix-ui/themes';
|
||||
import { useState, type RefObject } from 'react';
|
||||
import { useFullScreen } from '../../../../../hooks/useFullScreen';
|
||||
import { useFullscreenAvailability } from '../../../../../hooks/useFullscreenAvailability';
|
||||
import { useLocale } from '../../../../../hooks/useLocale';
|
||||
@@ -44,12 +13,6 @@ import {
|
||||
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
|
||||
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
||||
import type { ThicknessRange } from '../../../../Armor/components/StaticArmor';
|
||||
import { ModuleButton } from '../../../../ModuleButtons/ModuleButton';
|
||||
import { SmallTankIcon } from '../../../../SmallTankIcon';
|
||||
import { TankSearch } from '../../../../TankSearch';
|
||||
import { CustomShellButton } from './components/CustomShellButton';
|
||||
import { DynamicArmorSwitcher } from './components/DynamicArmorSwitcher';
|
||||
import { QuickInputs } from './components/QuickInputs';
|
||||
import { Thicknesses } from './components/Thicknesses';
|
||||
|
||||
interface OptionsProps {
|
||||
@@ -78,22 +41,22 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
const fullScreenAvailable = useFullscreenAvailability();
|
||||
const developerMode = App.use((state) => state.developerMode);
|
||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||
const antagonistGun = Duel.use((state) => state.antagonist.gun);
|
||||
const antagonistShell = Duel.use((state) => state.antagonist.shell);
|
||||
// const antagonistGun = Duel.use((state) => state.antagonist.gun);
|
||||
// const antagonistShell = Duel.use((state) => state.antagonist.shell);
|
||||
const [antagonistSelectorOpen, setAntagonistSelectorOpen] = useState(false);
|
||||
const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
||||
const hasCalibratedShells = useEquipment(103, true);
|
||||
// const hasCalibratedShells = useEquipment(103, true);
|
||||
const [tab, setTab] = useState('search');
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const hasEnhancedArmor = useEquipment(110);
|
||||
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
||||
// const hasEnhancedArmor = useEquipment(110);
|
||||
// const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const { strings, unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<>
|
||||
<QuickInputs />
|
||||
{/* <QuickInputs /> */}
|
||||
|
||||
{display === TankopediaDisplay.ShootingRange && (
|
||||
<Box
|
||||
@@ -116,7 +79,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
<Thicknesses thicknessRange={thicknessRange} />
|
||||
)}
|
||||
|
||||
{display === TankopediaDisplay.DynamicArmor && (
|
||||
{/* {display === TankopediaDisplay.DynamicArmor && (
|
||||
<Flex
|
||||
gap="2"
|
||||
direction="column"
|
||||
@@ -137,6 +100,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
])}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{antagonistUniqueGuns.length > 1 && (
|
||||
<Popover.Root>
|
||||
<Popover.Trigger>
|
||||
@@ -215,6 +179,8 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
)}
|
||||
|
||||
|
||||
<Flex
|
||||
direction="column"
|
||||
style={{
|
||||
@@ -321,7 +287,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
<DynamicArmorSwitcher />
|
||||
</Suspense>
|
||||
</Flex>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
<Flex
|
||||
direction="column"
|
||||
@@ -331,7 +297,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
left="50%"
|
||||
style={{ transform: 'translateX(-50%)' }}
|
||||
>
|
||||
{display === TankopediaDisplay.DynamicArmor && (
|
||||
{/* {display === TankopediaDisplay.DynamicArmor && (
|
||||
<>
|
||||
<Flex
|
||||
align="center"
|
||||
@@ -380,13 +346,13 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
onSelect={(tank) => {
|
||||
mutateDuel((draft) => {
|
||||
draft.antagonist.tank = tank;
|
||||
draft.antagonist.engine = tank.engines.at(-1)!;
|
||||
draft.antagonist.track = tank.tracks.at(-1)!;
|
||||
draft.antagonist.turret = tank.turrets.at(-1)!;
|
||||
draft.antagonist.gun =
|
||||
draft.antagonist.turret.guns.at(-1)!;
|
||||
draft.antagonist.shell =
|
||||
draft.antagonist.gun.gun_type!.value.base.shells[0];
|
||||
// draft.antagonist.engine = tank.engines.at(-1)!;
|
||||
// draft.antagonist.track = tank.tracks.at(-1)!;
|
||||
// draft.antagonist.turret = tank.turrets.at(-1)!;
|
||||
// draft.antagonist.gun =
|
||||
// draft.antagonist.turret.guns.at(-1)!;
|
||||
// draft.antagonist.shell =
|
||||
// draft.antagonist.gun.gun_type!.value.base.shells[0];
|
||||
});
|
||||
setAntagonistSelectorOpen(false);
|
||||
}}
|
||||
@@ -396,7 +362,8 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
</Dialog.Root>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
<Flex gap="3" align="center" mt="2">
|
||||
<SegmentedControl.Root
|
||||
value={`${display}`}
|
||||
@@ -441,7 +408,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
</SegmentedControl.Item> */}
|
||||
</SegmentedControl.Root>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
{/* <DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<IconButton variant="ghost" color="gray">
|
||||
<EyeOpenIcon />
|
||||
@@ -461,9 +428,9 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
{strings.website.tools.tanks.sandbox.poses.default}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</DropdownMenu.Root> */}
|
||||
|
||||
<DropdownMenu.Root>
|
||||
{/* <DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<IconButton variant="ghost" color="gray">
|
||||
<GearIcon />
|
||||
@@ -549,9 +516,9 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
{strings.website.tools.tanks.sandbox.settings.show_grid}
|
||||
</DropdownMenu.CheckboxItem>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</DropdownMenu.Root> */}
|
||||
|
||||
<Popover.Root>
|
||||
{/* <Popover.Root>
|
||||
<Popover.Trigger>
|
||||
<IconButton color="gray" variant="ghost">
|
||||
<CameraIcon />
|
||||
@@ -603,9 +570,9 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
</Popover.Close>
|
||||
</Flex>
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
</Popover.Root> */}
|
||||
|
||||
{fullScreenAvailable && (
|
||||
{/* {fullScreenAvailable && (
|
||||
<IconButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
@@ -617,7 +584,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
>
|
||||
{isFullScreen ? <ExitFullScreenIcon /> : <EnterFullScreenIcon />}
|
||||
</IconButton>
|
||||
)}
|
||||
)} */}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</>
|
||||
|
||||
+263
-271
@@ -1,22 +1,14 @@
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { invalidate, useThree } from '@react-three/fiber';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { PerspectiveCamera, Vector3 } from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useRef, useState } from 'react';
|
||||
import { PerspectiveCamera } from 'three';
|
||||
import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
|
||||
import { Pose } from '../../../../../../core/blitzkit/pose';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import {
|
||||
SHOOTING_RANGE_ZOOM_COEFFICIENTS,
|
||||
ShootingRangeZoom,
|
||||
TankopediaEphemeral,
|
||||
} from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
||||
import { aimTarget } from './SceneProps/constants';
|
||||
|
||||
const poseDistances: Record<Pose, number> = {
|
||||
[Pose.HullDown]: 15,
|
||||
@@ -43,302 +35,302 @@ export function Controls({ naked }: ControlsProps) {
|
||||
const camera = useThree((state) => state.camera as PerspectiveCamera);
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const orbitControls = useRef<OrbitControlsClass>(null);
|
||||
const protagonistTurret = Duel.use((state) => state.protagonist.turret);
|
||||
const antagonistTurret = Duel.use((state) => state.antagonist.turret);
|
||||
const protagonistTrack = Duel.use((state) => state.protagonist.track);
|
||||
// const protagonistTurret = Duel.use((state) => state.protagonist.turret);
|
||||
// const antagonistTurret = Duel.use((state) => state.antagonist.turret);
|
||||
// const protagonistTrack = Duel.use((state) => state.protagonist.track);
|
||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||
const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
||||
const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
||||
const protagonistModelDefinition =
|
||||
modelDefinitions.models[protagonistTank.id];
|
||||
const protagonistTrackModelDefinition =
|
||||
modelDefinitions.models[protagonistTank.id].tracks[protagonistTrack.id];
|
||||
const antagonistModelDefinition = modelDefinitions.models[antagonistTank.id];
|
||||
const protagonistTurretModelDefinition =
|
||||
protagonistModelDefinition.turrets[protagonistTurret.id];
|
||||
const antagonistTurretModelDefinition =
|
||||
antagonistModelDefinition.turrets[antagonistTurret.id];
|
||||
const protagonistGunModelDefinition =
|
||||
protagonistTurretModelDefinition.guns[
|
||||
protagonistGun.gun_type!.value.base.id
|
||||
];
|
||||
const protagonistHullOrigin = new Vector3(
|
||||
protagonistTrackModelDefinition.origin.x,
|
||||
protagonistTrackModelDefinition.origin.y,
|
||||
-protagonistTrackModelDefinition.origin.z,
|
||||
);
|
||||
const protagonistTurretOrigin = new Vector3(
|
||||
protagonistModelDefinition.turret_origin.x,
|
||||
protagonistModelDefinition.turret_origin.y,
|
||||
-protagonistModelDefinition.turret_origin.z,
|
||||
);
|
||||
const protagonistGunOrigin = new Vector3(
|
||||
protagonistTurretModelDefinition.gun_origin.x,
|
||||
protagonistTurretModelDefinition.gun_origin.y,
|
||||
-protagonistTurretModelDefinition.gun_origin.z,
|
||||
);
|
||||
const antagonistGunHeight =
|
||||
protagonistTrackModelDefinition.origin.y +
|
||||
antagonistModelDefinition.turret_origin.y +
|
||||
antagonistTurretModelDefinition.gun_origin.y;
|
||||
// const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
||||
// const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
||||
// const protagonistModelDefinition =
|
||||
// modelDefinitions.models[protagonistTank.id];
|
||||
// const protagonistTrackModelDefinition =
|
||||
// modelDefinitions.models[protagonistTank.id].tracks[protagonistTrack.id];
|
||||
// const antagonistModelDefinition = modelDefinitions.models[antagonistTank.id];
|
||||
// const protagonistTurretModelDefinition =
|
||||
// protagonistModelDefinition.turrets[protagonistTurret.id];
|
||||
// const antagonistTurretModelDefinition =
|
||||
// antagonistModelDefinition.turrets[antagonistTurret.id];
|
||||
// const protagonistGunModelDefinition =
|
||||
// protagonistTurretModelDefinition.guns[
|
||||
// protagonistGun.gun_type!.value.base.id
|
||||
// ];
|
||||
// const protagonistHullOrigin = new Vector3(
|
||||
// protagonistTrackModelDefinition.origin.x,
|
||||
// protagonistTrackModelDefinition.origin.y,
|
||||
// -protagonistTrackModelDefinition.origin.z,
|
||||
// );
|
||||
// const protagonistTurretOrigin = new Vector3(
|
||||
// protagonistModelDefinition.turret_origin.x,
|
||||
// protagonistModelDefinition.turret_origin.y,
|
||||
// -protagonistModelDefinition.turret_origin.z,
|
||||
// );
|
||||
// const protagonistGunOrigin = new Vector3(
|
||||
// protagonistTurretModelDefinition.gun_origin.x,
|
||||
// protagonistTurretModelDefinition.gun_origin.y,
|
||||
// -protagonistTurretModelDefinition.gun_origin.z,
|
||||
// );
|
||||
// const antagonistGunHeight =
|
||||
// protagonistTrackModelDefinition.origin.y +
|
||||
// antagonistModelDefinition.turret_origin.y +
|
||||
// antagonistTurretModelDefinition.gun_origin.y;
|
||||
const [autoRotate, setAutoRotate] = useState(
|
||||
display !== TankopediaDisplay.ShootingRange && !naked,
|
||||
);
|
||||
const gunHeight =
|
||||
protagonistHullOrigin.y +
|
||||
protagonistTurretOrigin.y +
|
||||
protagonistGunOrigin.y;
|
||||
const inspectModeInitialPosition = [
|
||||
-8,
|
||||
gunHeight + (naked ? 10 : 4),
|
||||
-13,
|
||||
] as const;
|
||||
const protagonistGunOriginOnlyY = new Vector3(
|
||||
0,
|
||||
protagonistTurretModelDefinition.gun_origin.y,
|
||||
0,
|
||||
);
|
||||
const shellOrigin = protagonistHullOrigin
|
||||
.clone()
|
||||
.add(protagonistTurretOrigin)
|
||||
.add(protagonistGunOriginOnlyY);
|
||||
// const gunHeight =
|
||||
// protagonistHullOrigin.y +
|
||||
// protagonistTurretOrigin.y +
|
||||
// protagonistGunOrigin.y;
|
||||
// const inspectModeInitialPosition = [
|
||||
// -8,
|
||||
// gunHeight + (naked ? 10 : 4),
|
||||
// -13,
|
||||
// ] as const;
|
||||
// const protagonistGunOriginOnlyY = new Vector3(
|
||||
// 0,
|
||||
// protagonistTurretModelDefinition.gun_origin.y,
|
||||
// 0,
|
||||
// );
|
||||
// const shellOrigin = protagonistHullOrigin
|
||||
// .clone()
|
||||
// .add(protagonistTurretOrigin)
|
||||
// .add(protagonistGunOriginOnlyY);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeTankopediaEphemeral = tankopediaEphemeralStore.subscribe(
|
||||
(state) => state.controlsEnabled,
|
||||
(enabled) => {
|
||||
if (orbitControls.current) orbitControls.current.enabled = enabled;
|
||||
},
|
||||
);
|
||||
// useEffect(() => {
|
||||
// const unsubscribeTankopediaEphemeral = tankopediaEphemeralStore.subscribe(
|
||||
// (state) => state.controlsEnabled,
|
||||
// (enabled) => {
|
||||
// if (orbitControls.current) orbitControls.current.enabled = enabled;
|
||||
// },
|
||||
// );
|
||||
|
||||
async function handlePoseEvent(event: Pose) {
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
122,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = await hasEquipment(
|
||||
124,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
);
|
||||
// async function handlePoseEvent(event: Pose) {
|
||||
// const duel = duelStore.getState();
|
||||
// const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||
// 122,
|
||||
// duel.protagonist.tank.equipment_preset,
|
||||
// duel.protagonist.equipmentMatrix,
|
||||
// );
|
||||
// const hasDownImprovedVerticalStabilizer = await hasEquipment(
|
||||
// 124,
|
||||
// duel.protagonist.tank.equipment_preset,
|
||||
// duel.protagonist.equipmentMatrix,
|
||||
// );
|
||||
|
||||
switch (event) {
|
||||
case Pose.HullDown: {
|
||||
const [pitch] = applyPitchYawLimits(
|
||||
-Infinity,
|
||||
0,
|
||||
protagonistGunModelDefinition.pitch,
|
||||
protagonistTurretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
);
|
||||
// switch (event) {
|
||||
// case Pose.HullDown: {
|
||||
// const [pitch] = applyPitchYawLimits(
|
||||
// -Infinity,
|
||||
// 0,
|
||||
// protagonistGunModelDefinition.pitch,
|
||||
// protagonistTurretModelDefinition.yaw,
|
||||
// hasImprovedVerticalStabilizer,
|
||||
// hasDownImprovedVerticalStabilizer,
|
||||
// );
|
||||
|
||||
camera.position
|
||||
.set(0, 0, 0)
|
||||
.add(protagonistHullOrigin)
|
||||
.add(protagonistTurretOrigin)
|
||||
.add(protagonistGunOrigin)
|
||||
.add(
|
||||
new Vector3(
|
||||
0,
|
||||
poseDistances[event] * Math.sin(pitch),
|
||||
poseDistances[event] * -Math.cos(pitch),
|
||||
),
|
||||
);
|
||||
camera.lookAt(
|
||||
protagonistHullOrigin
|
||||
.clone()
|
||||
.add(protagonistTurretOrigin)
|
||||
.add(protagonistGunOrigin),
|
||||
);
|
||||
orbitControls.current?.target.set(0, antagonistGunHeight, 0);
|
||||
// camera.position
|
||||
// .set(0, 0, 0)
|
||||
// .add(protagonistHullOrigin)
|
||||
// .add(protagonistTurretOrigin)
|
||||
// .add(protagonistGunOrigin)
|
||||
// .add(
|
||||
// new Vector3(
|
||||
// 0,
|
||||
// poseDistances[event] * Math.sin(pitch),
|
||||
// poseDistances[event] * -Math.cos(pitch),
|
||||
// ),
|
||||
// );
|
||||
// camera.lookAt(
|
||||
// protagonistHullOrigin
|
||||
// .clone()
|
||||
// .add(protagonistTurretOrigin)
|
||||
// .add(protagonistGunOrigin),
|
||||
// );
|
||||
// orbitControls.current?.target.set(0, antagonistGunHeight, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
// break;
|
||||
// }
|
||||
|
||||
case Pose.FaceHug: {
|
||||
const [pitch] = applyPitchYawLimits(
|
||||
0,
|
||||
0,
|
||||
protagonistGunModelDefinition.pitch,
|
||||
protagonistTurretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
);
|
||||
// case Pose.FaceHug: {
|
||||
// const [pitch] = applyPitchYawLimits(
|
||||
// 0,
|
||||
// 0,
|
||||
// protagonistGunModelDefinition.pitch,
|
||||
// protagonistTurretModelDefinition.yaw,
|
||||
// hasImprovedVerticalStabilizer,
|
||||
// hasDownImprovedVerticalStabilizer,
|
||||
// );
|
||||
|
||||
camera.position
|
||||
.set(0, 0, 0)
|
||||
.add(protagonistHullOrigin)
|
||||
.add(protagonistTurretOrigin)
|
||||
.add(protagonistGunOrigin)
|
||||
.add(
|
||||
new Vector3(
|
||||
0,
|
||||
poseDistances[event] * Math.sin(pitch),
|
||||
poseDistances[event] * -Math.cos(pitch),
|
||||
),
|
||||
);
|
||||
orbitControls.current?.target
|
||||
.set(0, 0, 0)
|
||||
.add(protagonistHullOrigin)
|
||||
.add(protagonistTurretOrigin)
|
||||
.add(protagonistGunOrigin)
|
||||
.add(
|
||||
new Vector3(
|
||||
0,
|
||||
0.5 * poseDistances[event] * Math.sin(pitch),
|
||||
0.5 * poseDistances[event] * -Math.cos(pitch),
|
||||
),
|
||||
);
|
||||
// camera.position
|
||||
// .set(0, 0, 0)
|
||||
// .add(protagonistHullOrigin)
|
||||
// .add(protagonistTurretOrigin)
|
||||
// .add(protagonistGunOrigin)
|
||||
// .add(
|
||||
// new Vector3(
|
||||
// 0,
|
||||
// poseDistances[event] * Math.sin(pitch),
|
||||
// poseDistances[event] * -Math.cos(pitch),
|
||||
// ),
|
||||
// );
|
||||
// orbitControls.current?.target
|
||||
// .set(0, 0, 0)
|
||||
// .add(protagonistHullOrigin)
|
||||
// .add(protagonistTurretOrigin)
|
||||
// .add(protagonistGunOrigin)
|
||||
// .add(
|
||||
// new Vector3(
|
||||
// 0,
|
||||
// 0.5 * poseDistances[event] * Math.sin(pitch),
|
||||
// 0.5 * poseDistances[event] * -Math.cos(pitch),
|
||||
// ),
|
||||
// );
|
||||
|
||||
break;
|
||||
}
|
||||
// break;
|
||||
// }
|
||||
|
||||
case Pose.Default: {
|
||||
camera.position.set(...inspectModeInitialPosition);
|
||||
orbitControls.current?.target.set(0, 1.25, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// case Pose.Default: {
|
||||
// camera.position.set(...inspectModeInitialPosition);
|
||||
// orbitControls.current?.target.set(0, 1.25, 0);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
invalidate();
|
||||
}
|
||||
// invalidate();
|
||||
// }
|
||||
|
||||
poseEvent.on(handlePoseEvent);
|
||||
// poseEvent.on(handlePoseEvent);
|
||||
|
||||
return () => {
|
||||
unsubscribeTankopediaEphemeral();
|
||||
poseEvent.off(handlePoseEvent);
|
||||
};
|
||||
}, [camera, protagonistTank.id, antagonistTank.id]);
|
||||
// return () => {
|
||||
// unsubscribeTankopediaEphemeral();
|
||||
// poseEvent.off(handlePoseEvent);
|
||||
// };
|
||||
// }, [camera, protagonistTank.id, antagonistTank.id]);
|
||||
|
||||
useEffect(() => {
|
||||
let isInitialIteration = true;
|
||||
// useEffect(() => {
|
||||
// let isInitialIteration = true;
|
||||
|
||||
function handleDisturbance() {
|
||||
setAutoRotate(false);
|
||||
}
|
||||
// function handleDisturbance() {
|
||||
// setAutoRotate(false);
|
||||
// }
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (display !== TankopediaDisplay.ShootingRange || event.repeat) return;
|
||||
// function handleKeyDown(event: KeyboardEvent) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange || event.repeat) return;
|
||||
|
||||
if (event.key === 'Shift') {
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shootingRangeZoom =
|
||||
draft.shootingRangeZoom === ShootingRangeZoom.Arcade
|
||||
? ShootingRangeZoom.Zoom0
|
||||
: ShootingRangeZoom.Arcade;
|
||||
});
|
||||
}
|
||||
}
|
||||
// if (event.key === 'Shift') {
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.shootingRangeZoom =
|
||||
// draft.shootingRangeZoom === ShootingRangeZoom.Arcade
|
||||
// ? ShootingRangeZoom.Zoom0
|
||||
// : ShootingRangeZoom.Arcade;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
function handleWheel(event: WheelEvent) {
|
||||
if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
// function handleWheel(event: WheelEvent) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
|
||||
event.preventDefault();
|
||||
// event.preventDefault();
|
||||
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shootingRangeZoom = clamp(
|
||||
draft.shootingRangeZoom - Math.sign(event.deltaY),
|
||||
ShootingRangeZoom.Arcade,
|
||||
ShootingRangeZoom.Zoom2,
|
||||
);
|
||||
});
|
||||
}
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.shootingRangeZoom = clamp(
|
||||
// draft.shootingRangeZoom - Math.sign(event.deltaY),
|
||||
// ShootingRangeZoom.Arcade,
|
||||
// ShootingRangeZoom.Zoom2,
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
function handleScroll(event: Event) {
|
||||
if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
// function handleScroll(event: Event) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
// event.preventDefault();
|
||||
// }
|
||||
|
||||
poseEvent.on(handleDisturbance);
|
||||
canvas.addEventListener('pointerdown', handleDisturbance);
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
canvas.addEventListener('wheel', handleWheel);
|
||||
document.body.addEventListener('scroll', handleScroll);
|
||||
// poseEvent.on(handleDisturbance);
|
||||
// canvas.addEventListener('pointerdown', handleDisturbance);
|
||||
// window.addEventListener('keydown', handleKeyDown);
|
||||
// canvas.addEventListener('wheel', handleWheel);
|
||||
// document.body.addEventListener('scroll', handleScroll);
|
||||
|
||||
function updateCamera() {
|
||||
if (!orbitControls.current) return;
|
||||
// function updateCamera() {
|
||||
// if (!orbitControls.current) return;
|
||||
|
||||
orbitControls.current.rotateSpeed = 0.25;
|
||||
// orbitControls.current.rotateSpeed = 0.25;
|
||||
|
||||
if (display === TankopediaDisplay.ShootingRange) {
|
||||
const { shootingRangeZoom } = tankopediaEphemeralStore.getState();
|
||||
// if (display === TankopediaDisplay.ShootingRange) {
|
||||
// const { shootingRangeZoom } = tankopediaEphemeralStore.getState();
|
||||
|
||||
orbitControls.current.enablePan = false;
|
||||
orbitControls.current.enableZoom = false;
|
||||
orbitControls.current.rotateSpeed *=
|
||||
SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
camera.fov =
|
||||
ARCADE_MODE_FOV * SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
// orbitControls.current.enablePan = false;
|
||||
// orbitControls.current.enableZoom = false;
|
||||
// orbitControls.current.rotateSpeed *=
|
||||
// SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
// camera.fov =
|
||||
// ARCADE_MODE_FOV * SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
|
||||
if (shootingRangeZoom === ShootingRangeZoom.Arcade) {
|
||||
orbitControls.current.target.set(0, gunHeight + 3, 0);
|
||||
// if (shootingRangeZoom === ShootingRangeZoom.Arcade) {
|
||||
// orbitControls.current.target.set(0, gunHeight + 3, 0);
|
||||
|
||||
if (isInitialIteration) {
|
||||
isInitialIteration = false;
|
||||
camera.position.set(
|
||||
0,
|
||||
gunHeight + ARCADE_MODE_DISTANCE * Math.sin(ARCADE_MODE_ANGLE),
|
||||
ARCADE_MODE_DISTANCE * Math.cos(ARCADE_MODE_ANGLE),
|
||||
);
|
||||
} else {
|
||||
camera.position
|
||||
.copy(aimTarget)
|
||||
.sub(orbitControls.current.target)
|
||||
.normalize()
|
||||
.multiplyScalar(-ARCADE_MODE_DISTANCE)
|
||||
.add(orbitControls.current.target);
|
||||
}
|
||||
} else {
|
||||
orbitControls.current.target.copy(shellOrigin);
|
||||
// if (isInitialIteration) {
|
||||
// isInitialIteration = false;
|
||||
// camera.position.set(
|
||||
// 0,
|
||||
// gunHeight + ARCADE_MODE_DISTANCE * Math.sin(ARCADE_MODE_ANGLE),
|
||||
// ARCADE_MODE_DISTANCE * Math.cos(ARCADE_MODE_ANGLE),
|
||||
// );
|
||||
// } else {
|
||||
// camera.position
|
||||
// .copy(aimTarget)
|
||||
// .sub(orbitControls.current.target)
|
||||
// .normalize()
|
||||
// .multiplyScalar(-ARCADE_MODE_DISTANCE)
|
||||
// .add(orbitControls.current.target);
|
||||
// }
|
||||
// } else {
|
||||
// orbitControls.current.target.copy(shellOrigin);
|
||||
|
||||
camera.position
|
||||
.copy(aimTarget)
|
||||
.sub(shellOrigin)
|
||||
.normalize()
|
||||
.multiplyScalar(-Number.EPSILON)
|
||||
.add(shellOrigin);
|
||||
}
|
||||
} else {
|
||||
camera.position.set(...inspectModeInitialPosition);
|
||||
orbitControls.current.target.set(0, gunHeight / (naked ? 4 : 2), 0);
|
||||
orbitControls.current.enablePan = true;
|
||||
orbitControls.current.enableZoom = true;
|
||||
camera.fov = naked ? 20 : 25;
|
||||
}
|
||||
// camera.position
|
||||
// .copy(aimTarget)
|
||||
// .sub(shellOrigin)
|
||||
// .normalize()
|
||||
// .multiplyScalar(-Number.EPSILON)
|
||||
// .add(shellOrigin);
|
||||
// }
|
||||
// } else {
|
||||
// camera.position.set(...inspectModeInitialPosition);
|
||||
// orbitControls.current.target.set(0, gunHeight / (naked ? 4 : 2), 0);
|
||||
// orbitControls.current.enablePan = true;
|
||||
// orbitControls.current.enableZoom = true;
|
||||
// camera.fov = naked ? 20 : 25;
|
||||
// }
|
||||
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
// camera.updateProjectionMatrix();
|
||||
// }
|
||||
|
||||
updateCamera();
|
||||
// updateCamera();
|
||||
|
||||
const unsubscribeDisplay = tankopediaEphemeralStore.subscribe(
|
||||
(state) => state.display,
|
||||
() => {
|
||||
handleDisturbance();
|
||||
updateCamera();
|
||||
},
|
||||
);
|
||||
// const unsubscribeDisplay = tankopediaEphemeralStore.subscribe(
|
||||
// (state) => state.display,
|
||||
// () => {
|
||||
// handleDisturbance();
|
||||
// updateCamera();
|
||||
// },
|
||||
// );
|
||||
|
||||
const unsubscribeShootingRangeZoom = tankopediaEphemeralStore.subscribe(
|
||||
(state) => state.shootingRangeZoom,
|
||||
updateCamera,
|
||||
);
|
||||
// const unsubscribeShootingRangeZoom = tankopediaEphemeralStore.subscribe(
|
||||
// (state) => state.shootingRangeZoom,
|
||||
// updateCamera,
|
||||
// );
|
||||
|
||||
return () => {
|
||||
canvas.removeEventListener('pointerdown', handleDisturbance);
|
||||
poseEvent.off(handleDisturbance);
|
||||
unsubscribeDisplay();
|
||||
unsubscribeShootingRangeZoom();
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
canvas.removeEventListener('wheel', handleWheel);
|
||||
document.body.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [display === TankopediaDisplay.ShootingRange]);
|
||||
// return () => {
|
||||
// canvas.removeEventListener('pointerdown', handleDisturbance);
|
||||
// poseEvent.off(handleDisturbance);
|
||||
// unsubscribeDisplay();
|
||||
// unsubscribeShootingRangeZoom();
|
||||
// window.removeEventListener('keydown', handleKeyDown);
|
||||
// canvas.removeEventListener('wheel', handleWheel);
|
||||
// document.body.removeEventListener('scroll', handleScroll);
|
||||
// };
|
||||
// }, [display === TankopediaDisplay.ShootingRange]);
|
||||
|
||||
return (
|
||||
<OrbitControls
|
||||
|
||||
+1
-6
@@ -2,19 +2,14 @@ import { Environment } from '@react-three/drei';
|
||||
import { times } from 'lodash-es';
|
||||
import { Euler } from 'three';
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
import { useModel } from '../../../../../../hooks/useModel';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
||||
|
||||
const LIGHTS_COUNT = 5;
|
||||
|
||||
export function Lighting() {
|
||||
const protagonist = Duel.use((state) => state.protagonist);
|
||||
const display = TankopediaEphemeral.use((state) => state.display);
|
||||
const { hasPbr } = useModel(protagonist.tank.id);
|
||||
const isBrighterLighting =
|
||||
!hasPbr && display !== TankopediaDisplay.StaticArmor;
|
||||
const isBrighterLighting = display !== TankopediaDisplay.StaticArmor;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
+169
-185
@@ -1,29 +1,13 @@
|
||||
import { asset, I_HAT, imgur, J_HAT } from '@blitzkit/core';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { imgur } from '@blitzkit/core';
|
||||
import { useFrame, useLoader } from '@react-three/fiber';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { useRef } from 'react';
|
||||
import {
|
||||
Group,
|
||||
MeshStandardMaterial,
|
||||
PerspectiveCamera,
|
||||
TextureLoader,
|
||||
Vector2,
|
||||
Vector3,
|
||||
} from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/Addons.js';
|
||||
import { degToRad, radToDeg } from 'three/src/math/MathUtils.js';
|
||||
import { MeshStandardMaterial, TextureLoader, Vector2 } from 'three';
|
||||
import { awaitableEquipmentDefinitions } from '../../../../../../../core/awaitables/equipmentDefinitions';
|
||||
import { awaitableModelDefinitions } from '../../../../../../../core/awaitables/modelDefinitions';
|
||||
import { awaitableProvisionDefinitions } from '../../../../../../../core/awaitables/provisionDefinitions';
|
||||
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelTransformEvent } from '../../../../../../../core/blitzkit/modelTransform';
|
||||
import { tankCharacteristics } from '../../../../../../../core/blitzkit/tankCharacteristics';
|
||||
import { Duel } from '../../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaDisplay } from '../../../../../../../stores/tankopediaPersistent/constants';
|
||||
import { TargetCircle } from './components/TargetCircle';
|
||||
import { aimTarget } from './constants';
|
||||
|
||||
const [modelDefinitions, equipmentDefinitions, provisionDefinitions] =
|
||||
await Promise.all([
|
||||
@@ -35,203 +19,203 @@ const [modelDefinitions, equipmentDefinitions, provisionDefinitions] =
|
||||
const emptyVector = new Vector2();
|
||||
|
||||
export function SceneProps() {
|
||||
const targetCircleWrapper = useRef<Group>(null);
|
||||
const clientTargetCircle = useRef<HTMLDivElement>(null);
|
||||
const serverTargetCircle = useRef<HTMLDivElement>(null);
|
||||
// const targetCircleWrapper = useRef<Group>(null);
|
||||
// const clientTargetCircle = useRef<HTMLDivElement>(null);
|
||||
// const serverTargetCircle = useRef<HTMLDivElement>(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);
|
||||
// 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 track = Duel.use((state) => state.protagonist.track);
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const gun = Duel.use((state) => state.protagonist.gun);
|
||||
const shell = Duel.use((state) => state.protagonist.shell);
|
||||
const engine = Duel.use((state) => state.protagonist.engine);
|
||||
const tankModelDefinition = modelDefinitions.models[tank.id];
|
||||
const trackModelDefinition =
|
||||
modelDefinitions.models[tank.id].tracks[track.id];
|
||||
const gunModelDefinition =
|
||||
tankModelDefinition.turrets[turret.id].guns[gun.gun_type!.value.base.id];
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const hullOrigin = new Vector3(
|
||||
trackModelDefinition.origin.x,
|
||||
trackModelDefinition.origin.y,
|
||||
-trackModelDefinition.origin.z,
|
||||
);
|
||||
const turretOrigin = new Vector3(
|
||||
tankModelDefinition.turret_origin.x,
|
||||
tankModelDefinition.turret_origin.y,
|
||||
-tankModelDefinition.turret_origin.z,
|
||||
);
|
||||
const gunOriginOnlyY = new Vector3(0, turretModelDefinition.gun_origin.y, 0);
|
||||
const shellOrigin = hullOrigin.clone().add(turretOrigin).add(gunOriginOnlyY);
|
||||
const camouflage = Duel.use((state) => state.protagonist.camouflage);
|
||||
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||
const consumables = Duel.use((state) => state.protagonist.consumables);
|
||||
const provisions = Duel.use((state) => state.protagonist.provisions);
|
||||
const equipmentMatrix = Duel.use(
|
||||
(state) => state.protagonist.equipmentMatrix,
|
||||
);
|
||||
const characteristics = tankCharacteristics(
|
||||
{
|
||||
tank,
|
||||
turret,
|
||||
gun,
|
||||
shell,
|
||||
consumables,
|
||||
applyDynamicArmor: false,
|
||||
applyReactiveArmor: false,
|
||||
applySpallLiner: false,
|
||||
camouflage,
|
||||
crewSkills,
|
||||
engine,
|
||||
equipmentMatrix,
|
||||
provisions,
|
||||
stockEngine: tank.engines[0],
|
||||
stockGun: tank.turrets[0].guns[0],
|
||||
stockTurret: tank.turrets[0],
|
||||
stockTrack: tank.tracks[0],
|
||||
track,
|
||||
},
|
||||
{ equipmentDefinitions, provisionDefinitions, tankModelDefinition },
|
||||
);
|
||||
let { dispersion } = characteristics;
|
||||
const gunRotationSpeed = degToRad(characteristics.gunTraverseSpeed);
|
||||
const mockTank = useLoader(GLTFLoader, asset(`3d/tanks/models/6929.glb`));
|
||||
// const turret = Duel.use((state) => state.protagonist.turret);
|
||||
// const track = Duel.use((state) => state.protagonist.track);
|
||||
// const tank = Duel.use((state) => state.protagonist.tank);
|
||||
// const gun = Duel.use((state) => state.protagonist.gun);
|
||||
// const shell = Duel.use((state) => state.protagonist.shell);
|
||||
// const engine = Duel.use((state) => state.protagonist.engine);
|
||||
// const tankModelDefinition = modelDefinitions.models[tank.id];
|
||||
// const trackModelDefinition =
|
||||
// modelDefinitions.models[tank.id].tracks[track.id];
|
||||
// const gunModelDefinition =
|
||||
// tankModelDefinition.turrets[turret.id].guns[gun.gun_type!.value.base.id];
|
||||
// const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
// const hullOrigin = new Vector3(
|
||||
// trackModelDefinition.origin.x,
|
||||
// trackModelDefinition.origin.y,
|
||||
// -trackModelDefinition.origin.z,
|
||||
// );
|
||||
// const turretOrigin = new Vector3(
|
||||
// tankModelDefinition.turret_origin.x,
|
||||
// tankModelDefinition.turret_origin.y,
|
||||
// -tankModelDefinition.turret_origin.z,
|
||||
// );
|
||||
// const gunOriginOnlyY = new Vector3(0, turretModelDefinition.gun_origin.y, 0);
|
||||
// const shellOrigin = hullOrigin.clone().add(turretOrigin).add(gunOriginOnlyY);
|
||||
// const camouflage = Duel.use((state) => state.protagonist.camouflage);
|
||||
// const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||
// const consumables = Duel.use((state) => state.protagonist.consumables);
|
||||
// const provisions = Duel.use((state) => state.protagonist.provisions);
|
||||
// const equipmentMatrix = Duel.use(
|
||||
// (state) => state.protagonist.equipmentMatrix,
|
||||
// );
|
||||
// const characteristics = tankCharacteristics(
|
||||
// {
|
||||
// tank,
|
||||
// turret,
|
||||
// gun,
|
||||
// shell,
|
||||
// consumables,
|
||||
// applyDynamicArmor: false,
|
||||
// applyReactiveArmor: false,
|
||||
// applySpallLiner: false,
|
||||
// camouflage,
|
||||
// crewSkills,
|
||||
// engine,
|
||||
// equipmentMatrix,
|
||||
// provisions,
|
||||
// stockEngine: tank.engines[0],
|
||||
// stockGun: tank.turrets[0].guns[0],
|
||||
// stockTurret: tank.turrets[0],
|
||||
// stockTrack: tank.tracks[0],
|
||||
// track,
|
||||
// },
|
||||
// { equipmentDefinitions, provisionDefinitions, tankModelDefinition },
|
||||
// );
|
||||
// let { dispersion } = characteristics;
|
||||
// const gunRotationSpeed = degToRad(characteristics.gunTraverseSpeed);
|
||||
// const mockTank = useLoader(GLTFLoader, asset(`3d/tanks/models/6929.glb`));
|
||||
const texture = useLoader(TextureLoader, imgur('C28Z8nU'));
|
||||
const path = new Vector3();
|
||||
const direction = new Vector3();
|
||||
texture.anisotropy = 2;
|
||||
// const path = new Vector3();
|
||||
// const direction = new Vector3();
|
||||
|
||||
useFrame(({ camera }) => {
|
||||
if (!material.current) return;
|
||||
material.current.opacity = clamp(0.5 * camera.position.y, 0, 1);
|
||||
});
|
||||
|
||||
let lastTime = 0;
|
||||
let DEBUG_disable = false;
|
||||
// let lastTime = 0;
|
||||
// let DEBUG_disable = false;
|
||||
|
||||
useFrame(({ raycaster, camera, clock, gl }) => {
|
||||
if (
|
||||
display !== TankopediaDisplay.ShootingRange ||
|
||||
!playground.current ||
|
||||
!targetCircleWrapper.current ||
|
||||
DEBUG_disable ||
|
||||
!clientTargetCircle.current ||
|
||||
!serverTargetCircle.current
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// useFrame(({ raycaster, camera, clock, gl }) => {
|
||||
// if (
|
||||
// display !== TankopediaDisplay.ShootingRange ||
|
||||
// !playground.current ||
|
||||
// !targetCircleWrapper.current ||
|
||||
// DEBUG_disable ||
|
||||
// !clientTargetCircle.current ||
|
||||
// !serverTargetCircle.current
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
raycaster.setFromCamera(emptyVector, camera);
|
||||
// raycaster.setFromCamera(emptyVector, camera);
|
||||
|
||||
const cameraIntersections = raycaster.intersectObjects(
|
||||
playground.current.children,
|
||||
true,
|
||||
);
|
||||
// const cameraIntersections = raycaster.intersectObjects(
|
||||
// playground.current.children,
|
||||
// true,
|
||||
// );
|
||||
|
||||
let length: number;
|
||||
// let length: number;
|
||||
|
||||
if (cameraIntersections.length === 0) {
|
||||
length = characteristics.shellRange;
|
||||
direction.set(0, 0, -1).applyQuaternion(camera.quaternion);
|
||||
aimTarget.copy(shellOrigin).addScaledVector(direction, length);
|
||||
} else {
|
||||
aimTarget.copy(cameraIntersections[0].point);
|
||||
path.copy(aimTarget).sub(shellOrigin);
|
||||
length = path.length();
|
||||
direction.copy(path).normalize();
|
||||
}
|
||||
// if (cameraIntersections.length === 0) {
|
||||
// length = characteristics.shellRange;
|
||||
// direction.set(0, 0, -1).applyQuaternion(camera.quaternion);
|
||||
// aimTarget.copy(shellOrigin).addScaledVector(direction, length);
|
||||
// } else {
|
||||
// aimTarget.copy(cameraIntersections[0].point);
|
||||
// path.copy(aimTarget).sub(shellOrigin);
|
||||
// length = path.length();
|
||||
// direction.copy(path).normalize();
|
||||
// }
|
||||
|
||||
const deltaT = clock.elapsedTime - lastTime;
|
||||
lastTime = clock.elapsedTime;
|
||||
// const deltaT = clock.elapsedTime - lastTime;
|
||||
// lastTime = clock.elapsedTime;
|
||||
|
||||
const desiredPitch = Math.asin(direction.y);
|
||||
const desiredYaw = Math.atan2(-direction.x, -direction.z);
|
||||
const deltaPitch = desiredPitch - lastPitch;
|
||||
const deltaYaw = desiredYaw - lastYaw;
|
||||
const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2);
|
||||
const maxPossibleRotationInTime = gunRotationSpeed * deltaT;
|
||||
const clampedDeltaAngle = Math.min(maxPossibleRotationInTime, deltaAngle);
|
||||
const clampedDeltaPitch =
|
||||
deltaAngle === 0 ? 0 : deltaPitch * (clampedDeltaAngle / deltaAngle);
|
||||
const clampedDeltaYaw =
|
||||
deltaAngle === 0 ? 0 : deltaYaw * (clampedDeltaAngle / deltaAngle);
|
||||
// const desiredPitch = Math.asin(direction.y);
|
||||
// const desiredYaw = Math.atan2(-direction.x, -direction.z);
|
||||
// const deltaPitch = desiredPitch - lastPitch;
|
||||
// const deltaYaw = desiredYaw - lastYaw;
|
||||
// const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2);
|
||||
// const maxPossibleRotationInTime = gunRotationSpeed * deltaT;
|
||||
// const clampedDeltaAngle = Math.min(maxPossibleRotationInTime, deltaAngle);
|
||||
// const clampedDeltaPitch =
|
||||
// deltaAngle === 0 ? 0 : deltaPitch * (clampedDeltaAngle / deltaAngle);
|
||||
// const clampedDeltaYaw =
|
||||
// deltaAngle === 0 ? 0 : deltaYaw * (clampedDeltaAngle / deltaAngle);
|
||||
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
lastPitch + clampedDeltaPitch,
|
||||
lastYaw + clampedDeltaYaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
);
|
||||
// const [pitch, yaw] = applyPitchYawLimits(
|
||||
// lastPitch + clampedDeltaPitch,
|
||||
// lastYaw + clampedDeltaYaw,
|
||||
// gunModelDefinition.pitch,
|
||||
// turretModelDefinition.yaw,
|
||||
// );
|
||||
|
||||
const actualDeltaPitch = pitch - lastPitch;
|
||||
const actualDeltaYaw = yaw - lastYaw;
|
||||
const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw);
|
||||
const rotationSpeed = deltaRotation / deltaT;
|
||||
const turretTraversePenalty =
|
||||
characteristics.dispersionTurretTraversing * radToDeg(rotationSpeed);
|
||||
// const actualDeltaPitch = pitch - lastPitch;
|
||||
// const actualDeltaYaw = yaw - lastYaw;
|
||||
// const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw);
|
||||
// const rotationSpeed = deltaRotation / deltaT;
|
||||
// const turretTraversePenalty =
|
||||
// characteristics.dispersionTurretTraversing * radToDeg(rotationSpeed);
|
||||
|
||||
const penalty = Math.sqrt(1 + turretTraversePenalty ** 2);
|
||||
// const penalty = Math.sqrt(1 + turretTraversePenalty ** 2);
|
||||
|
||||
if (penalty === 1) {
|
||||
const dispersionMod = dispersion - characteristics.dispersion;
|
||||
const dispersionModDischarged =
|
||||
dispersionMod * Math.exp(-deltaT / characteristics.aimTime) -
|
||||
dispersionMod;
|
||||
// if (penalty === 1) {
|
||||
// const dispersionMod = dispersion - characteristics.dispersion;
|
||||
// const dispersionModDischarged =
|
||||
// dispersionMod * Math.exp(-deltaT / characteristics.aimTime) -
|
||||
// dispersionMod;
|
||||
|
||||
dispersion += dispersionModDischarged;
|
||||
} else {
|
||||
dispersion = Math.max(characteristics.dispersion * penalty, dispersion);
|
||||
}
|
||||
// dispersion += dispersionModDischarged;
|
||||
// } else {
|
||||
// dispersion = Math.max(characteristics.dispersion * penalty, dispersion);
|
||||
// }
|
||||
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
lastPitch = pitch;
|
||||
lastYaw = yaw;
|
||||
// modelTransformEvent.emit({ pitch, yaw });
|
||||
// lastPitch = pitch;
|
||||
// lastYaw = yaw;
|
||||
|
||||
const gunDirection = new Vector3(0, 0, -1)
|
||||
.applyAxisAngle(I_HAT, pitch)
|
||||
.applyAxisAngle(J_HAT, yaw);
|
||||
// const gunDirection = new Vector3(0, 0, -1)
|
||||
// .applyAxisAngle(I_HAT, pitch)
|
||||
// .applyAxisAngle(J_HAT, yaw);
|
||||
|
||||
raycaster.set(shellOrigin, gunDirection);
|
||||
// raycaster.set(shellOrigin, gunDirection);
|
||||
|
||||
const gunIntersections = raycaster.intersectObjects(
|
||||
playground.current.children,
|
||||
true,
|
||||
);
|
||||
// const gunIntersections = raycaster.intersectObjects(
|
||||
// playground.current.children,
|
||||
// true,
|
||||
// );
|
||||
|
||||
let gunTarget: Vector3;
|
||||
// let gunTarget: Vector3;
|
||||
|
||||
if (gunIntersections.length === 0) {
|
||||
gunTarget = shellOrigin
|
||||
.clone()
|
||||
.add(gunDirection.multiplyScalar(characteristics.shellRange));
|
||||
} else {
|
||||
gunTarget = gunIntersections[0].point;
|
||||
}
|
||||
// if (gunIntersections.length === 0) {
|
||||
// gunTarget = shellOrigin
|
||||
// .clone()
|
||||
// .add(gunDirection.multiplyScalar(characteristics.shellRange));
|
||||
// } else {
|
||||
// gunTarget = gunIntersections[0].point;
|
||||
// }
|
||||
|
||||
targetCircleWrapper.current.position.copy(gunTarget);
|
||||
// targetCircleWrapper.current.position.copy(gunTarget);
|
||||
|
||||
const distanceToTarget = gunTarget.distanceTo(shellOrigin);
|
||||
const dispersionRadius = dispersion * (distanceToTarget / 100);
|
||||
const dispersionSize = 2 * dispersionRadius;
|
||||
const targetDistanceFromCamera = gunTarget.distanceTo(camera.position);
|
||||
const angularSize =
|
||||
2 * Math.atan(dispersionSize / (2 * targetDistanceFromCamera));
|
||||
const verticalScreenSize =
|
||||
2 * Math.tan(degToRad((camera as PerspectiveCamera).fov) / 2);
|
||||
const screenSpaceSize =
|
||||
gl.domElement.clientHeight * (angularSize / verticalScreenSize);
|
||||
const targetCircleDotsSize = `${screenSpaceSize}px`;
|
||||
// const distanceToTarget = gunTarget.distanceTo(shellOrigin);
|
||||
// const dispersionRadius = dispersion * (distanceToTarget / 100);
|
||||
// const dispersionSize = 2 * dispersionRadius;
|
||||
// const targetDistanceFromCamera = gunTarget.distanceTo(camera.position);
|
||||
// const angularSize =
|
||||
// 2 * Math.atan(dispersionSize / (2 * targetDistanceFromCamera));
|
||||
// const verticalScreenSize =
|
||||
// 2 * Math.tan(degToRad((camera as PerspectiveCamera).fov) / 2);
|
||||
// const screenSpaceSize =
|
||||
// gl.domElement.clientHeight * (angularSize / verticalScreenSize);
|
||||
// const targetCircleDotsSize = `${screenSpaceSize}px`;
|
||||
|
||||
clientTargetCircle.current.style.width = targetCircleDotsSize;
|
||||
clientTargetCircle.current.style.height = targetCircleDotsSize;
|
||||
serverTargetCircle.current.style.width = targetCircleDotsSize;
|
||||
serverTargetCircle.current.style.height = targetCircleDotsSize;
|
||||
});
|
||||
// clientTargetCircle.current.style.width = targetCircleDotsSize;
|
||||
// clientTargetCircle.current.style.height = targetCircleDotsSize;
|
||||
// serverTargetCircle.current.style.width = targetCircleDotsSize;
|
||||
// serverTargetCircle.current.style.height = targetCircleDotsSize;
|
||||
// });
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -242,7 +226,7 @@ export function SceneProps() {
|
||||
</mesh>
|
||||
)}
|
||||
|
||||
{display === TankopediaDisplay.ShootingRange && (
|
||||
{/* {display === TankopediaDisplay.ShootingRange && (
|
||||
<>
|
||||
<group ref={targetCircleWrapper}>
|
||||
<Html center occlude={false} zIndexRange={[0, 0]}>
|
||||
@@ -272,7 +256,7 @@ export function SceneProps() {
|
||||
</group>
|
||||
</group>
|
||||
</>
|
||||
)}
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
+2
@@ -18,6 +18,8 @@ import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent
|
||||
import { ModelTankWrapper } from '../../../../../Armor/components/ModelTankWrapper';
|
||||
|
||||
export function TankModel() {
|
||||
return null;
|
||||
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const duelStore = Duel.useStore();
|
||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||
|
||||
+89
-101
@@ -1,19 +1,7 @@
|
||||
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import {
|
||||
forwardRef,
|
||||
Suspense,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelTransformEvent } from '../../../../../core/blitzkit/modelTransform';
|
||||
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
|
||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||
import { forwardRef, Suspense, useImperativeHandle, useRef } from 'react';
|
||||
import { useOnScreen } from '../../../../../hooks/useOnScreen';
|
||||
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||
import { Duel } from '../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
|
||||
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
||||
@@ -40,16 +28,16 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
({ thicknessRange, naked }, ref) => {
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||
const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
||||
const protagonistTurret = Duel.use((state) => state.protagonist.turret);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[protagonistTurret.id];
|
||||
const gunModelDefinition =
|
||||
turretModelDefinition.guns[protagonistGun.gun_type!.value.base.id];
|
||||
// const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||
// const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||
// const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
||||
// const protagonistTurret = Duel.use((state) => state.protagonist.turret);
|
||||
// const mutateDuel = Duel.useMutation();
|
||||
// const tankModelDefinition = useTankModelDefinition();
|
||||
// const turretModelDefinition =
|
||||
// tankModelDefinition.turrets[protagonistTurret.id];
|
||||
// const gunModelDefinition =
|
||||
// turretModelDefinition.guns[protagonistGun.gun_type!.value.base.id];
|
||||
const display = TankopediaEphemeral.use((state) => state.display);
|
||||
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
||||
(state) => state.hideTankModelUnderArmor,
|
||||
@@ -72,97 +60,97 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
function handlePoseEvent(pose: Pose) {
|
||||
switch (pose) {
|
||||
case Pose.HullDown: {
|
||||
mutateDuel((draft) => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
-Infinity,
|
||||
0,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
);
|
||||
// useEffect(() => {
|
||||
// function handlePoseEvent(pose: Pose) {
|
||||
// switch (pose) {
|
||||
// case Pose.HullDown: {
|
||||
// mutateDuel((draft) => {
|
||||
// const [pitch, yaw] = applyPitchYawLimits(
|
||||
// -Infinity,
|
||||
// 0,
|
||||
// gunModelDefinition.pitch,
|
||||
// turretModelDefinition.yaw,
|
||||
// hasImprovedVerticalStabilizer,
|
||||
// hasDownImprovedVerticalStabilizer,
|
||||
// );
|
||||
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
draft.protagonist.pitch = pitch;
|
||||
draft.protagonist.yaw = yaw;
|
||||
});
|
||||
// modelTransformEvent.emit({ pitch, yaw });
|
||||
// draft.protagonist.pitch = pitch;
|
||||
// draft.protagonist.yaw = yaw;
|
||||
// });
|
||||
|
||||
break;
|
||||
}
|
||||
// break;
|
||||
// }
|
||||
|
||||
case Pose.FaceHug: {
|
||||
mutateDuel((draft) => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
Infinity,
|
||||
0,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
);
|
||||
// case Pose.FaceHug: {
|
||||
// mutateDuel((draft) => {
|
||||
// const [pitch, yaw] = applyPitchYawLimits(
|
||||
// Infinity,
|
||||
// 0,
|
||||
// gunModelDefinition.pitch,
|
||||
// turretModelDefinition.yaw,
|
||||
// hasImprovedVerticalStabilizer,
|
||||
// );
|
||||
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
draft.protagonist.pitch = pitch;
|
||||
draft.protagonist.yaw = yaw;
|
||||
});
|
||||
// modelTransformEvent.emit({ pitch, yaw });
|
||||
// draft.protagonist.pitch = pitch;
|
||||
// draft.protagonist.yaw = yaw;
|
||||
// });
|
||||
|
||||
break;
|
||||
}
|
||||
// break;
|
||||
// }
|
||||
|
||||
case Pose.Default:
|
||||
mutateDuel((draft) => {
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
0,
|
||||
0,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
);
|
||||
// case Pose.Default:
|
||||
// mutateDuel((draft) => {
|
||||
// const [pitch, yaw] = applyPitchYawLimits(
|
||||
// 0,
|
||||
// 0,
|
||||
// gunModelDefinition.pitch,
|
||||
// turretModelDefinition.yaw,
|
||||
// hasImprovedVerticalStabilizer,
|
||||
// );
|
||||
|
||||
modelTransformEvent.emit({ pitch, yaw });
|
||||
draft.protagonist.pitch = pitch;
|
||||
draft.protagonist.yaw = yaw;
|
||||
});
|
||||
// modelTransformEvent.emit({ pitch, yaw });
|
||||
// draft.protagonist.pitch = pitch;
|
||||
// draft.protagonist.yaw = yaw;
|
||||
// });
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
poseEvent.on(handlePoseEvent);
|
||||
// poseEvent.on(handlePoseEvent);
|
||||
|
||||
return () => {
|
||||
poseEvent.off(handlePoseEvent);
|
||||
};
|
||||
});
|
||||
// return () => {
|
||||
// poseEvent.off(handlePoseEvent);
|
||||
// };
|
||||
// });
|
||||
|
||||
useEffect(() => {
|
||||
mutateDuel((draft) => {
|
||||
[draft.protagonist.pitch, draft.protagonist.yaw] = applyPitchYawLimits(
|
||||
draft.protagonist.pitch,
|
||||
draft.protagonist.yaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
);
|
||||
});
|
||||
}, [protagonistGun, protagonistTurret]);
|
||||
// useEffect(() => {
|
||||
// mutateDuel((draft) => {
|
||||
// [draft.protagonist.pitch, draft.protagonist.yaw] = applyPitchYawLimits(
|
||||
// draft.protagonist.pitch,
|
||||
// draft.protagonist.yaw,
|
||||
// gunModelDefinition.pitch,
|
||||
// turretModelDefinition.yaw,
|
||||
// hasImprovedVerticalStabilizer,
|
||||
// );
|
||||
// });
|
||||
// }, [protagonistGun, protagonistTurret]);
|
||||
|
||||
useEffect(() => {
|
||||
if (display !== TankopediaDisplay.DynamicArmor) {
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}
|
||||
// useEffect(() => {
|
||||
// if (display !== TankopediaDisplay.DynamicArmor) {
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.shot = undefined;
|
||||
// });
|
||||
// }
|
||||
|
||||
if (display !== TankopediaDisplay.StaticArmor) {
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}
|
||||
}, [display]);
|
||||
// if (display !== TankopediaDisplay.StaticArmor) {
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.highlightArmor = undefined;
|
||||
// });
|
||||
// }
|
||||
// }, [display]);
|
||||
|
||||
return (
|
||||
<Canvas
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import { useRef } from 'react';
|
||||
import { Suspense, useRef } from 'react';
|
||||
import { NAVBAR_HEIGHT } from '../../../constants/navbar';
|
||||
import { Var } from '../../../core/radix/var';
|
||||
import { useFullScreen } from '../../../hooks/useFullScreen';
|
||||
@@ -7,6 +7,8 @@ import { useLocale } from '../../../hooks/useLocale';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
||||
import { Options } from './components/Options';
|
||||
import { TankSandbox } from './components/TankSandbox';
|
||||
import { TankSandboxLoader } from './components/TankSandboxLoader';
|
||||
|
||||
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
@@ -137,12 +139,12 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
<Box width="100%" height="100%">
|
||||
{skeleton && <TankSandboxLoader id={protagonist.id} />}
|
||||
|
||||
{/* <Suspense fallback={<TankSandboxLoader id={protagonist.id} />}>
|
||||
<Suspense fallback={<TankSandboxLoader id={protagonist.id} />}>
|
||||
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
||||
</Suspense> */}
|
||||
</Suspense>
|
||||
</Box>
|
||||
|
||||
{/* <Options canvas={canvas} thicknessRange={thicknessRange} /> */}
|
||||
<Options canvas={canvas} thicknessRange={thicknessRange} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -134,7 +134,7 @@ export const TankopediaEphemeral = createContextualStore((armor: TankArmor) => {
|
||||
armor,
|
||||
controlsEnabled: true,
|
||||
xpMultiplier: 1,
|
||||
display: TankopediaDisplay.Model,
|
||||
display: TankopediaDisplay.StaticArmor,
|
||||
})),
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user