mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: complete math
This commit is contained in:
1 parent
a2bf592eaf
commit
dc50c4a758
4 files changed
+43
-275
No files matched your search
@@ -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(
|
||||
|
||||
+5
-4
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user