mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
feat: start of dynamic armor
This commit is contained in:
1 parent
86e369af19
commit
cd0769675e
13 files changed
+300
-45
No files matched your search
@@ -46,6 +46,11 @@ import { DATA } from './constants';
|
||||
import { Avatar } from './skillIcons';
|
||||
import { TankParameters } from './tankIcons';
|
||||
|
||||
function ensureDynamicArmor(armor: ModelArmor) {
|
||||
if (!armor.dynamic) armor.dynamic = { false: [], true: [] };
|
||||
return armor as ModelArmor & Required<Pick<ModelArmor, 'dynamic'>>;
|
||||
}
|
||||
|
||||
export interface BlitzStrings {
|
||||
[key: string]: string;
|
||||
}
|
||||
@@ -210,6 +215,27 @@ interface VehicleDefinitions {
|
||||
};
|
||||
};
|
||||
};
|
||||
extras?: {
|
||||
armorsStatesController?: {
|
||||
state: [
|
||||
{
|
||||
id: 0;
|
||||
armors: string;
|
||||
type: 'default';
|
||||
enabled: true;
|
||||
factorsModifiers: '';
|
||||
default: '';
|
||||
},
|
||||
{
|
||||
id: 1;
|
||||
armors: string;
|
||||
type: 'default';
|
||||
enabled: false;
|
||||
factorsModifiers: '';
|
||||
},
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface VehicleDefinitionList {
|
||||
[key: string]: {
|
||||
@@ -1242,6 +1268,55 @@ export async function definitions() {
|
||||
Object.values(tankDefinitions[tankId].tracks).forEach((track) => {
|
||||
track.xp = trackXps.get(track.id);
|
||||
});
|
||||
|
||||
if (tankDefinition.root.extras?.armorsStatesController) {
|
||||
const [falsePlates, truePlates] =
|
||||
tankDefinition.root.extras.armorsStatesController.state.map(
|
||||
(state) => state.armors.split(' '),
|
||||
);
|
||||
|
||||
function applyDynamicArmor(plates: string[], type: `${boolean}`) {
|
||||
plates.forEach((plate) => {
|
||||
const [name, armorIndexString] = plate.split('.');
|
||||
const armorIndex = Number(armorIndexString.split('_')[1]);
|
||||
|
||||
if (name === 'hull') {
|
||||
ensureDynamicArmor(modelDefinitions[tankId].armor).dynamic[
|
||||
type
|
||||
].push(armorIndex);
|
||||
} else {
|
||||
const [module, moduleIndexString] = name.split('_');
|
||||
const moduleIndex = Number(moduleIndexString);
|
||||
|
||||
if (module === 'turret') {
|
||||
const turret = Object.values(
|
||||
modelDefinitions[tankId].turrets,
|
||||
).find((turret) => turret.model === moduleIndex);
|
||||
if (!turret) throw new Error('Turret not found');
|
||||
ensureDynamicArmor(turret.armor).dynamic[type].push(
|
||||
armorIndex,
|
||||
);
|
||||
} else if (module === 'gun') {
|
||||
Object.values(modelDefinitions[tankId].turrets).forEach(
|
||||
(turret) => {
|
||||
const gun = Object.values(turret.guns).find(
|
||||
(gun) => gun.model === moduleIndex,
|
||||
);
|
||||
if (!gun) throw new Error('Gun not found');
|
||||
ensureDynamicArmor(gun.armor).dynamic[type].push(
|
||||
armorIndex,
|
||||
);
|
||||
},
|
||||
);
|
||||
} else
|
||||
throw new Error(`Unknown dynamic armor module ${module}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
applyDynamicArmor(falsePlates, 'false');
|
||||
applyDynamicArmor(truePlates, 'true');
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -2,8 +2,8 @@ import { ChevronLeftIcon, MixIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Button, Flex, Heading } from '@radix-ui/themes';
|
||||
import Link from 'next/link';
|
||||
import { Suspense, use, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ThicknessRange } from '../../../../../../components/Armor/components/StaticArmor';
|
||||
import { classIcons } from '../../../../../../components/ClassIcon';
|
||||
import { ThicknessRange } from '../../../../../../components/StaticArmor';
|
||||
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
||||
import { tankDefinitions } from '../../../../../../core/blitzkit/tankDefinitions';
|
||||
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ShotDisplay } from '../../../../../../components/Armor/components/ShotD
|
||||
import {
|
||||
StaticArmor,
|
||||
ThicknessRange,
|
||||
} from '../../../../../../components/StaticArmor';
|
||||
} from '../../../../../../components/Armor/components/StaticArmor';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
|
||||
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
|
||||
@@ -48,8 +48,6 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
const display = TankopediaPersistent.use((state) => state.display);
|
||||
const onScreen = useOnScreen(canvas);
|
||||
|
||||
console.log(onScreen);
|
||||
|
||||
useImperativeHandle(ref, () => canvas.current!, []);
|
||||
|
||||
function handlePointerDown() {
|
||||
|
||||
@@ -22,13 +22,15 @@ import {
|
||||
Tooltip,
|
||||
} from '@radix-ui/themes';
|
||||
import { invalidate } from '@react-three/fiber';
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import { RefObject, use, useEffect, useState } from 'react';
|
||||
import { ThicknessRange } from '../../../../../../../components/Armor/components/StaticArmor';
|
||||
import { ModuleButton } from '../../../../../../../components/ModuleButtons/ModuleButton';
|
||||
import { SmallTankIcon } from '../../../../../../../components/SmallTankIcon';
|
||||
import { ThicknessRange } from '../../../../../../../components/StaticArmor';
|
||||
import { resolveNearPenetration } from '../../../../../../../core/blitz/resolveNearPenetration';
|
||||
import { resolvePenetrationCoefficient } from '../../../../../../../core/blitz/resolvePenetrationCoefficient';
|
||||
import { asset } from '../../../../../../../core/blitzkit/asset';
|
||||
import { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkit/checkConsumableProvisionInclusivity';
|
||||
import { consumableDefinitions } from '../../../../../../../core/blitzkit/consumableDefinitions';
|
||||
import { imgur } from '../../../../../../../core/blitzkit/imgur';
|
||||
import { Pose, poseEvent } from '../../../../../../../core/blitzkit/pose';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||
@@ -77,15 +79,29 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
(state) => state.model.visual.environment,
|
||||
);
|
||||
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||
const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
||||
const protagonistConsumables = Duel.use(
|
||||
(state) => state.protagonist.consumables,
|
||||
);
|
||||
const antagonistGun = Duel.use((state) => state.antagonist.gun);
|
||||
const antagonistShell = Duel.use((state) => state.antagonist.shell);
|
||||
const [antagonistSelectorOpen, setAntagonistSelectorOpen] = useState(false);
|
||||
const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
||||
const hasCalibratedShells = useEquipment(103, true);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
const [tab, setTab] = useState('search');
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const hasEnhancedArmor = useEquipment(110);
|
||||
const awaitedConsumableDefinitions = use(consumableDefinitions);
|
||||
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
|
||||
(consumable) =>
|
||||
checkConsumableProvisionInclusivity(
|
||||
consumable,
|
||||
protagonistTank,
|
||||
protagonistGun,
|
||||
),
|
||||
);
|
||||
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
||||
|
||||
useEffect(() => setDisplay(displayRaw), [displayRaw]);
|
||||
@@ -141,7 +157,6 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
).toFixed(0)}
|
||||
mm
|
||||
</Text>
|
||||
|
||||
{antagonistUniqueGuns.length > 1 && (
|
||||
<Popover.Root>
|
||||
<Popover.Trigger>
|
||||
@@ -201,7 +216,6 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
)}
|
||||
|
||||
<Flex
|
||||
direction="column"
|
||||
style={{
|
||||
@@ -303,6 +317,46 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
/>
|
||||
</IconButton>
|
||||
</Flex>
|
||||
|
||||
{consumablesList.some((consumable) => consumable.id === 73) && (
|
||||
<IconButton
|
||||
color={hasDynamicArmor ? undefined : 'gray'}
|
||||
variant="soft"
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
onClick={() => {
|
||||
invalidate();
|
||||
mutateDuel((draft) => {
|
||||
if (draft.protagonist.consumables.includes(73)) {
|
||||
draft.protagonist.consumables =
|
||||
draft.protagonist.consumables.filter((id) => id !== 73);
|
||||
} else {
|
||||
if (
|
||||
draft.protagonist.consumables.length ===
|
||||
protagonistTank.consumables
|
||||
) {
|
||||
draft.protagonist.consumables[
|
||||
protagonistTank.consumables - 1
|
||||
] = 73;
|
||||
} else {
|
||||
draft.protagonist.consumables.push(73);
|
||||
}
|
||||
}
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Calibrated Shells"
|
||||
src={asset('icons/consumables/73.webp')}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@@ -323,7 +377,10 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
|
||||
const anchor = document.createElement('a');
|
||||
|
||||
anchor.setAttribute('download', `${protagonist.name}.png`);
|
||||
anchor.setAttribute(
|
||||
'download',
|
||||
`${protagonistTank.name}.png`,
|
||||
);
|
||||
anchor.setAttribute(
|
||||
'href',
|
||||
canvas.current.toDataURL('image/png'),
|
||||
|
||||
@@ -46,6 +46,8 @@ export const TankModel = memo(() => {
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
console.log(node.name);
|
||||
|
||||
function translateTexture(offset: number) {
|
||||
const mesh = node.children[0] as Mesh;
|
||||
const material = mesh?.material as MeshStandardMaterial;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Button, Checkbox, Flex, Text } from '@radix-ui/themes';
|
||||
import { ThicknessRange } from '../../../../../components/StaticArmor';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
IconButton,
|
||||
Text,
|
||||
} from '@radix-ui/themes';
|
||||
import { invalidate } from '@react-three/fiber';
|
||||
import { use } from 'react';
|
||||
import { ThicknessRange } from '../../../../../components/Armor/components/StaticArmor';
|
||||
import { asset } from '../../../../../core/blitzkit/asset';
|
||||
import { checkConsumableProvisionInclusivity } from '../../../../../core/blitzkit/checkConsumableProvisionInclusivity';
|
||||
import { consumableDefinitions } from '../../../../../core/blitzkit/consumableDefinitions';
|
||||
import * as Duel from '../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../../../../stores/tankopediaPersistent';
|
||||
|
||||
@@ -18,10 +31,26 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
||||
const showPrimaryArmor = TankopediaPersistent.use(
|
||||
(state) => state.model.visual.showPrimaryArmor,
|
||||
);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const editStatic = TankopediaEphemeral.use((state) => state.editStatic);
|
||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||
const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||
const awaitedConsumableDefinitions = use(consumableDefinitions);
|
||||
const protagonistConsumables = Duel.use(
|
||||
(state) => state.protagonist.consumables,
|
||||
);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
|
||||
(consumable) =>
|
||||
checkConsumableProvisionInclusivity(
|
||||
consumable,
|
||||
protagonistTank,
|
||||
protagonistGun,
|
||||
),
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -169,6 +198,46 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
||||
<Checkbox size="2" checked={editStatic} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
{consumablesList.some((consumable) => consumable.id === 73) && (
|
||||
<IconButton
|
||||
color={hasDynamicArmor ? undefined : 'gray'}
|
||||
variant="soft"
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
onClick={() => {
|
||||
invalidate();
|
||||
mutateDuel((draft) => {
|
||||
if (draft.protagonist.consumables.includes(73)) {
|
||||
draft.protagonist.consumables =
|
||||
draft.protagonist.consumables.filter((id) => id !== 73);
|
||||
} else {
|
||||
if (
|
||||
draft.protagonist.consumables.length ===
|
||||
protagonistTank.consumables
|
||||
) {
|
||||
draft.protagonist.consumables[
|
||||
protagonistTank.consumables - 1
|
||||
] = 73;
|
||||
} else {
|
||||
draft.protagonist.consumables.push(73);
|
||||
}
|
||||
}
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Calibrated Shells"
|
||||
src={asset('icons/consumables/73.webp')}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -28,6 +28,10 @@ export const PrimaryArmorScene = memo(() => {
|
||||
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
||||
const turretOrigin = correctZYTuple(tankModelDefinition.turretOrigin);
|
||||
const gunOrigin = correctZYTuple(turretModelDefinition.gunOrigin);
|
||||
const protagonistConsumables = Duel.use(
|
||||
(state) => state.protagonist.consumables,
|
||||
);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
|
||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||
|
||||
@@ -43,7 +47,14 @@ export const PrimaryArmorScene = memo(() => {
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (!isVisible || spaced || thickness === undefined) return null;
|
||||
if (
|
||||
!isVisible ||
|
||||
spaced ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<PrimaryArmorSceneComponent
|
||||
@@ -67,7 +78,14 @@ export const PrimaryArmorScene = memo(() => {
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (!isVisible || spaced || thickness === undefined) return null;
|
||||
if (
|
||||
!isVisible ||
|
||||
spaced ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<group position={hullOrigin}>
|
||||
@@ -94,7 +112,14 @@ export const PrimaryArmorScene = memo(() => {
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (!isVisible || spaced || thickness === undefined) return null;
|
||||
if (
|
||||
!isVisible ||
|
||||
spaced ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<group position={hullOrigin}>
|
||||
|
||||
@@ -45,6 +45,10 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
gunModelDefinition.mask === undefined
|
||||
? undefined
|
||||
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
||||
const protagonistConsumables = Duel.use(
|
||||
(state) => state.protagonist.consumables,
|
||||
);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
|
||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||
|
||||
@@ -60,7 +64,13 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (!isVisible || thickness === undefined) return null;
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<SpacedArmorSceneComponent
|
||||
@@ -105,7 +115,13 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (!isVisible || thickness === undefined) return null;
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<group position={hullOrigin}>
|
||||
@@ -134,7 +150,13 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
armorId,
|
||||
);
|
||||
|
||||
if (!isVisible || thickness === undefined) return null;
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<group position={hullOrigin}>
|
||||
|
||||
+31
-22
@@ -1,23 +1,23 @@
|
||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||
import { memo, useRef } from 'react';
|
||||
import { Group, Plane, Vector2, Vector3 } from 'three';
|
||||
import { applyPitchYawLimits } from '../../core/blitz/applyPitchYawLimits';
|
||||
import { correctZYTuple } from '../../core/blitz/correctZYTuple';
|
||||
import { hasEquipment } from '../../core/blitzkit/hasEquipment';
|
||||
import { modelTransformEvent } from '../../core/blitzkit/modelTransform';
|
||||
import { nameToArmorId } from '../../core/blitzkit/nameToArmorId';
|
||||
import { resolveArmor } from '../../core/blitzkit/resolveThickness';
|
||||
import { normalizeAngleRad } from '../../core/math/normalizeAngleRad';
|
||||
import { useArmor } from '../../hooks/useArmor';
|
||||
import { useModel } from '../../hooks/useModel';
|
||||
import { useTankModelDefinition } from '../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../hooks/useTankTransform';
|
||||
import * as Duel from '../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../stores/tankopediaPersistent';
|
||||
import { ModelTankWrapper } from '../Armor/components/ModelTankWrapper';
|
||||
import { ArmorType } from '../Armor/components/SpacedArmorScene';
|
||||
import { StaticArmorSceneComponent } from '../Armor/components/StaticArmorSceneComponent';
|
||||
import { applyPitchYawLimits } from '../../../core/blitz/applyPitchYawLimits';
|
||||
import { correctZYTuple } from '../../../core/blitz/correctZYTuple';
|
||||
import { hasEquipment } from '../../../core/blitzkit/hasEquipment';
|
||||
import { modelTransformEvent } from '../../../core/blitzkit/modelTransform';
|
||||
import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
|
||||
import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
|
||||
import { normalizeAngleRad } from '../../../core/math/normalizeAngleRad';
|
||||
import { useArmor } from '../../../hooks/useArmor';
|
||||
import { useModel } from '../../../hooks/useModel';
|
||||
import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../../hooks/useTankTransform';
|
||||
import * as Duel from '../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
|
||||
import { ModelTankWrapper } from './ModelTankWrapper';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
import { StaticArmorSceneComponent } from './StaticArmorSceneComponent';
|
||||
|
||||
interface ArmorSceneProps {
|
||||
thicknessRange: ThicknessRange;
|
||||
@@ -64,6 +64,10 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
const showExternalModules = TankopediaPersistent.use(
|
||||
(state) => state.model.visual.showExternalModules,
|
||||
);
|
||||
const protagonistConsumables = Duel.use(
|
||||
(state) => state.protagonist.consumables,
|
||||
);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
|
||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||
|
||||
@@ -83,7 +87,9 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced)
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
@@ -138,7 +144,9 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced)
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -231,10 +239,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced)
|
||||
) {
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
}
|
||||
|
||||
const position = new Vector2();
|
||||
const delta = new Vector2();
|
||||
@@ -23,12 +23,12 @@ import {
|
||||
} from '../../../core/blitzkit/modelTransform';
|
||||
import { discardClippingPlane } from '../../../core/three/discardClippingPlane';
|
||||
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
|
||||
import { ThicknessRange } from '../../StaticArmor';
|
||||
import { ArmorType } from './SpacedArmorScene';
|
||||
import {
|
||||
ArmorUserData,
|
||||
ExternalModuleVariant,
|
||||
} from './SpacedArmorSceneComponent';
|
||||
import { ThicknessRange } from './StaticArmor';
|
||||
|
||||
type StaticArmorSceneComponentProps = {
|
||||
name: string;
|
||||
|
||||
@@ -26,7 +26,7 @@ export function ConsumablesManager({
|
||||
}: ConsumablesManagerProps) {
|
||||
return (
|
||||
<Flex wrap="wrap" gap="2" {...props}>
|
||||
{consumables.map((consumable, index) => {
|
||||
{consumables.map((consumable) => {
|
||||
const isSelected = selected.some((id) => id === consumable.id);
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,6 +7,7 @@ export type ModelDefinitions = Record<number, ModelDefinition>;
|
||||
export interface ModelArmor {
|
||||
thickness: Record<number, number>;
|
||||
spaced?: number[];
|
||||
// dynamic?: Record<`${boolean}`, number[]>;
|
||||
}
|
||||
|
||||
export interface BoundingBox {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function useConsumable(id: number, consumables: number[]) {
|
||||
return consumables.includes(id);
|
||||
}
|
||||
Reference in new issue
Block a user