mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-22 19:19:52 +02:00
feat: upgrade tankopedia stores
This commit is contained in:
1 parent
5bf9f8ca9e
commit
84bbb05d43
26 files changed
+356
-356
No files matched your search
@@ -11,7 +11,7 @@ import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||
import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModelDefinitions } from '../../../hooks/useModelDefinitions';
|
||||
import { useDuel } from '../../../stores/duel';
|
||||
import { useTankopediaPersistent } from '../../../stores/tankopedia';
|
||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||
import { CoreArmorSceneComponent } from './CoreArmorSceneComponent';
|
||||
import { ShotDisplay } from './ShotDisplay';
|
||||
|
||||
@@ -20,7 +20,8 @@ export const CoreArmorScene = memo(() => {
|
||||
const modelDefinitions = useModelDefinitions();
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const initialTankopediaState = useTankopediaPersistent.getState();
|
||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||
const initialTankopediaState = tankopediaPersistentStore.getState();
|
||||
const protagonist = useDuel((draft) => draft.protagonist!);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -99,7 +100,7 @@ export const CoreArmorScene = memo(() => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = useTankopediaPersistent.subscribe(
|
||||
const unsubscribe = tankopediaPersistentStore.subscribe(
|
||||
(state) => state.mode,
|
||||
(mode) => {
|
||||
if (wrapper.current) wrapper.current.visible = mode === 'armor';
|
||||
|
||||
@@ -10,10 +10,7 @@ import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
||||
import { ShellDefinition } from '../../../../core/blitzkit/tankDefinitions';
|
||||
import { EquipmentMatrix, useDuel } from '../../../../stores/duel';
|
||||
import {
|
||||
TankopediaPersistent,
|
||||
useTankopediaPersistent,
|
||||
} from '../../../../stores/tankopedia';
|
||||
import * as TankopediaPersistent from '../../../../stores/tankopediaPersistent';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
import { spacedArmorRenderTarget } from './target';
|
||||
@@ -31,6 +28,7 @@ export function CoreArmorSceneComponent({
|
||||
node,
|
||||
thickness,
|
||||
}: CoreArmorSceneComponentProps) {
|
||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||
const material = new ShaderMaterial({
|
||||
fragmentShader,
|
||||
vertexShader,
|
||||
@@ -77,7 +75,7 @@ export function CoreArmorSceneComponent({
|
||||
await handleAntagonistEquipmentChange(duel.antagonist!.equipmentMatrix);
|
||||
}
|
||||
function handleVisualChange(
|
||||
visual: TankopediaPersistent['model']['visual'],
|
||||
visual: TankopediaPersistent.TankopediaPersistent['model']['visual'],
|
||||
) {
|
||||
material.uniforms.greenPenetration.value = visual.greenPenetration;
|
||||
material.uniforms.opaque.value = visual.opaque || visual.wireframe;
|
||||
@@ -113,7 +111,7 @@ export function CoreArmorSceneComponent({
|
||||
}
|
||||
|
||||
handleShellChange(useDuel.getState().antagonist!.shell);
|
||||
handleVisualChange(useTankopediaPersistent.getState().model.visual);
|
||||
handleVisualChange(tankopediaPersistentStore.getState().model.visual);
|
||||
handleProtagonistEquipmentChange(
|
||||
useDuel.getState().protagonist!.equipmentMatrix,
|
||||
);
|
||||
@@ -123,7 +121,7 @@ export function CoreArmorSceneComponent({
|
||||
|
||||
const unsubscribes = [
|
||||
useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange),
|
||||
useTankopediaPersistent.subscribe(
|
||||
tankopediaPersistentStore.subscribe(
|
||||
(state) => state.model.visual,
|
||||
handleVisualChange,
|
||||
),
|
||||
|
||||
@@ -4,15 +4,11 @@ import { useFrame } from '@react-three/fiber';
|
||||
import { ComponentProps, useRef } from 'react';
|
||||
import { Euler, Mesh, Quaternion } from 'three';
|
||||
import { J_HAT } from '../../../constants/axis';
|
||||
import {
|
||||
ShotLayerNonExternal,
|
||||
ShotStatus,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../stores/tankopedia';
|
||||
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||
import { ShotDisplayCard } from './ShotDisplayCard';
|
||||
|
||||
export const shotStatusColors: Record<
|
||||
ShotStatus,
|
||||
TankopediaEphemeral.ShotStatus,
|
||||
ComponentProps<typeof Text>['color']
|
||||
> = {
|
||||
blocked: 'red',
|
||||
@@ -27,7 +23,7 @@ const TRACER_THICK = 1 / 32;
|
||||
const TRACER_THIN = TRACER_THICK / 2;
|
||||
|
||||
export function ShotDisplay() {
|
||||
const shot = useTankopediaTemporary((state) => state.shot);
|
||||
const shot = TankopediaEphemeral.use((state) => state.shot);
|
||||
const inTracer = useRef<Mesh>(null);
|
||||
const outTracer = useRef<Mesh>(null);
|
||||
|
||||
@@ -55,13 +51,16 @@ export function ShotDisplay() {
|
||||
}, 0) + SHOT_DISPLAY_LENGTH_INFINITY;
|
||||
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.in.layers.at(-1) as TankopediaEphemeral.ShotLayerNonExternal
|
||||
).point.distanceTo(
|
||||
(shot.out.layers.at(-1) as TankopediaEphemeral.ShotLayerNonExternal)
|
||||
.point,
|
||||
)
|
||||
: SHOT_DISPLAY_LENGTH_INFINITY;
|
||||
const inLast = shot.in.layers.findLast(
|
||||
(layer) => layer.type !== null,
|
||||
) as ShotLayerNonExternal;
|
||||
) as TankopediaEphemeral.ShotLayerNonExternal;
|
||||
const outTitleColor = shot.out
|
||||
? shotStatusColors[shot.out.status]
|
||||
: undefined;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { Card, Flex, Inset, Separator, Text } from '@radix-ui/themes';
|
||||
import { ComponentProps } from 'react';
|
||||
import { radToDeg } from 'three/src/math/MathUtils';
|
||||
import {
|
||||
ShotLayer,
|
||||
ShotStatus,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../stores/tankopedia';
|
||||
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||
import { shotStatusColors } from './ShotDisplay';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
import { ExternalModuleVariant } from './SpacedArmorSceneComponent';
|
||||
@@ -22,7 +18,7 @@ const externalLayerNames: Record<ExternalModuleVariant, string> = {
|
||||
track: 'Tracks',
|
||||
};
|
||||
|
||||
const shotStatusNames: Record<ShotStatus, string> = {
|
||||
const shotStatusNames: Record<TankopediaEphemeral.ShotStatus, string> = {
|
||||
blocked: 'Blocked',
|
||||
ricochet: 'Ricochet',
|
||||
penetration: 'penetration',
|
||||
@@ -39,7 +35,7 @@ function LayerEntry({
|
||||
layerIndex?: string;
|
||||
shotStatusColor: ComponentProps<typeof Text>['color'];
|
||||
layerName: string;
|
||||
layer: ShotLayer;
|
||||
layer: TankopediaEphemeral.ShotLayer;
|
||||
angle?: number;
|
||||
}) {
|
||||
return (
|
||||
@@ -113,7 +109,7 @@ function LayerEntry({
|
||||
}
|
||||
|
||||
export function ShotDisplayCard() {
|
||||
const shot = useTankopediaTemporary((state) => state.shot);
|
||||
const shot = TankopediaEphemeral.use((state) => state.shot);
|
||||
|
||||
if (!shot) return null;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModel } from '../../../hooks/useModel';
|
||||
import { useModelDefinitions } from '../../../hooks/useModelDefinitions';
|
||||
import { useDuel } from '../../../stores/duel';
|
||||
import { useTankopediaPersistent } from '../../../stores/tankopedia';
|
||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||
import { SpacedArmorSceneComponent } from './SpacedArmorSceneComponent';
|
||||
|
||||
export enum ArmorType {
|
||||
@@ -30,7 +30,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
const modelDefinitions = useModelDefinitions();
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const initialTankopediaState = useTankopediaPersistent.getState();
|
||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||
const initialTankopediaState = tankopediaPersistentStore.getState();
|
||||
const protagonist = useDuel((draft) => draft.protagonist!);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -109,7 +110,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = useTankopediaPersistent.subscribe(
|
||||
const unsubscribe = tankopediaPersistentStore.subscribe(
|
||||
(state) => state.mode,
|
||||
(mode) => {
|
||||
if (wrapper.current) wrapper.current.visible = mode === 'armor';
|
||||
|
||||
@@ -17,12 +17,7 @@ import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
||||
import { ShellType } from '../../../../core/blitzkit/tankDefinitions';
|
||||
import { useDuel } from '../../../../stores/duel';
|
||||
import {
|
||||
Shot,
|
||||
ShotLayerBase,
|
||||
ShotLayerNonExternal,
|
||||
useTankopediaTemporary,
|
||||
} from '../../../../stores/tankopedia';
|
||||
import * as TankopediaEphemeral from '../../../../stores/tankopediaEphemeral';
|
||||
import { ArmorType } from '../SpacedArmorScene';
|
||||
import { SpacedArmorSubExternal } from './components/SpacedArmorSubExternal';
|
||||
import { SpacedArmorSubSpaced } from './components/SpacedArmorSubSpaced';
|
||||
@@ -77,6 +72,7 @@ export function SpacedArmorSceneComponent({
|
||||
clip,
|
||||
...props
|
||||
}: SpacedArmorSceneComponentProps) {
|
||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||
const camera = useThree((state) => state.camera);
|
||||
|
||||
async function shoot(
|
||||
@@ -88,7 +84,7 @@ export function SpacedArmorSceneComponent({
|
||||
const { antagonist, protagonist } = useDuel.getState();
|
||||
const shell = antagonist!.shell;
|
||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||
const shot: Shot = {
|
||||
const shot: TankopediaEphemeral.Shot = {
|
||||
damage: -1,
|
||||
containsGaps: shell.type === ShellType.HEAT,
|
||||
in: {
|
||||
@@ -150,7 +146,9 @@ export function SpacedArmorSceneComponent({
|
||||
);
|
||||
|
||||
if (shell.type === ShellType.HEAT && index > 0) {
|
||||
const lastLayer = shot.in.layers.at(-1) as ShotLayerBase;
|
||||
const lastLayer = shot.in.layers.at(
|
||||
-1,
|
||||
) as TankopediaEphemeral.ShotLayerBase;
|
||||
const distance = lastLayer.point.distanceTo(intersection.point);
|
||||
remainingPenetration -= 0.5 * remainingPenetration * distance;
|
||||
const blocked = remainingPenetration <= 0;
|
||||
@@ -244,8 +242,12 @@ export function SpacedArmorSceneComponent({
|
||||
index++;
|
||||
}
|
||||
|
||||
const lastLayer = shot.in.layers.at(-1) as ShotLayerNonExternal | undefined;
|
||||
const firstLayer = shot.in.layers[0] as ShotLayerBase | undefined;
|
||||
const lastLayer = shot.in.layers.at(-1) as
|
||||
| TankopediaEphemeral.ShotLayerNonExternal
|
||||
| undefined;
|
||||
const firstLayer = shot.in.layers[0] as
|
||||
| TankopediaEphemeral.ShotLayerBase
|
||||
| undefined;
|
||||
|
||||
if (!lastLayer || !firstLayer) {
|
||||
return null;
|
||||
@@ -370,7 +372,7 @@ export function SpacedArmorSceneComponent({
|
||||
true,
|
||||
))!;
|
||||
|
||||
useTankopediaTemporary.setState({ shot });
|
||||
tankopediaEphemeralStore.setState({ shot });
|
||||
},
|
||||
},
|
||||
node.uuid,
|
||||
|
||||
Reference in new issue
Block a user