mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: dynamic model defs
This commit is contained in:
1 parent
3889c0f75d
commit
8ba56069c4
18 files changed
+127
-57
No files matched your search
@@ -28,6 +28,7 @@ import {
|
|||||||
} from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
} from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||||
import { useAdExempt } from '../../../../../../hooks/useAdExempt';
|
import { useAdExempt } from '../../../../../../hooks/useAdExempt';
|
||||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||||
|
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
||||||
import * as Duel from '../../../../../../stores/duel';
|
import * as Duel from '../../../../../../stores/duel';
|
||||||
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
|
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
|
||||||
import { Info } from './components/Info';
|
import { Info } from './components/Info';
|
||||||
@@ -37,7 +38,6 @@ export function Characteristics() {
|
|||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const exempt = useAdExempt();
|
const exempt = useAdExempt();
|
||||||
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
||||||
const awaitedModelDefinitions = use(modelDefinitions);
|
|
||||||
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
||||||
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||||
@@ -68,7 +68,8 @@ export function Characteristics() {
|
|||||||
const stockTrack = tank.tracks[0];
|
const stockTrack = tank.tracks[0];
|
||||||
const stockTurret = tank.turrets[0];
|
const stockTurret = tank.turrets[0];
|
||||||
const stockGun = stockTurret.guns[0];
|
const stockGun = stockTurret.guns[0];
|
||||||
const tankModelDefinition = awaitedModelDefinitions[tank.id];
|
const awaitedModelDefinitions = use(modelDefinitions);
|
||||||
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
const stats = tankCharacteristics(
|
const stats = tankCharacteristics(
|
||||||
@@ -94,8 +95,8 @@ export function Characteristics() {
|
|||||||
applySpallLiner: false,
|
applySpallLiner: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
tankModelDefinition,
|
||||||
equipmentDefinitions: awaitedEquipmentDefinitions,
|
equipmentDefinitions: awaitedEquipmentDefinitions,
|
||||||
modelDefinitions: awaitedModelDefinitions,
|
|
||||||
provisionDefinitions: awaitedProvisionDefinitions,
|
provisionDefinitions: awaitedProvisionDefinitions,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { Canvas } from '@react-three/fiber';
|
import { Canvas } from '@react-three/fiber';
|
||||||
import { Suspense, use, useEffect, useRef } from 'react';
|
import { Suspense, useEffect, useRef } from 'react';
|
||||||
import { Armor } from '../../../../../../components/Armor';
|
import { Armor } from '../../../../../../components/Armor';
|
||||||
import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay';
|
import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay';
|
||||||
import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay';
|
import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay';
|
||||||
import { StaticArmor } from '../../../../../../components/StaticArmor';
|
import { StaticArmor } from '../../../../../../components/StaticArmor';
|
||||||
import { ThicknessRange } from '../../../../../../components/StaticArmor/components/StaticArmorScene';
|
import { ThicknessRange } from '../../../../../../components/StaticArmor/components/StaticArmorScene';
|
||||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { modelDefinitions } from '../../../../../../core/blitzkit/modelDefinitions';
|
|
||||||
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
|
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
|
||||||
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
|
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
|
||||||
import { tankDefinitions } from '../../../../../../core/blitzkit/tankDefinitions';
|
|
||||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||||
|
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
||||||
import * as Duel from '../../../../../../stores/duel';
|
import * as Duel from '../../../../../../stores/duel';
|
||||||
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
|
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
|
||||||
import * as TankopediaPersistent from '../../../../../../stores/tankopediaPersistent';
|
import * as TankopediaPersistent from '../../../../../../stores/tankopediaPersistent';
|
||||||
@@ -29,11 +28,9 @@ export function TankSandbox({ thicknessRange }: TankSandboxProps) {
|
|||||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const canvas = useRef<HTMLCanvasElement>(null);
|
||||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
const awaitedModelDefinitions = use(modelDefinitions);
|
|
||||||
const awaitedTankDefinitions = use(tankDefinitions);
|
|
||||||
const protagonist = Duel.use((state) => state.protagonist);
|
const protagonist = Duel.use((state) => state.protagonist);
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonist.turret.id];
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import { ModelTankWrapper } from '../../../../../../../components/Armor/componen
|
|||||||
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { hasEquipment } from '../../../../../../../core/blitzkit/hasEquipment';
|
import { hasEquipment } from '../../../../../../../core/blitzkit/hasEquipment';
|
||||||
import { jsxTree } from '../../../../../../../core/blitzkit/jsxTree';
|
import { jsxTree } from '../../../../../../../core/blitzkit/jsxTree';
|
||||||
import { modelDefinitions } from '../../../../../../../core/blitzkit/modelDefinitions';
|
|
||||||
import { modelTransformEvent } from '../../../../../../../core/blitzkit/modelTransform';
|
import { modelTransformEvent } from '../../../../../../../core/blitzkit/modelTransform';
|
||||||
import { normalizeAngleRad } from '../../../../../../../core/math/normalizeAngleRad';
|
import { normalizeAngleRad } from '../../../../../../../core/math/normalizeAngleRad';
|
||||||
import { useAwait } from '../../../../../../../hooks/useAwait';
|
|
||||||
import { useModel } from '../../../../../../../hooks/useModel';
|
import { useModel } from '../../../../../../../hooks/useModel';
|
||||||
|
import { useTankModelDefinition } from '../../../../../../../hooks/useTankModelDefinition';
|
||||||
import { useTankTransform } from '../../../../../../../hooks/useTankTransform';
|
import { useTankTransform } from '../../../../../../../hooks/useTankTransform';
|
||||||
import * as Duel from '../../../../../../../stores/duel';
|
import * as Duel from '../../../../../../../stores/duel';
|
||||||
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
|
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
|
||||||
@@ -19,7 +18,6 @@ import { TankopediaDisplay } from '../../../../../../../stores/tankopediaPersist
|
|||||||
export const TankModel = memo(() => {
|
export const TankModel = memo(() => {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const duelStore = Duel.useStore();
|
const duelStore = Duel.useStore();
|
||||||
const awaitedModelDefinitions = useAwait(modelDefinitions);
|
|
||||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||||
const canvas = useThree((state) => state.gl.domElement);
|
const canvas = useThree((state) => state.gl.domElement);
|
||||||
@@ -27,18 +25,15 @@ export const TankModel = memo(() => {
|
|||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null);
|
||||||
const mutateTankopediaTemporary = TankopediaEphemeral.useMutation();
|
const mutateTankopediaTemporary = TankopediaEphemeral.useMutation();
|
||||||
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
|
||||||
|
|
||||||
if (!awaitedModelDefinitions) return;
|
|
||||||
|
|
||||||
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonist.turret.id];
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
const { gltf } = useModel(protagonist.tank.id);
|
const { gltf } = useModel(protagonist.tank.id);
|
||||||
const nodes = Object.values(gltf.nodes);
|
const nodes = Object.values(gltf.nodes);
|
||||||
|
|
||||||
|
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModelTankWrapper ref={hullContainer}>
|
<ModelTankWrapper ref={hullContainer}>
|
||||||
{nodes.map((node) => {
|
{nodes.map((node) => {
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
import { HeightIcon, WidthIcon } from '@radix-ui/react-icons';
|
import { HeightIcon, WidthIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, TextField } from '@radix-ui/themes';
|
import { Flex, TextField } from '@radix-ui/themes';
|
||||||
import { use, useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { degToRad, radToDeg } from 'three/src/math/MathUtils';
|
import { degToRad, radToDeg } from 'three/src/math/MathUtils';
|
||||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { modelDefinitions } from '../../../../../../core/blitzkit/modelDefinitions';
|
|
||||||
import {
|
import {
|
||||||
modelTransformEvent,
|
modelTransformEvent,
|
||||||
ModelTransformEventData,
|
ModelTransformEventData,
|
||||||
} from '../../../../../../core/blitzkit/modelTransform';
|
} from '../../../../../../core/blitzkit/modelTransform';
|
||||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||||
|
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
||||||
import * as Duel from '../../../../../../stores/duel';
|
import * as Duel from '../../../../../../stores/duel';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
|
|
||||||
export function QuickInputs() {
|
export function QuickInputs() {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const protagonist = Duel.use((state) => state.protagonist);
|
const protagonist = Duel.use((state) => state.protagonist);
|
||||||
const awaitedModelDefinitions = use(modelDefinitions);
|
|
||||||
const yawInput = useRef<HTMLInputElement>(null);
|
const yawInput = useRef<HTMLInputElement>(null);
|
||||||
const pitchInput = useRef<HTMLInputElement>(null);
|
const pitchInput = useRef<HTMLInputElement>(null);
|
||||||
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonist.turret.id];
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
import { modelDefinitions } from '../../../../core/blitzkit/modelDefinitions';
|
||||||
import { provisionDefinitions } from '../../../../core/blitzkit/provisionDefinitions';
|
import { provisionDefinitions } from '../../../../core/blitzkit/provisionDefinitions';
|
||||||
import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions';
|
import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions';
|
||||||
import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants';
|
import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants';
|
||||||
@@ -17,6 +18,8 @@ export default async function Layout({
|
|||||||
}) {
|
}) {
|
||||||
const id = Number(params.id);
|
const id = Number(params.id);
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
|
const awaitedModelDefinitions = await modelDefinitions;
|
||||||
|
const tankModelDefinition = awaitedModelDefinitions[id];
|
||||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||||
const tank = awaitedTankDefinitions[id];
|
const tank = awaitedTankDefinitions[id];
|
||||||
const title = `${tank.name} - Tier ${TIER_ROMAN_NUMERALS[tank.tier]} ${
|
const title = `${tank.name} - Tier ${TIER_ROMAN_NUMERALS[tank.tier]} ${
|
||||||
@@ -25,7 +28,7 @@ export default async function Layout({
|
|||||||
const description = `Statistics, armor, and equipment for ${tank.name}`;
|
const description = `Statistics, armor, and equipment for ${tank.name}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TankopediaEphemeral.Provider>
|
<TankopediaEphemeral.Provider data={tankModelDefinition}>
|
||||||
<TankFilters.Provider>
|
<TankFilters.Provider>
|
||||||
<Duel.Provider
|
<Duel.Provider
|
||||||
data={{
|
data={{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { ShotDisplaySection } from './components/ShotDisplaySection';
|
|||||||
import { VideoSection } from './components/VideoSection';
|
import { VideoSection } from './components/VideoSection';
|
||||||
|
|
||||||
export default function Page({ params }: { params: { id: string } }) {
|
export default function Page({ params }: { params: { id: string } }) {
|
||||||
|
const id = Number(params.id);
|
||||||
const exempt = useAdExempt();
|
const exempt = useAdExempt();
|
||||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
@@ -73,7 +74,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper p="0" noMaxWidth color="purple" size={1600}>
|
<PageWrapper p="0" noMaxWidth color="purple" size={1600}>
|
||||||
<HeroSection id={Number(params.id)} />
|
<HeroSection id={id} />
|
||||||
<ShotDisplaySection />
|
<ShotDisplaySection />
|
||||||
{!exempt && <AdMidSectionResponsive />}
|
{!exempt && <AdMidSectionResponsive />}
|
||||||
<CharacteristicsSection />
|
<CharacteristicsSection />
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Card, Code, Flex, Text } from '@radix-ui/themes';
|
import { Card, Code, Flex, Text, TextField } from '@radix-ui/themes';
|
||||||
import { Html } from '@react-three/drei';
|
import { Html } from '@react-three/drei';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
import { radToDeg } from 'three/src/math/MathUtils';
|
import { radToDeg } from 'three/src/math/MathUtils';
|
||||||
|
import { ModelArmor } from '../../../core/blitzkit/modelDefinitions';
|
||||||
|
import { resolveArmorIndex } from '../../../core/blitzkit/resolveArmorIndex';
|
||||||
import * as App from '../../../stores/app';
|
import * as App from '../../../stores/app';
|
||||||
|
import * as Duel from '../../../stores/duel';
|
||||||
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||||
import { layerTypeNames } from './ShotDisplayCard';
|
import { layerTypeNames } from './ShotDisplayCard';
|
||||||
import { ArmorType } from './SpacedArmorScene';
|
import { ArmorType } from './SpacedArmorScene';
|
||||||
@@ -10,7 +14,14 @@ export function ArmorPlateDisplay() {
|
|||||||
const highlightArmor = TankopediaEphemeral.use(
|
const highlightArmor = TankopediaEphemeral.use(
|
||||||
(state) => state.highlightArmor,
|
(state) => state.highlightArmor,
|
||||||
);
|
);
|
||||||
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const developerMode = App.use((state) => state.developerMode);
|
const developerMode = App.use((state) => state.developerMode);
|
||||||
|
const devThicknessInput = useRef<HTMLInputElement>(null);
|
||||||
|
const duelStore = Duel.useStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
devThicknessInput.current?.focus();
|
||||||
|
});
|
||||||
|
|
||||||
if (highlightArmor === undefined) return null;
|
if (highlightArmor === undefined) return null;
|
||||||
|
|
||||||
@@ -42,9 +53,60 @@ export function ArmorPlateDisplay() {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{developerMode && (
|
{developerMode && (
|
||||||
<Text mt="2" color="gray" size="2">
|
<>
|
||||||
<b>DEV:</b> <Code>{highlightArmor.name}</Code>
|
<Text mt="2" color="gray" size="2">
|
||||||
</Text>
|
<b>DEV:</b> <Code>{highlightArmor.name}</Code>
|
||||||
|
</Text>
|
||||||
|
<TextField.Root
|
||||||
|
type="number"
|
||||||
|
style={{ width: 96 }}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
devThicknessInput.current?.blur();
|
||||||
|
mutateTankopediaEphemeral((draft) => {
|
||||||
|
draft.highlightArmor = undefined;
|
||||||
|
const { protagonist } = duelStore.getState();
|
||||||
|
let armor: ModelArmor;
|
||||||
|
|
||||||
|
if (highlightArmor.name.startsWith('hull_')) {
|
||||||
|
armor = draft.model.armor;
|
||||||
|
} else if (highlightArmor.name.startsWith('turret_')) {
|
||||||
|
armor =
|
||||||
|
draft.model.turrets[protagonist.turret.id].armor;
|
||||||
|
} else if (highlightArmor.name.startsWith('gun_')) {
|
||||||
|
armor =
|
||||||
|
draft.model.turrets[protagonist.turret.id].guns[
|
||||||
|
protagonist.gun.id
|
||||||
|
].armor;
|
||||||
|
} else return;
|
||||||
|
|
||||||
|
const index = resolveArmorIndex(highlightArmor.name);
|
||||||
|
|
||||||
|
if (index === undefined) return;
|
||||||
|
|
||||||
|
const input = event.currentTarget.valueAsNumber;
|
||||||
|
|
||||||
|
if (!isNaN(input)) {
|
||||||
|
armor.thickness[index] = input;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
devThicknessInput.current?.blur();
|
||||||
|
mutateTankopediaEphemeral((draft) => {
|
||||||
|
draft.highlightArmor = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
key={highlightArmor.name}
|
||||||
|
defaultValue={highlightArmor.thickness.toFixed(0)}
|
||||||
|
mt="1"
|
||||||
|
autoFocus
|
||||||
|
ref={devThicknessInput}
|
||||||
|
size="1"
|
||||||
|
>
|
||||||
|
<TextField.Slot side="right">mm</TextField.Slot>
|
||||||
|
</TextField.Root>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { correctZYTuple } from '../../../core/blitz/correctZYTuple';
|
|||||||
import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
||||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||||
import { useArmor } from '../../../hooks/useArmor';
|
import { useArmor } from '../../../hooks/useArmor';
|
||||||
import { useModelDefinitions } from '../../../hooks/useModelDefinitions';
|
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||||
import * as Duel from '../../../stores/duel';
|
import * as Duel from '../../../stores/duel';
|
||||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||||
@@ -13,7 +13,6 @@ import { PrimaryArmorSceneComponent } from './PrimaryArmorSceneComponent';
|
|||||||
|
|
||||||
export const PrimaryArmorScene = memo(() => {
|
export const PrimaryArmorScene = memo(() => {
|
||||||
const wrapper = useRef<Group>(null);
|
const wrapper = useRef<Group>(null);
|
||||||
const modelDefinitions = useModelDefinitions();
|
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null);
|
||||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||||
@@ -23,7 +22,7 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
const gun = Duel.use((state) => state.protagonist.gun);
|
const gun = Duel.use((state) => state.protagonist.gun);
|
||||||
const armorGltf = useArmor(tank.id);
|
const armorGltf = useArmor(tank.id);
|
||||||
const armorNodes = Object.values(armorGltf.nodes);
|
const armorNodes = Object.values(armorGltf.nodes);
|
||||||
const tankModelDefinition = modelDefinitions[tank.id];
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
@@ -32,6 +31,12 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
const gunOrigin = correctZYTuple(turretModelDefinition.gunOrigin);
|
const gunOrigin = correctZYTuple(turretModelDefinition.gunOrigin);
|
||||||
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
const tankopediaPersistentStore = TankopediaPersistent.useStore();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
tankModelDefinition.armor,
|
||||||
|
turretModelDefinition.armor,
|
||||||
|
gunModelDefinition.armor,
|
||||||
|
);
|
||||||
|
|
||||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
|||||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||||
import { useArmor } from '../../../hooks/useArmor';
|
import { useArmor } from '../../../hooks/useArmor';
|
||||||
import { useModel } from '../../../hooks/useModel';
|
import { useModel } from '../../../hooks/useModel';
|
||||||
import { useModelDefinitions } from '../../../hooks/useModelDefinitions';
|
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||||
import * as Duel from '../../../stores/duel';
|
import * as Duel from '../../../stores/duel';
|
||||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
import { ModelTankWrapper } from './ModelTankWrapper';
|
||||||
@@ -23,7 +23,6 @@ interface SpacedArmorSceneProps {
|
|||||||
|
|
||||||
export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||||
const wrapper = useRef<Group>(null);
|
const wrapper = useRef<Group>(null);
|
||||||
const modelDefinitions = useModelDefinitions();
|
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null);
|
||||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||||
@@ -35,7 +34,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
const { gltf: modelGltf } = useModel(tank.id);
|
const { gltf: modelGltf } = useModel(tank.id);
|
||||||
const armorNodes = Object.values(armorGltf.nodes);
|
const armorNodes = Object.values(armorGltf.nodes);
|
||||||
const modelNodes = Object.values(modelGltf.nodes);
|
const modelNodes = Object.values(modelGltf.nodes);
|
||||||
const tankModelDefinition = modelDefinitions[tank.id];
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
|
|||||||
@@ -92,9 +92,9 @@ export function SpacedArmorSceneComponent({
|
|||||||
...props
|
...props
|
||||||
}: SpacedArmorSceneComponentProps) {
|
}: SpacedArmorSceneComponentProps) {
|
||||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||||
|
const duelStore = Duel.useStore();
|
||||||
const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation();
|
||||||
const camera = useThree((state) => state.camera);
|
const camera = useThree((state) => state.camera);
|
||||||
const duelStore = Duel.useStore();
|
|
||||||
|
|
||||||
if (props.static) {
|
if (props.static) {
|
||||||
const x = clamp(thickness / props.thicknessRange.quartile, 0, 1);
|
const x = clamp(thickness / props.thicknessRange.quartile, 0, 1);
|
||||||
@@ -134,7 +134,7 @@ export function SpacedArmorSceneComponent({
|
|||||||
depthWrite,
|
depthWrite,
|
||||||
...(clip ? { clippingPlanes: [clip] } : {}),
|
...(clip ? { clippingPlanes: [clip] } : {}),
|
||||||
}),
|
}),
|
||||||
[],
|
[thickness],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,7 +182,7 @@ export function SpacedArmorSceneComponent({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, []);
|
}, [thickness]);
|
||||||
|
|
||||||
return jsxTree(node, {
|
return jsxTree(node, {
|
||||||
material,
|
material,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
|||||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||||
import { useArmor } from '../../../hooks/useArmor';
|
import { useArmor } from '../../../hooks/useArmor';
|
||||||
import { useModel } from '../../../hooks/useModel';
|
import { useModel } from '../../../hooks/useModel';
|
||||||
import { useModelDefinitions } from '../../../hooks/useModelDefinitions';
|
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||||
import * as Duel from '../../../stores/duel';
|
import * as Duel from '../../../stores/duel';
|
||||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||||
@@ -25,7 +25,6 @@ export interface ThicknessRange {
|
|||||||
export const StaticArmorScene = memo<StaticArmorSceneProps>(
|
export const StaticArmorScene = memo<StaticArmorSceneProps>(
|
||||||
({ thicknessRange }) => {
|
({ thicknessRange }) => {
|
||||||
const wrapper = useRef<Group>(null);
|
const wrapper = useRef<Group>(null);
|
||||||
const awaitedTankModelDefinitions = useModelDefinitions();
|
|
||||||
const turretContainer = useRef<Group>(null);
|
const turretContainer = useRef<Group>(null);
|
||||||
const gunContainer = useRef<Group>(null);
|
const gunContainer = useRef<Group>(null);
|
||||||
const protagonist = Duel.use((draft) => draft.protagonist);
|
const protagonist = Duel.use((draft) => draft.protagonist);
|
||||||
@@ -37,7 +36,7 @@ export const StaticArmorScene = memo<StaticArmorSceneProps>(
|
|||||||
const { gltf: modelGltf } = useModel(tank.id);
|
const { gltf: modelGltf } = useModel(tank.id);
|
||||||
const armorNodes = Object.values(armorGltf.nodes);
|
const armorNodes = Object.values(armorGltf.nodes);
|
||||||
const modelNodes = Object.values(modelGltf.nodes);
|
const modelNodes = Object.values(modelGltf.nodes);
|
||||||
const tankModelDefinition = awaitedTankModelDefinitions[tank.id];
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
@@ -58,6 +57,8 @@ export const StaticArmorScene = memo<StaticArmorSceneProps>(
|
|||||||
(state) => state.model.visual.showExternalModules,
|
(state) => state.model.visual.showExternalModules,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log('yeah');
|
||||||
|
|
||||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface BoundingBox {
|
|||||||
min: Vector3Tuple;
|
min: Vector3Tuple;
|
||||||
max: Vector3Tuple;
|
max: Vector3Tuple;
|
||||||
}
|
}
|
||||||
interface ModelDefinition {
|
export interface ModelDefinition {
|
||||||
armor: ModelArmor;
|
armor: ModelArmor;
|
||||||
turretOrigin: Vector3Tuple;
|
turretOrigin: Vector3Tuple;
|
||||||
turretRotation?: InitialTurretRotation;
|
turretRotation?: InitialTurretRotation;
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
const pattern = /\d+$/;
|
||||||
|
|
||||||
|
export function resolveArmorIndex(name: string) {
|
||||||
|
const match = name.match(pattern)?.[0];
|
||||||
|
return match ? parseInt(match) : undefined;
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { resolvePenetrationCoefficient } from '../blitz/resolvePenetrationCoeffi
|
|||||||
import { coefficient } from './coefficient';
|
import { coefficient } from './coefficient';
|
||||||
import { degressiveStat } from './degressiveStat';
|
import { degressiveStat } from './degressiveStat';
|
||||||
import { EquipmentDefinitions } from './equipmentDefinitions';
|
import { EquipmentDefinitions } from './equipmentDefinitions';
|
||||||
import { ModelDefinitions } from './modelDefinitions';
|
import { ModelDefinition, ModelDefinitions } from './modelDefinitions';
|
||||||
import { normalizeBoundingBox } from './normalizeBoundingBox';
|
import { normalizeBoundingBox } from './normalizeBoundingBox';
|
||||||
import { progressiveStat } from './progressiveStat';
|
import { progressiveStat } from './progressiveStat';
|
||||||
import { ProvisionDefinitions } from './provisionDefinitions';
|
import { ProvisionDefinitions } from './provisionDefinitions';
|
||||||
@@ -66,17 +66,16 @@ export function tankCharacteristics(
|
|||||||
applySpallLiner: boolean;
|
applySpallLiner: boolean;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelDefinitions,
|
tankModelDefinition,
|
||||||
equipmentDefinitions,
|
equipmentDefinitions,
|
||||||
provisionDefinitions,
|
provisionDefinitions,
|
||||||
}: {
|
}: {
|
||||||
modelDefinitions: ModelDefinitions;
|
tankModelDefinition: ModelDefinition;
|
||||||
equipmentDefinitions: EquipmentDefinitions;
|
equipmentDefinitions: EquipmentDefinitions;
|
||||||
provisionDefinitions: ProvisionDefinitions;
|
provisionDefinitions: ProvisionDefinitions;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const presetRows = equipmentDefinitions.presets[tank.equipment];
|
const presetRows = equipmentDefinitions.presets[tank.equipment];
|
||||||
const tankModelDefinition = modelDefinitions[tank.id];
|
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { modelDefinitions } from '../core/blitzkit/modelDefinitions';
|
|
||||||
import { useAwait } from './useAwait';
|
|
||||||
|
|
||||||
export function useModelDefinitions() {
|
|
||||||
return useAwait(modelDefinitions);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as TankopediaEphemeral from '../stores/tankopediaEphemeral';
|
||||||
|
|
||||||
|
export function useTankModelDefinition() {
|
||||||
|
return TankopediaEphemeral.use((state) => state.model);
|
||||||
|
}
|
||||||
@@ -3,23 +3,21 @@ import { Euler, Group, Vector3 } from 'three';
|
|||||||
import { degToRad } from 'three/src/math/MathUtils';
|
import { degToRad } from 'three/src/math/MathUtils';
|
||||||
import { I_HAT, J_HAT, K_HAT } from '../constants/axis';
|
import { I_HAT, J_HAT, K_HAT } from '../constants/axis';
|
||||||
import { correctZYTuple } from '../core/blitz/correctZYTuple';
|
import { correctZYTuple } from '../core/blitz/correctZYTuple';
|
||||||
import { modelDefinitions } from '../core/blitzkit/modelDefinitions';
|
|
||||||
import {
|
import {
|
||||||
ModelTransformEventData,
|
ModelTransformEventData,
|
||||||
modelTransformEvent,
|
modelTransformEvent,
|
||||||
} from '../core/blitzkit/modelTransform';
|
} from '../core/blitzkit/modelTransform';
|
||||||
import { DuelMember } from '../stores/duel';
|
import { DuelMember } from '../stores/duel';
|
||||||
import { useAwait } from './useAwait';
|
import { useTankModelDefinition } from './useTankModelDefinition';
|
||||||
|
|
||||||
export function useTankTransform(
|
export function useTankTransform(
|
||||||
member: DuelMember,
|
member: DuelMember,
|
||||||
turretContainer: RefObject<Group>,
|
turretContainer: RefObject<Group>,
|
||||||
gunContainer: RefObject<Group>,
|
gunContainer: RefObject<Group>,
|
||||||
) {
|
) {
|
||||||
const awaitedModelDefinitions = useAwait(modelDefinitions);
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tankModelDefinition = awaitedModelDefinitions[member.tank.id];
|
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[member.track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[member.track.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[member.turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[member.turret.id];
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import { create } from 'zustand';
|
|||||||
import { subscribeWithSelector } from 'zustand/middleware';
|
import { subscribeWithSelector } from 'zustand/middleware';
|
||||||
import { ArmorType } from '../components/Armor/components/SpacedArmorScene';
|
import { ArmorType } from '../components/Armor/components/SpacedArmorScene';
|
||||||
import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent';
|
import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent';
|
||||||
|
import { ModelDefinition } from '../core/blitzkit/modelDefinitions';
|
||||||
import { createNextSafeStore } from '../core/zustand/createNextSafeStore';
|
import { createNextSafeStore } from '../core/zustand/createNextSafeStore';
|
||||||
|
|
||||||
interface TankopediaEphemeral {
|
interface TankopediaEphemeral {
|
||||||
shot?: Shot;
|
shot?: Shot;
|
||||||
skills: Record<string, number>;
|
skills: Record<string, number>;
|
||||||
controlsEnabled: boolean;
|
controlsEnabled: boolean;
|
||||||
|
model: ModelDefinition;
|
||||||
highlightArmor?: {
|
highlightArmor?: {
|
||||||
name: string;
|
name: string;
|
||||||
point: Vector3;
|
point: Vector3;
|
||||||
@@ -86,10 +88,11 @@ export type ArmorPiercingLayer =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const { Provider, use, useMutation, useStore } = createNextSafeStore(
|
export const { Provider, use, useMutation, useStore } = createNextSafeStore(
|
||||||
() =>
|
(model: ModelDefinition) =>
|
||||||
create<TankopediaEphemeral>()(
|
create<TankopediaEphemeral>()(
|
||||||
subscribeWithSelector<TankopediaEphemeral>(() => ({
|
subscribeWithSelector<TankopediaEphemeral>(() => ({
|
||||||
skills: {},
|
skills: {},
|
||||||
|
model,
|
||||||
controlsEnabled: true,
|
controlsEnabled: true,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in new issue
Block a user