feat: equalizer for visual armor sandbox

This commit is contained in:
TrèsAbhi committed 2026-04-03 12:31:46 -05:00
1 parent 145de2bbbc
commit 1a008a4852
5 files changed
+96 -55

No files matched your search

@@ -19,7 +19,7 @@ import {
import { degToRad } from "three/src/math/MathUtils.js";
import { hasEquipment } from "../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../core/blitzkit/jsxTree";
import { Duel, type EquipmentMatrix } from "../../../../stores/duel";
import { Duel } from "../../../../stores/duel";
import { Tankopedia } from "../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
import { transitionEvent } from "../../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
@@ -98,14 +98,8 @@ export function PrimaryArmorSceneComponent({
material.uniforms.damage.value = shell.armor_damage;
material.uniforms.explosionRadius.value = shell.explosion_radius;
handleProtagonistEquipmentChange(
Duel.state.protagonist.equipmentMatrix,
true,
);
handleAntagonistEquipmentChange(
Duel.state.antagonist.equipmentMatrix,
true,
);
handleProtagonistEquipmentChange(true);
handleAntagonistEquipmentChange(true);
invalidate();
}
@@ -122,25 +116,25 @@ export function PrimaryArmorSceneComponent({
function handleWireframeChange(wireframe: boolean) {
material.wireframe = wireframe;
}
function handleProtagonistEquipmentChange(
equipment: EquipmentMatrix,
noInvalidate = false,
) {
function handleProtagonistEquipmentChange(noInvalidate = false) {
const equipment = Duel.state.protagonist.equipmentMatrix;
const hasEnhancedArmor = hasEquipment(
110,
Duel.state.protagonist.tank.equipment_preset,
equipment,
);
material.uniforms.thickness.value = hasEnhancedArmor
? thickness * 1.03
: thickness;
const equalizer =
(Duel.state.equalize
? Duel.state.protagonist.tank.equalizer?.armor
: undefined) ?? 1;
material.uniforms.thickness.value =
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
if (!noInvalidate) invalidate();
}
function handleAntagonistEquipmentChange(
equipment: EquipmentMatrix,
noInvalidate = false,
) {
function handleAntagonistEquipmentChange(noInvalidate = false) {
const equipment = Duel.state.antagonist.equipmentMatrix;
const tankopediaEphemeral = Tankopedia.state;
const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
@@ -150,10 +144,15 @@ export function PrimaryArmorSceneComponent({
Duel.state.antagonist.tank.equipment_preset,
equipment,
);
const equalize =
(Duel.state.equalize
? Duel.state.antagonist.tank.equalizer?.penetration
: undefined) ?? 1;
material.uniforms.penetration.value =
penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
resolvePenetrationCoefficient(hasCalibratedShells, shell.type) *
equalize;
if (!noInvalidate) invalidate();
}
@@ -169,8 +168,8 @@ export function PrimaryArmorSceneComponent({
);
handleOpaqueChange(TankopediaPersistent.state.opaque);
handleWireframeChange(TankopediaPersistent.state.wireframe);
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
transitionEvent.on(handleTransitionEvent);
@@ -188,11 +187,18 @@ export function PrimaryArmorSceneComponent({
TankopediaPersistent.on((state) => state.opaque, handleOpaqueChange),
Duel.on(
(state) => state.protagonist.equipmentMatrix,
(equipment) => handleProtagonistEquipmentChange(equipment),
() => handleProtagonistEquipmentChange(),
),
Duel.on(
(state) => state.antagonist.equipmentMatrix,
(equipment) => handleAntagonistEquipmentChange(equipment),
() => handleAntagonistEquipmentChange(),
),
Duel.on(
(state) => state.equalize,
() => {
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
},
),
() => transitionEvent.off(handleTransitionEvent),
];
@@ -52,7 +52,7 @@ export function ShotDisplay() {
useEffect(() => {
if (shot?.splashRadius !== undefined) {
const outlineGeometry = new BufferGeometry().setFromPoints(
new Path().absarc(0, 0, 1, 0, Math.PI * 2).getSpacedPoints(50)
new Path().absarc(0, 0, 1, 0, Math.PI * 2).getSpacedPoints(50),
);
const outlineMaterial = new LineDashedMaterial({
color: "orange",
@@ -61,7 +61,7 @@ export function ShotDisplay() {
});
const outline = new Line(
outlineGeometry,
outlineMaterial
outlineMaterial,
).computeLineDistances();
splashRadiusWrapper.current?.add(outline);
@@ -134,11 +134,11 @@ export function ShotDisplay() {
const outLength =
shot.out && shot.out.layers.length > 0
? (shot.in.layers.at(-1) as ShotLayerNonExternal).point.distanceTo(
(shot.out.layers.at(-1) as ShotLayerNonExternal).point
(shot.out.layers.at(-1) as ShotLayerNonExternal).point,
)
: SHOT_DISPLAY_LENGTH_INFINITY;
const inLast = shot.in.layers.findLast(
(layer) => layer.type !== null
(layer) => layer.type !== null,
) as ShotLayerNonExternal;
const outTitleColor = shot.out
? shotStatusColors[shot.out.status]
@@ -154,7 +154,7 @@ export function ShotDisplay() {
position={inLast.point}
ref={splashRadiusWrapper}
rotation={new Euler().setFromQuaternion(
new Quaternion().setFromUnitVectors(K_HAT, shot.in.surfaceNormal)
new Quaternion().setFromUnitVectors(K_HAT, shot.in.surfaceNormal),
)}
>
<mesh>
@@ -225,7 +225,7 @@ export function ShotDisplay() {
<group
position={inLast.point}
rotation={new Euler().setFromQuaternion(
new Quaternion().setFromUnitVectors(J_HAT, shot.in.surfaceNormal)
new Quaternion().setFromUnitVectors(J_HAT, shot.in.surfaceNormal),
)}
>
<mesh position={[0, inLength / 2, 0]} renderOrder={2}>
@@ -253,8 +253,8 @@ export function ShotDisplay() {
rotation={new Euler().setFromQuaternion(
new Quaternion().setFromUnitVectors(
J_HAT,
shot.out.surfaceNormal.clone()
)
shot.out.surfaceNormal.clone(),
),
)}
>
<mesh position={[0, outLength / 2, 0]} renderOrder={2}>
@@ -10,7 +10,7 @@ import {
import type { ArmorUserData, ExternalModuleVariant } from "../..";
import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../../../core/blitzkit/jsxTree";
import { Duel, type EquipmentMatrix } from "../../../../../../stores/duel";
import { Duel } from "../../../../../../stores/duel";
import { Tankopedia } from "../../../../../../stores/tankopedia";
import { ArmorType } from "../../../SpacedArmorScene";
import fragmentShader from "./shaders/fragment.glsl?raw";
@@ -52,17 +52,23 @@ export function SpacedArmorSubExternal({
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
material.uniforms.penetration.value = shell.penetration!.near;
}
function handleProtagonistEquipmentChange(equipment: EquipmentMatrix) {
function handleProtagonistEquipmentChange() {
const equipment = Duel.state.protagonist.equipmentMatrix;
const hasEnhancedArmor = hasEquipment(
110,
Duel.state.protagonist.tank.equipment_preset,
equipment,
);
material.uniforms.thickness.value = hasEnhancedArmor
? thickness * 1.03
: thickness;
const equalizer =
(Duel.state.equalize
? Duel.state.protagonist.tank.equalizer?.armor
: undefined) ?? 1;
material.uniforms.thickness.value =
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
}
function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
function handleAntagonistEquipmentChange() {
const equipment = Duel.state.antagonist.equipmentMatrix;
const tankopediaEphemeral = Tankopedia.state;
const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
@@ -72,15 +78,20 @@ export function SpacedArmorSubExternal({
Duel.state.antagonist.tank.equipment_preset,
equipment,
);
const equalize =
(Duel.state.equalize
? Duel.state.antagonist.tank.equalizer?.penetration
: undefined) ?? 1;
material.uniforms.penetration.value =
penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
resolvePenetrationCoefficient(hasCalibratedShells, shell.type) *
equalize;
}
handleShellChange();
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
const unsubscribes = [
Duel.on((state) => state.antagonist.shell, handleShellChange),
@@ -93,6 +104,13 @@ export function SpacedArmorSubExternal({
(state) => state.antagonist.equipmentMatrix,
handleAntagonistEquipmentChange,
),
Duel.on(
(state) => state.equalize,
() => {
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
},
),
];
return () => {
@@ -10,7 +10,7 @@ import { degToRad } from "three/src/math/MathUtils.js";
import type { ArmorUserData } from "../..";
import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../../../core/blitzkit/jsxTree";
import { Duel, type EquipmentMatrix } from "../../../../../../stores/duel";
import { Duel } from "../../../../../../stores/duel";
import { Tankopedia } from "../../../../../../stores/tankopedia";
import { ArmorType } from "../../../SpacedArmorScene";
import fragmentShader from "./shaders/fragment.glsl?raw";
@@ -62,17 +62,23 @@ export function SpacedArmorSubSpaced({
shell.normalization ?? 0,
);
}
function handleProtagonistEquipmentChange(equipment: EquipmentMatrix) {
function handleProtagonistEquipmentChange() {
const equipment = Duel.state.protagonist.equipmentMatrix;
const hasEnhancedArmor = hasEquipment(
110,
Duel.state.protagonist.tank.equipment_preset,
equipment,
);
material.uniforms.thickness.value = hasEnhancedArmor
? thickness * 1.03
: thickness;
const equalizer =
(Duel.state.equalize
? Duel.state.protagonist.tank.equalizer?.armor
: undefined) ?? 1;
material.uniforms.thickness.value =
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
}
function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
function handleAntagonistEquipmentChange() {
const equipment = Duel.state.antagonist.equipmentMatrix;
const tankopediaEphemeral = Tankopedia.state;
const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
@@ -82,15 +88,20 @@ export function SpacedArmorSubSpaced({
Duel.state.antagonist.tank.equipment_preset,
equipment,
);
const equalize =
(Duel.state.equalize
? Duel.state.antagonist.tank.equalizer?.penetration
: undefined) ?? 1;
material.uniforms.penetration.value =
penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type);
resolvePenetrationCoefficient(hasCalibratedShells, shell.type) *
equalize;
}
handleShellChange();
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
const unsubscribes = [
Duel.on((state) => state.antagonist.shell, handleShellChange),
@@ -103,6 +114,13 @@ export function SpacedArmorSubSpaced({
(state) => state.antagonist.equipmentMatrix,
handleAntagonistEquipmentChange,
),
Duel.on(
(state) => state.equalize,
() => {
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
},
),
];
return () => {
@@ -18,7 +18,6 @@ import {
import { degToRad } from "three/src/math/MathUtils.js";
import { hasEquipment } from "../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../core/blitzkit/jsxTree";
import { defaultEqualizer } from "../../../../core/blitzkit/tankToDuelMember";
import { discardClippingPlane } from "../../../../core/three/discardClippingPlane";
import { Duel } from "../../../../stores/duel";
import {
@@ -90,8 +89,8 @@ export function SpacedArmorSceneComponent({
const shell = customShell ?? Duel.state.antagonist.shell;
const { equalize } = Duel.state;
const equalizer =
(equalize ? Duel.state.antagonist.tank.equalizer : undefined) ??
defaultEqualizer;
(equalize ? Duel.state.antagonist.tank.equalizer?.damage : undefined) ??
1;
const cameraNormal = camera.position.clone().sub(point).normalize();
const shot: Shot = {
splashRadius:
@@ -372,7 +371,7 @@ export function SpacedArmorSceneComponent({
}
}
shot.damage *= equalizer.damage;
shot.damage *= equalizer;
return shot;
},