mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-22 11:17:05 +02:00
feat: armor modes
This commit is contained in:
1 parent
ffa1395b50
commit
f1410cf321
42 files changed
+293
-264
No files matched your search
@@ -9,6 +9,10 @@ import {
|
||||
} from '@radix-ui/themes';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useRef } from 'react';
|
||||
import { radToDeg } from 'three/src/math/MathUtils.js';
|
||||
import { resolveArmorIndex } from '../../../core/blitzkit/resolveArmorIndex';
|
||||
import { App } from '../../../stores/app';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import { layerTypeNames } from './ShotDisplayCard';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
|
||||
@@ -6,6 +6,7 @@ import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||
import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
||||
import { PrimaryArmorSceneComponent } from './PrimaryArmorSceneComponent';
|
||||
|
||||
|
||||
+26
-21
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
ShellDefinition,
|
||||
type ShellDefinition,
|
||||
canSplash,
|
||||
isExplosive,
|
||||
resolveNearPenetration,
|
||||
@@ -8,13 +8,13 @@ import {
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import { useEffect } from 'react';
|
||||
import { MeshBasicMaterial, Object3D, ShaderMaterial, Vector2 } from 'three';
|
||||
import { degToRad } from 'three/src/math/MathUtils';
|
||||
import { hasEquipment } from '../../../../../../website-legacy/src/core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../../../website-legacy/src/core/blitzkit/jsxTree';
|
||||
import * as Duel from '../../../../../../website-legacy/src/stores/duel';
|
||||
import * as TankopediaPersistent from '../../../../../../website-legacy/src/stores/tankopediaPersistent';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
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 { TankopediaPersistent } from '../../../../stores/tankopediaPersistent';
|
||||
import fragmentShader from './shaders/fragment.glsl?raw';
|
||||
import vertexShader from './shaders/vertex.glsl?raw';
|
||||
import { spacedArmorRenderTarget } from './target';
|
||||
|
||||
interface PrimaryArmorSceneComponentProps {
|
||||
@@ -76,15 +76,17 @@ export function PrimaryArmorSceneComponent({
|
||||
await handleProtagonistEquipmentChange(duel.protagonist.equipmentMatrix);
|
||||
await handleAntagonistEquipmentChange(duel.antagonist.equipmentMatrix);
|
||||
}
|
||||
function handleVisualChange(
|
||||
visual: TankopediaPersistent.TankopediaPersistent['model']['visual'],
|
||||
) {
|
||||
material.uniforms.greenPenetration.value = visual.greenPenetration;
|
||||
material.uniforms.opaque.value = visual.opaque || visual.wireframe;
|
||||
material.wireframe = visual.wireframe;
|
||||
function handleGreenPenetrationChange(greenPenetration: boolean) {
|
||||
material.uniforms.greenPenetration.value = greenPenetration;
|
||||
}
|
||||
function handleOpaqueChange(opaque: boolean) {
|
||||
material.uniforms.opaque.value = opaque;
|
||||
}
|
||||
function handleWireframeChange(wireframe: boolean) {
|
||||
material.wireframe = wireframe;
|
||||
}
|
||||
async function handleProtagonistEquipmentChange(
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
equipment: EquipmentMatrix,
|
||||
) {
|
||||
const duel = duelStore.getState();
|
||||
const hasEnhancedArmor = await hasEquipment(
|
||||
@@ -96,9 +98,7 @@ export function PrimaryArmorSceneComponent({
|
||||
? thickness * 1.03
|
||||
: thickness;
|
||||
}
|
||||
async function handleAntagonistEquipmentChange(
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||
const duel = duelStore.getState();
|
||||
const shell = duel.antagonist.shell;
|
||||
const penetration = resolveNearPenetration(shell.penetration);
|
||||
@@ -114,7 +114,11 @@ export function PrimaryArmorSceneComponent({
|
||||
}
|
||||
|
||||
handleShellChange(duelStore.getState().antagonist.shell);
|
||||
handleVisualChange(tankopediaPersistentStore.getState().model.visual);
|
||||
handleGreenPenetrationChange(
|
||||
tankopediaPersistentStore.getState().greenPenetration,
|
||||
);
|
||||
handleOpaqueChange(tankopediaPersistentStore.getState().opaque);
|
||||
handleWireframeChange(tankopediaPersistentStore.getState().wireframe);
|
||||
handleProtagonistEquipmentChange(
|
||||
duelStore.getState().protagonist.equipmentMatrix,
|
||||
);
|
||||
@@ -125,9 +129,10 @@ export function PrimaryArmorSceneComponent({
|
||||
const unsubscribes = [
|
||||
duelStore.subscribe((state) => state.antagonist.shell, handleShellChange),
|
||||
tankopediaPersistentStore.subscribe(
|
||||
(state) => state.model.visual,
|
||||
handleVisualChange,
|
||||
(state) => state.greenPenetration,
|
||||
handleGreenPenetrationChange,
|
||||
),
|
||||
|
||||
duelStore.subscribe(
|
||||
(state) => state.protagonist.equipmentMatrix,
|
||||
handleProtagonistEquipmentChange,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { J_HAT, K_HAT } from '@blitzkit/core';
|
||||
import { Box, Flex, Text } from '@radix-ui/themes';
|
||||
import { K_HAT } from '@blitzkit/core';
|
||||
import { Box, Text } from '@radix-ui/themes';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { type ComponentProps, useEffect, useRef } from 'react';
|
||||
import { useEffect, useRef, type ComponentProps } from 'react';
|
||||
import {
|
||||
BufferGeometry,
|
||||
DoubleSide,
|
||||
@@ -16,12 +16,16 @@ import {
|
||||
Path,
|
||||
Quaternion,
|
||||
} from 'three';
|
||||
import { useFullScreen } from '../../../../../website-legacy/src/hooks/useFullScreen';
|
||||
import * as TankopediaEphemeral from '../../../../../website-legacy/src/stores/tankopediaEphemeral';
|
||||
import { useFullScreen } from '../../../hooks/useFullScreen';
|
||||
import {
|
||||
TankopediaEphemeral,
|
||||
type ShotLayerNonExternal,
|
||||
type ShotStatus,
|
||||
} from '../../../stores/tankopediaEphemeral';
|
||||
import { ShotDisplayCard } from './ShotDisplayCard';
|
||||
|
||||
export const shotStatusColors: Record<
|
||||
TankopediaEphemeral.ShotStatus,
|
||||
ShotStatus,
|
||||
ComponentProps<typeof Text>['color']
|
||||
> = {
|
||||
blocked: 'red',
|
||||
@@ -127,16 +131,13 @@ export function ShotDisplay() {
|
||||
}, 0) + SHOT_DISPLAY_LENGTH_INFINITY;
|
||||
const outLength =
|
||||
shot.out && shot.out.layers.length > 0
|
||||
? (
|
||||
shot.in.layers.at(-1) as TankopediaEphemeral.ShotLayerNonExternal
|
||||
).point.distanceTo(
|
||||
(shot.out.layers.at(-1) as TankopediaEphemeral.ShotLayerNonExternal)
|
||||
.point,
|
||||
? (shot.in.layers.at(-1) as ShotLayerNonExternal).point.distanceTo(
|
||||
(shot.out.layers.at(-1) as ShotLayerNonExternal).point,
|
||||
)
|
||||
: SHOT_DISPLAY_LENGTH_INFINITY;
|
||||
const inLast = shot.in.layers.findLast(
|
||||
(layer) => layer.type !== null,
|
||||
) as TankopediaEphemeral.ShotLayerNonExternal;
|
||||
) as ShotLayerNonExternal;
|
||||
const outTitleColor = shot.out
|
||||
? shotStatusColors[shot.out.status]
|
||||
: undefined;
|
||||
@@ -182,7 +183,7 @@ export function ShotDisplay() {
|
||||
</Box>
|
||||
</Html>
|
||||
|
||||
{[...shot.in.layers, ...(shot.out?.layers ?? [])].map((layer, index) => {
|
||||
{/* {[...shot.in.layers, ...(shot.out?.layers ?? [])].map((layer, index) => {
|
||||
if (layer.type === null) return null;
|
||||
|
||||
const shotStatusColor =
|
||||
@@ -275,7 +276,7 @@ export function ShotDisplay() {
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
)}
|
||||
)} */}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +1,11 @@
|
||||
import {
|
||||
Card,
|
||||
CardProps,
|
||||
Flex,
|
||||
Inset,
|
||||
Separator,
|
||||
Text,
|
||||
} from '@radix-ui/themes';
|
||||
import { Flex, Text } from '@radix-ui/themes';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { radToDeg } from 'three/src/math/MathUtils';
|
||||
import * as TankopediaEphemeral from '../../../../../website-legacy/src/stores/tankopediaEphemeral';
|
||||
import { shotStatusColors } from './ShotDisplay';
|
||||
import type {
|
||||
ShotLayer,
|
||||
ShotStatus,
|
||||
} from '../../../stores/tankopediaEphemeral';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
import { ExternalModuleVariant } from './SpacedArmorSceneComponent';
|
||||
import type { ExternalModuleVariant } from './SpacedArmorSceneComponent';
|
||||
|
||||
export const layerTypeNames: Record<ArmorType | 'null', string> = {
|
||||
[ArmorType.Primary]: 'Primary',
|
||||
@@ -25,7 +19,7 @@ const externalLayerNames: Record<ExternalModuleVariant, string> = {
|
||||
track: 'Tracks',
|
||||
};
|
||||
|
||||
const shotStatusNames: Record<TankopediaEphemeral.ShotStatus, string> = {
|
||||
const shotStatusNames: Record<ShotStatus, string> = {
|
||||
blocked: 'Blocked',
|
||||
ricochet: 'Ricochet',
|
||||
penetration: 'penetration',
|
||||
@@ -42,7 +36,7 @@ function LayerEntry({
|
||||
layerIndex?: string;
|
||||
shotStatusColor: ComponentProps<typeof Text>['color'];
|
||||
layerName: string;
|
||||
layer: TankopediaEphemeral.ShotLayer;
|
||||
layer: ShotLayer;
|
||||
angle?: number;
|
||||
}) {
|
||||
return (
|
||||
@@ -95,7 +89,7 @@ function LayerEntry({
|
||||
}
|
||||
|
||||
interface ShotDisplayCardProps extends CardProps {
|
||||
shot: TankopediaEphemeral.Shot;
|
||||
shot: Shot;
|
||||
}
|
||||
|
||||
export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModel } from '../../../hooks/useModel';
|
||||
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
||||
import { SpacedArmorSceneComponent } from './SpacedArmorSceneComponent';
|
||||
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../../../core/blitzkit/jsxTree';
|
||||
import { Duel, type EquipmentMatrix } from '../../../../../../stores/duel';
|
||||
import { ArmorType } from '../../../SpacedArmorScene';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
import fragmentShader from './shaders/fragment.glsl?raw';
|
||||
import vertexShader from './shaders/vertex.glsl?raw';
|
||||
|
||||
interface SpacedArmorSubExternalProps {
|
||||
node: Object3D;
|
||||
|
||||
+11
-13
@@ -1,24 +1,24 @@
|
||||
import {
|
||||
ShellDefinition,
|
||||
isExplosive,
|
||||
resolveNearPenetration,
|
||||
resolvePenetrationCoefficient,
|
||||
type ShellDefinition,
|
||||
} from '@blitzkit/core';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
AdditiveBlending,
|
||||
MeshBasicMaterial,
|
||||
Object3D,
|
||||
ShaderMaterial,
|
||||
type Object3D,
|
||||
} from 'three';
|
||||
import { degToRad } from 'three/src/math/MathUtils';
|
||||
import { ArmorUserData } from '../..';
|
||||
import { hasEquipment } from '../../../../../../../../website-legacy/src/core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../../../../../website-legacy/src/core/blitzkit/jsxTree';
|
||||
import * as Duel from '../../../../../../../../website-legacy/src/stores/duel';
|
||||
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 { ArmorType } from '../../../SpacedArmorScene';
|
||||
import fragmentShader from './shaders/fragment.glsl';
|
||||
import vertexShader from './shaders/vertex.glsl';
|
||||
import fragmentShader from './shaders/fragment.glsl?raw';
|
||||
import vertexShader from './shaders/vertex.glsl?raw';
|
||||
|
||||
interface SpacedArmorSubSpacedProps {
|
||||
node: Object3D;
|
||||
@@ -66,7 +66,7 @@ export function SpacedArmorSubSpaced({
|
||||
);
|
||||
}
|
||||
async function handleProtagonistEquipmentChange(
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
equipment: EquipmentMatrix,
|
||||
) {
|
||||
const duel = duelStore.getState();
|
||||
const hasEnhancedArmor = await hasEquipment(
|
||||
@@ -78,9 +78,7 @@ export function SpacedArmorSubSpaced({
|
||||
? thickness * 1.03
|
||||
: thickness;
|
||||
}
|
||||
async function handleAntagonistEquipmentChange(
|
||||
equipment: Duel.EquipmentMatrix,
|
||||
) {
|
||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||
const duel = duelStore.getState();
|
||||
const shell = duelStore.getState().antagonist.shell;
|
||||
const penetration = resolveNearPenetration(shell.penetration);
|
||||
|
||||
+16
-12
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
type ShellType,
|
||||
ShellType,
|
||||
isExplosive,
|
||||
resolveNearPenetration,
|
||||
resolvePenetrationCoefficient,
|
||||
@@ -16,9 +16,17 @@ import {
|
||||
Scene,
|
||||
Vector3,
|
||||
} from 'three';
|
||||
import { degToRad } from 'three/src/math/MathUtils';
|
||||
import * as Duel from '../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../stores/tankopediaEphemeral';
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
import { hasEquipment } from '../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../core/blitzkit/jsxTree';
|
||||
import { discardClippingPlane } from '../../../../core/three/discardClippingPlane';
|
||||
import { Duel } from '../../../../stores/duel';
|
||||
import {
|
||||
type Shot,
|
||||
type ShotLayerBase,
|
||||
type ShotLayerNonExternal,
|
||||
TankopediaEphemeral,
|
||||
} from '../../../../stores/tankopediaEphemeral';
|
||||
import { ArmorType } from '../SpacedArmorScene';
|
||||
import { SpacedArmorSubExternal } from './components/SpacedArmorSubExternal';
|
||||
import { SpacedArmorSubSpaced } from './components/SpacedArmorSubSpaced';
|
||||
@@ -83,7 +91,7 @@ export function SpacedArmorSceneComponent({
|
||||
const { antagonist, protagonist } = duelStore.getState();
|
||||
const shell = antagonist.shell;
|
||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||
const shot: TankopediaEphemeral.Shot = {
|
||||
const shot: Shot = {
|
||||
splashRadius: shell.explosionRadius,
|
||||
damage: -1,
|
||||
containsGaps: shell.type === ShellType.HEAT,
|
||||
@@ -157,9 +165,7 @@ export function SpacedArmorSceneComponent({
|
||||
);
|
||||
|
||||
if (shell.type === ShellType.HEAT && index > 0) {
|
||||
const lastLayer = shot.in.layers.at(
|
||||
-1,
|
||||
) as TankopediaEphemeral.ShotLayerBase;
|
||||
const lastLayer = shot.in.layers.at(-1) as ShotLayerBase;
|
||||
const distance = lastLayer.point.distanceTo(intersection.point);
|
||||
remainingPenetration -= 0.5 * remainingPenetration * distance;
|
||||
const blocked = remainingPenetration <= 0;
|
||||
@@ -255,11 +261,9 @@ export function SpacedArmorSceneComponent({
|
||||
}
|
||||
|
||||
const lastLayer = shot.in.layers.at(-1) as
|
||||
| TankopediaEphemeral.ShotLayerNonExternal
|
||||
| undefined;
|
||||
const firstLayer = shot.in.layers[0] as
|
||||
| TankopediaEphemeral.ShotLayerBase
|
||||
| ShotLayerNonExternal
|
||||
| undefined;
|
||||
const firstLayer = shot.in.layers[0] as ShotLayerBase | undefined;
|
||||
|
||||
if (!lastLayer || !firstLayer) {
|
||||
return null;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModel } from '../../../hooks/useModel';
|
||||
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
|
||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { I_HAT, J_HAT } from '@blitzkit/core';
|
||||
import { MeshProps, useThree } from '@react-three/fiber';
|
||||
import { type MeshProps, useThree } from '@react-three/fiber';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
@@ -15,20 +15,20 @@ import {
|
||||
Quaternion,
|
||||
Vector3,
|
||||
} from 'three';
|
||||
import { unrotateDavaVector } from '../../../../../website-legacy/src/core/blitz/unrotateDavaVector';
|
||||
import { jsxTree } from '../../../../../website-legacy/src/core/blitzkit/jsxTree';
|
||||
import { unrotateDavaVector } from '../../../core/blitz/unrotateDavaVector';
|
||||
import { jsxTree } from '../../../core/blitzkit/jsxTree';
|
||||
import {
|
||||
ModelTransformEventData,
|
||||
type ModelTransformEventData,
|
||||
modelTransformEvent,
|
||||
} from '../../../../../website-legacy/src/core/blitzkit/modelTransform';
|
||||
import { discardClippingPlane } from '../../../../../website-legacy/src/core/three/discardClippingPlane';
|
||||
import * as TankopediaEphemeral from '../../../../../website-legacy/src/stores/tankopediaEphemeral';
|
||||
} from '../../../core/blitzkit/modelTransform';
|
||||
import { discardClippingPlane } from '../../../core/three/discardClippingPlane';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
import {
|
||||
import type {
|
||||
ArmorUserData,
|
||||
ExternalModuleVariant,
|
||||
} from './SpacedArmorSceneComponent';
|
||||
import { ThicknessRange } from './StaticArmor';
|
||||
import type { ThicknessRange } from './StaticArmor';
|
||||
|
||||
type StaticArmorSceneComponentProps = {
|
||||
name: string;
|
||||
|
||||
Reference in new issue
Block a user