Files
blitzkit/packages/website/src/components/SpacedArmorSubSpaced/index.tsx
T

184 lines
5.0 KiB
TypeScript
Raw Normal View History

2025-09-01 22:56:39 -05:00
import { isExplosive, resolvePenetrationCoefficient } from "@blitzkit/core";
import { useEffect } from "react";
2024-02-27 17:15:24 -06:00
import {
AdditiveBlending,
MeshBasicMaterial,
ShaderMaterial,
2024-09-22 21:12:18 -05:00
type Object3D,
2025-09-01 22:56:39 -05:00
} from "three";
import { degToRad } from "three/src/math/MathUtils.js";
2026-09-27 16:46:22 -05:00
import { jsxTree } from "../../core/blitzkit/jsxTree";
2026-09-30 17:47:23 -05:00
import { DuelModule, getDuel } from "../../hooks/useDuel";
import { hasEquipment } from "../../hooks/useEquipment";
2026-09-27 16:46:22 -05:00
import { Tankopedia } from "../../stores/tankopedia";
2026-09-30 17:47:23 -05:00
import { ArmorType } from "../SpacedArmorScene";
import type { ArmorUserData } from "../SpacedArmorSceneComponent";
2025-09-01 22:56:39 -05:00
import fragmentShader from "./shaders/fragment.glsl?raw";
import vertexShader from "./shaders/vertex.glsl?raw";
2024-02-27 17:15:24 -06:00
interface SpacedArmorSubSpacedProps {
node: Object3D;
thickness: number;
}
const depthWriteMaterial = new MeshBasicMaterial({
depthWrite: true,
colorWrite: false,
});
export function SpacedArmorSubSpaced({
node,
thickness,
}: SpacedArmorSubSpacedProps) {
const material = new ShaderMaterial({
fragmentShader,
vertexShader,
depthTest: true,
depthWrite: false,
blending: AdditiveBlending,
uniforms: {
2024-02-27 18:08:50 -06:00
thickness: { value: null },
2024-02-27 17:15:24 -06:00
penetration: { value: null },
caliber: { value: null },
ricochet: { value: null },
normalization: { value: null },
},
});
useEffect(() => {
2024-11-10 20:59:01 -06:00
function handleShellChange() {
2025-09-01 22:56:39 -05:00
const tankopediaEphemeral = Tankopedia.state;
2025-09-02 16:24:13 -05:00
const shell =
2026-09-30 17:47:23 -05:00
tankopediaEphemeral.customShell ??
getDuel("antagonist", DuelModule.Shell);
2024-11-10 20:59:01 -06:00
2026-03-31 14:11:45 -05:00
material.uniforms.penetration.value = shell.penetration!.near;
2024-02-27 17:15:24 -06:00
material.uniforms.caliber.value = shell.caliber;
2024-05-24 13:48:50 -05:00
material.uniforms.ricochet.value = degToRad(
2026-03-31 14:11:45 -05:00
isExplosive(shell.type) ? 90 : shell.ricochet!,
2024-05-24 13:48:50 -05:00
);
2024-02-27 17:15:24 -06:00
material.uniforms.normalization.value = degToRad(
2026-03-31 14:11:45 -05:00
shell.normalization ?? 0,
2024-02-27 17:15:24 -06:00
);
}
2026-04-03 12:31:46 -05:00
function handleProtagonistEquipmentChange() {
2026-09-30 17:47:23 -05:00
const protagonist = getDuel("protagonist", DuelModule.Tank);
2025-08-29 13:42:44 -05:00
const hasEnhancedArmor = hasEquipment(
2024-04-04 13:16:12 -05:00
110,
2026-09-30 17:47:23 -05:00
protagonist.equipment_preset,
Tankopedia.state.protagonist.equipment,
2024-04-04 13:16:12 -05:00
);
2026-04-03 12:31:46 -05:00
const equalizer =
2026-09-30 17:47:23 -05:00
(Tankopedia.state.equalize
? protagonist.equalizer?.armor
2026-04-03 12:31:46 -05:00
: undefined) ?? 1;
material.uniforms.thickness.value =
2026-07-31 19:48:03 -05:00
thickness * (hasEnhancedArmor ? 1.04 : 1) * equalizer;
2024-02-27 18:08:50 -06:00
}
2026-04-03 12:31:46 -05:00
function handleAntagonistEquipmentChange() {
2026-09-30 17:47:23 -05:00
const antagonist = getDuel("antagonist", DuelModule.Tank);
2025-09-01 22:56:39 -05:00
const tankopediaEphemeral = Tankopedia.state;
2025-09-02 16:24:13 -05:00
const shell =
2026-09-30 17:47:23 -05:00
tankopediaEphemeral.customShell ??
getDuel("antagonist", DuelModule.Shell);
2026-03-31 14:11:45 -05:00
const penetration = shell.penetration!.near;
2025-08-29 13:42:44 -05:00
const hasCalibratedShells = hasEquipment(
2024-04-04 13:16:12 -05:00
103,
2026-09-30 17:47:23 -05:00
antagonist.equipment_preset,
Tankopedia.state.antagonist.equipment,
2024-04-04 13:16:12 -05:00
);
2026-04-03 12:31:46 -05:00
const equalize =
2026-09-30 17:47:23 -05:00
(Tankopedia.state.equalize
? antagonist.equalizer?.penetration
2026-04-03 12:31:46 -05:00
: undefined) ?? 1;
2024-05-25 13:32:00 -05:00
material.uniforms.penetration.value =
penetration *
2026-04-03 12:57:43 -05:00
resolvePenetrationCoefficient(
hasCalibratedShells,
2026-09-30 17:47:23 -05:00
Tankopedia.state.equalize,
2026-04-03 12:57:43 -05:00
shell.type,
2026-09-30 17:47:23 -05:00
antagonist.equalizer,
2026-04-03 12:57:43 -05:00
) *
2026-04-03 12:31:46 -05:00
equalize;
2024-02-27 18:13:26 -06:00
}
2024-02-27 17:15:24 -06:00
2024-11-10 20:59:01 -06:00
handleShellChange();
2026-04-03 12:31:46 -05:00
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
2024-02-27 18:08:50 -06:00
const unsubscribes = [
2026-09-30 17:47:23 -05:00
Tankopedia.on((state) => state.antagonist.shell, handleShellChange),
2025-09-01 22:56:39 -05:00
Tankopedia.on((state) => state.customShell, handleShellChange),
2026-09-30 17:47:23 -05:00
Tankopedia.on(
(state) => state.protagonist.equipment,
2026-03-31 14:11:45 -05:00
handleProtagonistEquipmentChange,
2024-02-27 18:13:26 -06:00
),
2026-09-30 17:47:23 -05:00
Tankopedia.on(
(state) => state.antagonist.equipment,
2026-03-31 14:11:45 -05:00
handleAntagonistEquipmentChange,
2024-02-27 18:08:50 -06:00
),
2026-09-30 17:47:23 -05:00
Tankopedia.on(
2026-04-03 12:31:46 -05:00
(state) => state.equalize,
() => {
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
},
),
2024-02-27 18:08:50 -06:00
];
return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe());
};
2024-04-02 09:52:46 -05:00
});
2024-02-27 17:15:24 -06:00
return (
<>
2025-04-15 00:11:49 -05:00
{jsxTree(node, {
2025-04-15 16:29:14 -05:00
group(_, props, key) {
return <group {...props} key={`${key}-spaced-sub-spaced-exclude`} />;
},
2025-04-15 00:11:49 -05:00
mesh(_, props, key) {
return (
<mesh
{...props}
2025-04-15 16:29:14 -05:00
key={`${key}-spaced-sub-spaced-exclude`}
2025-04-15 00:11:49 -05:00
renderOrder={2}
material={material}
onClick={() => {}}
userData={
{
type: ArmorType.Spaced,
thickness,
} satisfies ArmorUserData
}
/>
);
2024-04-21 11:09:24 -05:00
},
2025-04-15 00:11:49 -05:00
})}
2024-04-13 10:36:13 -05:00
2025-04-15 00:11:49 -05:00
{jsxTree(node, {
2025-04-15 16:29:14 -05:00
group(_, props, key) {
return <group {...props} key={`${key}-spaced-sub-spaced-include`} />;
},
2025-04-15 00:11:49 -05:00
mesh(_, props, key) {
return (
<mesh
{...props}
2025-04-15 16:29:14 -05:00
key={`${key}-spaced-sub-spaced-include`}
2025-04-15 00:11:49 -05:00
renderOrder={5}
material={depthWriteMaterial}
/>
);
2024-04-21 11:09:24 -05:00
},
2025-04-15 00:11:49 -05:00
})}
2024-02-27 17:15:24 -06:00
</>
);
}