mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: custom shell functionality
This commit is contained in:
1 parent
9a1c66932a
commit
dbc3d0df3a
6 files changed
+58
-21
No files matched your search
+14
-5
@@ -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,
|
||||||
|
|||||||
+14
-7
@@ -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,
|
||||||
|
|||||||
+15
-8
@@ -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,
|
||||||
|
|||||||
+2
-1
@@ -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,
|
||||||
|
|||||||
+8
@@ -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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user