diff --git a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx index 5fb0e17f2..b78ecaf0e 100644 --- a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx +++ b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx @@ -1,5 +1,4 @@ import { - type ShellDefinition, canSplash, isExplosive, resolvePenetrationCoefficient, @@ -11,6 +10,7 @@ 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 { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral'; import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent'; import fragmentShader from './shaders/fragment.glsl?raw'; import vertexShader from './shaders/vertex.glsl?raw'; @@ -31,6 +31,7 @@ export function PrimaryArmorSceneComponent({ }: PrimaryArmorSceneComponentProps) { const tankopediaPersistentStore = TankopediaPersistent.useStore(); const duelStore = Duel.useStore(); + const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); const material = new ShaderMaterial({ fragmentShader, vertexShader, @@ -58,7 +59,11 @@ export function PrimaryArmorSceneComponent({ }); useEffect(() => { - async function handleShellChange(shell: ShellDefinition) { + async function handleShellChange() { + const duel = duelStore.getState(); + const tankopediaEphemeral = tankopediaEphemeralStore.getState(); + const shell = tankopediaEphemeral.customShell ?? duel.antagonist.shell; + material.uniforms.caliber.value = shell.caliber; material.uniforms.ricochet.value = degToRad( isExplosive(shell.type) ? 90 : shell.ricochet!, @@ -71,7 +76,6 @@ export function PrimaryArmorSceneComponent({ material.uniforms.damage.value = shell.armor_damage; material.uniforms.explosionRadius.value = shell.explosion_radius; - const duel = duelStore.getState(); await handleProtagonistEquipmentChange(duel.protagonist.equipmentMatrix); await handleAntagonistEquipmentChange(duel.antagonist.equipmentMatrix); invalidate(); @@ -104,7 +108,8 @@ export function PrimaryArmorSceneComponent({ } async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { const duel = duelStore.getState(); - const shell = duel.antagonist.shell; + const tankopediaEphemeral = tankopediaEphemeralStore.getState(); + const shell = tankopediaEphemeral.customShell ?? duel.antagonist.shell; const penetration = shell.penetration.near; const hasCalibratedShells = await hasEquipment( 103, @@ -119,7 +124,7 @@ export function PrimaryArmorSceneComponent({ invalidate(); } - handleShellChange(duelStore.getState().antagonist.shell); + handleShellChange(); handleGreenPenetrationChange( tankopediaPersistentStore.getState().greenPenetration, ); @@ -134,6 +139,10 @@ export function PrimaryArmorSceneComponent({ const unsubscribes = [ duelStore.subscribe((state) => state.antagonist.shell, handleShellChange), + tankopediaEphemeralStore.subscribe( + (state) => state.customShell, + handleShellChange, + ), tankopediaPersistentStore.subscribe( (state) => state.greenPenetration, handleGreenPenetrationChange, diff --git a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx index aafd08e77..4d4a162eb 100644 --- a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx +++ b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx @@ -1,7 +1,4 @@ -import { - resolvePenetrationCoefficient, - type ShellDefinition, -} from '@blitzkit/core'; +import { resolvePenetrationCoefficient } from '@blitzkit/core'; import { invalidate } from '@react-three/fiber'; import { useEffect } from 'react'; import { @@ -15,6 +12,7 @@ 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 { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral'; import { ArmorType } from '../../../SpacedArmorScene'; import fragmentShader from './shaders/fragment.glsl?raw'; import vertexShader from './shaders/vertex.glsl?raw'; @@ -32,6 +30,7 @@ export function SpacedArmorSubExternal({ variant, clip, }: SpacedArmorSubExternalProps) { + const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); const duelStore = Duel.useStore(); const material = new ShaderMaterial({ fragmentShader, @@ -50,7 +49,10 @@ export function SpacedArmorSubExternal({ }); useEffect(() => { - function handleShellChange(shell: ShellDefinition) { + function handleShellChange() { + const duel = duelStore.getState(); + const tankopediaEphemeral = tankopediaEphemeralStore.getState(); + const shell = tankopediaEphemeral.customShell ?? duel.antagonist.shell; material.uniforms.penetration.value = shell.penetration.near; invalidate(); } @@ -71,7 +73,8 @@ export function SpacedArmorSubExternal({ } async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { const duel = duelStore.getState(); - const shell = duel.antagonist.shell; + const tankopediaEphemeral = tankopediaEphemeralStore.getState(); + const shell = tankopediaEphemeral.customShell ?? duel.antagonist.shell; const penetration = shell.penetration.near; const hasCalibratedShells = await hasEquipment( 103, @@ -86,7 +89,7 @@ export function SpacedArmorSubExternal({ invalidate(); } - handleShellChange(duelStore.getState().antagonist.shell); + handleShellChange(); handleProtagonistEquipmentChange( duelStore.getState().protagonist.equipmentMatrix, ); @@ -96,6 +99,10 @@ export function SpacedArmorSubExternal({ const unsubscribes = [ duelStore.subscribe((state) => state.antagonist.shell, handleShellChange), + tankopediaEphemeralStore.subscribe( + (state) => state.customShell, + handleShellChange, + ), duelStore.subscribe( (state) => state.protagonist.equipmentMatrix, handleProtagonistEquipmentChange, diff --git a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx index dcf4afb79..0dbba9c45 100644 --- a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx +++ b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx @@ -1,8 +1,4 @@ -import { - isExplosive, - resolvePenetrationCoefficient, - type ShellDefinition, -} from '@blitzkit/core'; +import { isExplosive, resolvePenetrationCoefficient } from '@blitzkit/core'; import { invalidate } from '@react-three/fiber'; import { useEffect } from 'react'; import { @@ -16,6 +12,7 @@ import type { ArmorUserData } from '../..'; import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment'; import { jsxTree } from '../../../../../../core/blitzkit/jsxTree'; import { Duel, type EquipmentMatrix } from '../../../../../../stores/duel'; +import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral'; import { ArmorType } from '../../../SpacedArmorScene'; import fragmentShader from './shaders/fragment.glsl?raw'; import vertexShader from './shaders/vertex.glsl?raw'; @@ -34,6 +31,7 @@ export function SpacedArmorSubSpaced({ node, thickness, }: SpacedArmorSubSpacedProps) { + const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); const duelStore = Duel.useStore(); const material = new ShaderMaterial({ fragmentShader, @@ -53,7 +51,11 @@ export function SpacedArmorSubSpaced({ }); useEffect(() => { - function handleShellChange(shell: ShellDefinition) { + function handleShellChange() { + const duel = duelStore.getState(); + const tankopediaEphemeral = tankopediaEphemeralStore.getState(); + const shell = tankopediaEphemeral.customShell ?? duel.antagonist.shell; + material.uniforms.penetration.value = shell.penetration.near; material.uniforms.caliber.value = shell.caliber; material.uniforms.ricochet.value = degToRad( @@ -82,7 +84,8 @@ export function SpacedArmorSubSpaced({ } async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { const duel = duelStore.getState(); - const shell = duelStore.getState().antagonist.shell; + const tankopediaEphemeral = tankopediaEphemeralStore.getState(); + const shell = tankopediaEphemeral.customShell ?? duel.antagonist.shell; const penetration = shell.penetration.near; const hasCalibratedShells = await hasEquipment( 103, @@ -97,7 +100,7 @@ export function SpacedArmorSubSpaced({ invalidate(); } - handleShellChange(duelStore.getState().antagonist.shell); + handleShellChange(); handleProtagonistEquipmentChange( duelStore.getState().protagonist.equipmentMatrix, ); @@ -107,6 +110,10 @@ export function SpacedArmorSubSpaced({ const unsubscribes = [ duelStore.subscribe((state) => state.antagonist.shell, handleShellChange), + tankopediaEphemeralStore.subscribe( + (state) => state.customShell, + handleShellChange, + ), duelStore.subscribe( (state) => state.protagonist.equipmentMatrix, handleProtagonistEquipmentChange, diff --git a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx index d1ee159ef..5f038985e 100644 --- a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx +++ b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx @@ -88,7 +88,8 @@ export function SpacedArmorSceneComponent({ remainingPenetrationInput?: number, ) => { const { antagonist, protagonist } = duelStore.getState(); - const shell = antagonist.shell; + const { customShell } = tankopediaEphemeralStore.getState(); + const shell = customShell ?? antagonist.shell; const cameraNormal = camera.position.clone().sub(point).normalize(); const shot: Shot = { splashRadius: shell.explosion_radius, diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/Options/components/CustomShellButton.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/Options/components/CustomShellButton.tsx index 495b6b802..815df2d02 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/Options/components/CustomShellButton.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/Options/components/CustomShellButton.tsx @@ -200,6 +200,14 @@ export function CustomShellButton() { Cancel +