feat: custom shell functionality

This commit is contained in:
tresabhi committed 2024-11-10 20:59:01 -06:00
1 parent 9a1c66932a
commit dbc3d0df3a
6 files changed
+58 -21

No files matched your search

@@ -1,5 +1,4 @@
import { import {
type ShellDefinition,
canSplash, canSplash,
isExplosive, isExplosive,
resolvePenetrationCoefficient, resolvePenetrationCoefficient,
@@ -11,6 +10,7 @@ import { degToRad } from 'three/src/math/MathUtils.js';
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 { Duel, type EquipmentMatrix } from '../../../../stores/duel'; import { Duel, type EquipmentMatrix } from '../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent'; import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent';
import fragmentShader from './shaders/fragment.glsl?raw'; import fragmentShader from './shaders/fragment.glsl?raw';
import vertexShader from './shaders/vertex.glsl?raw'; import vertexShader from './shaders/vertex.glsl?raw';
@@ -31,6 +31,7 @@ export function PrimaryArmorSceneComponent({
}: PrimaryArmorSceneComponentProps) { }: PrimaryArmorSceneComponentProps) {
const tankopediaPersistentStore = TankopediaPersistent.useStore(); const tankopediaPersistentStore = TankopediaPersistent.useStore();
const duelStore = Duel.useStore(); const duelStore = Duel.useStore();
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
const material = new ShaderMaterial({ const material = new ShaderMaterial({
fragmentShader, fragmentShader,
vertexShader, vertexShader,
@@ -58,7 +59,11 @@ export function PrimaryArmorSceneComponent({
}); });
useEffect(() => { 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.caliber.value = shell.caliber;
material.uniforms.ricochet.value = degToRad( material.uniforms.ricochet.value = degToRad(
isExplosive(shell.type) ? 90 : shell.ricochet!, isExplosive(shell.type) ? 90 : shell.ricochet!,
@@ -71,7 +76,6 @@ export function PrimaryArmorSceneComponent({
material.uniforms.damage.value = shell.armor_damage; material.uniforms.damage.value = shell.armor_damage;
material.uniforms.explosionRadius.value = shell.explosion_radius; material.uniforms.explosionRadius.value = shell.explosion_radius;
const duel = duelStore.getState();
await handleProtagonistEquipmentChange(duel.protagonist.equipmentMatrix); await handleProtagonistEquipmentChange(duel.protagonist.equipmentMatrix);
await handleAntagonistEquipmentChange(duel.antagonist.equipmentMatrix); await handleAntagonistEquipmentChange(duel.antagonist.equipmentMatrix);
invalidate(); invalidate();
@@ -104,7 +108,8 @@ export function PrimaryArmorSceneComponent({
} }
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
const duel = duelStore.getState(); 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 penetration = shell.penetration.near;
const hasCalibratedShells = await hasEquipment( const hasCalibratedShells = await hasEquipment(
103, 103,
@@ -119,7 +124,7 @@ export function PrimaryArmorSceneComponent({
invalidate(); invalidate();
} }
handleShellChange(duelStore.getState().antagonist.shell); handleShellChange();
handleGreenPenetrationChange( handleGreenPenetrationChange(
tankopediaPersistentStore.getState().greenPenetration, tankopediaPersistentStore.getState().greenPenetration,
); );
@@ -134,6 +139,10 @@ export function PrimaryArmorSceneComponent({
const unsubscribes = [ const unsubscribes = [
duelStore.subscribe((state) => state.antagonist.shell, handleShellChange), duelStore.subscribe((state) => state.antagonist.shell, handleShellChange),
tankopediaEphemeralStore.subscribe(
(state) => state.customShell,
handleShellChange,
),
tankopediaPersistentStore.subscribe( tankopediaPersistentStore.subscribe(
(state) => state.greenPenetration, (state) => state.greenPenetration,
handleGreenPenetrationChange, handleGreenPenetrationChange,
@@ -1,7 +1,4 @@
import { import { resolvePenetrationCoefficient } from '@blitzkit/core';
resolvePenetrationCoefficient,
type ShellDefinition,
} from '@blitzkit/core';
import { invalidate } from '@react-three/fiber'; import { invalidate } from '@react-three/fiber';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { import {
@@ -15,6 +12,7 @@ import type { ArmorUserData, ExternalModuleVariant } from '../..';
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 { Duel, type EquipmentMatrix } from '../../../../../../stores/duel'; import { Duel, type EquipmentMatrix } from '../../../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
import { ArmorType } from '../../../SpacedArmorScene'; import { ArmorType } from '../../../SpacedArmorScene';
import fragmentShader from './shaders/fragment.glsl?raw'; import fragmentShader from './shaders/fragment.glsl?raw';
import vertexShader from './shaders/vertex.glsl?raw'; import vertexShader from './shaders/vertex.glsl?raw';
@@ -32,6 +30,7 @@ export function SpacedArmorSubExternal({
variant, variant,
clip, clip,
}: SpacedArmorSubExternalProps) { }: SpacedArmorSubExternalProps) {
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
const duelStore = Duel.useStore(); const duelStore = Duel.useStore();
const material = new ShaderMaterial({ const material = new ShaderMaterial({
fragmentShader, fragmentShader,
@@ -50,7 +49,10 @@ export function SpacedArmorSubExternal({
}); });
useEffect(() => { 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.penetration.value = shell.penetration.near;
invalidate(); invalidate();
} }
@@ -71,7 +73,8 @@ export function SpacedArmorSubExternal({
} }
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
const duel = duelStore.getState(); 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 penetration = shell.penetration.near;
const hasCalibratedShells = await hasEquipment( const hasCalibratedShells = await hasEquipment(
103, 103,
@@ -86,7 +89,7 @@ export function SpacedArmorSubExternal({
invalidate(); invalidate();
} }
handleShellChange(duelStore.getState().antagonist.shell); handleShellChange();
handleProtagonistEquipmentChange( handleProtagonistEquipmentChange(
duelStore.getState().protagonist.equipmentMatrix, duelStore.getState().protagonist.equipmentMatrix,
); );
@@ -96,6 +99,10 @@ export function SpacedArmorSubExternal({
const unsubscribes = [ const unsubscribes = [
duelStore.subscribe((state) => state.antagonist.shell, handleShellChange), duelStore.subscribe((state) => state.antagonist.shell, handleShellChange),
tankopediaEphemeralStore.subscribe(
(state) => state.customShell,
handleShellChange,
),
duelStore.subscribe( duelStore.subscribe(
(state) => state.protagonist.equipmentMatrix, (state) => state.protagonist.equipmentMatrix,
handleProtagonistEquipmentChange, handleProtagonistEquipmentChange,
@@ -1,8 +1,4 @@
import { import { isExplosive, resolvePenetrationCoefficient } from '@blitzkit/core';
isExplosive,
resolvePenetrationCoefficient,
type ShellDefinition,
} from '@blitzkit/core';
import { invalidate } from '@react-three/fiber'; import { invalidate } from '@react-three/fiber';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { import {
@@ -16,6 +12,7 @@ import type { ArmorUserData } from '../..';
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 { Duel, type EquipmentMatrix } from '../../../../../../stores/duel'; import { Duel, type EquipmentMatrix } from '../../../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
import { ArmorType } from '../../../SpacedArmorScene'; import { ArmorType } from '../../../SpacedArmorScene';
import fragmentShader from './shaders/fragment.glsl?raw'; import fragmentShader from './shaders/fragment.glsl?raw';
import vertexShader from './shaders/vertex.glsl?raw'; import vertexShader from './shaders/vertex.glsl?raw';
@@ -34,6 +31,7 @@ export function SpacedArmorSubSpaced({
node, node,
thickness, thickness,
}: SpacedArmorSubSpacedProps) { }: SpacedArmorSubSpacedProps) {
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
const duelStore = Duel.useStore(); const duelStore = Duel.useStore();
const material = new ShaderMaterial({ const material = new ShaderMaterial({
fragmentShader, fragmentShader,
@@ -53,7 +51,11 @@ export function SpacedArmorSubSpaced({
}); });
useEffect(() => { 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.penetration.value = shell.penetration.near;
material.uniforms.caliber.value = shell.caliber; material.uniforms.caliber.value = shell.caliber;
material.uniforms.ricochet.value = degToRad( material.uniforms.ricochet.value = degToRad(
@@ -82,7 +84,8 @@ export function SpacedArmorSubSpaced({
} }
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
const duel = duelStore.getState(); 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 penetration = shell.penetration.near;
const hasCalibratedShells = await hasEquipment( const hasCalibratedShells = await hasEquipment(
103, 103,
@@ -97,7 +100,7 @@ export function SpacedArmorSubSpaced({
invalidate(); invalidate();
} }
handleShellChange(duelStore.getState().antagonist.shell); handleShellChange();
handleProtagonistEquipmentChange( handleProtagonistEquipmentChange(
duelStore.getState().protagonist.equipmentMatrix, duelStore.getState().protagonist.equipmentMatrix,
); );
@@ -107,6 +110,10 @@ export function SpacedArmorSubSpaced({
const unsubscribes = [ const unsubscribes = [
duelStore.subscribe((state) => state.antagonist.shell, handleShellChange), duelStore.subscribe((state) => state.antagonist.shell, handleShellChange),
tankopediaEphemeralStore.subscribe(
(state) => state.customShell,
handleShellChange,
),
duelStore.subscribe( duelStore.subscribe(
(state) => state.protagonist.equipmentMatrix, (state) => state.protagonist.equipmentMatrix,
handleProtagonistEquipmentChange, handleProtagonistEquipmentChange,
@@ -88,7 +88,8 @@ export function SpacedArmorSceneComponent({
remainingPenetrationInput?: number, remainingPenetrationInput?: number,
) => { ) => {
const { antagonist, protagonist } = duelStore.getState(); 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 cameraNormal = camera.position.clone().sub(point).normalize();
const shot: Shot = { const shot: Shot = {
splashRadius: shell.explosion_radius, splashRadius: shell.explosion_radius,
@@ -200,6 +200,14 @@ export function CustomShellButton() {
Cancel Cancel
</Button> </Button>
</Dialog.Close> </Dialog.Close>
<Button
color="red"
onClick={() => {
setCustomShellDraft(antagonistShell);
}}
>
Reset
</Button>
<Dialog.Close> <Dialog.Close>
<Button <Button
onClick={() => { onClick={() => {
@@ -6,6 +6,7 @@ import { NAVBAR_HEIGHT } from '../../../constants/navbar';
import { Var } from '../../../core/radix/var'; import { Var } from '../../../core/radix/var';
import { useFullScreen } from '../../../hooks/useFullScreen'; import { useFullScreen } from '../../../hooks/useFullScreen';
import { Duel } from '../../../stores/duel'; import { Duel } from '../../../stores/duel';
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps'; import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
import type { ThicknessRange } from '../../Armor/components/StaticArmor'; import type { ThicknessRange } from '../../Armor/components/StaticArmor';
import { classIcons } from '../../ClassIcon'; import { classIcons } from '../../ClassIcon';
@@ -46,6 +47,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
}, [protagonist]); }, [protagonist]);
const duelStore = Duel.useStore(); const duelStore = Duel.useStore();
const mutateDuel = Duel.useMutation(); const mutateDuel = Duel.useMutation();
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
useEffect(() => { useEffect(() => {
function handleKeyDown(event: KeyboardEvent) { function handleKeyDown(event: KeyboardEvent) {
@@ -57,6 +59,9 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
mutateDuel((draft) => { mutateDuel((draft) => {
draft.antagonist.shell = shells[index]; draft.antagonist.shell = shells[index];
}); });
mutateTankopediaEphemeral((draft) => {
draft.customShell = undefined;
});
} }
}); });
} }