feat: cards

This commit is contained in:
tresabhi committed 2025-03-23 19:07:37 -05:00
1 parent f1462162f4
commit c7017a7a15
6 files changed
+142 -28

No files matched your search

@@ -1,17 +1,22 @@
import { memo } from 'react';
import { DissectorModuleCard } from './DissectorModuleCard';
import { DissectorSceneComponent } from './DissectorSceneComponent';
export const Dissector = memo(() => {
return (
<group>
<DissectorSceneComponent group="hull" />
<DissectorSceneComponent group="chassis" />
<group position={[0.7, 1.6, 0]}>
<DissectorSceneComponent group="turret_01" />
<group position={[1, 0.36, 0]}>
<DissectorSceneComponent group="gun_01" />
<>
<DissectorModuleCard />
<group>
<DissectorSceneComponent group="hull" />
<DissectorSceneComponent group="chassis" />
<group position={[0.7, 1.6, 0]}>
<DissectorSceneComponent group="turret_01" />
<group position={[1, 0.36, 0]}>
<DissectorSceneComponent group="gun_01" />
</group>
</group>
</group>
</group>
</>
);
});
@@ -0,0 +1,32 @@
import { Card, Text } from '@radix-ui/themes';
import { Html } from '@react-three/drei';
import { useEffect, useState } from 'react';
import {
lastModuleSelect,
moduleSelectEvent,
type ModuleSelectEventData,
} from '../../../core/blitzkit/moduleSelect';
export function DissectorModuleCard() {
const [moduleSelection, setModuleSelection] = useState(lastModuleSelect);
useEffect(() => {
function handleModuleSelectEvent(event: ModuleSelectEventData) {
setModuleSelection(event);
}
return moduleSelectEvent.on(handleModuleSelectEvent);
}, []);
if (!moduleSelection.selected) return null;
return (
<group position={[0, 1, 0]}>
<Html position={moduleSelection.position}>
<Card>
<Text>{moduleSelection.module}</Text>
</Card>
</Html>
</group>
);
}
@@ -18,9 +18,11 @@ import {
type CSGGeometryRef,
} from '@react-three/csg';
import { useGLTF } from '@react-three/drei';
import { useThree } from '@react-three/fiber';
import { Fragment, useEffect, useRef } from 'react';
import type { Mesh } from 'three';
import {
Box3,
Color,
DoubleSide,
Group,
@@ -37,6 +39,11 @@ import {
type DissectEventData,
} from '../../../core/blitzkit/dissect';
import { jsxTree } from '../../../core/blitzkit/jsxTree';
import {
lastModuleSelect,
moduleSelectEvent,
type ModuleSelectEventData,
} from '../../../core/blitzkit/moduleSelect';
import { Duel } from '../../../stores/duel';
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
import { groupNameRegex } from './StaticArmorSceneComponent';
@@ -88,6 +95,7 @@ export function DissectorSceneComponent({
const armor = TankopediaEphemeral.use((state) => state.armor);
const groupName = group.match(groupNameRegex)![1];
const groupArmor = armor.groups[groupName];
const camera = useThree((state) => state.camera);
return jsxTree(gltf.scene, {
mesh(mesh, props) {
@@ -115,7 +123,7 @@ export function DissectorSceneComponent({
const material = useRef(
new MeshStandardMaterial({
transparent: isTrack,
opacity: 2 ** -1,
opacity: 2 ** -5,
side: DoubleSide,
color: moduleColor[armorPlate.type],
clippingPlanes: isModule ? [] : [clippingPlane.current],
@@ -141,6 +149,8 @@ export function DissectorSceneComponent({
}),
);
const worldPosition = new Vector3();
const boundsTop = new Vector3();
const bounds = new Box3();
useEffect(() => {
const offset = new Vector3();
@@ -169,11 +179,28 @@ export function DissectorSceneComponent({
csg.current.update();
}
function handleModuleSelectEvent(event: ModuleSelectEventData) {
if (!isModule) return;
const isThisComponentSelected =
event.selected &&
event.group === groupName &&
event.module === armorName;
const shouldBeVisible = !event.selected || isThisComponentSelected;
material.current.transparent = !shouldBeVisible;
outlineMaterial.current.transparent = !shouldBeVisible;
}
dissectEvent.on(handleDissectEvent);
moduleSelectEvent.on(handleModuleSelectEvent);
handleDissectEvent(lastDissection);
handleModuleSelectEvent(lastModuleSelect);
return () => {
dissectEvent.off(handleDissectEvent);
moduleSelectEvent.off(handleModuleSelectEvent);
};
}, []);
@@ -184,25 +211,44 @@ export function DissectorSceneComponent({
</lineSegments>
{isModule && (
<mesh
material={material.current}
ref={baseWrapper}
onPointerDown={(event) => {
event.stopPropagation();
}}
>
<Geometry ref={csg}>
<Base geometry={props.geometry} />
<>
<mesh
material={material.current}
ref={baseWrapper}
onClick={(event) => {
if (!baseWrapper.current) return;
<group rotation={[0, 0, 0]} ref={subtractionWrapper}>
<Subtraction position={[NEGATIVE_SIZE / 2, 0, 0]}>
<boxGeometry
args={[NEGATIVE_SIZE, NEGATIVE_SIZE, NEGATIVE_SIZE]}
/>
</Subtraction>
</group>
</Geometry>
</mesh>
const isAlreadySelected =
lastModuleSelect.selected &&
lastModuleSelect.group === groupName &&
lastModuleSelect.module === armorName;
if (!isAlreadySelected) {
moduleSelectEvent.emit({
selected: true,
group: groupName,
module: armorName,
position: event.point.toArray(),
});
}
}}
onPointerDown={(event) => {
event.stopPropagation();
}}
>
<Geometry ref={csg}>
<Base geometry={props.geometry} />
<group rotation={[0, 0, 0]} ref={subtractionWrapper}>
<Subtraction position={[NEGATIVE_SIZE / 2, 0, 0]}>
<boxGeometry
args={[NEGATIVE_SIZE, NEGATIVE_SIZE, NEGATIVE_SIZE]}
/>
</Subtraction>
</group>
</Geometry>
</mesh>
</>
)}
{!isModule && (
@@ -1,6 +1,10 @@
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
import { Canvas } from '@react-three/fiber';
import { forwardRef, Suspense, useImperativeHandle, useRef } from 'react';
import {
lastModuleSelect,
moduleSelectEvent,
} from '../../../../../core/blitzkit/moduleSelect';
import { useOnScreen } from '../../../../../hooks/useOnScreen';
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
@@ -172,6 +176,10 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
draft.shot = undefined;
draft.highlightArmor = undefined;
});
if (lastModuleSelect.selected) {
moduleSelectEvent.emit({ selected: false });
}
}}
style={{
userSelect: 'none',
@@ -1,5 +1,6 @@
import { EventManager } from '@blitzkit/core';
import { degToRad } from 'three/src/math/MathUtils.js';
import { lastModuleSelect, moduleSelectEvent } from './moduleSelect';
export interface DissectEventData {
rotation: number;
@@ -13,4 +14,7 @@ export const lastDissection: DissectEventData = {
offset: 0.8,
};
dissectEvent.on((data) => Object.assign(lastDissection, data));
dissectEvent.on((data) => {
if (lastModuleSelect.selected) moduleSelectEvent.emit({ selected: false });
Object.assign(lastDissection, data);
});
@@ -0,0 +1,19 @@
import { EventManager } from '@blitzkit/core';
import type { Vector3Tuple } from 'three';
export type ModuleSelectEventData =
| {
selected: false;
}
| {
selected: true;
group: string;
module: string;
position: Vector3Tuple;
};
export const moduleSelectEvent = new EventManager<ModuleSelectEventData>();
export const lastModuleSelect: ModuleSelectEventData = { selected: false };
moduleSelectEvent.on((data) => Object.assign(lastModuleSelect, data));