diff --git a/src/components/ArmorMesh/components/Armor/index.tsx b/src/components/ArmorMesh/components/Armor/index.tsx index 7263fb04a..11074b576 100644 --- a/src/components/ArmorMesh/components/Armor/index.tsx +++ b/src/components/ArmorMesh/components/Armor/index.tsx @@ -186,22 +186,18 @@ export function ArmorMesh({ if (mesh.current !== (coreArmorMesh as unknown as Mesh)) return; - let accumulatedThickness = 0; - const armorPiercingLayers = intersectionsTillFirstCoreArmor.map( + const armorPiercingLayersRaw = intersectionsTillFirstCoreArmor.map( (event, index) => { switch (event.object.userData.type) { case 'externalModule': { - accumulatedThickness += - event.object.userData.thickness * thicknessCoefficient; - return { nominal: event.object.userData.thickness * thicknessCoefficient, angled: event.object.userData.thickness * thicknessCoefficient, ricochet: false, - block: accumulatedThickness > resolvedPenetration, type: 'external', + distance: event.distance, } satisfies ArmorPiercingLayer; } @@ -235,28 +231,62 @@ export function ArmorMesh({ const angled = (event.object.userData.thickness * thicknessCoefficient) / Math.cos(angle - degToRad(normalization)); - accumulatedThickness += angled; return { nominal: event.object.userData.thickness * thicknessCoefficient, angled, ricochet, - block: accumulatedThickness > resolvedPenetration, type: event.object.userData.type === 'coreArmor' ? 'core' : 'spaced', + distance: event.distance, } satisfies ArmorPiercingLayer; } } }, ); + const gaps = armorPiercingLayersRaw.map((layerA, aIndex) => { + if (aIndex === armorPiercingLayersRaw.length - 1) return null; + const layerB = armorPiercingLayersRaw[aIndex + 1]; + + return { + type: 'gap', + gap: Math.abs(layerB.distance - layerA.distance), + } satisfies ArmorPiercingLayer; + }); + const armorPiercingLayers: ArmorPiercingLayer[] = []; + + armorPiercingLayersRaw.forEach((layer, index) => { + const gap = gaps[index]; + + armorPiercingLayers.push(layer); + if (gap) armorPiercingLayers.push(gap); + }); + + console.log(armorPiercingLayers); + + let remainingPenetration = resolvedPenetration; + let accumulatedThickness = 0; + const explosiveCapable = isExplosive(shell.type); + + armorPiercingLayers.forEach((layer) => { + if (layer.type !== 'gap') { + remainingPenetration -= layer.angled; + accumulatedThickness += layer.angled; + } else if (explosiveCapable) { + // there is a 50% penetration loss per meter for HE based shells + remainingPenetration -= 0.5 * remainingPenetration * layer.gap; + } + + remainingPenetration = Math.max(0, remainingPenetration); + }); const hasRicochet = armorPiercingLayers.some( - ({ ricochet }) => ricochet, + (layer) => layer.type !== 'gap' && layer.ricochet, ); - const hasPenetrated = resolvedPenetration >= accumulatedThickness; + const hasPenetrated = remainingPenetration >= accumulatedThickness; mutateTankopediaTemporary((draft) => { draft.shot = { diff --git a/src/components/ShotDisplay.tsx b/src/components/ShotDisplay.tsx index 16e3f6378..6910b8ea7 100644 --- a/src/components/ShotDisplay.tsx +++ b/src/components/ShotDisplay.tsx @@ -106,23 +106,32 @@ export function ShotDisplay() { paddingLeft: shot.thicknesses.length > 1 ? 8 : undefined, }} > - {shot.thicknesses.map(({ nominal, angled, type }, index) => ( + {shot.thicknesses.map((layer, index) => ( {shot.thicknesses.length > 1 && ( - {index + 1}. {type[0].toUpperCase()} - {type.slice(1)} + {index + 1}. {layer.type[0].toUpperCase()} + {layer.type.slice(1)} )} - - {nominal}mm + {layer.type !== 'gap' && ( + + )} + + {Math.round( + layer.type === 'gap' + ? layer.gap * 1000 + : layer.nominal, + )} + mm + - {type !== 'external' && ( + {layer.type !== 'external' && layer.type !== 'gap' && ( - {Math.round(angled)}mm + {Math.round(layer.angled)}mm )} diff --git a/src/stores/tankopedia.ts b/src/stores/tankopedia.ts index 4fba02d7b..eccabe1d5 100644 --- a/src/stores/tankopedia.ts +++ b/src/stores/tankopedia.ts @@ -76,13 +76,18 @@ export type Shot = { angle: number; }; -export interface ArmorPiercingLayer { - nominal: number; - angled: number; - ricochet: boolean; - block: boolean; - type: 'core' | 'spaced' | 'external'; -} +export type ArmorPiercingLayer = + | { + distance: number; + nominal: number; + angled: number; + ricochet: boolean; + type: 'core' | 'spaced' | 'external'; + } + | { + type: 'gap'; + gap: number; + }; export const useTankopediaPersistent = create()( persist(