mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +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,
|
Tooltip,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { invalidate } from '@react-three/fiber';
|
import { invalidate } from '@react-three/fiber';
|
||||||
import { RefObject, use, useEffect, useState } from 'react';
|
import { RefObject, useEffect, useState } from 'react';
|
||||||
import { ThicknessRange } from '../../../../../../../components/Armor/components/StaticArmor';
|
import { ThicknessRange } from '../../../../../../../../components/Armor/components/StaticArmor';
|
||||||
import { ModuleButton } from '../../../../../../../components/ModuleButtons/ModuleButton';
|
import { ModuleButton } from '../../../../../../../../components/ModuleButtons/ModuleButton';
|
||||||
import { SmallTankIcon } from '../../../../../../../components/SmallTankIcon';
|
import { SmallTankIcon } from '../../../../../../../../components/SmallTankIcon';
|
||||||
import { resolveNearPenetration } from '../../../../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../../../../../core/blitz/resolveNearPenetration';
|
||||||
import { resolvePenetrationCoefficient } from '../../../../../../../core/blitz/resolvePenetrationCoefficient';
|
import { resolvePenetrationCoefficient } from '../../../../../../../../core/blitz/resolvePenetrationCoefficient';
|
||||||
import { asset } from '../../../../../../../core/blitzkit/asset';
|
import { asset } from '../../../../../../../../core/blitzkit/asset';
|
||||||
import { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkit/checkConsumableProvisionInclusivity';
|
import { imgur } from '../../../../../../../../core/blitzkit/imgur';
|
||||||
import { consumableDefinitions } from '../../../../../../../core/blitzkit/consumableDefinitions';
|
import { Pose, poseEvent } from '../../../../../../../../core/blitzkit/pose';
|
||||||
import { imgur } from '../../../../../../../core/blitzkit/imgur';
|
import { TIER_ROMAN_NUMERALS } from '../../../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||||
import { Pose, poseEvent } from '../../../../../../../core/blitzkit/pose';
|
import { uniqueGuns } from '../../../../../../../../core/blitzkit/uniqueGuns';
|
||||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../../core/blitzkit/tankDefinitions/constants';
|
import { useArmor } from '../../../../../../../../hooks/useArmor';
|
||||||
import { uniqueGuns } from '../../../../../../../core/blitzkit/uniqueGuns';
|
import { useEquipment } from '../../../../../../../../hooks/useEquipment';
|
||||||
import { useEquipment } from '../../../../../../../hooks/useEquipment';
|
import { useFullScreen } from '../../../../../../../../hooks/useFullScreen';
|
||||||
import { useFullScreen } from '../../../../../../../hooks/useFullScreen';
|
import { useFullscreenAvailability } from '../../../../../../../../hooks/useFullscreenAvailability';
|
||||||
import { useFullscreenAvailability } from '../../../../../../../hooks/useFullscreenAvailability';
|
import * as App from '../../../../../../../../stores/app';
|
||||||
import * as App from '../../../../../../../stores/app';
|
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';
|
import { TankopediaDisplay } from '../../../../../../../../stores/tankopediaPersistent/constants';
|
||||||
import { TankopediaDisplay } from '../../../../../../../stores/tankopediaPersistent/constants';
|
import { TankSearch } from '../../../../../components/TankSearch';
|
||||||
import { TankSearch } from '../../../../components/TankSearch';
|
import { ENVIRONMENTS } from '../../../Lighting';
|
||||||
import { ENVIRONMENTS } from '../../Lighting';
|
import { QuickInputs } from '../../../QuickInputs';
|
||||||
import { QuickInputs } from '../../QuickInputs';
|
import { Thicknesses } from '../../../Thicknesses';
|
||||||
import { Thicknesses } from '../../Thicknesses';
|
import { DynamicArmorSwitcher } from './components/DynamicArmorSwitcher';
|
||||||
|
|
||||||
interface OptionsProps {
|
interface OptionsProps {
|
||||||
thicknessRange: ThicknessRange;
|
thicknessRange: ThicknessRange;
|
||||||
@@ -78,28 +78,14 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
);
|
);
|
||||||
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
||||||
const protagonistTank = 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 antagonistGun = Duel.use((state) => state.antagonist.gun);
|
||||||
const antagonistShell = Duel.use((state) => state.antagonist.shell);
|
const antagonistShell = Duel.use((state) => state.antagonist.shell);
|
||||||
const [antagonistSelectorOpen, setAntagonistSelectorOpen] = useState(false);
|
const [antagonistSelectorOpen, setAntagonistSelectorOpen] = useState(false);
|
||||||
const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
||||||
const hasCalibratedShells = useEquipment(103, true);
|
const hasCalibratedShells = useEquipment(103, true);
|
||||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
|
||||||
const [tab, setTab] = useState('search');
|
const [tab, setTab] = useState('search');
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const hasEnhancedArmor = useEquipment(110);
|
const hasEnhancedArmor = useEquipment(110);
|
||||||
const awaitedConsumableDefinitions = use(consumableDefinitions);
|
|
||||||
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
|
|
||||||
(consumable) =>
|
|
||||||
checkConsumableProvisionInclusivity(
|
|
||||||
consumable,
|
|
||||||
protagonistTank,
|
|
||||||
protagonistGun,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
||||||
|
|
||||||
useEffect(() => setDisplay(displayRaw), [displayRaw]);
|
useEffect(() => setDisplay(displayRaw), [displayRaw]);
|
||||||
@@ -291,45 +277,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{consumablesList.some((consumable) => consumable.id === 73) && (
|
<DynamicArmorSwitcher />
|
||||||
<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>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1,21 +1,11 @@
|
|||||||
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons';
|
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import { Box, Button, Checkbox, Flex, Text } from '@radix-ui/themes';
|
||||||
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 { ThicknessRange } from '../../../../../components/Armor/components/StaticArmor';
|
||||||
import { asset } from '../../../../../core/blitzkit/asset';
|
import { useArmor } from '../../../../../hooks/useArmor';
|
||||||
import { checkConsumableProvisionInclusivity } from '../../../../../core/blitzkit/checkConsumableProvisionInclusivity';
|
|
||||||
import { consumableDefinitions } from '../../../../../core/blitzkit/consumableDefinitions';
|
|
||||||
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';
|
||||||
|
import { DynamicArmorSwitcher } from './Model/components/Options/components/DynamicArmorSwitcher';
|
||||||
|
|
||||||
interface ThicknessesProps {
|
interface ThicknessesProps {
|
||||||
thicknessRange: ThicknessRange;
|
thicknessRange: ThicknessRange;
|
||||||
@@ -36,21 +26,8 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
|||||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const editStatic = TankopediaEphemeral.use((state) => state.editStatic);
|
const editStatic = TankopediaEphemeral.use((state) => state.editStatic);
|
||||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||||
const protagonistGun = Duel.use((state) => state.protagonist.gun);
|
|
||||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||||
const awaitedConsumableDefinitions = use(consumableDefinitions);
|
const { hasDynamicArmor } = useArmor(protagonistTank.id);
|
||||||
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 (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
@@ -199,45 +176,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{consumablesList.some((consumable) => consumable.id === 73) && (
|
<DynamicArmorSwitcher />
|
||||||
<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>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
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).gltf;
|
||||||
const armorNodes = Object.values(armorGltf.nodes);
|
const armorNodes = Object.values(armorGltf.nodes);
|
||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||||
@@ -28,10 +28,9 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
||||||
const turretOrigin = correctZYTuple(tankModelDefinition.turretOrigin);
|
const turretOrigin = correctZYTuple(tankModelDefinition.turretOrigin);
|
||||||
const gunOrigin = correctZYTuple(turretModelDefinition.gunOrigin);
|
const gunOrigin = correctZYTuple(turretModelDefinition.gunOrigin);
|
||||||
const protagonistConsumables = Duel.use(
|
const isDynamicArmorActive = Duel.use((state) =>
|
||||||
(state) => state.protagonist.consumables,
|
state.protagonist.consumables.includes(73),
|
||||||
);
|
);
|
||||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
|
||||||
|
|
||||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||||
|
|
||||||
@@ -51,8 +50,8 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
!isVisible ||
|
!isVisible ||
|
||||||
spaced ||
|
spaced ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_00'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -82,8 +81,8 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
!isVisible ||
|
!isVisible ||
|
||||||
spaced ||
|
spaced ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_00'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -116,8 +115,8 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
!isVisible ||
|
!isVisible ||
|
||||||
spaced ||
|
spaced ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_01')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_00'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
const gun = Duel.use((state) => state.protagonist.gun);
|
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 { 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);
|
||||||
@@ -45,10 +45,9 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
gunModelDefinition.mask === undefined
|
gunModelDefinition.mask === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
|
||||||
const protagonistConsumables = Duel.use(
|
const isDynamicArmorActive = Duel.use((state) =>
|
||||||
(state) => state.protagonist.consumables,
|
state.protagonist.consumables.includes(73),
|
||||||
);
|
);
|
||||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
|
||||||
|
|
||||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||||
|
|
||||||
@@ -67,8 +66,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -118,8 +117,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -153,8 +152,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
if (
|
if (
|
||||||
!isVisible ||
|
!isVisible ||
|
||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
const track = Duel.use((state) => state.protagonist.track);
|
const track = Duel.use((state) => state.protagonist.track);
|
||||||
const turret = Duel.use((state) => state.protagonist.turret);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
const gun = Duel.use((state) => state.protagonist.gun);
|
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 { 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);
|
||||||
@@ -64,10 +64,9 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
const showExternalModules = TankopediaPersistent.use(
|
const showExternalModules = TankopediaPersistent.use(
|
||||||
(state) => state.model.visual.showExternalModules,
|
(state) => state.model.visual.showExternalModules,
|
||||||
);
|
);
|
||||||
const protagonistConsumables = Duel.use(
|
const isDynamicArmorActive = Duel.use((state) =>
|
||||||
(state) => state.protagonist.consumables,
|
state.protagonist.consumables.includes(73),
|
||||||
);
|
);
|
||||||
const hasDynamicArmor = protagonistConsumables.includes(73);
|
|
||||||
|
|
||||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||||
|
|
||||||
@@ -88,8 +87,8 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(!showPrimaryArmor && !spaced) ||
|
(!showPrimaryArmor && !spaced) ||
|
||||||
(!showSpacedArmor && spaced) ||
|
(!showSpacedArmor && spaced) ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -145,8 +144,8 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(!showPrimaryArmor && !spaced) ||
|
(!showPrimaryArmor && !spaced) ||
|
||||||
(!showSpacedArmor && spaced) ||
|
(!showSpacedArmor && spaced) ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -240,8 +239,8 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
thickness === undefined ||
|
thickness === undefined ||
|
||||||
(!showPrimaryArmor && !spaced) ||
|
(!showPrimaryArmor && !spaced) ||
|
||||||
(!showSpacedArmor && spaced) ||
|
(!showSpacedArmor && spaced) ||
|
||||||
(hasDynamicArmor && node.name.includes('state_01')) ||
|
(isDynamicArmorActive && node.name.includes('state_00')) ||
|
||||||
(!hasDynamicArmor && node.name.includes('state_00'))
|
(!isDynamicArmorActive && node.name.includes('state_01'))
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export function StaticArmorSceneComponent({
|
|||||||
const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation();
|
||||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||||
const camera = useThree((state) => state.camera);
|
const camera = useThree((state) => state.camera);
|
||||||
const scene = useThree((state) => state.scene);
|
|
||||||
const x = thickness / thicknessRange.value;
|
const x = thickness / thicknessRange.value;
|
||||||
const xClamped = clamp(x, 0, 1);
|
const xClamped = clamp(x, 0, 1);
|
||||||
let color: Color;
|
let color: Color;
|
||||||
|
|||||||
+16
-2
@@ -2,10 +2,24 @@ import { ObjectMap, useLoader } from '@react-three/fiber';
|
|||||||
import { GLTF, GLTFLoader } from 'three-stdlib';
|
import { GLTF, GLTFLoader } from 'three-stdlib';
|
||||||
import { asset } from '../core/blitzkit/asset';
|
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) {
|
export function useArmor(id: number) {
|
||||||
const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`));
|
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];
|
return cache[id];
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user