mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: armor display
This commit is contained in:
1 parent
6aa4686c30
commit
a7d8f08522
8 files changed
+186
-18
No files matched your search
@@ -6,7 +6,10 @@ import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
|
|||||||
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
|
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
|
||||||
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
|
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
|
||||||
import commitMultipleFiles from '../../src/core/blitzkrieg/commitMultipleFiles';
|
import commitMultipleFiles from '../../src/core/blitzkrieg/commitMultipleFiles';
|
||||||
import { ModelDefinitions } from '../../src/core/blitzkrieg/modelDefinitions';
|
import {
|
||||||
|
ModelArmor,
|
||||||
|
ModelDefinitions,
|
||||||
|
} from '../../src/core/blitzkrieg/modelDefinitions';
|
||||||
import {
|
import {
|
||||||
GunDefinition,
|
GunDefinition,
|
||||||
ShellType,
|
ShellType,
|
||||||
@@ -40,14 +43,20 @@ type ShellDefinitionsList = Record<
|
|||||||
> & {
|
> & {
|
||||||
icons: Record<string, string>;
|
icons: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
type VehicleDefinitionArmor = Record<
|
||||||
|
string,
|
||||||
|
number | { vehicleDamageFactor: 0; '#text': number }
|
||||||
|
>;
|
||||||
interface VehicleDefinitions {
|
interface VehicleDefinitions {
|
||||||
invisibility: { moving: number; still: number; firePenalty: number };
|
invisibility: { moving: number; still: number; firePenalty: number };
|
||||||
hull: {
|
hull: {
|
||||||
|
armor: VehicleDefinitionArmor;
|
||||||
turretPositions: { turret: string };
|
turretPositions: { turret: string };
|
||||||
turretInitialRotation?: { yaw: 0; pitch: 6.5; roll: 0 };
|
turretInitialRotation?: { yaw: 0; pitch: 6.5; roll: 0 };
|
||||||
};
|
};
|
||||||
turrets0: {
|
turrets0: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
armor: VehicleDefinitionArmor;
|
||||||
userString: number;
|
userString: number;
|
||||||
level: number;
|
level: number;
|
||||||
yawLimits: string | string[];
|
yawLimits: string | string[];
|
||||||
@@ -55,6 +64,7 @@ interface VehicleDefinitions {
|
|||||||
models: { undamaged: string };
|
models: { undamaged: string };
|
||||||
guns: {
|
guns: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
armor: VehicleDefinitionArmor;
|
||||||
reloadTime: number;
|
reloadTime: number;
|
||||||
maxAmmo: number;
|
maxAmmo: number;
|
||||||
extraPitchLimits?: {
|
extraPitchLimits?: {
|
||||||
@@ -174,6 +184,28 @@ export async function buildDefinitions() {
|
|||||||
.map(Number) as Vector3Tuple;
|
.map(Number) as Vector3Tuple;
|
||||||
const tankId = toUniqueId(nation, tank.id);
|
const tankId = toUniqueId(nation, tank.id);
|
||||||
const tankTags = tank.tags.split(' ');
|
const tankTags = tank.tags.split(' ');
|
||||||
|
const hullArmor: ModelArmor = { thickness: {} };
|
||||||
|
|
||||||
|
Object.keys(tankDefinition.root.hull.armor)
|
||||||
|
.filter((name) => name.startsWith('armor_'))
|
||||||
|
.forEach((name) => {
|
||||||
|
const armorIdString = name.match(/armor_(\d+)/)?.[1];
|
||||||
|
|
||||||
|
if (armorIdString === undefined) {
|
||||||
|
throw new SyntaxError(`Invalid armor id: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const armorId = parseInt(armorIdString);
|
||||||
|
const armorRaw = tankDefinition.root.hull.armor[name];
|
||||||
|
|
||||||
|
if (typeof armorRaw === 'number') {
|
||||||
|
hullArmor.thickness[armorId] = armorRaw;
|
||||||
|
} else {
|
||||||
|
if (!hullArmor.spaced) hullArmor.spaced = [];
|
||||||
|
hullArmor.thickness[armorId] = armorRaw['#text'];
|
||||||
|
hullArmor.spaced.push(armorId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
tankDefinitions[tankId] = {
|
tankDefinitions[tankId] = {
|
||||||
id: tankId,
|
id: tankId,
|
||||||
@@ -202,6 +234,7 @@ export async function buildDefinitions() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
modelDefinitions[tankId] = {
|
modelDefinitions[tankId] = {
|
||||||
|
armor: hullArmor,
|
||||||
turretOrigin,
|
turretOrigin,
|
||||||
turretRotation: tankDefinition.root.hull.turretInitialRotation
|
turretRotation: tankDefinition.root.hull.turretInitialRotation
|
||||||
? {
|
? {
|
||||||
@@ -234,6 +267,28 @@ export async function buildDefinitions() {
|
|||||||
)
|
)
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.map(Number) as Vector3Tuple;
|
.map(Number) as Vector3Tuple;
|
||||||
|
const turretArmor: ModelArmor = { thickness: {} };
|
||||||
|
|
||||||
|
Object.keys(turret.armor)
|
||||||
|
.filter((name) => name.startsWith('armor_'))
|
||||||
|
.forEach((name) => {
|
||||||
|
const armorIdString = name.match(/armor_(\d+)/)?.[1];
|
||||||
|
|
||||||
|
if (armorIdString === undefined) {
|
||||||
|
throw new SyntaxError(`Invalid armor id: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const armorId = parseInt(armorIdString);
|
||||||
|
const armorRaw = turret.armor[name];
|
||||||
|
|
||||||
|
if (typeof armorRaw === 'number') {
|
||||||
|
turretArmor.thickness[armorId] = armorRaw;
|
||||||
|
} else {
|
||||||
|
if (!turretArmor.spaced) turretArmor.spaced = [];
|
||||||
|
turretArmor.thickness[armorId] = armorRaw['#text'];
|
||||||
|
turretArmor.spaced.push(armorId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
tankDefinitions[tankId].turrets.push({
|
tankDefinitions[tankId].turrets.push({
|
||||||
id: turretId,
|
id: turretId,
|
||||||
@@ -247,6 +302,7 @@ export async function buildDefinitions() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
modelDefinitions[tankId].turrets[turretId] = {
|
modelDefinitions[tankId].turrets[turretId] = {
|
||||||
|
armor: turretArmor,
|
||||||
gunOrigin,
|
gunOrigin,
|
||||||
model: turretModel,
|
model: turretModel,
|
||||||
yaw:
|
yaw:
|
||||||
@@ -301,6 +357,25 @@ export async function buildDefinitions() {
|
|||||||
? turretGunEntry.extraPitchLimits.transition
|
? turretGunEntry.extraPitchLimits.transition
|
||||||
: turretGunEntry.extraPitchLimits.transition.at(-1)!
|
: turretGunEntry.extraPitchLimits.transition.at(-1)!
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const gunArmor: ModelArmor = { thickness: {} };
|
||||||
|
|
||||||
|
Object.keys(turretGunEntry.armor)
|
||||||
|
.filter((name) => name.startsWith('armor_'))
|
||||||
|
.forEach((name) => {
|
||||||
|
const armorIdString = name.match(/armor_(\d+)/)?.[1];
|
||||||
|
if (armorIdString === undefined) {
|
||||||
|
throw new SyntaxError(`Invalid armor id: ${name}`);
|
||||||
|
}
|
||||||
|
const armorId = parseInt(armorIdString);
|
||||||
|
const armorRaw = turretGunEntry.armor[name];
|
||||||
|
if (typeof armorRaw === 'number') {
|
||||||
|
gunArmor.thickness[armorId] = armorRaw;
|
||||||
|
} else {
|
||||||
|
if (!gunArmor.spaced) gunArmor.spaced = [];
|
||||||
|
gunArmor.thickness[armorId] = armorRaw['#text'];
|
||||||
|
gunArmor.spaced.push(armorId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
tankDefinitions[tankId].turrets[turretIndex].guns.push({
|
tankDefinitions[tankId].turrets[turretIndex].guns.push({
|
||||||
id: gunId,
|
id: gunId,
|
||||||
@@ -314,6 +389,7 @@ export async function buildDefinitions() {
|
|||||||
} as GunDefinition);
|
} as GunDefinition);
|
||||||
|
|
||||||
modelDefinitions[tankId].turrets[turretId].guns[gunId] = {
|
modelDefinitions[tankId].turrets[turretId].guns[gunId] = {
|
||||||
|
armor: gunArmor,
|
||||||
model: gunModel,
|
model: gunModel,
|
||||||
pitch: {
|
pitch: {
|
||||||
min: gunPitch[0],
|
min: gunPitch[0],
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { Loader } from '../../../../components/Loader';
|
|||||||
import { Controls } from '../Control';
|
import { Controls } from '../Control';
|
||||||
import { RotationInputs } from '../RotationInputs';
|
import { RotationInputs } from '../RotationInputs';
|
||||||
import { SceneProps } from '../SceneProps';
|
import { SceneProps } from '../SceneProps';
|
||||||
|
import { TankArmor } from './components/TankArmor';
|
||||||
import { TankModel } from './components/TankModel';
|
import { TankModel } from './components/TankModel';
|
||||||
|
|
||||||
export function TankDisplay() {
|
export function TankDisplay() {
|
||||||
@@ -131,6 +132,7 @@ export function TankDisplay() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<TankModel ref={hullContainer} />
|
<TankModel ref={hullContainer} />
|
||||||
|
{mode === 'armor' && <TankArmor />}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { useLoader } from '@react-three/fiber';
|
||||||
|
import {
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import { Group, Mesh, Vector3 } from 'three';
|
||||||
|
import { GLTFLoader } from 'three-stdlib';
|
||||||
|
import { X_AXIS } from '../../../../../../../constants/axis';
|
||||||
|
import { asset } from '../../../../../../../core/blitzkrieg/asset';
|
||||||
|
import {
|
||||||
|
ModelDefinitions,
|
||||||
|
modelDefinitions,
|
||||||
|
} from '../../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
|
import { useTankopedia } from '../../../../../../../stores/tankopedia';
|
||||||
|
|
||||||
|
export const TankArmor = forwardRef<Group>((_props, ref) => {
|
||||||
|
const hullContainer = useRef<Group>(null);
|
||||||
|
const protagonist = useTankopedia((state) => {
|
||||||
|
if (!state.areTanksAssigned) return;
|
||||||
|
return state.protagonist;
|
||||||
|
});
|
||||||
|
const [awaitedModelDefinitions, setAwaitedModelDefinitions] = useState<
|
||||||
|
ModelDefinitions | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
setAwaitedModelDefinitions(await modelDefinitions);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => hullContainer.current!);
|
||||||
|
|
||||||
|
if (!awaitedModelDefinitions) return null;
|
||||||
|
if (!protagonist) return null;
|
||||||
|
|
||||||
|
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
||||||
|
const turretModelDefinition =
|
||||||
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
|
const turretOrigin = new Vector3(
|
||||||
|
tankModelDefinition.turretOrigin[0],
|
||||||
|
tankModelDefinition.turretOrigin[1],
|
||||||
|
-tankModelDefinition.turretOrigin[2],
|
||||||
|
).applyAxisAngle(X_AXIS, Math.PI / 2);
|
||||||
|
const gunOrigin = new Vector3(
|
||||||
|
turretModelDefinition.gunOrigin[0],
|
||||||
|
turretModelDefinition.gunOrigin[1],
|
||||||
|
-turretModelDefinition.gunOrigin[2],
|
||||||
|
).applyAxisAngle(X_AXIS, Math.PI / 2);
|
||||||
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
|
const gltf = useLoader(
|
||||||
|
GLTFLoader,
|
||||||
|
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group ref={hullContainer} rotation={[-Math.PI / 2, 0, 0]}>
|
||||||
|
{Object.values(gltf.nodes).map((node) => {
|
||||||
|
const armor = Number(node.name.match(/.+_armor_(\d+)/)?.[1]);
|
||||||
|
const position = new Vector3();
|
||||||
|
|
||||||
|
if (node.name.startsWith('turret_')) position.add(turretOrigin);
|
||||||
|
if (node.name.startsWith('gun_'))
|
||||||
|
position.add(turretOrigin).add(gunOrigin);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
key={node.uuid}
|
||||||
|
geometry={(node as Mesh).geometry}
|
||||||
|
position={position}
|
||||||
|
>
|
||||||
|
<meshBasicMaterial color="red" transparent opacity={0.5} />
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
});
|
||||||
+4
-4
@@ -1,14 +1,14 @@
|
|||||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||||
import { RefObject, forwardRef, useImperativeHandle, useRef } from 'react';
|
import { RefObject, forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
import { Group, Mesh, Object3D, Vector3 } from 'three';
|
import { Group, Mesh, Object3D, Vector3 } from 'three';
|
||||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import {
|
import {
|
||||||
InitialTurretRotation,
|
InitialTurretRotation,
|
||||||
PitchLimits,
|
PitchLimits,
|
||||||
YawLimits,
|
YawLimits,
|
||||||
} from '../../../../../core/blitzkrieg/modelDefinitions';
|
} from '../../../../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
import { resolveJsxTree } from '../../../../../core/blitzkrieg/resolveJsxTree';
|
import { resolveJsxTree } from '../../../../../../../../../core/blitzkrieg/resolveJsxTree';
|
||||||
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
|
import { normalizeAnglePI } from '../../../../../../../../../core/math/normalizeAngle180';
|
||||||
|
|
||||||
interface GunContainerProps {
|
interface GunContainerProps {
|
||||||
turretOrigin: Vector3;
|
turretOrigin: Vector3;
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||||
import { ReactNode, forwardRef, useImperativeHandle, useRef } from 'react';
|
import { ReactNode, forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
import { Group, Mesh, MeshStandardMaterial, Object3D, Vector2 } from 'three';
|
import { Group, Mesh, MeshStandardMaterial, Object3D, Vector2 } from 'three';
|
||||||
import { resolveJsxTree } from '../../../../../core/blitzkrieg/resolveJsxTree';
|
import { resolveJsxTree } from '../../../../../../../../../core/blitzkrieg/resolveJsxTree';
|
||||||
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
|
import { normalizeAnglePI } from '../../../../../../../../../core/math/normalizeAngle180';
|
||||||
|
|
||||||
interface HullContainerProps {
|
interface HullContainerProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
+4
-4
@@ -7,14 +7,14 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Euler, Group, Mesh, Object3D, Vector3 } from 'three';
|
import { Euler, Group, Mesh, Object3D, Vector3 } from 'three';
|
||||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import {
|
import {
|
||||||
InitialTurretRotation,
|
InitialTurretRotation,
|
||||||
PitchLimits,
|
PitchLimits,
|
||||||
YawLimits,
|
YawLimits,
|
||||||
} from '../../../../../core/blitzkrieg/modelDefinitions';
|
} from '../../../../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
import { resolveJsxTree } from '../../../../../core/blitzkrieg/resolveJsxTree';
|
import { resolveJsxTree } from '../../../../../../../../../core/blitzkrieg/resolveJsxTree';
|
||||||
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
|
import { normalizeAnglePI } from '../../../../../../../../../core/math/normalizeAngle180';
|
||||||
|
|
||||||
interface TurretContainerProps {
|
interface TurretContainerProps {
|
||||||
turretOrigin: Vector3;
|
turretOrigin: Vector3;
|
||||||
+7
-7
@@ -8,18 +8,18 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { Group, Vector3 } from 'three';
|
import { Group, Vector3 } from 'three';
|
||||||
import { GLTFLoader } from 'three-stdlib';
|
import { GLTFLoader } from 'three-stdlib';
|
||||||
import { X_AXIS } from '../../../../../../../constants/axis';
|
import { X_AXIS } from '../../../../../../../../constants/axis';
|
||||||
import { asset } from '../../../../../../../core/blitzkrieg/asset';
|
import { asset } from '../../../../../../../../core/blitzkrieg/asset';
|
||||||
import {
|
import {
|
||||||
ModelDefinitions,
|
ModelDefinitions,
|
||||||
modelDefinitions,
|
modelDefinitions,
|
||||||
} from '../../../../../../../core/blitzkrieg/modelDefinitions';
|
} from '../../../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
import mutateTankopedia, {
|
import mutateTankopedia, {
|
||||||
useTankopedia,
|
useTankopedia,
|
||||||
} from '../../../../../../../stores/tankopedia';
|
} from '../../../../../../../../stores/tankopedia';
|
||||||
import { GunContainer } from '../../GunContainer';
|
import { GunContainer } from './components/GunContainer';
|
||||||
import { HullContainer } from '../../HullContainer';
|
import { HullContainer } from './components/HullContainer';
|
||||||
import { TurretContainer } from '../../TurretContainer';
|
import { TurretContainer } from './components/TurretContainer';
|
||||||
|
|
||||||
export const TankModel = forwardRef<Group>((_props, ref) => {
|
export const TankModel = forwardRef<Group>((_props, ref) => {
|
||||||
// "TypeError: dispatcher.use is not a function"
|
// "TypeError: dispatcher.use is not a function"
|
||||||
@@ -2,7 +2,14 @@ import { Vector3Tuple } from 'three';
|
|||||||
import { asset } from './asset';
|
import { asset } from './asset';
|
||||||
|
|
||||||
export type ModelDefinitions = Record<number, ModelDefinition>;
|
export type ModelDefinitions = Record<number, ModelDefinition>;
|
||||||
|
|
||||||
|
export interface ModelArmor {
|
||||||
|
thickness: Record<number, number>;
|
||||||
|
spaced?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
interface ModelDefinition {
|
interface ModelDefinition {
|
||||||
|
armor: ModelArmor;
|
||||||
turretOrigin: Vector3Tuple;
|
turretOrigin: Vector3Tuple;
|
||||||
turretRotation?: InitialTurretRotation;
|
turretRotation?: InitialTurretRotation;
|
||||||
turrets: Record<number, TurretModelDefinition>;
|
turrets: Record<number, TurretModelDefinition>;
|
||||||
@@ -13,6 +20,7 @@ export interface InitialTurretRotation {
|
|||||||
roll: number;
|
roll: number;
|
||||||
}
|
}
|
||||||
interface TurretModelDefinition {
|
interface TurretModelDefinition {
|
||||||
|
armor: ModelArmor;
|
||||||
model: number;
|
model: number;
|
||||||
gunOrigin: Vector3Tuple;
|
gunOrigin: Vector3Tuple;
|
||||||
guns: Record<number, GunModelDefinition>;
|
guns: Record<number, GunModelDefinition>;
|
||||||
@@ -23,6 +31,7 @@ export interface YawLimits {
|
|||||||
max: number;
|
max: number;
|
||||||
}
|
}
|
||||||
interface GunModelDefinition {
|
interface GunModelDefinition {
|
||||||
|
armor: ModelArmor;
|
||||||
model: number;
|
model: number;
|
||||||
pitch: PitchLimits;
|
pitch: PitchLimits;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user