diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx index 8a49656fe..13aadb5d3 100644 --- a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx @@ -149,7 +149,7 @@ export function Modules() { ) ) { draft.protagonist!.gun = draft.protagonist!.turret.guns.at(-1)!; - draft.protagonist!.shell = draft.protagonist!.gun.shells.at(-1)!; + draft.protagonist!.shell = draft.protagonist!.gun.shells[0]; } } else if (unlock.type === 'gun') { const gunInTurret = draft.protagonist!.turret.guns.find( @@ -157,7 +157,7 @@ export function Modules() { ); if (gunInTurret) { draft.protagonist!.gun = gunInTurret; - draft.protagonist!.shell = gunInTurret.shells.at(-1)!; + draft.protagonist!.shell = gunInTurret.shells[0]; } else { // TODO: warn somehow? const suitableTurret = draft.protagonist!.tank.turrets.find( @@ -169,7 +169,7 @@ export function Modules() { draft.protagonist!.turret = suitableTurret; draft.protagonist!.gun = gunInSuitableTurret; - draft.protagonist!.shell = gunInSuitableTurret.shells.at(-1)!; + draft.protagonist!.shell = gunInSuitableTurret.shells[0]; } } else if (unlock.type === 'engine') { draft.protagonist!.engine = draft.protagonist!.tank.engines.find( diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx index 09689392a..1dc904787 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx @@ -9,6 +9,7 @@ import { degToRad } from 'three/src/math/MathUtils'; import { ArmorUserData } from '../..'; import { isExplosive } from '../../../../../../core/blitz/isExplosive'; import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration'; +import { resolvePenetrationCoefficient } from '../../../../../../core/blitz/resolvePenetrationCoefficient'; import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment'; import { jsxTree } from '../../../../../../core/blitzkit/jsxTree'; import { ShellDefinition } from '../../../../../../core/blitzkit/tankDefinitions'; @@ -16,8 +17,6 @@ import { EquipmentMatrix, useDuel } from '../../../../../../stores/duel'; import { ArmorType } from '../../../SpacedArmorScene'; import fragmentShader from './shaders/fragment.glsl'; import vertexShader from './shaders/vertex.glsl'; -import { resolvePenetrationCoefficient } from '../../../../../../core/blitz/resolvePenetrationCoefficient'; -import { hasColorSpace } from '@react-three/fiber/dist/declarations/src/core/utils'; interface SpacedArmorSubSpacedProps { node: Object3D; @@ -85,8 +84,10 @@ export function SpacedArmorSubSpaced({ duel.antagonist!.tank.equipment, equipment, ); - - material.uniforms.penetration.value=penetration*resolvePenetrationCoefficient(hasCalibratedShells, shell.type) + + material.uniforms.penetration.value = + penetration * + resolvePenetrationCoefficient(hasCalibratedShells, shell.type); } handleShellChange(useDuel.getState().antagonist!.shell); diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx index eabf9e99e..bfdce108b 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx @@ -18,6 +18,7 @@ import { useDuel } from '../../../../stores/duel'; import { Shot, ShotLayerBase, + ShotLayerNonExternal, useTankopediaTemporary, } from '../../../../stores/tankopedia'; import { ArmorType } from '../SpacedArmorScene'; @@ -107,13 +108,13 @@ export function SpacedArmorSceneComponent({ const hasCalibratedShells = await hasEquipment( 103, - protagonist!.tank.equipment, - protagonist!.equipmentMatrix, + antagonist!.tank.equipment, + antagonist!.equipmentMatrix, ); const hasEnhancedArmor = await hasEquipment( 110, - antagonist!.tank.equipment, - antagonist!.equipmentMatrix, + protagonist!.tank.equipment, + protagonist!.equipmentMatrix, ); const penetration = resolveNearPenetration(shell.penetration) * @@ -186,6 +187,7 @@ export function SpacedArmorSceneComponent({ status: blocked ? 'blocked' : 'penetration', }); } else { + const thickness = layer.thickness; const ricochet = degToRad( isExplosive(shell.type) ? 90 : shell.ricochet!, ); @@ -245,19 +247,24 @@ export function SpacedArmorSceneComponent({ index++; } - const lastLayer = shot.layersIn.at(-1) as ShotLayerBase; + const lastLayer = shot.layersIn.at(-1) as ShotLayerNonExternal; const firstLayer = shot.layersIn[0] as ShotLayerBase; if (shell.type === ShellType.HE) { - const totalThickness = shot.layersIn.reduce((acc, layer) => { - if (layer.type === null) return acc; + const totalSpacedArmorThickness = shot.layersIn.reduce( + (acc, layer) => { + if (layer.type === null || layer.type === ArmorType.Core) { + return acc; + } - if (layer.type === ArmorType.External) { - return acc + layer.thickness; - } + if (layer.type === ArmorType.External) { + return acc + layer.thickness; + } - return acc + layer.thicknessAngled; - }, 0); + return acc + layer.thicknessAngled; + }, + 0, + ); const distanceFromSpacedArmor = firstLayer.point.distanceTo( lastLayer.point, ); @@ -266,15 +273,26 @@ export function SpacedArmorSceneComponent({ 0.5 * shell.damage.armor * (1 - distanceFromSpacedArmor / shell.explosionRadius!) - - 1.1 * totalThickness, + 1.1 * + (lastLayer.thicknessAngled + + Math.min(penetration, totalSpacedArmorThickness)), ); + /** + * Above is some peculiar math. We're multiplying 1.1 by the + * thickness of the core plate plus the total spaced armor + * thickness capped to the penetration itself. This is because + * the shader is normalized by the penetration value; hence, + * the total spaced armor thickness cannot ever be thicker + * than the penetration. This is a precision error on the + * shader-side and I do not intend on fixing it. Instead, + * for parity's sake, I am capping the value here. + */ + if ( shot.layersIn.length > 1 || lastLayer.status === 'blocked' ) { - console.log(finalDamage); - shot.status = finalDamage > 0 ? 'splash' : 'blocked'; shot.damage = finalDamage; } else { @@ -296,257 +314,6 @@ export function SpacedArmorSceneComponent({ console.log(shot); useTankopediaTemporary.setState({ shot }); - - // 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.03 : 1; - // const shell = useDuel.getState().antagonist!.shell; - // const explosive = isExplosive(shell.type); - // const splashing = canSplash(shell.type); - // const penetration = - // resolveNearPenetration(shell.penetration) * - // (hasCalibratedShells - // ? duel.antagonist!.shell.type === 'ap' - // ? 1.08 - // : duel.antagonist!.shell.type === 'ap_cr' - // ? 1.05 - // : duel.antagonist!.shell.type === 'hc' - // ? 1.13 - // : 1.08 - // : 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 intersectionsWithDuplicates = intersectionsFiltered.slice( - // 0, - // intersectionsFiltered.findIndex( - // ({ object }) => object.userData.type === ArmorType.Core, - // ) + 1, - // ); - // const firstGunIntersectionIndex = - // intersectionsWithDuplicates.findIndex( - // ({ object }) => - // object.userData.type === ArmorType.External && - // object.userData.variant === 'gun', - // ); - // const firstTrackIntersectionIndex = - // intersectionsWithDuplicates.findIndex( - // ({ object }) => - // object.userData.type === ArmorType.External && - // object.userData.variant === 'track', - // ); - // 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 (shell.type === 'hc') { - // 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 thickness = - // thicknessCoefficient * - // intersection.object.userData.thickness; - - // if (layerIndex !== 0 && startingIndex === 0) { - // const previousIntersection = intersections[layerIndex - 1]; - // const distance = - // intersection.distance - previousIntersection.distance; - // if (shell.type === 'hc') { - // remainingPenetration -= - // 0.5 * remainingPenetration * distance; - // } - // const wasted = remainingPenetration < 0 || splashing; - - // 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, - // 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( - // isExplosive(shell.type) ? 90 : shell.ricochet!, - // ); - // 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++; - // } - // } - - // useTankopediaTemporary.setState({ shot }); }, }, node.uuid, diff --git a/src/components/ModuleManager.tsx b/src/components/ModuleManager.tsx index 60668e3f7..e51014e1e 100644 --- a/src/components/ModuleManager.tsx +++ b/src/components/ModuleManager.tsx @@ -63,7 +63,7 @@ export function ModuleManager({ const draft = { ...modules }; draft.turret = turret; draft.gun = draft.turret.guns.at(-1)!; - draft.shell = draft.gun.shells.at(-1)!; + draft.shell = draft.gun.shells[0]; onChange?.(draft); setTurretMenuOpen(false); @@ -106,7 +106,7 @@ export function ModuleManager({ onClick={() => { const draft = { ...modules }; draft.gun = gun; - draft.shell = draft.gun.shells.at(-1)!; + draft.shell = draft.gun.shells[0]; onChange?.(draft); setGunMenuOpen(false);