mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-31 06:29:51 +01:00
feat: new tab
This commit is contained in:
1 parent
7f056500ca
commit
7605e1d6f2
10 files changed
+136
-55
No files matched your search
@@ -855,6 +855,10 @@
|
|||||||
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
|
||||||
|
|
||||||
"sandbox": {
|
"sandbox": {
|
||||||
|
"dissector": {
|
||||||
|
"name": "Dissector"
|
||||||
|
},
|
||||||
|
|
||||||
"poses": {
|
"poses": {
|
||||||
"hull_down": "Hull down",
|
"hull_down": "Hull down",
|
||||||
"face_hug": "Face hug",
|
"face_hug": "Face hug",
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { memo } from 'react';
|
||||||
|
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" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { asset } from '@blitzkit/core';
|
||||||
|
import { useGLTF } from '@react-three/drei';
|
||||||
|
import {
|
||||||
|
Color,
|
||||||
|
DoubleSide,
|
||||||
|
LineBasicMaterial,
|
||||||
|
Material,
|
||||||
|
MeshStandardMaterial,
|
||||||
|
} from 'three';
|
||||||
|
import { jsxTree } from '../../../core/blitzkit/jsxTree';
|
||||||
|
import { Duel } from '../../../stores/duel';
|
||||||
|
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||||
|
import { groupNameRegex } from './StaticArmorSceneComponent';
|
||||||
|
|
||||||
|
interface DissectorSceneComponentProps {
|
||||||
|
group: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const armorColor = new Color().multiplyScalar(1 / 16);
|
||||||
|
|
||||||
|
const moduleColor: Record<string, Color> = {
|
||||||
|
Armor: armorColor,
|
||||||
|
ArmorScreen: armorColor,
|
||||||
|
|
||||||
|
Commander: new Color('white'),
|
||||||
|
Driver: new Color('white'),
|
||||||
|
Gunner1: new Color('white'),
|
||||||
|
Loader1: new Color('white'),
|
||||||
|
|
||||||
|
SurveyingDevice: new Color('purple'),
|
||||||
|
Radio: new Color('indigo'),
|
||||||
|
Gun: new Color('pink'),
|
||||||
|
FuelTank: new Color('yellow'),
|
||||||
|
Engine: new Color('orange'),
|
||||||
|
AmmoBay: new Color('gold'),
|
||||||
|
LeftTrack: new Color('green'),
|
||||||
|
RightTrack: new Color('green'),
|
||||||
|
TurretRotator: new Color('blue'),
|
||||||
|
Transmission: new Color('cyan'),
|
||||||
|
};
|
||||||
|
const moduleOutlineColor: Record<string, Color> = {};
|
||||||
|
|
||||||
|
for (const key in moduleColor) {
|
||||||
|
moduleOutlineColor[key] = moduleColor[key].clone().multiplyScalar(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DissectorSceneComponent({
|
||||||
|
group,
|
||||||
|
}: DissectorSceneComponentProps) {
|
||||||
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
|
const gltf = useGLTF(asset(`tanks/${tank.id}/collision/${group}.glb`));
|
||||||
|
const armor = TankopediaEphemeral.use((state) => state.armor);
|
||||||
|
const groupName = group.match(groupNameRegex)![1];
|
||||||
|
const groupArmor = armor.groups[groupName];
|
||||||
|
|
||||||
|
return jsxTree(gltf.scene, {
|
||||||
|
mesh(mesh, props) {
|
||||||
|
if (!(mesh.material instanceof Material)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const armorName = mesh.material.name;
|
||||||
|
const armorPlate = groupArmor.armors[armorName];
|
||||||
|
|
||||||
|
if (!(armorPlate.type in moduleColor)) {
|
||||||
|
throw new TypeError(`Unhandled armor type: ${armorPlate.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const material = new MeshStandardMaterial({
|
||||||
|
side: DoubleSide,
|
||||||
|
color: moduleColor[armorPlate.type],
|
||||||
|
});
|
||||||
|
const outlineMaterial = new LineBasicMaterial({
|
||||||
|
side: DoubleSide,
|
||||||
|
color: moduleOutlineColor[armorPlate.type],
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<mesh {...props} material={material} />
|
||||||
|
|
||||||
|
<lineSegments material={outlineMaterial}>
|
||||||
|
<edgesGeometry args={[props.geometry, 45]} />
|
||||||
|
</lineSegments>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,62 +1,19 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { StaticArmorSceneComponents } from './StaticArmorSceneComponents';
|
import { StaticArmorSceneComponent } from './StaticArmorSceneComponent';
|
||||||
|
|
||||||
export interface ThicknessRange {
|
export interface ThicknessRange {
|
||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StaticArmor = memo(() => {
|
export const StaticArmor = memo(() => {
|
||||||
// const wrapper = useRef<Group>(null);
|
|
||||||
// const turretContainer = useRef<Group>(null);
|
|
||||||
// const gunContainer = useRef<Group>(null);
|
|
||||||
// const protagonist = Duel.use((draft) => draft.protagonist);
|
|
||||||
// const tank = Duel.use((state) => state.protagonist.tank);
|
|
||||||
// const track = Duel.use((state) => state.protagonist.track);
|
|
||||||
// const turret = Duel.use((state) => state.protagonist.turret);
|
|
||||||
// const gun = Duel.use((state) => state.protagonist.gun);
|
|
||||||
// const { gltf: armorGltf } = useArmor(tank.id);
|
|
||||||
// const { gltf: modelGltf } = useModel(tank.id);
|
|
||||||
// const armorNodes = Object.values(armorGltf.nodes);
|
|
||||||
// const modelNodes = Object.values(modelGltf.nodes);
|
|
||||||
// const tankModelDefinition = useTankModelDefinition();
|
|
||||||
// const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
|
||||||
// const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
|
||||||
// const gunModelDefinition =
|
|
||||||
// turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
// const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
|
||||||
// const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
|
||||||
// const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
|
||||||
// const mutateDuel = Duel.useMutation();
|
|
||||||
// const canvas = useThree((state) => state.gl.domElement);
|
|
||||||
// const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
|
||||||
// const duelStore = Duel.useStore();
|
|
||||||
// const maskOrigin =
|
|
||||||
// gunModelDefinition.mask === undefined
|
|
||||||
// ? undefined
|
|
||||||
// : gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
|
||||||
// const showPrimaryArmor = TankopediaPersistent.use(
|
|
||||||
// (state) => state.showPrimaryArmor,
|
|
||||||
// );
|
|
||||||
// const showSpacedArmor = TankopediaPersistent.use(
|
|
||||||
// (state) => state.showSpacedArmor,
|
|
||||||
// );
|
|
||||||
// const showExternalModules = TankopediaPersistent.use(
|
|
||||||
// (state) => state.showExternalModules,
|
|
||||||
// );
|
|
||||||
// const isDynamicArmorActive = Duel.use((state) =>
|
|
||||||
// state.protagonist.consumables.includes(73),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// useTankTransform(track, turret, turretContainer, gunContainer);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group>
|
<group>
|
||||||
<StaticArmorSceneComponents group="hull" />
|
<StaticArmorSceneComponent group="hull" />
|
||||||
<StaticArmorSceneComponents group="chassis" />
|
<StaticArmorSceneComponent group="chassis" />
|
||||||
<group position={[0.7, 1.6, 0]}>
|
<group position={[0.7, 1.6, 0]}>
|
||||||
<StaticArmorSceneComponents group="turret_01" />
|
<StaticArmorSceneComponent group="turret_01" />
|
||||||
<group position={[1, 0.36, 0]}>
|
<group position={[1, 0.36, 0]}>
|
||||||
<StaticArmorSceneComponents group="gun_01" />
|
<StaticArmorSceneComponent group="gun_01" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
+4
-4
@@ -12,16 +12,16 @@ import { Duel } from '../../../stores/duel';
|
|||||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||||
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
|
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
|
||||||
|
|
||||||
interface StaticArmorSceneComponentsProps {
|
interface StaticArmorSceneComponentsProp {
|
||||||
group: string;
|
group: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduleColor = new Color(192 / 255, 192 / 255, 192 / 255);
|
const moduleColor = new Color(192 / 255, 192 / 255, 192 / 255);
|
||||||
const groupNameRegex = /([a-z]+)(_\d+)?/;
|
export const groupNameRegex = /([a-z]+)(_\d+)?/;
|
||||||
|
|
||||||
export function StaticArmorSceneComponents({
|
export function StaticArmorSceneComponent({
|
||||||
group,
|
group,
|
||||||
}: StaticArmorSceneComponentsProps) {
|
}: StaticArmorSceneComponentsProp) {
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const showModules = TankopediaPersistent.use((state) => state.showModules);
|
const showModules = TankopediaPersistent.use((state) => state.showModules);
|
||||||
const showArmorScreen = TankopediaPersistent.use(
|
const showArmorScreen = TankopediaPersistent.use(
|
||||||
@@ -395,6 +395,15 @@ export function Options({ canvas }: OptionsProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</SegmentedControl.Item>
|
</SegmentedControl.Item>
|
||||||
|
<SegmentedControl.Item value={`${TankopediaDisplay.Dissector}`}>
|
||||||
|
<Tooltip
|
||||||
|
content={strings.website.tools.tanks.sandbox.dissector.name}
|
||||||
|
>
|
||||||
|
<Flex height="100%" align="center">
|
||||||
|
<img src={imgur('ErJEWbY')} style={{ height: '1.25em' }} />
|
||||||
|
</Flex>
|
||||||
|
</Tooltip>
|
||||||
|
</SegmentedControl.Item>
|
||||||
{/* <SegmentedControl.Item value={`${TankopediaDisplay.ShootingRange}`}>
|
{/* <SegmentedControl.Item value={`${TankopediaDisplay.ShootingRange}`}>
|
||||||
<Tooltip content="Shooting range">
|
<Tooltip content="Shooting range">
|
||||||
<Flex height="100%" align="center">
|
<Flex height="100%" align="center">
|
||||||
|
|||||||
+2
-1
@@ -20,7 +20,8 @@ export function Lighting() {
|
|||||||
environmentRotation={new Euler(0, degToRad(180), 0)}
|
environmentRotation={new Euler(0, degToRad(180), 0)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{display === TankopediaDisplay.StaticArmor && (
|
{(display === TankopediaDisplay.StaticArmor ||
|
||||||
|
display === TankopediaDisplay.Dissector) && (
|
||||||
<Environment preset="lobby" />
|
<Environment preset="lobby" />
|
||||||
)}
|
)}
|
||||||
{display === TankopediaDisplay.ShootingRange && (
|
{display === TankopediaDisplay.ShootingRange && (
|
||||||
|
|||||||
+4
-1
@@ -1,12 +1,14 @@
|
|||||||
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
|
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
|
||||||
import { Canvas } from '@react-three/fiber';
|
import { Canvas } from '@react-three/fiber';
|
||||||
import { forwardRef, Suspense, useImperativeHandle, useRef } from 'react';
|
import { forwardRef, Suspense, useImperativeHandle, useRef } from 'react';
|
||||||
|
import { Plane, Vector3 } from 'three';
|
||||||
import { useOnScreen } from '../../../../../hooks/useOnScreen';
|
import { useOnScreen } from '../../../../../hooks/useOnScreen';
|
||||||
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||||
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
|
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
|
||||||
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
||||||
import { Armor } from '../../../../Armor';
|
import { Armor } from '../../../../Armor';
|
||||||
import { ArmorPlateDisplay } from '../../../../Armor/components/ArmorPlateDisplay';
|
import { ArmorPlateDisplay } from '../../../../Armor/components/ArmorPlateDisplay';
|
||||||
|
import { Dissector } from '../../../../Armor/components/Dissector';
|
||||||
import { ShotDisplay } from '../../../../Armor/components/ShotDisplay';
|
import { ShotDisplay } from '../../../../Armor/components/ShotDisplay';
|
||||||
import { StaticArmor } from '../../../../Armor/components/StaticArmor';
|
import { StaticArmor } from '../../../../Armor/components/StaticArmor';
|
||||||
import { AutoClear } from './components/AutoClear';
|
import { AutoClear } from './components/AutoClear';
|
||||||
@@ -158,7 +160,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
: 'never'
|
: 'never'
|
||||||
}
|
}
|
||||||
gl={{
|
gl={{
|
||||||
clippingPlanes: [],
|
clippingPlanes: [new Plane(new Vector3(0, 0, 1))],
|
||||||
localClippingEnabled: true,
|
localClippingEnabled: true,
|
||||||
preserveDrawingBuffer: true,
|
preserveDrawingBuffer: true,
|
||||||
}}
|
}}
|
||||||
@@ -190,6 +192,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
<Suspense fallback={<ModelLoader />}>
|
<Suspense fallback={<ModelLoader />}>
|
||||||
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
||||||
{display === TankopediaDisplay.StaticArmor && <StaticArmor />}
|
{display === TankopediaDisplay.StaticArmor && <StaticArmor />}
|
||||||
|
{display === TankopediaDisplay.Dissector && <Dissector />}
|
||||||
<Lighting />
|
<Lighting />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export const TankopediaEphemeral = createContextualStore(
|
|||||||
armor,
|
armor,
|
||||||
controlsEnabled: true,
|
controlsEnabled: true,
|
||||||
xpMultiplier: 1,
|
xpMultiplier: 1,
|
||||||
display: TankopediaDisplay.StaticArmor,
|
display: TankopediaDisplay.Dissector,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,5 +37,6 @@ export enum TankopediaDisplay {
|
|||||||
Model,
|
Model,
|
||||||
DynamicArmor,
|
DynamicArmor,
|
||||||
StaticArmor,
|
StaticArmor,
|
||||||
|
Dissector,
|
||||||
ShootingRange,
|
ShootingRange,
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user