mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
fix: deterministic and correct boolean dynamic armor
This commit is contained in:
1 parent
319c865ef0
commit
952523d3f9
8 files changed
+127
-177
No files matched your search
+53
@@ -0,0 +1,53 @@
|
||||
import { IconButton } from '@radix-ui/themes';
|
||||
import { invalidate } from '@react-three/fiber';
|
||||
import { asset } from '../../../../../../../../../core/blitzkit/asset';
|
||||
import { useArmor } from '../../../../../../../../../hooks/useArmor';
|
||||
import * as Duel from '../../../../../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../../../../../stores/tankopediaEphemeral';
|
||||
|
||||
export function DynamicArmorSwitcher() {
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
const { hasDynamicArmor } = useArmor(tank.id);
|
||||
|
||||
if (!hasDynamicArmor) return null;
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
color={isDynamicArmorActive ? 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 === tank.consumables) {
|
||||
draft.protagonist.consumables[tank.consumables - 1] = 73;
|
||||
} else {
|
||||
draft.protagonist.consumables.push(73);
|
||||
}
|
||||
}
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Dynamic Armor"
|
||||
src={asset('icons/consumables/73.webp')}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
+26
-78
@@ -20,31 +20,31 @@ import {
|
||||
Tooltip,
|
||||
} from '@radix-ui/themes';
|
||||
import { invalidate } from '@react-three/fiber';
|
||||
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 { 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';
|
||||
import { uniqueGuns } from '../../../../../../../core/blitzkit/uniqueGuns';
|
||||
import { useEquipment } from '../../../../../../../hooks/useEquipment';
|
||||
import { useFullScreen } from '../../../../../../../hooks/useFullScreen';
|
||||
import { useFullscreenAvailability } from '../../../../../../../hooks/useFullscreenAvailability';
|
||||
import * as App from '../../../../../../../stores/app';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../../../../../../stores/tankopediaPersistent';
|
||||
import { TankopediaDisplay } from '../../../../../../../stores/tankopediaPersistent/constants';
|
||||
import { TankSearch } from '../../../../components/TankSearch';
|
||||
import { ENVIRONMENTS } from '../../Lighting';
|
||||
import { QuickInputs } from '../../QuickInputs';
|
||||
import { Thicknesses } from '../../Thicknesses';
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import { ThicknessRange } from '../../../../../../../../components/Armor/components/StaticArmor';
|
||||
import { ModuleButton } from '../../../../../../../../components/ModuleButtons/ModuleButton';
|
||||
import { SmallTankIcon } from '../../../../../../../../components/SmallTankIcon';
|
||||
import { resolveNearPenetration } from '../../../../../../../../core/blitz/resolveNearPenetration';
|
||||
import { resolvePenetrationCoefficient } from '../../../../../../../../core/blitz/resolvePenetrationCoefficient';
|
||||
import { asset } from '../../../../../../../../core/blitzkit/asset';
|
||||
import { imgur } from '../../../../../../../../core/blitzkit/imgur';
|
||||
import { Pose, poseEvent } from '../../../../../../../../core/blitzkit/pose';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||
import { uniqueGuns } from '../../../../../../../../core/blitzkit/uniqueGuns';
|
||||
import { useArmor } from '../../../../../../../../hooks/useArmor';
|
||||
import { useEquipment } from '../../../../../../../../hooks/useEquipment';
|
||||
import { useFullScreen } from '../../../../../../../../hooks/useFullScreen';
|
||||
import { useFullscreenAvailability } from '../../../../../../../../hooks/useFullscreenAvailability';
|
||||
import * as App from '../../../../../../../../stores/app';
|
||||
import * as Duel from '../../../../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../../../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../../../../../../../stores/tankopediaPersistent';
|
||||
import { TankopediaDisplay } from '../../../../../../../../stores/tankopediaPersistent/constants';
|
||||
import { TankSearch } from '../../../../../components/TankSearch';
|
||||
import { ENVIRONMENTS } from '../../../Lighting';
|
||||
import { QuickInputs } from '../../../QuickInputs';
|
||||
import { Thicknesses } from '../../../Thicknesses';
|
||||
import { DynamicArmorSwitcher } from './components/DynamicArmorSwitcher';
|
||||
|
||||
interface OptionsProps {
|
||||
thicknessRange: ThicknessRange;
|
||||
@@ -78,28 +78,14 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
);
|
||||
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
||||
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]);
|
||||
@@ -291,45 +277,7 @@ 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>
|
||||
)}
|
||||
<DynamicArmorSwitcher />
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
IconButton,
|
||||
Text,
|
||||
} from '@radix-ui/themes';
|
||||
import { invalidate } from '@react-three/fiber';
|
||||
import { use } from 'react';
|
||||
import { Box, Button, Checkbox, Flex, Text } from '@radix-ui/themes';
|
||||
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 { useArmor } from '../../../../../hooks/useArmor';
|
||||
import * as Duel from '../../../../../stores/duel';
|
||||
import * as TankopediaEphemeral from '../../../../../stores/tankopediaEphemeral';
|
||||
import * as TankopediaPersistent from '../../../../../stores/tankopediaPersistent';
|
||||
import { DynamicArmorSwitcher } from './Model/components/Options/components/DynamicArmorSwitcher';
|
||||
|
||||
interface ThicknessesProps {
|
||||
thicknessRange: ThicknessRange;
|
||||
@@ -36,21 +26,8 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
||||
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,
|
||||
),
|
||||
);
|
||||
const { hasDynamicArmor } = useArmor(protagonistTank.id);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -199,45 +176,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
||||
</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>
|
||||
)}
|
||||
<DynamicArmorSwitcher />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export const PrimaryArmorScene = memo(() => {
|
||||
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 armorGltf = useArmor(tank.id);
|
||||
const armorGltf = useArmor(tank.id).gltf;
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||
@@ -28,10 +28,9 @@ 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 isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
|
||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||
|
||||
@@ -51,8 +50,8 @@ export const PrimaryArmorScene = memo(() => {
|
||||
!isVisible ||
|
||||
spaced ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
@@ -82,8 +81,8 @@ export const PrimaryArmorScene = memo(() => {
|
||||
!isVisible ||
|
||||
spaced ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
@@ -116,8 +115,8 @@ export const PrimaryArmorScene = memo(() => {
|
||||
!isVisible ||
|
||||
spaced ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_00'))
|
||||
)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
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 armorGltf = useArmor(tank.id);
|
||||
const { gltf: armorGltf } = useArmor(tank.id);
|
||||
const { gltf: modelGltf } = useModel(tank.id);
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const modelNodes = Object.values(modelGltf.nodes);
|
||||
@@ -45,10 +45,9 @@ 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 isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
|
||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||
|
||||
@@ -67,8 +66,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
)
|
||||
return null;
|
||||
|
||||
@@ -118,8 +117,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
)
|
||||
return null;
|
||||
|
||||
@@ -153,8 +152,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
||||
if (
|
||||
!isVisible ||
|
||||
thickness === undefined ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
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 armorGltf = useArmor(tank.id);
|
||||
const { gltf: armorGltf } = useArmor(tank.id);
|
||||
const { gltf: modelGltf } = useModel(tank.id);
|
||||
const armorNodes = Object.values(armorGltf.nodes);
|
||||
const modelNodes = Object.values(modelGltf.nodes);
|
||||
@@ -64,10 +64,9 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
const showExternalModules = TankopediaPersistent.use(
|
||||
(state) => state.model.visual.showExternalModules,
|
||||
);
|
||||
const protagonistConsumables = Duel.use(
|
||||
(state) => state.protagonist.consumables,
|
||||
const isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
);
|
||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
||||
|
||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||
|
||||
@@ -88,8 +87,8 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
)
|
||||
return null;
|
||||
|
||||
@@ -145,8 +144,8 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -240,8 +239,8 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
||||
thickness === undefined ||
|
||||
(!showPrimaryArmor && !spaced) ||
|
||||
(!showSpacedArmor && spaced) ||
|
||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
||||
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||
)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ export function StaticArmorSceneComponent({
|
||||
const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation();
|
||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||
const camera = useThree((state) => state.camera);
|
||||
const scene = useThree((state) => state.scene);
|
||||
const x = thickness / thicknessRange.value;
|
||||
const xClamped = clamp(x, 0, 1);
|
||||
let color: Color;
|
||||
|
||||
+16
-2
@@ -2,10 +2,24 @@ import { ObjectMap, useLoader } from '@react-three/fiber';
|
||||
import { GLTF, GLTFLoader } from 'three-stdlib';
|
||||
import { asset } from '../core/blitzkit/asset';
|
||||
|
||||
const cache: Record<number, GLTF & ObjectMap> = {};
|
||||
const cache: Record<
|
||||
number,
|
||||
{ gltf: GLTF & ObjectMap; hasDynamicArmor: boolean }
|
||||
> = {};
|
||||
|
||||
export function useArmor(id: number) {
|
||||
const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`));
|
||||
if (!cache[id]) cache[id] = gltf;
|
||||
|
||||
console.log(Object.values(gltf.nodes).map((node) => node.name));
|
||||
|
||||
if (!cache[id]) {
|
||||
cache[id] = {
|
||||
gltf,
|
||||
hasDynamicArmor: Object.values(gltf.nodes).some(
|
||||
(node) =>
|
||||
node.name.includes('state_00') || node.name.includes('state_01'),
|
||||
),
|
||||
};
|
||||
}
|
||||
return cache[id];
|
||||
}
|
||||
Reference in new issue
Block a user