mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: card
This commit is contained in:
1 parent
ce3c9b936b
commit
c12fb0d668
5 files changed
+88
-8
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { Suspense, use, useEffect, useRef } from 'react';
|
||||
import { Armor } from '../../../../../../components/Armor';
|
||||
import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay';
|
||||
import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay';
|
||||
import { StaticArmor } from '../../../../../../components/StaticArmor';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
@@ -157,6 +158,9 @@ export function TankSandbox() {
|
||||
<SceneProps />
|
||||
{display !== TankopediaDisplay.StaticArmor && <TankModel />}
|
||||
|
||||
<ShotDisplay />
|
||||
<ArmorPlateDisplay />
|
||||
|
||||
{/* idk why the shot display doesn't work without suspense here lol */}
|
||||
<Suspense fallback={<ModelLoader />}>
|
||||
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
||||
@@ -165,8 +169,6 @@ export function TankSandbox() {
|
||||
)}
|
||||
<Lighting />
|
||||
</Suspense>
|
||||
|
||||
<ShotDisplay />
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Card, Flex, Text } from '@radix-ui/themes';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { radToDeg } from 'three/src/math/MathUtils';
|
||||
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||
import { layerTypeNames } from './ShotDisplayCard';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
|
||||
export function ArmorPlateDisplay() {
|
||||
const highlightArmor = TankopediaEphemeral.use(
|
||||
(state) => state.highlightArmor,
|
||||
);
|
||||
|
||||
if (highlightArmor === undefined) return null;
|
||||
|
||||
return (
|
||||
<group position={highlightArmor.point}>
|
||||
<Html>
|
||||
<Card
|
||||
ml="9"
|
||||
style={{ whiteSpace: 'nowrap', color: highlightArmor.color }}
|
||||
>
|
||||
<Flex direction="column">
|
||||
<Text weight="bold">
|
||||
{layerTypeNames[highlightArmor.type]}{' '}
|
||||
{highlightArmor.thickness.toFixed(0)}
|
||||
<Text size="1" weight="regular">
|
||||
mm
|
||||
</Text>
|
||||
</Text>
|
||||
{highlightArmor.type !== ArmorType.External && (
|
||||
<Text size="2" color="gray">
|
||||
{highlightArmor.thicknessAngled.toFixed(0)}
|
||||
<Text size="1">
|
||||
mm {radToDeg(highlightArmor.angle).toFixed(0)}°
|
||||
</Text>
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Card>
|
||||
</Html>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { shotStatusColors } from './ShotDisplay';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
import { ExternalModuleVariant } from './SpacedArmorSceneComponent';
|
||||
|
||||
const layerTypeNames: Record<ArmorType | 'null', string> = {
|
||||
export const layerTypeNames: Record<ArmorType | 'null', string> = {
|
||||
[ArmorType.Core]: 'Core',
|
||||
[ArmorType.Spaced]: 'Spaced',
|
||||
[ArmorType.External]: 'External',
|
||||
|
||||
@@ -3,6 +3,8 @@ import { clamp } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Color,
|
||||
DoubleSide,
|
||||
FrontSide,
|
||||
Intersection,
|
||||
MeshBasicMaterial,
|
||||
MeshStandardMaterial,
|
||||
@@ -74,6 +76,7 @@ const omitMaterial = new MeshBasicMaterial({
|
||||
depthWrite: true,
|
||||
});
|
||||
const unselectedColor = new Color(0x404040);
|
||||
const externalModuleColor = new Color(0, 1 / 4, 1 / 2);
|
||||
|
||||
/**
|
||||
* Render orders allocations:
|
||||
@@ -395,7 +398,7 @@ export function SpacedArmorSceneComponent({
|
||||
break;
|
||||
|
||||
case ArmorType.External:
|
||||
color = new Color(0, 1, 1);
|
||||
color = externalModuleColor;
|
||||
opacity = 1 / 8;
|
||||
renderOrder = 0;
|
||||
depthWrite = false;
|
||||
@@ -427,9 +430,10 @@ export function SpacedArmorSceneComponent({
|
||||
if (selectedName === undefined) {
|
||||
// nothing selected, go back to defaults
|
||||
material.opacity = opacity;
|
||||
material.transparent = true;
|
||||
material.transparent = opacity < 1;
|
||||
material.color = color;
|
||||
material.depthWrite = props.type !== ArmorType.External;
|
||||
material.side = FrontSide;
|
||||
} else if (
|
||||
selectedName === props.name ||
|
||||
(props.name.startsWith('chassis_') &&
|
||||
@@ -444,17 +448,19 @@ export function SpacedArmorSceneComponent({
|
||||
material.transparent = false;
|
||||
material.color = color;
|
||||
material.depthWrite = true;
|
||||
material.side = DoubleSide;
|
||||
} else {
|
||||
// something else selected, become background
|
||||
material.opacity = 1 / 4;
|
||||
material.transparent = true;
|
||||
material.color = unselectedColor;
|
||||
material.depthWrite = props.type !== ArmorType.External;
|
||||
material.side = FrontSide;
|
||||
}
|
||||
}
|
||||
|
||||
const unsubscribe = tankopediaEphemeralStore.subscribe(
|
||||
(state) => state.highlightArmor,
|
||||
(state) => state.highlightArmor?.name,
|
||||
handleHighlightArmor,
|
||||
);
|
||||
|
||||
@@ -474,8 +480,25 @@ export function SpacedArmorSceneComponent({
|
||||
|
||||
onClick(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const { point } = event;
|
||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||
const surfaceNormal = event
|
||||
.normal!.clone()
|
||||
.applyQuaternion(event.object.getWorldQuaternion(new Quaternion()));
|
||||
const angle = surfaceNormal.angleTo(cameraNormal);
|
||||
const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle);
|
||||
|
||||
mutateTankopediaEphemeralStore((draft) => {
|
||||
draft.highlightArmor = props.name;
|
||||
draft.highlightArmor = {
|
||||
type: props.type,
|
||||
name: props.name,
|
||||
point,
|
||||
thickness,
|
||||
thicknessAngled,
|
||||
angle,
|
||||
color: `#${color.getHexString()}`,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,7 +11,19 @@ interface TankopediaEphemeral {
|
||||
shot?: Shot;
|
||||
skills: Record<string, number>;
|
||||
controlsEnabled: boolean;
|
||||
highlightArmor?: string;
|
||||
highlightArmor?: {
|
||||
name: string;
|
||||
point: Vector3;
|
||||
thickness: number;
|
||||
color: string;
|
||||
} & (
|
||||
| {
|
||||
type: ArmorType.Core | ArmorType.Spaced;
|
||||
thicknessAngled: number;
|
||||
angle: number;
|
||||
}
|
||||
| { type: ArmorType.External }
|
||||
);
|
||||
}
|
||||
|
||||
export interface ShotLayerBase {
|
||||
|
||||
Reference in new issue
Block a user