mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-27 12:39:49 +01:00
feat: start rewrite on onClick armor
This commit is contained in:
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 { lerp } from 'three/src/math/MathUtils';
|
||||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { isExplosive } from '../../../../../../core/blitz/isExplosive';
|
import { isExplosive } from '../../../../../../core/blitz/isExplosive';
|
||||||
|
import { resolvePenetrationCoefficient } from '../../../../../../core/blitz/resolvePenetrationCoefficient';
|
||||||
import { asset } from '../../../../../../core/blitzkit/asset';
|
import { asset } from '../../../../../../core/blitzkit/asset';
|
||||||
import { coefficient } from '../../../../../../core/blitzkit/coefficient';
|
import { coefficient } from '../../../../../../core/blitzkit/coefficient';
|
||||||
import { equipmentDefinitions } from '../../../../../../core/blitzkit/equipmentDefinitions';
|
import { equipmentDefinitions } from '../../../../../../core/blitzkit/equipmentDefinitions';
|
||||||
@@ -101,13 +102,7 @@ export function Characteristics() {
|
|||||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
const penetrationCoefficient = coefficient([
|
const penetrationCoefficient = coefficient([
|
||||||
hasCalibratedShells,
|
hasCalibratedShells,
|
||||||
shell.type === 'ap'
|
resolvePenetrationCoefficient(true, shell.type) - 1,
|
||||||
? 0.08
|
|
||||||
: shell.type === 'ap_cr'
|
|
||||||
? 0.05
|
|
||||||
: shell.type === 'hc'
|
|
||||||
? 0.13
|
|
||||||
: 0.08,
|
|
||||||
]);
|
]);
|
||||||
const penetrationLossOverDistanceCoefficient = coefficient([
|
const penetrationLossOverDistanceCoefficient = coefficient([
|
||||||
hasSupercharger,
|
hasSupercharger,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { degToRad } from 'three/src/math/MathUtils';
|
|||||||
import { canSplash } from '../../../../core/blitz/canSplash';
|
import { canSplash } from '../../../../core/blitz/canSplash';
|
||||||
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
||||||
import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
|
||||||
|
import { resolvePenetrationCoefficient } from '../../../../core/blitz/resolvePenetrationCoefficient';
|
||||||
import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
||||||
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
||||||
import { ShellDefinition } from '../../../../core/blitzkit/tankDefinitions';
|
import { ShellDefinition } from '../../../../core/blitzkit/tankDefinitions';
|
||||||
@@ -98,25 +99,17 @@ export function CoreArmorSceneComponent({
|
|||||||
}
|
}
|
||||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||||
const duel = useDuel.getState();
|
const duel = useDuel.getState();
|
||||||
const penetration = resolveNearPenetration(
|
const shell = duel.antagonist!.shell;
|
||||||
duel.antagonist!.shell.penetration,
|
const penetration = resolveNearPenetration(shell.penetration);
|
||||||
);
|
|
||||||
const hasCalibratedShells = await hasEquipment(
|
const hasCalibratedShells = await hasEquipment(
|
||||||
103,
|
103,
|
||||||
duel.antagonist!.tank.equipment,
|
duel.antagonist!.tank.equipment,
|
||||||
equipment,
|
equipment,
|
||||||
);
|
);
|
||||||
|
|
||||||
material.uniforms.penetration.value = hasCalibratedShells
|
material.uniforms.penetration.value =
|
||||||
? penetration *
|
penetration *
|
||||||
(duel.antagonist!.shell.type === 'ap'
|
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
|
||||||
? 1.08
|
|
||||||
: duel.antagonist!.shell.type === 'ap_cr'
|
|
||||||
? 1.05
|
|
||||||
: duel.antagonist!.shell.type === 'hc'
|
|
||||||
? 1.13
|
|
||||||
: 1.08)
|
|
||||||
: penetration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShellChange(useDuel.getState().antagonist!.shell);
|
handleShellChange(useDuel.getState().antagonist!.shell);
|
||||||
|
|||||||
@@ -35,30 +35,33 @@ const TRACER_THICK = 1 / 64;
|
|||||||
const TRACER_THIN = TRACER_THICK / 2;
|
const TRACER_THIN = TRACER_THICK / 2;
|
||||||
|
|
||||||
export function ShotDisplay() {
|
export function ShotDisplay() {
|
||||||
|
return null;
|
||||||
|
|
||||||
const shot = useTankopediaTemporary((state) => state.shot);
|
const shot = useTankopediaTemporary((state) => state.shot);
|
||||||
const preMidPointTracer = useRef<Mesh>(null);
|
const preMidPointTracer = useRef<Mesh>(null);
|
||||||
const postMidPointTracer = 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,
|
(layer, index) => index !== 0 && layer.type !== null,
|
||||||
);
|
);
|
||||||
const { shell } = useDuel.getState().antagonist!;
|
const { shell } = useDuel.getState().antagonist!;
|
||||||
const explosive = isExplosive(shell.type);
|
const explosive = isExplosive(shell.type);
|
||||||
const splashing = canSplash(shell.type);
|
const splashing = canSplash(shell.type);
|
||||||
const midPointLayer = (shot?.layers.findLast(
|
const midPointLayer = (shot?.layersIn.findLast(
|
||||||
(layer) => layer.type !== null && layer.status === 'ricochet',
|
(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>;
|
shot?.layersIn.findLast((layer) => layer.type !== null)) as ShotLayerBase;
|
||||||
const lastLayer = shot?.layers.at(-1)!;
|
const firstLayer = shot?.layersIn[0] as Exclude<ShotLayer, ShotLayerGap>;
|
||||||
|
const lastLayer = shot?.layersIn.at(-1)!;
|
||||||
const distanceFromSpacedArmor = shot
|
const distanceFromSpacedArmor = shot
|
||||||
? firstLayer.point.distanceTo(shot.point)
|
? firstLayer.point.distanceTo(shot.point)
|
||||||
: undefined;
|
: undefined;
|
||||||
const preMidPointLayers = shot?.layers.slice(
|
const preMidPointLayers = shot?.layersIn.slice(
|
||||||
0,
|
0,
|
||||||
shot.layers.indexOf(midPointLayer as ShotLayer),
|
shot.layersIn.indexOf(midPointLayer as ShotLayer),
|
||||||
);
|
);
|
||||||
const postMidPointLayers = shot?.layers.slice(
|
const postMidPointLayers = shot?.layersIn.slice(
|
||||||
shot.layers.indexOf(midPointLayer as ShotLayer),
|
shot.layersIn.indexOf(midPointLayer as ShotLayer),
|
||||||
shot.layers.length,
|
shot.layersIn.length,
|
||||||
);
|
);
|
||||||
const preMidPointGap = shot
|
const preMidPointGap = shot
|
||||||
? preMidPointLayers!.reduce((accumulator, layer) => {
|
? preMidPointLayers!.reduce((accumulator, layer) => {
|
||||||
@@ -84,6 +87,8 @@ export function ShotDisplay() {
|
|||||||
return accumulator;
|
return accumulator;
|
||||||
}, 0) || LENGTH_INFINITY
|
}, 0) || LENGTH_INFINITY
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const spacedBlockedHE =
|
||||||
|
shell.type === 'he' && postMidPointGap && postMidPointGap > 0;
|
||||||
|
|
||||||
useFrame(({ clock }) => {
|
useFrame(({ clock }) => {
|
||||||
if (!shot || !preMidPointTracer.current) return;
|
if (!shot || !preMidPointTracer.current) return;
|
||||||
@@ -175,11 +180,11 @@ export function ShotDisplay() {
|
|||||||
</group>
|
</group>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{shot.layers.map((layer, index) => {
|
{shot.layersIn.map((layer, index) => {
|
||||||
if (layer.type === null) return;
|
if (layer.type === null) return;
|
||||||
|
|
||||||
const shellRotation = normalToEuler(layer.shellNormal);
|
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 =
|
const trueStatus =
|
||||||
nextLayer?.status === 'blocked' ? 'blocked' : layer.status;
|
nextLayer?.status === 'blocked' ? 'blocked' : layer.status;
|
||||||
|
|
||||||
@@ -247,9 +252,12 @@ export function ShotDisplay() {
|
|||||||
<Text>
|
<Text>
|
||||||
{lastLayer.status[0].toUpperCase()}
|
{lastLayer.status[0].toUpperCase()}
|
||||||
{lastLayer.status.slice(1)}
|
{lastLayer.status.slice(1)}
|
||||||
|
|
||||||
{lastLayer.status === 'penetration' &&
|
{lastLayer.status === 'penetration' &&
|
||||||
|
!spacedBlockedHE &&
|
||||||
`: ${Math.round(shell.damage.armor)}hp`}
|
`: ${Math.round(shell.damage.armor)}hp`}
|
||||||
{lastLayer.status === 'blocked' &&
|
|
||||||
|
{(lastLayer.status === 'blocked' || spacedBlockedHE) &&
|
||||||
shell.type === 'he' &&
|
shell.type === 'he' &&
|
||||||
`: ${NaNFallback(
|
`: ${NaNFallback(
|
||||||
Math.max(
|
Math.max(
|
||||||
@@ -269,7 +277,7 @@ export function ShotDisplay() {
|
|||||||
)}hp`}
|
)}hp`}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{shot.layers.map((layer, index) => {
|
{shot.layersIn.map((layer, index) => {
|
||||||
if (layer.type === null && (!explosive || splashing)) return null;
|
if (layer.type === null && (!explosive || splashing)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+7
-13
@@ -8,6 +8,7 @@ import {
|
|||||||
} from 'three';
|
} from 'three';
|
||||||
import { ArmorUserData, ExternalModuleVariant } from '../..';
|
import { ArmorUserData, ExternalModuleVariant } from '../..';
|
||||||
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
||||||
|
import { resolvePenetrationCoefficient } from '../../../../../../core/blitz/resolvePenetrationCoefficient';
|
||||||
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||||
import { jsxTree } from '../../../../../../core/blitzkit/jsxTree';
|
import { jsxTree } from '../../../../../../core/blitzkit/jsxTree';
|
||||||
import { ShellDefinition } from '../../../../../../core/blitzkit/tankDefinitions';
|
import { ShellDefinition } from '../../../../../../core/blitzkit/tankDefinitions';
|
||||||
@@ -66,24 +67,17 @@ export function SpacedArmorSubExternal({
|
|||||||
}
|
}
|
||||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||||
const duel = useDuel.getState();
|
const duel = useDuel.getState();
|
||||||
const penetration = resolveNearPenetration(
|
const shell = duel.antagonist!.shell;
|
||||||
duel.antagonist!.shell.penetration,
|
const penetration = resolveNearPenetration(shell.penetration);
|
||||||
);
|
|
||||||
const hasCalibratedShells = await hasEquipment(
|
const hasCalibratedShells = await hasEquipment(
|
||||||
103,
|
103,
|
||||||
duel.antagonist!.tank.equipment,
|
duel.antagonist!.tank.equipment,
|
||||||
equipment,
|
equipment,
|
||||||
);
|
);
|
||||||
material.uniforms.penetration.value = hasCalibratedShells
|
|
||||||
? penetration *
|
material.uniforms.penetration.value =
|
||||||
(duel.antagonist!.shell.type === 'ap'
|
penetration *
|
||||||
? 1.08
|
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
|
||||||
: duel.antagonist!.shell.type === 'ap_cr'
|
|
||||||
? 1.05
|
|
||||||
: duel.antagonist!.shell.type === 'hc'
|
|
||||||
? 1.13
|
|
||||||
: 1.08)
|
|
||||||
: penetration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShellChange(useDuel.getState().antagonist!.shell);
|
handleShellChange(useDuel.getState().antagonist!.shell);
|
||||||
|
|||||||
+4
-10
@@ -16,6 +16,8 @@ import { EquipmentMatrix, useDuel } from '../../../../../../stores/duel';
|
|||||||
import { ArmorType } from '../../../SpacedArmorScene';
|
import { ArmorType } from '../../../SpacedArmorScene';
|
||||||
import fragmentShader from './shaders/fragment.glsl';
|
import fragmentShader from './shaders/fragment.glsl';
|
||||||
import vertexShader from './shaders/vertex.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 {
|
interface SpacedArmorSubSpacedProps {
|
||||||
node: Object3D;
|
node: Object3D;
|
||||||
@@ -83,16 +85,8 @@ export function SpacedArmorSubSpaced({
|
|||||||
duel.antagonist!.tank.equipment,
|
duel.antagonist!.tank.equipment,
|
||||||
equipment,
|
equipment,
|
||||||
);
|
);
|
||||||
material.uniforms.penetration.value = hasCalibratedShells
|
|
||||||
? penetration *
|
material.uniforms.penetration.value=penetration*resolvePenetrationCoefficient(hasCalibratedShells, shell.type)
|
||||||
(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShellChange(useDuel.getState().antagonist!.shell);
|
handleShellChange(useDuel.getState().antagonist!.shell);
|
||||||
|
|||||||
@@ -5,21 +5,19 @@ import {
|
|||||||
Object3D,
|
Object3D,
|
||||||
Plane,
|
Plane,
|
||||||
Quaternion,
|
Quaternion,
|
||||||
Raycaster,
|
|
||||||
Scene,
|
Scene,
|
||||||
Vector3,
|
|
||||||
} from 'three';
|
} from 'three';
|
||||||
import { degToRad } from 'three/src/math/MathUtils';
|
import { degToRad } from 'three/src/math/MathUtils';
|
||||||
import { canSplash } from '../../../../core/blitz/canSplash';
|
|
||||||
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
import { isExplosive } from '../../../../core/blitz/isExplosive';
|
||||||
import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
|
||||||
|
import { resolvePenetrationCoefficient } from '../../../../core/blitz/resolvePenetrationCoefficient';
|
||||||
import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
||||||
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
||||||
|
import { ShellType } from '../../../../core/blitzkit/tankDefinitions';
|
||||||
import { useDuel } from '../../../../stores/duel';
|
import { useDuel } from '../../../../stores/duel';
|
||||||
import {
|
import {
|
||||||
Shot,
|
Shot,
|
||||||
ShotLayerBase,
|
ShotLayerBase,
|
||||||
useTankopediaPersistent,
|
|
||||||
useTankopediaTemporary,
|
useTankopediaTemporary,
|
||||||
} from '../../../../stores/tankopedia';
|
} from '../../../../stores/tankopedia';
|
||||||
import { ArmorType } from '../SpacedArmorScene';
|
import { ArmorType } from '../SpacedArmorScene';
|
||||||
@@ -94,254 +92,448 @@ export function SpacedArmorSceneComponent({
|
|||||||
async onClick(event) {
|
async onClick(event) {
|
||||||
event.stopPropagation();
|
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 = {
|
const shot: Shot = {
|
||||||
layers: [],
|
status: 'blocked',
|
||||||
|
damage: -1,
|
||||||
|
layersIn: [],
|
||||||
point: event.point,
|
point: event.point,
|
||||||
}; // TODO: type this
|
};
|
||||||
let remainingPenetration = penetration;
|
const { antagonist, protagonist } = useDuel.getState();
|
||||||
|
const shell = antagonist!.shell;
|
||||||
const shellNormal = camera.position
|
const cameraNormal = camera.position
|
||||||
.clone()
|
.clone()
|
||||||
.sub(event.point)
|
.sub(event.point)
|
||||||
.normalize();
|
.normalize();
|
||||||
|
|
||||||
pushLogs(
|
const hasCalibratedShells = await hasEquipment(
|
||||||
shellNormal,
|
103,
|
||||||
filterIntersections(event.intersections),
|
protagonist!.tank.equipment,
|
||||||
0,
|
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[]) {
|
for (const intersection of filteredIntersections) {
|
||||||
const showSpacedArmor =
|
if (intersection.object.userData.type === ArmorType.External) {
|
||||||
useTankopediaPersistent.getState().model.visual
|
if (
|
||||||
.showSpacedArmor;
|
!encounteredExternalModuleVariants.includes(
|
||||||
const intersectionsTyped = intersectionsRaw.filter(
|
intersection.object.userData.variant,
|
||||||
({ object }) => object.userData.type !== undefined,
|
)
|
||||||
) as ArmorMeshIntersection[];
|
) {
|
||||||
const intersectionsFiltered = intersectionsTyped.filter(
|
noDuplicateIntersections.push(intersection);
|
||||||
({ object }) =>
|
encounteredExternalModuleVariants.push(
|
||||||
showSpacedArmor
|
intersection.object.userData.variant,
|
||||||
? true
|
);
|
||||||
: object.userData.type === ArmorType.Core,
|
}
|
||||||
);
|
} else {
|
||||||
const intersectionsWithDuplicates = intersectionsFiltered.slice(
|
noDuplicateIntersections.push(intersection);
|
||||||
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) {
|
if (intersection.object.userData.type === ArmorType.Core) {
|
||||||
intersections = intersections.filter(({ object }, index) => {
|
break;
|
||||||
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(
|
let remainingPenetration = penetration;
|
||||||
shellNormal: Vector3,
|
let index = 0;
|
||||||
intersections: ArmorMeshIntersection[],
|
for (const intersection of noDuplicateIntersections) {
|
||||||
startingIndex: number,
|
const layer = intersection.object.userData;
|
||||||
) {
|
|
||||||
let layerIndex = startingIndex;
|
|
||||||
let loopIndex = 0;
|
|
||||||
|
|
||||||
if (layerIndex !== 0 && intersections.length > 0) {
|
if (shell.type === ShellType.HEAT && index > 0) {
|
||||||
const distance = (
|
const lastLayer = shot.layersIn.at(-1) as ShotLayerBase;
|
||||||
shot.layers.at(-1) as ShotLayerBase
|
const distance = lastLayer.point.distanceTo(
|
||||||
).point.distanceTo(intersections[0].point);
|
intersection.point,
|
||||||
if (explosive && !splashing)
|
);
|
||||||
remainingPenetration -=
|
remainingPenetration -= 0.5 * remainingPenetration * distance;
|
||||||
0.5 * remainingPenetration * distance;
|
const blocked = remainingPenetration <= 0;
|
||||||
const blocked = remainingPenetration < 0;
|
|
||||||
|
|
||||||
shot.layers.push({
|
shot.layersIn.push({
|
||||||
type: null,
|
type: null,
|
||||||
status: blocked ? 'blocked' : 'penetration',
|
|
||||||
distance,
|
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
|
const surfaceNormal = intersection
|
||||||
.normal!.clone()
|
.normal!.clone()
|
||||||
.applyQuaternion(
|
.applyQuaternion(
|
||||||
event.object.getWorldQuaternion(new Quaternion()),
|
event.object.getWorldQuaternion(new Quaternion()),
|
||||||
);
|
);
|
||||||
const thickness =
|
const angle = surfaceNormal.angleTo(cameraNormal);
|
||||||
thicknessCoefficient *
|
const threeCalibersRule =
|
||||||
intersection.object.userData.thickness;
|
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) {
|
if (!threeCalibersRule && angle >= ricochet) {
|
||||||
const previousIntersection = intersections[layerIndex - 1];
|
remainingPenetration *= 0.75;
|
||||||
const distance =
|
|
||||||
intersection.distance - previousIntersection.distance;
|
|
||||||
if (explosive && !splashing)
|
|
||||||
remainingPenetration -=
|
|
||||||
0.5 * remainingPenetration * distance;
|
|
||||||
const wasted = remainingPenetration < 0;
|
|
||||||
|
|
||||||
shot.layers.push({
|
shot.layersIn.push({
|
||||||
type: null,
|
type: layer.type,
|
||||||
status: wasted ? 'blocked' : 'penetration',
|
index,
|
||||||
distance,
|
thickness,
|
||||||
});
|
thicknessAngled: finalThickness,
|
||||||
|
|
||||||
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,
|
point: intersection.point,
|
||||||
thickness: intersection.object.userData.thickness,
|
surfaceNormal: intersection.face!.normal,
|
||||||
status: blocked ? 'blocked' : 'penetration',
|
status: 'ricochet',
|
||||||
|
angle,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (splashing) {
|
break;
|
||||||
remainingPenetration = Math.max(0, remainingPenetration);
|
|
||||||
} else if (blocked) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const type = intersection.object.userData.type;
|
remainingPenetration -= finalThickness;
|
||||||
const angle = surfaceNormal.angleTo(shellNormal);
|
const blocked = remainingPenetration <= 0;
|
||||||
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.layersIn.push({
|
||||||
shot.layers.push({
|
type: layer.type,
|
||||||
type,
|
index,
|
||||||
index: layerIndex,
|
thickness,
|
||||||
shellNormal,
|
thicknessAngled: finalThickness,
|
||||||
surfaceNormal,
|
point: intersection.point,
|
||||||
point: intersection.point,
|
surfaceNormal: intersection.face!.normal,
|
||||||
thickness,
|
status: blocked ? 'blocked' : 'penetration',
|
||||||
thicknessAngled: finalThickness,
|
angle,
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 });
|
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,
|
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;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { EquipmentMatrix } from '../../stores/duel';
|
import { EquipmentMatrix } from '../../stores/duel';
|
||||||
import { resolveNearPenetration } from '../blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../blitz/resolveNearPenetration';
|
||||||
|
import { resolvePenetrationCoefficient } from '../blitz/resolvePenetrationCoefficient';
|
||||||
import { coefficient } from './coefficient';
|
import { coefficient } from './coefficient';
|
||||||
import { degressiveStat } from './degressiveStat';
|
import { degressiveStat } from './degressiveStat';
|
||||||
import { EquipmentDefinitions } from './equipmentDefinitions';
|
import { EquipmentDefinitions } from './equipmentDefinitions';
|
||||||
@@ -151,13 +152,7 @@ export function tankCharacteristics(
|
|||||||
coefficient([hasAdrenaline && gun.type === 'regular', 0.17]);
|
coefficient([hasAdrenaline && gun.type === 'regular', 0.17]);
|
||||||
const penetrationCoefficient = coefficient([
|
const penetrationCoefficient = coefficient([
|
||||||
hasCalibratedShells,
|
hasCalibratedShells,
|
||||||
shell.type === 'ap'
|
resolvePenetrationCoefficient(true, shell.type) - 1,
|
||||||
? 0.08
|
|
||||||
: shell.type === 'ap_cr'
|
|
||||||
? 0.05
|
|
||||||
: shell.type === 'hc'
|
|
||||||
? 0.13
|
|
||||||
: 0.08,
|
|
||||||
]);
|
]);
|
||||||
const healthCoefficient = coefficient(
|
const healthCoefficient = coefficient(
|
||||||
[hasSandbagArmor, 0.03],
|
[hasSandbagArmor, 0.03],
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ import { asset } from '../asset';
|
|||||||
import { fetchCdonLz4 } from '../fetchCdonLz4';
|
import { fetchCdonLz4 } from '../fetchCdonLz4';
|
||||||
import { TIERS } from './constants';
|
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 =
|
export type CrewMember =
|
||||||
| 'commander'
|
| 'commander'
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ export interface ShotLayerBase {
|
|||||||
index: number;
|
index: number;
|
||||||
thickness: number;
|
thickness: number;
|
||||||
point: Vector3;
|
point: Vector3;
|
||||||
shellNormal: Vector3;
|
|
||||||
surfaceNormal: Vector3;
|
surfaceNormal: Vector3;
|
||||||
status: 'blocked' | 'penetration' | 'ricochet';
|
status: 'blocked' | 'penetration' | 'ricochet';
|
||||||
}
|
}
|
||||||
@@ -74,8 +73,11 @@ export interface ShotLayerGap {
|
|||||||
export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap;
|
export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap;
|
||||||
|
|
||||||
export type Shot = {
|
export type Shot = {
|
||||||
|
status: 'penetration' | 'blocked' | 'ricochet' | 'splash';
|
||||||
|
damage: number;
|
||||||
point: Vector3;
|
point: Vector3;
|
||||||
layers: ShotLayer[];
|
layersIn: ShotLayer[];
|
||||||
|
layersOut?: ShotLayer[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ArmorPiercingLayer =
|
export type ArmorPiercingLayer =
|
||||||
|
|||||||
Reference in new issue
Block a user