feat: start rewrite on onClick armor

This commit is contained in:
TrèsAbhi committed 2024-05-24 20:32:39 -05:00
1 parent 82933aaf1e
commit 542145467c
10 files changed
+476 -282

No files matched your search

@@ -13,6 +13,7 @@ import { Fragment, use, useEffect, useRef, useState } from 'react';
import { lerp } from 'three/src/math/MathUtils';
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
import { isExplosive } from '../../../../../../core/blitz/isExplosive';
import { resolvePenetrationCoefficient } from '../../../../../../core/blitz/resolvePenetrationCoefficient';
import { asset } from '../../../../../../core/blitzkit/asset';
import { coefficient } from '../../../../../../core/blitzkit/coefficient';
import { equipmentDefinitions } from '../../../../../../core/blitzkit/equipmentDefinitions';
@@ -101,13 +102,7 @@ export function Characteristics() {
const hasImprovedVerticalStabilizer = useEquipment(122);
const penetrationCoefficient = coefficient([
hasCalibratedShells,
shell.type === 'ap'
? 0.08
: shell.type === 'ap_cr'
? 0.05
: shell.type === 'hc'
? 0.13
: 0.08,
resolvePenetrationCoefficient(true, shell.type) - 1,
]);
const penetrationLossOverDistanceCoefficient = coefficient([
hasSupercharger,
@@ -5,6 +5,7 @@ import { degToRad } from 'three/src/math/MathUtils';
import { canSplash } from '../../../../core/blitz/canSplash';
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';
@@ -98,25 +99,17 @@ export function CoreArmorSceneComponent({
}
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
const duel = useDuel.getState();
const penetration = resolveNearPenetration(
duel.antagonist!.shell.penetration,
);
const shell = duel.antagonist!.shell;
const penetration = resolveNearPenetration(shell.penetration);
const hasCalibratedShells = await hasEquipment(
103,
duel.antagonist!.tank.equipment,
equipment,
);
material.uniforms.penetration.value = hasCalibratedShells
? penetration *
(duel.antagonist!.shell.type === 'ap'
? 1.08
: duel.antagonist!.shell.type === 'ap_cr'
? 1.05
: duel.antagonist!.shell.type === 'hc'
? 1.13
: 1.08)
: penetration;
material.uniforms.penetration.value =
penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
}
handleShellChange(useDuel.getState().antagonist!.shell);
+22 -14
View File
@@ -35,30 +35,33 @@ const TRACER_THICK = 1 / 64;
const TRACER_THIN = TRACER_THICK / 2;
export function ShotDisplay() {
return null;
const shot = useTankopediaTemporary((state) => state.shot);
const preMidPointTracer = useRef<Mesh>(null);
const postMidPointTracer = useRef<Mesh>(null);
const hasMultipleLayers = shot?.layers.some(
const hasMultipleLayers = shot?.layersIn.some(
(layer, index) => index !== 0 && layer.type !== null,
);
const { shell } = useDuel.getState().antagonist!;
const explosive = isExplosive(shell.type);
const splashing = canSplash(shell.type);
const midPointLayer = (shot?.layers.findLast(
const midPointLayer = (shot?.layersIn.findLast(
(layer) => layer.type !== null && layer.status === 'ricochet',
) ?? shot?.layers.findLast((layer) => layer.type !== null)) as ShotLayerBase;
const firstLayer = shot?.layers[0] as Exclude<ShotLayer, ShotLayerGap>;
const lastLayer = shot?.layers.at(-1)!;
) ??
shot?.layersIn.findLast((layer) => layer.type !== null)) as ShotLayerBase;
const firstLayer = shot?.layersIn[0] as Exclude<ShotLayer, ShotLayerGap>;
const lastLayer = shot?.layersIn.at(-1)!;
const distanceFromSpacedArmor = shot
? firstLayer.point.distanceTo(shot.point)
: undefined;
const preMidPointLayers = shot?.layers.slice(
const preMidPointLayers = shot?.layersIn.slice(
0,
shot.layers.indexOf(midPointLayer as ShotLayer),
shot.layersIn.indexOf(midPointLayer as ShotLayer),
);
const postMidPointLayers = shot?.layers.slice(
shot.layers.indexOf(midPointLayer as ShotLayer),
shot.layers.length,
const postMidPointLayers = shot?.layersIn.slice(
shot.layersIn.indexOf(midPointLayer as ShotLayer),
shot.layersIn.length,
);
const preMidPointGap = shot
? preMidPointLayers!.reduce((accumulator, layer) => {
@@ -84,6 +87,8 @@ export function ShotDisplay() {
return accumulator;
}, 0) || LENGTH_INFINITY
: undefined;
const spacedBlockedHE =
shell.type === 'he' && postMidPointGap && postMidPointGap > 0;
useFrame(({ clock }) => {
if (!shot || !preMidPointTracer.current) return;
@@ -175,11 +180,11 @@ export function ShotDisplay() {
</group>
)}
{shot.layers.map((layer, index) => {
{shot.layersIn.map((layer, index) => {
if (layer.type === null) return;
const shellRotation = normalToEuler(layer.shellNormal);
const nextLayer = shot.layers[index + 1] as ShotLayerGap | undefined;
const nextLayer = shot.layersIn[index + 1] as ShotLayerGap | undefined;
const trueStatus =
nextLayer?.status === 'blocked' ? 'blocked' : layer.status;
@@ -247,9 +252,12 @@ export function ShotDisplay() {
<Text>
{lastLayer.status[0].toUpperCase()}
{lastLayer.status.slice(1)}
{lastLayer.status === 'penetration' &&
!spacedBlockedHE &&
`: ${Math.round(shell.damage.armor)}hp`}
{lastLayer.status === 'blocked' &&
{(lastLayer.status === 'blocked' || spacedBlockedHE) &&
shell.type === 'he' &&
`: ${NaNFallback(
Math.max(
@@ -269,7 +277,7 @@ export function ShotDisplay() {
)}hp`}
</Text>
{shot.layers.map((layer, index) => {
{shot.layersIn.map((layer, index) => {
if (layer.type === null && (!explosive || splashing)) return null;
return (
@@ -8,6 +8,7 @@ import {
} from 'three';
import { ArmorUserData, ExternalModuleVariant } from '../..';
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';
@@ -66,24 +67,17 @@ export function SpacedArmorSubExternal({
}
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
const duel = useDuel.getState();
const penetration = resolveNearPenetration(
duel.antagonist!.shell.penetration,
);
const shell = duel.antagonist!.shell;
const penetration = resolveNearPenetration(shell.penetration);
const hasCalibratedShells = await hasEquipment(
103,
duel.antagonist!.tank.equipment,
equipment,
);
material.uniforms.penetration.value = hasCalibratedShells
? penetration *
(duel.antagonist!.shell.type === 'ap'
? 1.08
: duel.antagonist!.shell.type === 'ap_cr'
? 1.05
: duel.antagonist!.shell.type === 'hc'
? 1.13
: 1.08)
: penetration;
material.uniforms.penetration.value =
penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
}
handleShellChange(useDuel.getState().antagonist!.shell);
@@ -16,6 +16,8 @@ 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;
@@ -83,16 +85,8 @@ export function SpacedArmorSubSpaced({
duel.antagonist!.tank.equipment,
equipment,
);
material.uniforms.penetration.value = hasCalibratedShells
? penetration *
(duel.antagonist!.shell.type === 'ap'
? 1.08
: duel.antagonist!.shell.type === 'ap_cr'
? 1.05
: duel.antagonist!.shell.type === 'hc'
? 1.13
: 1.08)
: penetration;
material.uniforms.penetration.value=penetration*resolvePenetrationCoefficient(hasCalibratedShells, shell.type)
}
handleShellChange(useDuel.getState().antagonist!.shell);
@@ -5,21 +5,19 @@ import {
Object3D,
Plane,
Quaternion,
Raycaster,
Scene,
Vector3,
} from 'three';
import { degToRad } from 'three/src/math/MathUtils';
import { canSplash } from '../../../../core/blitz/canSplash';
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 { ShellType } from '../../../../core/blitzkit/tankDefinitions';
import { useDuel } from '../../../../stores/duel';
import {
Shot,
ShotLayerBase,
useTankopediaPersistent,
useTankopediaTemporary,
} from '../../../../stores/tankopedia';
import { ArmorType } from '../SpacedArmorScene';
@@ -94,254 +92,448 @@ export function SpacedArmorSceneComponent({
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.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: [],
status: 'blocked',
damage: -1,
layersIn: [],
point: event.point,
}; // TODO: type this
let remainingPenetration = penetration;
const shellNormal = camera.position
};
const { antagonist, protagonist } = useDuel.getState();
const shell = antagonist!.shell;
const cameraNormal = camera.position
.clone()
.sub(event.point)
.normalize();
pushLogs(
shellNormal,
filterIntersections(event.intersections),
0,
const hasCalibratedShells = await hasEquipment(
103,
protagonist!.tank.equipment,
protagonist!.equipmentMatrix,
);
const hasEnhancedArmor = await hasEquipment(
110,
antagonist!.tank.equipment,
antagonist!.equipmentMatrix,
);
const penetration =
resolveNearPenetration(shell.penetration) *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
const thicknessCoefficient = hasEnhancedArmor ? 1.03 : 1;
const filteredIntersections = event.intersections.filter(
(intersection) => 'type' in intersection.object.userData,
) as unknown as Intersection<
Object3D & { userData: ArmorUserData }
>[];
const encounteredExternalModuleVariants: ExternalModuleVariant[] =
[];
const noDuplicateIntersections: typeof filteredIntersections = [];
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;
for (const intersection of filteredIntersections) {
if (intersection.object.userData.type === ArmorType.External) {
if (
!encounteredExternalModuleVariants.includes(
intersection.object.userData.variant,
)
) {
noDuplicateIntersections.push(intersection);
encounteredExternalModuleVariants.push(
intersection.object.userData.variant,
);
}
} else {
noDuplicateIntersections.push(intersection);
if (firstGunIntersectionIndex !== -1) {
intersections = intersections.filter(({ object }, index) => {
return object.userData.type === ArmorType.External &&
object.userData.variant === 'gun'
? index === firstGunIntersectionIndex
: true;
});
if (intersection.object.userData.type === ArmorType.Core) {
break;
}
}
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;
let remainingPenetration = penetration;
let index = 0;
for (const intersection of noDuplicateIntersections) {
const layer = intersection.object.userData;
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;
if (shell.type === ShellType.HEAT && index > 0) {
const lastLayer = shot.layersIn.at(-1) as ShotLayerBase;
const distance = lastLayer.point.distanceTo(
intersection.point,
);
remainingPenetration -= 0.5 * remainingPenetration * distance;
const blocked = remainingPenetration <= 0;
shot.layers.push({
shot.layersIn.push({
type: null,
status: blocked ? 'blocked' : 'penetration',
distance,
status: blocked ? 'blocked' : 'penetration',
});
if (blocked) return;
if (blocked) break;
}
for (const intersection of intersections) {
if (layer.type === ArmorType.External) {
const thickness = layer.thickness * thicknessCoefficient;
remainingPenetration -= thickness;
const blocked =
remainingPenetration <= 0 || shell.type === ShellType.HE;
shot.layersIn.push({
type: ArmorType.External,
index,
thickness,
point: intersection.point,
surfaceNormal: intersection.face!.normal,
status: blocked ? 'blocked' : 'penetration',
});
} else {
const ricochet = degToRad(
isExplosive(shell.type) ? 90 : shell.ricochet!,
);
const surfaceNormal = intersection
.normal!.clone()
.applyQuaternion(
event.object.getWorldQuaternion(new Quaternion()),
);
const thickness =
thicknessCoefficient *
intersection.object.userData.thickness;
const angle = surfaceNormal.angleTo(cameraNormal);
const threeCalibersRule =
shell.caliber > thickness * 3 || index > 0;
const twoCalibersRule = shell.caliber > thickness * 2;
const normalization = degToRad(shell.normalization ?? 0);
const finalNormalization = twoCalibersRule
? (1.4 * normalization * shell.caliber) / (2.0 * thickness)
: normalization;
const finalThickness =
thickness /
Math.cos(Math.max(0, angle - finalNormalization));
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) {
remainingPenetration *= 0.75;
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,
shot.layersIn.push({
type: layer.type,
index,
thickness,
thicknessAngled: finalThickness,
point: intersection.point,
thickness: intersection.object.userData.thickness,
status: blocked ? 'blocked' : 'penetration',
surfaceNormal: intersection.face!.normal,
status: 'ricochet',
angle,
});
if (splashing) {
remainingPenetration = Math.max(0, remainingPenetration);
} else if (blocked) {
break;
}
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));
remainingPenetration -= finalThickness;
const blocked = remainingPenetration <= 0;
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',
});
}
shot.layersIn.push({
type: layer.type,
index,
thickness,
thicknessAngled: finalThickness,
point: intersection.point,
surfaceNormal: intersection.face!.normal,
status: blocked ? 'blocked' : 'penetration',
angle,
});
}
layerIndex++;
loopIndex++;
}
if (remainingPenetration <= 0) break;
index++;
}
const lastLayer = shot.layersIn.at(-1) as ShotLayerBase;
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;
if (layer.type === ArmorType.External) {
return acc + layer.thickness;
}
return acc + layer.thicknessAngled;
}, 0);
const distanceFromSpacedArmor = firstLayer.point.distanceTo(
lastLayer.point,
);
const finalDamage = Math.max(
0,
0.5 *
shell.damage.armor *
(1 - distanceFromSpacedArmor / shell.explosionRadius!) -
1.1 * totalThickness,
);
if (
shot.layersIn.length > 1 ||
lastLayer.status === 'blocked'
) {
shot.status = finalDamage > 0 ? 'splash' : 'blocked';
shot.damage = finalDamage;
} else {
shot.status = 'penetration';
shot.damage = shell.damage.armor;
}
} else {
shot.status = lastLayer.status;
}
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,
@@ -0,0 +1,16 @@
import { ShellType } from '../blitzkit/tankDefinitions';
export function resolvePenetrationCoefficient(
calibratedShells: boolean,
type: ShellType,
) {
if (!calibratedShells) return 1;
return type === 'ap'
? 1.08
: type === 'ap_cr'
? 1.05
: type === 'hc'
? 1.13
: 1.08;
}
+2 -7
View File
@@ -1,5 +1,6 @@
import { EquipmentMatrix } from '../../stores/duel';
import { resolveNearPenetration } from '../blitz/resolveNearPenetration';
import { resolvePenetrationCoefficient } from '../blitz/resolvePenetrationCoefficient';
import { coefficient } from './coefficient';
import { degressiveStat } from './degressiveStat';
import { EquipmentDefinitions } from './equipmentDefinitions';
@@ -151,13 +152,7 @@ export function tankCharacteristics(
coefficient([hasAdrenaline && gun.type === 'regular', 0.17]);
const penetrationCoefficient = coefficient([
hasCalibratedShells,
shell.type === 'ap'
? 0.08
: shell.type === 'ap_cr'
? 0.05
: shell.type === 'hc'
? 0.13
: 0.08,
resolvePenetrationCoefficient(true, shell.type) - 1,
]);
const healthCoefficient = coefficient(
[hasSandbagArmor, 0.03],
+6 -1
View File
@@ -5,7 +5,12 @@ import { asset } from '../asset';
import { fetchCdonLz4 } from '../fetchCdonLz4';
import { TIERS } from './constants';
export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he';
export enum ShellType {
AP = 'ap',
APCR = 'ap_cr',
HEAT = 'hc',
HE = 'he',
}
export type CrewMember =
| 'commander'
+4 -2
View File
@@ -50,7 +50,6 @@ export interface ShotLayerBase {
index: number;
thickness: number;
point: Vector3;
shellNormal: Vector3;
surfaceNormal: Vector3;
status: 'blocked' | 'penetration' | 'ricochet';
}
@@ -74,8 +73,11 @@ export interface ShotLayerGap {
export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap;
export type Shot = {
status: 'penetration' | 'blocked' | 'ricochet' | 'splash';
damage: number;
point: Vector3;
layers: ShotLayer[];
layersIn: ShotLayer[];
layersOut?: ShotLayer[];
};
export type ArmorPiercingLayer =