diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx
index 32c0fde34..0d165b513 100644
--- a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx
+++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx
@@ -124,11 +124,15 @@ export const TankModel = memo(() => {
event.stopPropagation();
}
- return jsxTree(node, {
- castShadow: true,
- receiveShadow: true,
- onPointerDown,
- });
+ return jsxTree(
+ node,
+ {
+ castShadow: true,
+ receiveShadow: true,
+ onPointerDown,
+ },
+ node.uuid,
+ );
})}
@@ -189,11 +193,15 @@ export const TankModel = memo(() => {
window.removeEventListener('pointerup', handlePointerUp);
}
- return jsxTree(node, {
- castShadow: true,
- receiveShadow: true,
- onPointerDown,
- });
+ return jsxTree(
+ node,
+ {
+ castShadow: true,
+ receiveShadow: true,
+ onPointerDown,
+ },
+ node.uuid,
+ );
})}
@@ -255,11 +263,15 @@ export const TankModel = memo(() => {
window.removeEventListener('pointerup', handlePointerUp);
}
- return jsxTree(node, {
- castShadow: true,
- receiveShadow: true,
- onPointerDown,
- });
+ return jsxTree(
+ node,
+ {
+ castShadow: true,
+ receiveShadow: true,
+ onPointerDown,
+ },
+ node.uuid,
+ );
})}
diff --git a/src/components/Armor/components/CoreArmorSceneComponent/index.tsx b/src/components/Armor/components/CoreArmorSceneComponent/index.tsx
index 079f04dca..3a67eaef1 100644
--- a/src/components/Armor/components/CoreArmorSceneComponent/index.tsx
+++ b/src/components/Armor/components/CoreArmorSceneComponent/index.tsx
@@ -153,14 +153,22 @@ export function CoreArmorSceneComponent({
return (
<>
- {jsxTree(node, {
- renderOrder: 0,
- material: excludeMaterial,
- })}
- {jsxTree(node, {
- renderOrder: 1,
- material,
- })}
+ {jsxTree(
+ node,
+ {
+ renderOrder: 0,
+ material: excludeMaterial,
+ },
+ node.uuid,
+ )}
+ {jsxTree(
+ node,
+ {
+ renderOrder: 1,
+ material,
+ },
+ node.uuid,
+ )}
>
);
}
diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx
index 8f3b12d16..dd11dfd41 100644
--- a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx
+++ b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx
@@ -107,26 +107,34 @@ export function SpacedArmorSubExternal({
return (
<>
- {jsxTree(node, {
- renderOrder: 3,
- material: new MeshBasicMaterial({
- colorWrite: false,
- depthTest: true,
- depthWrite: true,
- ...(clip ? { clippingPlanes: [clip] } : {}),
- }),
- onClick() {},
- userData: {
- type: ArmorType.External,
- thickness,
- variant,
- } satisfies ArmorUserData,
- })}
+ {jsxTree(
+ node,
+ {
+ renderOrder: 3,
+ material: new MeshBasicMaterial({
+ colorWrite: false,
+ depthTest: true,
+ depthWrite: true,
+ ...(clip ? { clippingPlanes: [clip] } : {}),
+ }),
+ onClick() {},
+ userData: {
+ type: ArmorType.External,
+ thickness,
+ variant,
+ } satisfies ArmorUserData,
+ },
+ node.uuid,
+ )}
- {jsxTree(node, {
- renderOrder: 4,
- material,
- })}
+ {jsxTree(
+ node,
+ {
+ renderOrder: 4,
+ material,
+ },
+ node.uuid,
+ )}
>
);
}
diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx
index c283f3ce2..fe181ff15 100644
--- a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx
+++ b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx
@@ -113,20 +113,28 @@ export function SpacedArmorSubSpaced({
return (
<>
- {jsxTree(node, {
- renderOrder: 2,
- material,
- onClick() {},
- userData: {
- type: ArmorType.Spaced,
- thickness,
- } satisfies ArmorUserData,
- })}
+ {jsxTree(
+ node,
+ {
+ renderOrder: 2,
+ material,
+ onClick() {},
+ userData: {
+ type: ArmorType.Spaced,
+ thickness,
+ } satisfies ArmorUserData,
+ },
+ node.uuid,
+ )}
- {jsxTree(node, {
- renderOrder: 5,
- material: depthWriteMaterial,
- })}
+ {jsxTree(
+ node,
+ {
+ renderOrder: 5,
+ material: depthWriteMaterial,
+ },
+ node.uuid,
+ )}
>
);
}
diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx
index 607be66ec..338ce2ee0 100644
--- a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx
+++ b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx
@@ -81,251 +81,265 @@ export function SpacedArmorSceneComponent({
return (
<>
{props.type === ArmorType.Core &&
- jsxTree(node, {
- renderOrder: 0,
- material: omitMaterial,
- userData: {
- type: ArmorType.Core,
- thickness,
- } satisfies ArmorUserData,
+ jsxTree(
+ node,
+ {
+ renderOrder: 0,
+ material: omitMaterial,
+ userData: {
+ type: ArmorType.Core,
+ thickness,
+ } satisfies ArmorUserData,
- async onClick(event) {
- event.stopPropagation();
+ async onClick(event) {
+ event.stopPropagation();
- const duel = useDuel.getState();
- const hasCalibratedShells = await hasEquipment(
- 103,
- duel.antagonist!.tank.equipment,
- duel.antagonist!.equipmentMatrix,
- );
- const hasEnhancedArmor = await hasEquipment(
- 110,
- duel.protagonist!.tank.equipment,
- duel.protagonist!.equipmentMatrix,
- );
- const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1;
- const shell = useDuel.getState().antagonist!.shell;
- const explosive = isExplosive(shell.type);
- const splashing = canSplash(shell.type);
- const penetration =
- resolveNearPenetration(shell.penetration) *
- (hasCalibratedShells
- ? isExplosive(shell.type)
- ? 1.1
- : 1.05
- : 1);
- const shot: Shot = {
- layers: [],
- point: event.point,
- }; // TODO: type this
- let remainingPenetration = penetration;
-
- const shellNormal = camera.position
- .clone()
- .sub(event.point)
- .normalize();
-
- pushLogs(shellNormal, filterIntersections(event.intersections), 0);
-
- function filterIntersections(intersectionsRaw: Intersection[]) {
- const showSpacedArmor =
- useTankopediaPersistent.getState().model.visual.showSpacedArmor;
- const intersectionsTyped = intersectionsRaw.filter(
- ({ object }) => object.userData.type !== undefined,
- ) as ArmorMeshIntersection[];
- const intersectionsFiltered = intersectionsTyped.filter(
- ({ object }) =>
- showSpacedArmor
- ? true
- : object.userData.type === ArmorType.Core,
+ const duel = useDuel.getState();
+ const hasCalibratedShells = await hasEquipment(
+ 103,
+ duel.antagonist!.tank.equipment,
+ duel.antagonist!.equipmentMatrix,
);
- const intersectionsWithDuplicates = intersectionsFiltered.slice(
+ const hasEnhancedArmor = await hasEquipment(
+ 110,
+ duel.protagonist!.tank.equipment,
+ duel.protagonist!.equipmentMatrix,
+ );
+ const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1;
+ const shell = useDuel.getState().antagonist!.shell;
+ const explosive = isExplosive(shell.type);
+ const splashing = canSplash(shell.type);
+ const penetration =
+ resolveNearPenetration(shell.penetration) *
+ (hasCalibratedShells
+ ? isExplosive(shell.type)
+ ? 1.1
+ : 1.05
+ : 1);
+ const shot: Shot = {
+ layers: [],
+ point: event.point,
+ }; // TODO: type this
+ let remainingPenetration = penetration;
+
+ const shellNormal = camera.position
+ .clone()
+ .sub(event.point)
+ .normalize();
+
+ pushLogs(
+ shellNormal,
+ filterIntersections(event.intersections),
0,
- intersectionsFiltered.findIndex(
- ({ object }) => object.userData.type === ArmorType.Core,
- ) + 1,
);
- const firstGunIntersectionIndex =
- intersectionsWithDuplicates.findIndex(
+
+ function filterIntersections(intersectionsRaw: Intersection[]) {
+ const showSpacedArmor =
+ useTankopediaPersistent.getState().model.visual
+ .showSpacedArmor;
+ const intersectionsTyped = intersectionsRaw.filter(
+ ({ object }) => object.userData.type !== undefined,
+ ) as ArmorMeshIntersection[];
+ const intersectionsFiltered = intersectionsTyped.filter(
({ object }) =>
- object.userData.type === ArmorType.External &&
- object.userData.variant === 'gun',
+ showSpacedArmor
+ ? true
+ : object.userData.type === ArmorType.Core,
);
- const firstTrackIntersectionIndex =
- intersectionsWithDuplicates.findIndex(
- ({ object }) =>
- object.userData.type === ArmorType.External &&
- object.userData.variant === 'track',
+ const intersectionsWithDuplicates = intersectionsFiltered.slice(
+ 0,
+ intersectionsFiltered.findIndex(
+ ({ object }) => object.userData.type === ArmorType.Core,
+ ) + 1,
);
- let intersections = intersectionsWithDuplicates;
-
- if (firstGunIntersectionIndex !== -1) {
- intersections = intersections.filter(({ object }, index) => {
- return object.userData.type === ArmorType.External &&
- object.userData.variant === 'gun'
- ? index === firstGunIntersectionIndex
- : true;
- });
- }
- if (firstTrackIntersectionIndex !== -1) {
- intersections = intersections.filter(({ object }, index) => {
- return object.userData.type === ArmorType.External &&
- object.userData.variant === 'track'
- ? index === firstTrackIntersectionIndex
- : true;
- });
- }
-
- return intersections;
- }
-
- function pushLogs(
- shellNormal: Vector3,
- intersections: ArmorMeshIntersection[],
- startingIndex: number,
- ) {
- let layerIndex = startingIndex;
- let loopIndex = 0;
-
- if (layerIndex !== 0 && intersections.length > 0) {
- const distance = (
- shot.layers.at(-1) as ShotLayerBase
- ).point.distanceTo(intersections[0].point);
- if (explosive && !splashing)
- remainingPenetration -= 0.5 * remainingPenetration * distance;
- const blocked = remainingPenetration < 0;
-
- shot.layers.push({
- type: null,
- status: blocked ? 'blocked' : 'penetration',
- distance,
- });
-
- if (blocked) return;
- }
-
- for (const intersection of intersections) {
- const surfaceNormal = intersection
- .normal!.clone()
- .applyQuaternion(
- event.object.getWorldQuaternion(new Quaternion()),
+ const firstGunIntersectionIndex =
+ intersectionsWithDuplicates.findIndex(
+ ({ object }) =>
+ object.userData.type === ArmorType.External &&
+ object.userData.variant === 'gun',
);
- const thickness =
- thicknessCoefficient * intersection.object.userData.thickness;
+ const firstTrackIntersectionIndex =
+ intersectionsWithDuplicates.findIndex(
+ ({ object }) =>
+ object.userData.type === ArmorType.External &&
+ object.userData.variant === 'track',
+ );
+ let intersections = intersectionsWithDuplicates;
- if (layerIndex !== 0 && startingIndex === 0) {
- const previousIntersection = intersections[layerIndex - 1];
- const distance =
- intersection.distance - previousIntersection.distance;
+ if (firstGunIntersectionIndex !== -1) {
+ intersections = intersections.filter(({ object }, index) => {
+ return object.userData.type === ArmorType.External &&
+ object.userData.variant === 'gun'
+ ? index === firstGunIntersectionIndex
+ : true;
+ });
+ }
+ if (firstTrackIntersectionIndex !== -1) {
+ intersections = intersections.filter(({ object }, index) => {
+ return object.userData.type === ArmorType.External &&
+ object.userData.variant === 'track'
+ ? index === firstTrackIntersectionIndex
+ : true;
+ });
+ }
+
+ return intersections;
+ }
+
+ function pushLogs(
+ shellNormal: Vector3,
+ intersections: ArmorMeshIntersection[],
+ startingIndex: number,
+ ) {
+ let layerIndex = startingIndex;
+ let loopIndex = 0;
+
+ if (layerIndex !== 0 && intersections.length > 0) {
+ const distance = (
+ shot.layers.at(-1) as ShotLayerBase
+ ).point.distanceTo(intersections[0].point);
if (explosive && !splashing)
remainingPenetration -=
0.5 * remainingPenetration * distance;
- const wasted = remainingPenetration < 0;
-
- shot.layers.push({
- type: null,
- status: wasted ? 'blocked' : 'penetration',
- distance,
- });
-
- if (wasted && !splashing) return;
- }
-
- if (intersection.object.userData.type === ArmorType.External) {
- remainingPenetration -= thickness;
const blocked = remainingPenetration < 0;
shot.layers.push({
- type: ArmorType.External,
- index: layerIndex,
- shellNormal,
- surfaceNormal,
- point: intersection.point,
- thickness: intersection.object.userData.thickness,
+ type: null,
status: blocked ? 'blocked' : 'penetration',
+ distance,
});
- if (splashing) {
- remainingPenetration = Math.max(0, remainingPenetration);
- } else if (blocked) {
- break;
- }
- } else {
- const type = intersection.object.userData.type;
- const angle = surfaceNormal.angleTo(shellNormal);
- const ricochet = degToRad(shell.ricochet ?? 90);
- const normalization = degToRad(shell.normalization ?? 0);
- const threeCalibersRule =
- shell.caliber > thickness * 3 || layerIndex > 0;
- const twoCalibersRule = shell.caliber > thickness * 2;
- const finalNormalization = twoCalibersRule
- ? (1.4 * normalization * shell.caliber) / (2.0 * thickness)
- : normalization;
- const finalThickness =
- thickness === 0
- ? 0
- : thickness /
- Math.cos(Math.max(0, angle - finalNormalization));
+ if (blocked) return;
+ }
+
+ for (const intersection of intersections) {
+ const surfaceNormal = intersection
+ .normal!.clone()
+ .applyQuaternion(
+ event.object.getWorldQuaternion(new Quaternion()),
+ );
+ const thickness =
+ thicknessCoefficient *
+ intersection.object.userData.thickness;
+
+ if (layerIndex !== 0 && startingIndex === 0) {
+ const previousIntersection = intersections[layerIndex - 1];
+ const distance =
+ intersection.distance - previousIntersection.distance;
+ if (explosive && !splashing)
+ remainingPenetration -=
+ 0.5 * remainingPenetration * distance;
+ const wasted = remainingPenetration < 0;
- if (!threeCalibersRule && angle >= ricochet) {
shot.layers.push({
- type,
- index: layerIndex,
- shellNormal,
- surfaceNormal,
- point: intersection.point,
- thickness,
- thicknessAngled: finalThickness,
- angle,
- status: 'ricochet',
+ type: null,
+ status: wasted ? 'blocked' : 'penetration',
+ distance,
});
- remainingPenetration *= 0.75;
+ if (wasted && !splashing) return;
+ }
- const raycaster = new Raycaster();
- const newShellNormal = shellNormal
- .clone()
- .reflect(surfaceNormal);
- const newCameraNormal = newShellNormal
- .clone()
- .multiplyScalar(-1);
-
- raycaster.set(intersection.point, newCameraNormal);
- pushLogs(
- newShellNormal,
- filterIntersections(
- raycaster.intersectObjects(scene.children, true),
- ),
- layerIndex + 1,
- );
-
- break;
- } else {
- remainingPenetration -= finalThickness;
+ if (
+ intersection.object.userData.type === ArmorType.External
+ ) {
+ remainingPenetration -= thickness;
const blocked = remainingPenetration < 0;
shot.layers.push({
- type,
+ type: ArmorType.External,
index: layerIndex,
shellNormal,
surfaceNormal,
point: intersection.point,
- thickness,
- thicknessAngled: finalThickness,
- angle,
+ thickness: intersection.object.userData.thickness,
status: blocked ? 'blocked' : 'penetration',
});
+
+ if (splashing) {
+ remainingPenetration = Math.max(0, remainingPenetration);
+ } else if (blocked) {
+ break;
+ }
+ } else {
+ const type = intersection.object.userData.type;
+ const angle = surfaceNormal.angleTo(shellNormal);
+ const ricochet = degToRad(shell.ricochet ?? 90);
+ const normalization = degToRad(shell.normalization ?? 0);
+ const threeCalibersRule =
+ shell.caliber > thickness * 3 || layerIndex > 0;
+ const twoCalibersRule = shell.caliber > thickness * 2;
+ const finalNormalization = twoCalibersRule
+ ? (1.4 * normalization * shell.caliber) /
+ (2.0 * thickness)
+ : normalization;
+ const finalThickness =
+ thickness === 0
+ ? 0
+ : thickness /
+ Math.cos(Math.max(0, angle - finalNormalization));
+
+ if (!threeCalibersRule && angle >= ricochet) {
+ shot.layers.push({
+ type,
+ index: layerIndex,
+ shellNormal,
+ surfaceNormal,
+ point: intersection.point,
+ thickness,
+ thicknessAngled: finalThickness,
+ angle,
+ status: 'ricochet',
+ });
+
+ remainingPenetration *= 0.75;
+
+ const raycaster = new Raycaster();
+ const newShellNormal = shellNormal
+ .clone()
+ .reflect(surfaceNormal);
+ const newCameraNormal = newShellNormal
+ .clone()
+ .multiplyScalar(-1);
+
+ raycaster.set(intersection.point, newCameraNormal);
+ pushLogs(
+ newShellNormal,
+ filterIntersections(
+ raycaster.intersectObjects(scene.children, true),
+ ),
+ layerIndex + 1,
+ );
+
+ break;
+ } else {
+ remainingPenetration -= finalThickness;
+ const blocked = remainingPenetration < 0;
+
+ shot.layers.push({
+ type,
+ index: layerIndex,
+ shellNormal,
+ surfaceNormal,
+ point: intersection.point,
+ thickness,
+ thicknessAngled: finalThickness,
+ angle,
+ status: blocked ? 'blocked' : 'penetration',
+ });
+ }
}
+
+ layerIndex++;
+ loopIndex++;
}
-
- layerIndex++;
- loopIndex++;
}
- }
- useTankopediaTemporary.setState({ shot });
+ useTankopediaTemporary.setState({ shot });
+ },
},
- })}
+ node.uuid,
+ )}
{props.type === ArmorType.Spaced && (