mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: complete tankoepdia page
This commit is contained in:
1 parent
2b5ffb2b84
commit
a54ba78dbf
39 files changed
+790
-161
No files matched your search
@@ -65,7 +65,7 @@ export const averageDefinitionsArray = averageDefinitions.then((data) =>
|
|||||||
({
|
({
|
||||||
id: Number(key),
|
id: Number(key),
|
||||||
...(value as AverageDefinitionsEntry),
|
...(value as AverageDefinitionsEntry),
|
||||||
} satisfies AverageDefinitionsEntryWithId),
|
}) satisfies AverageDefinitionsEntryWithId,
|
||||||
)
|
)
|
||||||
.filter((tank) => tank.mu.battles > 0),
|
.filter((tank) => tank.mu.battles > 0),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
export * from './assertSecret';
|
export * from './assertSecret';
|
||||||
export * from './asset';
|
export * from './asset';
|
||||||
export * from './availableProvisions';
|
export * from './availableProvisions';
|
||||||
// export * from './averageDefinitions';
|
export * from './averageDefinitions';
|
||||||
// export * from './camouflageDefinitions';
|
// export * from './camouflageDefinitions';
|
||||||
// export * from './checkConsumableProvisionInclusivity';
|
// export * from './checkConsumableProvisionInclusivity';
|
||||||
// export * from './coefficient';
|
export * from './coefficient';
|
||||||
// export * from './consumableDefinitions';
|
export * from './consumableDefinitions';
|
||||||
// export * from './context';
|
// export * from './context';
|
||||||
// export * from './createDefaultSkills';
|
export * from './createDefaultSkills';
|
||||||
// export * from './degressiveStat';
|
export * from './degressiveStat';
|
||||||
// export * from './discoveredIdDefinitions';
|
// export * from './discoveredIdDefinitions';
|
||||||
// export * from './emblemURL';
|
// export * from './emblemURL';
|
||||||
export * from './equipmentDefinitions';
|
export * from './equipmentDefinitions';
|
||||||
@@ -29,13 +29,13 @@ export * from './normalizeBoundingBox';
|
|||||||
// export * from './parseBkni';
|
// export * from './parseBkni';
|
||||||
// export * from './patientFetch';
|
// export * from './patientFetch';
|
||||||
// export * from './patientFetchJSON';
|
// export * from './patientFetchJSON';
|
||||||
// export * from './progressiveStat';
|
export * from './progressiveStat';
|
||||||
export * from './provisionDefinitions';
|
export * from './provisionDefinitions';
|
||||||
export * from './resolveDpm';
|
export * from './resolveDpm';
|
||||||
export * from './resolvePenetrationCoefficient';
|
export * from './resolvePenetrationCoefficient';
|
||||||
// export * from './reviews';
|
export * from './reviews';
|
||||||
export * from './skillDefinitions';
|
export * from './skillDefinitions';
|
||||||
// export * from './sum';
|
export * from './sum';
|
||||||
export * from './superCompress';
|
export * from './superCompress';
|
||||||
export * from './superDecompress';
|
export * from './superDecompress';
|
||||||
export * from './tankDefinitions';
|
export * from './tankDefinitions';
|
||||||
@@ -44,6 +44,6 @@ export * from './tankNames';
|
|||||||
export * from './unionBoundingBox';
|
export * from './unionBoundingBox';
|
||||||
export * from './uniqueGuns';
|
export * from './uniqueGuns';
|
||||||
// export * from './unwrapBlitzkitResponse';
|
// export * from './unwrapBlitzkitResponse';
|
||||||
// export * from './votes';
|
export * from './votes';
|
||||||
// export * from './withCORSProxy';
|
// export * from './withCORSProxy';
|
||||||
// export * from './youtubers';
|
export * from './youtubers';
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import protobuf from 'protobufjs';
|
import protobuf from 'protobufjs';
|
||||||
import average_definitions from '../../protos/average_definitions.proto';
|
import average_definitions from '../../protos/average_definitions.proto?raw';
|
||||||
import reviews from '../../protos/reviews.proto';
|
import reviews from '../../protos/reviews.proto?raw';
|
||||||
|
|
||||||
export type ProtoSource = keyof typeof protos;
|
export type ProtoSource = keyof typeof protos;
|
||||||
|
|
||||||
|
|||||||
+13
-14
@@ -20,24 +20,23 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import { Fragment, use, useEffect, useRef, useState } from 'react';
|
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||||
import { lerp } from 'three/src/math/MathUtils';
|
import { Ad, AdType } from '../../../../../../../../website/src/components/Ad';
|
||||||
import { Ad, AdType } from '../../../../../../components/Ad';
|
import { InfoWithDelta } from '../../../../../../../../website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta';
|
||||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../../../website/src/core/blitz/applyPitchYawLimits';
|
||||||
import { tankCharacteristics } from '../../../../../../core/blitzkit/tankCharacteristics';
|
import { useAdExempt } from '../../../../../../../../website/src/hooks/useAdExempt';
|
||||||
import { useAdExempt } from '../../../../../../hooks/useAdExempt';
|
import { useEquipment } from '../../../../../../../../website/src/hooks/useEquipment';
|
||||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
import { useTankModelDefinition } from '../../../../../../../../website/src/hooks/useTankModelDefinition';
|
||||||
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
import { Duel } from '../../../../../../../../website/src/stores/duel';
|
||||||
import * as Duel from '../../../../../../stores/duel';
|
import { TankopediaEphemeral } from '../../../../../../../../website/src/stores/tankopediaEphemeral';
|
||||||
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
|
import { Info } from '../../../../performance/components/Info';
|
||||||
import { Info } from './components/Info';
|
|
||||||
import { InfoWithDelta } from './components/InfoWithDelta';
|
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||||
|
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||||
|
|
||||||
export function Characteristics() {
|
export function Characteristics() {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const exempt = useAdExempt();
|
const exempt = useAdExempt();
|
||||||
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
|
||||||
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);
|
||||||
const hasImprovedVentilation = useEquipment(102);
|
const hasImprovedVentilation = useEquipment(102);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { PageWrapper } from '../../../../../components/PageWrapper';
|
|||||||
import { useAdExempt } from '../../../../../hooks/useAdExempt';
|
import { useAdExempt } from '../../../../../hooks/useAdExempt';
|
||||||
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 { VideoSection } from './VideoSection';
|
import { VideoSection } from '../../../../../../../website/src/components/Tankopedia/VideoSection';
|
||||||
|
|
||||||
export function Content() {
|
export function Content() {
|
||||||
const exempt = useAdExempt();
|
const exempt = useAdExempt();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { PageWrapper } from '../../../../components/PageWrapper';
|
|||||||
import { useAdExempt } from '../../../../hooks/useAdExempt';
|
import { useAdExempt } from '../../../../hooks/useAdExempt';
|
||||||
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 { VideoSection } from './components/VideoSection';
|
import { VideoSection } from '../../../../../../website/src/components/Tankopedia/VideoSection';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const exempt = useAdExempt();
|
const exempt = useAdExempt();
|
||||||
|
|||||||
@@ -18,5 +18,5 @@
|
|||||||
"../website/src/components/Tankopedia/HistorySection.tsx",
|
"../website/src/components/Tankopedia/HistorySection.tsx",
|
||||||
"../website/src/components/Tankopedia/CalloutsSection.tsx",
|
"../website/src/components/Tankopedia/CalloutsSection.tsx",
|
||||||
"../website/src/components/Tankopedia/ShotDisplaySection.tsx"
|
"../website/src/components/Tankopedia/ShotDisplaySection.tsx"
|
||||||
]
|
, "../website/src/components/Tankopedia/CharacteristicsSection/components/ConfigurationChildWrapper.tsx", "../website/src/components/Tankopedia/VideoSection.tsx" ]
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ConsumableEntry } from '@blitzkit/core';
|
import type { ConsumableEntry } from '@blitzkit/core/src/blitzkit/consumableDefinitions';
|
||||||
import { Flex, type FlexProps } from '@radix-ui/themes';
|
import { Flex, type FlexProps } from '@radix-ui/themes';
|
||||||
import { ConsumableButton } from './ModuleButtons/ConsumableButton';
|
import { ConsumableButton } from './ModuleButtons/ConsumableButton';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { EquipmentPreset } from '@blitzkit/core';
|
import type { EquipmentPreset } from '@blitzkit/core';
|
||||||
import { Flex } from '@radix-ui/themes';
|
import { Flex } from '@radix-ui/themes';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { EquipmentMatrix } from '../../../website-legacy/src/stores/duel';
|
import type { EquipmentMatrix } from '../stores/duel';
|
||||||
import { EquipmentButton } from './ModuleButtons/EquipmentButton';
|
import { EquipmentButton } from './ModuleButtons/EquipmentButton';
|
||||||
|
|
||||||
interface EquipmentManagerProps {
|
interface EquipmentManagerProps {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { asset, consumableDefinitions } from '@blitzkit/core';
|
import { asset } from '@blitzkit/core';
|
||||||
|
import { consumableDefinitions } from '@blitzkit/core/src/blitzkit/consumableDefinitions';
|
||||||
import { ClockIcon, ReloadIcon } from '@radix-ui/react-icons';
|
import { ClockIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, Text } from '@radix-ui/themes';
|
import { Flex, Text } from '@radix-ui/themes';
|
||||||
import { useDelta } from '../../../../website-legacy/src/hooks/useDelta';
|
import { useDelta } from '../../hooks/useDelta';
|
||||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||||
import { TankComponentButtonProps } from './TankComponentButton';
|
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||||
|
|
||||||
interface ConsumableButtonProps extends TankComponentButtonProps {
|
interface ConsumableButtonProps extends TankComponentButtonProps {
|
||||||
consumable: number;
|
consumable: number;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { asset, equipmentDefinitions } from '@blitzkit/core';
|
import { asset, equipmentDefinitions } from '@blitzkit/core';
|
||||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||||
import { TankComponentButtonProps } from './TankComponentButton';
|
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||||
|
|
||||||
interface EquipmentButtonProps extends TankComponentButtonProps {
|
interface EquipmentButtonProps extends TankComponentButtonProps {
|
||||||
equipment: number;
|
equipment: number;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { asset, provisionDefinitions } from '@blitzkit/core';
|
import { asset, provisionDefinitions } from '@blitzkit/core';
|
||||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||||
import { TankComponentButtonProps } from './TankComponentButton';
|
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||||
|
|
||||||
interface ProvisionButtonProps extends TankComponentButtonProps {
|
interface ProvisionButtonProps extends TankComponentButtonProps {
|
||||||
provision: number;
|
provision: number;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { MinusCircledIcon } from '@radix-ui/react-icons';
|
import { MinusCircledIcon } from '@radix-ui/react-icons';
|
||||||
import { Callout, Flex } from '@radix-ui/themes';
|
import { Callout, Flex } from '@radix-ui/themes';
|
||||||
import { ExperimentIcon } from '../../../../../components/ExperimentIcon';
|
import { Duel } from '../../stores/duel';
|
||||||
import * as Duel from '../../../../../stores/duel';
|
import { ExperimentIcon } from '../ExperimentIcon';
|
||||||
|
|
||||||
export function CalloutsSection() {
|
export function CalloutsSection() {
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
|
|||||||
+3
-11
@@ -1,6 +1,6 @@
|
|||||||
import { CaretDownIcon, CaretUpIcon } from '@radix-ui/react-icons';
|
import { CaretDownIcon, CaretUpIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, Text } from '@radix-ui/themes';
|
import { Flex, Separator, Text } from '@radix-ui/themes';
|
||||||
import { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface InfoProps {
|
export interface InfoProps {
|
||||||
name: ReactNode;
|
name: ReactNode;
|
||||||
@@ -41,15 +41,7 @@ export function Info({
|
|||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{children !== undefined && (
|
{children !== undefined && <Separator style={{ flex: 1 }} />}
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
height: 1,
|
|
||||||
backgroundColor: 'red',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{children !== undefined && (
|
{children !== undefined && (
|
||||||
<Flex align="center" gap="1">
|
<Flex align="center" gap="1">
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
import { useDelta } from '../../../../../../../hooks/useDelta';
|
import { useDelta } from '../../../../../../hooks/useDelta';
|
||||||
import { Info, InfoProps } from './Info';
|
import { type InfoProps, Info } from './Info';
|
||||||
|
|
||||||
interface InfoWithDeltaProps extends InfoProps {
|
interface InfoWithDeltaProps extends InfoProps {
|
||||||
children: number;
|
children: number;
|
||||||
+632
@@ -0,0 +1,632 @@
|
|||||||
|
import {
|
||||||
|
asset,
|
||||||
|
coefficient,
|
||||||
|
CREW_MEMBER_NAMES,
|
||||||
|
equipmentDefinitions,
|
||||||
|
GUN_TYPE_NAMES,
|
||||||
|
isExplosive,
|
||||||
|
provisionDefinitions,
|
||||||
|
resolvePenetrationCoefficient,
|
||||||
|
} from '@blitzkit/core';
|
||||||
|
import { AccessibilityIcon, InfoCircledIcon } from '@radix-ui/react-icons';
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
IconButton,
|
||||||
|
Popover,
|
||||||
|
Slider,
|
||||||
|
Text,
|
||||||
|
TextField,
|
||||||
|
Tooltip,
|
||||||
|
} from '@radix-ui/themes';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { Fragment } from 'react/jsx-runtime';
|
||||||
|
import { lerp } from 'three/src/math/MathUtils.js';
|
||||||
|
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||||
|
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||||
|
import { useAdExempt } from '../../../../../hooks/useAdExempt';
|
||||||
|
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||||
|
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||||
|
import { Duel } from '../../../../../stores/duel';
|
||||||
|
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||||
|
import { Ad, AdType } from '../../../../Ad';
|
||||||
|
import { Info } from './components/Info';
|
||||||
|
import { InfoWithDelta } from './components/InfoWithDelta';
|
||||||
|
|
||||||
|
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||||
|
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||||
|
|
||||||
|
export function Characteristics() {
|
||||||
|
const mutateDuel = Duel.useMutation();
|
||||||
|
const exempt = useAdExempt();
|
||||||
|
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||||
|
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||||
|
const hasImprovedVentilation = useEquipment(102);
|
||||||
|
const crewMastery = Duel.use((state) => state.protagonist.crewMastery);
|
||||||
|
const [penetrationDistance, setPenetrationDistance] = useState(250);
|
||||||
|
const setPenetrationDistanceDebounced = debounce((value: number) => {
|
||||||
|
setPenetrationDistance(value);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
const provisions = Duel.use((state) => state.protagonist.provisions);
|
||||||
|
const provisionCrewBonus =
|
||||||
|
provisions.reduce(
|
||||||
|
(total, provision) =>
|
||||||
|
awaitedProvisionDefinitions[provision].crew
|
||||||
|
? total + awaitedProvisionDefinitions[provision].crew! / 100
|
||||||
|
: total,
|
||||||
|
0,
|
||||||
|
) + (hasImprovedVentilation ? 0.08 : 0);
|
||||||
|
const commanderMastery = crewMastery + provisionCrewBonus;
|
||||||
|
const consumables = Duel.use((state) => state.protagonist.consumables);
|
||||||
|
const camouflage = Duel.use((state) => state.protagonist.camouflage);
|
||||||
|
const equipmentMatrix = Duel.use(
|
||||||
|
(state) => state.protagonist.equipmentMatrix,
|
||||||
|
);
|
||||||
|
const { tank, turret, gun, engine, track, shell } = Duel.use(
|
||||||
|
(state) => state.protagonist,
|
||||||
|
);
|
||||||
|
const stockEngine = tank.engines[0];
|
||||||
|
const stockTrack = tank.tracks[0];
|
||||||
|
const stockTurret = tank.turrets[0];
|
||||||
|
const stockGun = stockTurret.guns[0];
|
||||||
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
|
const stats = tankCharacteristics(
|
||||||
|
{
|
||||||
|
tank,
|
||||||
|
camouflage,
|
||||||
|
consumables,
|
||||||
|
crewMastery,
|
||||||
|
crewSkills,
|
||||||
|
engine,
|
||||||
|
equipmentMatrix,
|
||||||
|
gun,
|
||||||
|
provisions,
|
||||||
|
shell,
|
||||||
|
turret,
|
||||||
|
track,
|
||||||
|
stockEngine,
|
||||||
|
stockGun,
|
||||||
|
stockTrack,
|
||||||
|
stockTurret,
|
||||||
|
applyReactiveArmor: false,
|
||||||
|
applyDynamicArmor: false,
|
||||||
|
applySpallLiner: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tankModelDefinition,
|
||||||
|
equipmentDefinitions: awaitedEquipmentDefinitions,
|
||||||
|
provisionDefinitions: awaitedProvisionDefinitions,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasSupercharger = useEquipment(107);
|
||||||
|
const hasCalibratedShells = useEquipment(103);
|
||||||
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
|
const penetrationCoefficient = coefficient([
|
||||||
|
hasCalibratedShells,
|
||||||
|
resolvePenetrationCoefficient(true, shell.type) - 1,
|
||||||
|
]);
|
||||||
|
const penetrationLossOverDistanceCoefficient = coefficient([
|
||||||
|
hasSupercharger,
|
||||||
|
-0.6,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (penetrationDistanceInput.current) {
|
||||||
|
penetrationDistanceInput.current.value = `${penetrationDistance}`;
|
||||||
|
}
|
||||||
|
}, [penetrationDistance]);
|
||||||
|
useEffect(() => {
|
||||||
|
mutateDuel((draft) => {
|
||||||
|
[draft.protagonist.pitch, draft.protagonist.yaw] = applyPitchYawLimits(
|
||||||
|
draft.protagonist.pitch,
|
||||||
|
draft.protagonist.yaw,
|
||||||
|
gunModelDefinition.pitch,
|
||||||
|
turretModelDefinition.yaw,
|
||||||
|
hasImprovedVerticalStabilizer,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, [hasImprovedVerticalStabilizer]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
gap="8"
|
||||||
|
direction={{
|
||||||
|
initial: 'column',
|
||||||
|
md: 'row',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex direction="column" gap="8" style={{ flex: 1 }}>
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
<Flex align="center" gap="4">
|
||||||
|
<Heading size="5">Firepower</Heading>
|
||||||
|
|
||||||
|
<Flex>
|
||||||
|
{gun.shells.map((thisShell, shellIndex) => (
|
||||||
|
<Tooltip content={thisShell.name} key={thisShell.id}>
|
||||||
|
<IconButton
|
||||||
|
color={thisShell.id === shell.id ? undefined : 'gray'}
|
||||||
|
variant="soft"
|
||||||
|
style={{
|
||||||
|
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||||
|
borderBottomLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||||
|
borderTopRightRadius:
|
||||||
|
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||||
|
borderBottomRightRadius:
|
||||||
|
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||||
|
marginLeft: shellIndex === 0 ? 0 : -1,
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
mutateDuel((draft) => {
|
||||||
|
draft.protagonist.shell = thisShell;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={thisShell.name}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
|
||||||
|
<InfoWithDelta name="DPM" decimals={0} unit="hp / min">
|
||||||
|
{stats.dpm}
|
||||||
|
</InfoWithDelta>
|
||||||
|
{gun.type === 'autoReloader' && (
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={0}
|
||||||
|
indent
|
||||||
|
name="Effective at 60s"
|
||||||
|
unit="hp / min"
|
||||||
|
>
|
||||||
|
{stats.dpmEffective!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
{gun.type !== 'regular' && (
|
||||||
|
<InfoWithDelta name="Shells">{stats.shells}</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
{gun.type === 'autoReloader' ? (
|
||||||
|
<>
|
||||||
|
<Info indent name="Most optimal shell index">
|
||||||
|
{stats.mostOptimalShellIndex! + 1}
|
||||||
|
</Info>
|
||||||
|
<Info name="Shell reloads" unit="s" />
|
||||||
|
{stats.shellReloads!.map((reload, index) => (
|
||||||
|
<InfoWithDelta
|
||||||
|
key={index}
|
||||||
|
indent
|
||||||
|
name={`Shell ${index + 1}`}
|
||||||
|
decimals={2}
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{reload}
|
||||||
|
</InfoWithDelta>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={2}
|
||||||
|
name="Reload"
|
||||||
|
unit="s"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.shellReload!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
{(gun.type === 'autoLoader' || gun.type === 'autoReloader') && (
|
||||||
|
<InfoWithDelta
|
||||||
|
indent
|
||||||
|
decimals={2}
|
||||||
|
name="Intra-clip"
|
||||||
|
unit="s"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.intraClip!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
<InfoWithDelta decimals={0} name="Penetration" unit="mm">
|
||||||
|
{stats.penetration}
|
||||||
|
</InfoWithDelta>
|
||||||
|
{typeof shell.penetration !== 'number' && (
|
||||||
|
<>
|
||||||
|
<InfoWithDelta
|
||||||
|
indent
|
||||||
|
decimals={0}
|
||||||
|
name={`At ${penetrationDistance}m`}
|
||||||
|
>
|
||||||
|
{lerp(
|
||||||
|
shell.penetration[0],
|
||||||
|
shell.penetration[1],
|
||||||
|
(penetrationDistance *
|
||||||
|
penetrationLossOverDistanceCoefficient) /
|
||||||
|
500,
|
||||||
|
) * penetrationCoefficient}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Flex align="center" gap="2" style={{ paddingLeft: 24 }}>
|
||||||
|
<Text color="gray">Distance</Text>
|
||||||
|
<Slider
|
||||||
|
key={penetrationDistance}
|
||||||
|
min={0}
|
||||||
|
max={500}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
defaultValue={[penetrationDistance]}
|
||||||
|
onValueChange={([value]) => {
|
||||||
|
setPenetrationDistanceDebounced(value);
|
||||||
|
|
||||||
|
if (!penetrationDistanceInput.current) return;
|
||||||
|
|
||||||
|
penetrationDistanceInput.current.value = value.toString();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextField.Root
|
||||||
|
style={{ width: 64 }}
|
||||||
|
ref={penetrationDistanceInput}
|
||||||
|
defaultValue={penetrationDistance}
|
||||||
|
onBlur={() => {
|
||||||
|
setPenetrationDistance(
|
||||||
|
Math.min(
|
||||||
|
parseInt(penetrationDistanceInput.current!.value),
|
||||||
|
500,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.currentTarget.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextField.Slot side="right">m</TextField.Slot>
|
||||||
|
</TextField.Root>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<InfoWithDelta name="Damage" unit="hp" decimals={0}>
|
||||||
|
{stats.damage}
|
||||||
|
</InfoWithDelta>
|
||||||
|
{gun.type !== 'regular' && (
|
||||||
|
<InfoWithDelta name="Clipping potential" indent>
|
||||||
|
{stats.clipDamage!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
<InfoWithDelta name="Module damage" unit="hp" decimals={0}>
|
||||||
|
{stats.moduleDamage}
|
||||||
|
</InfoWithDelta>
|
||||||
|
{isExplosive(shell.type) && (
|
||||||
|
<InfoWithDelta name="Splash radius" unit="m" decimals={0}>
|
||||||
|
{stats.explosionRadius!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
<InfoWithDelta name="Caliber" decimals={0} unit="mm">
|
||||||
|
{stats.caliber}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta name="Normalization" decimals={0} unit="°">
|
||||||
|
{stats.shellNormalization}
|
||||||
|
</InfoWithDelta>
|
||||||
|
{!isExplosive(shell.type) && (
|
||||||
|
<InfoWithDelta
|
||||||
|
name="Ricochet"
|
||||||
|
decimals={0}
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
unit="°"
|
||||||
|
>
|
||||||
|
{stats.shellRicochet!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
<InfoWithDelta name="Shell velocity" unit="m/s" decimals={0}>
|
||||||
|
{stats.shellVelocity}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={2}
|
||||||
|
name="Aim time"
|
||||||
|
unit="s"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.aimTime}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Dispersion at 100m" />
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={3}
|
||||||
|
indent
|
||||||
|
name="Still"
|
||||||
|
unit="m"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.dispersion}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
prefix="+"
|
||||||
|
decimals={3}
|
||||||
|
indent
|
||||||
|
name="Moving"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.dispersionMoving}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={3}
|
||||||
|
prefix="+"
|
||||||
|
indent
|
||||||
|
name="Hull traversing"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.dispersionHullTraversing}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={3}
|
||||||
|
prefix="+"
|
||||||
|
indent
|
||||||
|
name="Turret traversing"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.dispersionTurretTraversing}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={3}
|
||||||
|
prefix="+"
|
||||||
|
indent
|
||||||
|
name="After shooting"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.dispersionShooting}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={1}
|
||||||
|
prefix="x "
|
||||||
|
indent
|
||||||
|
name="Gun damaged"
|
||||||
|
unit="scalar"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.dispersionGunDamaged}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={3}
|
||||||
|
indent
|
||||||
|
name="Maximum"
|
||||||
|
unit="m"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.maxDispersion}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Gun flexibility" unit="°" />
|
||||||
|
<InfoWithDelta decimals={1} indent name="Depression">
|
||||||
|
{stats.gunDepression}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="Elevation">
|
||||||
|
{stats.gunElevation}
|
||||||
|
</InfoWithDelta>
|
||||||
|
{gunModelDefinition.pitch.front && (
|
||||||
|
<>
|
||||||
|
<InfoWithDelta decimals={1} indent name="Frontal depression">
|
||||||
|
{stats.gunFrontalDepression!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="Frontal elevation">
|
||||||
|
{stats.gunFrontalElevation!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{gunModelDefinition.pitch.back && (
|
||||||
|
<>
|
||||||
|
<InfoWithDelta decimals={1} indent name="Rear depression">
|
||||||
|
{stats.gunRearDepression!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="Rear elevation">
|
||||||
|
{stats.gunRearElevation!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{turretModelDefinition.yaw && (
|
||||||
|
<>
|
||||||
|
<InfoWithDelta decimals={1} indent name="Azimuth left">
|
||||||
|
{stats.azimuthLeft!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="Azimuth right">
|
||||||
|
{stats.azimuthRight!}
|
||||||
|
</InfoWithDelta>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
<Flex gap="2" align="center">
|
||||||
|
<Heading size="5">Crew training</Heading>
|
||||||
|
|
||||||
|
<Popover.Root>
|
||||||
|
<Popover.Trigger>
|
||||||
|
<IconButton variant="ghost">
|
||||||
|
<InfoCircledIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Popover.Trigger>
|
||||||
|
|
||||||
|
<Popover.Content>
|
||||||
|
<Flex gap="1" align="center">
|
||||||
|
<AccessibilityIcon />
|
||||||
|
<Text>
|
||||||
|
represents the other roles a crew member can take.
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{tank.crew.map((member) => {
|
||||||
|
const count = member.count ?? 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment key={member.type}>
|
||||||
|
<InfoWithDelta
|
||||||
|
key={`${member.type}-root`}
|
||||||
|
name={`${CREW_MEMBER_NAMES[member.type]}${
|
||||||
|
count > 1 ? ` x ${count}` : ''
|
||||||
|
}`}
|
||||||
|
unit="%"
|
||||||
|
decimals={0}
|
||||||
|
>
|
||||||
|
{(member.type === 'commander'
|
||||||
|
? commanderMastery
|
||||||
|
: commanderMastery * 1.1) * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
|
||||||
|
{member.substitute && (
|
||||||
|
<InfoWithDelta
|
||||||
|
key={`${member.type}-substitute`}
|
||||||
|
decimals={0}
|
||||||
|
unit="%"
|
||||||
|
indent
|
||||||
|
name={
|
||||||
|
<>
|
||||||
|
<Flex align="center" gap="1" display="inline-flex">
|
||||||
|
<AccessibilityIcon />
|
||||||
|
{member.substitute
|
||||||
|
.map((sub, index) =>
|
||||||
|
index === 0 ? CREW_MEMBER_NAMES[sub] : sub,
|
||||||
|
)
|
||||||
|
.join(', ')}
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{commanderMastery * 1.05 * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{!exempt && (
|
||||||
|
<Flex
|
||||||
|
justify="center"
|
||||||
|
display={{
|
||||||
|
initial: 'flex',
|
||||||
|
xs: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Ad type={AdType.MediumRectangleHorizontalPurple} />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Flex direction="column" gap="8" style={{ flex: 1 }}>
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
<Heading size="5">Maneuverability</Heading>
|
||||||
|
<Info name="Speed" unit="kph" />
|
||||||
|
<InfoWithDelta decimals={0} indent name="Forwards">
|
||||||
|
{stats.speedForwards}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={0} indent name="Backwards">
|
||||||
|
{stats.speedBackwards}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={0} name="Engine power" unit="hp">
|
||||||
|
{stats.enginePower}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
decimals={1}
|
||||||
|
name="Weight"
|
||||||
|
unit="tn"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
>
|
||||||
|
{stats.weight}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Terrain coefficients" />
|
||||||
|
<InfoWithDelta decimals={0} unit="%" indent name="Hard">
|
||||||
|
{stats.hardTerrainCoefficient}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={0} unit="%" indent name="Medium">
|
||||||
|
{stats.mediumTerrainCoefficient}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={0} unit="%" indent name="Soft">
|
||||||
|
{stats.softTerrainCoefficient}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Raw terrain coefficients" />
|
||||||
|
<InfoWithDelta decimals={2} unit="%" indent name="Hard">
|
||||||
|
{stats.hardTerrainCoefficientRaw}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={2} unit="%" indent name="Medium">
|
||||||
|
{stats.mediumTerrainCoefficientRaw}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={2} unit="%" indent name="Soft">
|
||||||
|
{stats.softTerrainCoefficientRaw}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Power to weight ratio" unit="hp/tn" />
|
||||||
|
<InfoWithDelta decimals={1} indent name="On hard terrain">
|
||||||
|
{stats.powerToWeightRatioHardTerrain}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="On medium terrain">
|
||||||
|
{stats.powerToWeightRatioMediumTerrain}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="On soft terrain">
|
||||||
|
{stats.powerToWeightRatioSoftTerrain}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta name="Turret traverse speed" unit="°/s" decimals={1}>
|
||||||
|
{stats.turretTraverseSpeed}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Hull traverse speed" unit="°/s" />
|
||||||
|
<InfoWithDelta decimals={1} indent name="On hard terrain">
|
||||||
|
{stats.hullTraverseHardTerrain}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="On medium terrain">
|
||||||
|
{stats.hullTraverseMediumTerrain}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta decimals={1} indent name="On soft terrain">
|
||||||
|
{stats.hullTraverseSoftTerrain}
|
||||||
|
</InfoWithDelta>
|
||||||
|
</Flex>
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
<Heading size="5">Survivability</Heading>
|
||||||
|
<InfoWithDelta name="Health" unit="hp" decimals={0}>
|
||||||
|
{stats.health}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta
|
||||||
|
name="Fire chance"
|
||||||
|
unit="%"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
decimals={0}
|
||||||
|
>
|
||||||
|
{stats.fireChance * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta name="View range" unit="m" decimals={0}>
|
||||||
|
{stats.viewRange}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Camouflage" unit="%" />
|
||||||
|
<InfoWithDelta indent name="Still" decimals={0}>
|
||||||
|
{stats.camouflageStill * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta indent name="Moving" decimals={0}>
|
||||||
|
{stats.camouflageMoving * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta indent name="Shooting still" decimals={0}>
|
||||||
|
{stats.camouflageShootingStill * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta indent name="Shooting moving" decimals={0}>
|
||||||
|
{stats.camouflageShootingMoving * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<InfoWithDelta indent name="Caught on fire" decimals={0}>
|
||||||
|
{stats.camouflageCaughtOnFire * 100}
|
||||||
|
</InfoWithDelta>
|
||||||
|
<Info name="Width" unit="m" decimals={0} deltaType="lowerIsBetter">
|
||||||
|
{stats.width}
|
||||||
|
</Info>
|
||||||
|
<Info name="Height" unit="m" decimals={0} deltaType="lowerIsBetter">
|
||||||
|
{stats.height}
|
||||||
|
</Info>
|
||||||
|
<Info name="Length" unit="m" decimals={0} deltaType="lowerIsBetter">
|
||||||
|
{stats.length}
|
||||||
|
</Info>
|
||||||
|
<Info name="Volume" unit="m" decimals={0} deltaType="lowerIsBetter">
|
||||||
|
{stats.volume}
|
||||||
|
</Info>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
import { Flex, FlexProps } from '@radix-ui/themes';
|
import { type FlexProps, Flex } from '@radix-ui/themes';
|
||||||
import { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
type ConfigurationChildWrapperProps = FlexProps & {
|
type ConfigurationChildWrapperProps = FlexProps & {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
+8
-9
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {} from '@blitzkit/core';
|
||||||
checkConsumableProvisionInclusivity,
|
import { checkConsumableProvisionInclusivity } from '@blitzkit/core/src/blitzkit/checkConsumableProvisionInclusivity';
|
||||||
consumableDefinitions,
|
import { consumableDefinitions } from '@blitzkit/core/src/blitzkit/consumableDefinitions';
|
||||||
} from '@blitzkit/core';
|
|
||||||
import { InfoCircledIcon } from '@radix-ui/react-icons';
|
import { InfoCircledIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -11,16 +10,16 @@ import {
|
|||||||
Popover,
|
Popover,
|
||||||
Text,
|
Text,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { use } from 'react';
|
import { useEquipment } from '../../../../hooks/useEquipment';
|
||||||
import { ConsumablesManager } from '../../../../../../../components/ConsumablesManager';
|
import { Duel } from '../../../../stores/duel';
|
||||||
import { useEquipment } from '../../../../../../../hooks/useEquipment';
|
import { ConsumablesManager } from '../../../ConsumablesManager';
|
||||||
import * as Duel from '../../../../../../../stores/duel';
|
|
||||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
|
|
||||||
|
const awaitedConsumableDefinitions = await consumableDefinitions;
|
||||||
|
|
||||||
export function Consumables() {
|
export function Consumables() {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const protagonist = Duel.use((state) => state.protagonist);
|
const protagonist = Duel.use((state) => state.protagonist);
|
||||||
const awaitedConsumableDefinitions = use(consumableDefinitions);
|
|
||||||
const consumables = Duel.use((state) => state.protagonist.consumables);
|
const consumables = Duel.use((state) => state.protagonist.consumables);
|
||||||
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
|
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
|
||||||
(consumable) =>
|
(consumable) =>
|
||||||
+4
-4
@@ -1,14 +1,14 @@
|
|||||||
import { equipmentDefinitions } from '@blitzkit/core';
|
import { equipmentDefinitions } from '@blitzkit/core';
|
||||||
import { Button, Flex, Heading } from '@radix-ui/themes';
|
import { Button, Flex, Heading } from '@radix-ui/themes';
|
||||||
import { use } from 'react';
|
import { Duel } from '../../../../stores/duel';
|
||||||
import { EquipmentManager } from '../../../../../../../components/EquipmentManager';
|
import { EquipmentManager } from '../../../EquipmentManager';
|
||||||
import * as Duel from '../../../../../../../stores/duel';
|
|
||||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
|
|
||||||
|
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||||
|
|
||||||
export function Equipment() {
|
export function Equipment() {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const protagonist = Duel.use((state) => state.protagonist);
|
const protagonist = Duel.use((state) => state.protagonist);
|
||||||
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
|
||||||
const equipmentPreset =
|
const equipmentPreset =
|
||||||
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment];
|
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment];
|
||||||
const equipmentMatrix = Duel.use(
|
const equipmentMatrix = Duel.use(
|
||||||
+2
-2
@@ -2,8 +2,8 @@ import { asset } from '@blitzkit/core';
|
|||||||
import { Flex, Heading, Slider, TextField } from '@radix-ui/themes';
|
import { Flex, Heading, Slider, TextField } from '@radix-ui/themes';
|
||||||
import { clamp, debounce } from 'lodash-es';
|
import { clamp, debounce } from 'lodash-es';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { GenericTankComponentButton } from '../../../../../../../components/ModuleButtons/GenericTankComponentButton';
|
import { Duel } from '../../../../stores/duel';
|
||||||
import * as Duel from '../../../../../../../stores/duel';
|
import { GenericTankComponentButton } from '../../../ModuleButtons/GenericTankComponentButton';
|
||||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
|
|
||||||
export function Miscellaneous() {
|
export function Miscellaneous() {
|
||||||
+20
-20
@@ -1,14 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
asset,
|
asset,
|
||||||
formatCompact,
|
formatCompact,
|
||||||
ModuleDefinition,
|
|
||||||
ModuleType,
|
|
||||||
TankDefinition,
|
|
||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
tankIcon,
|
tankIcon,
|
||||||
Tier,
|
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
Unlock,
|
type ModuleDefinition,
|
||||||
|
type ModuleType,
|
||||||
|
type TankDefinition,
|
||||||
|
type Tier,
|
||||||
|
type Unlock,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -19,9 +19,8 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
Text,
|
Text,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { use } from 'react';
|
|
||||||
import * as Duel from '../../../../../../../stores/duel';
|
|
||||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
|
import { Duel } from '../../../../stores/duel';
|
||||||
|
|
||||||
function ModuleButton({
|
function ModuleButton({
|
||||||
tier,
|
tier,
|
||||||
@@ -112,9 +111,10 @@ function ModuleButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
|
|
||||||
export function Modules() {
|
export function Modules() {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const awaitedTankDefinitions = use(tankDefinitions);
|
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const hasUpgrades =
|
const hasUpgrades =
|
||||||
tank.turrets.length > 1 ||
|
tank.turrets.length > 1 ||
|
||||||
@@ -264,23 +264,23 @@ export function Modules() {
|
|||||||
(unlock.type === 'turret'
|
(unlock.type === 'turret'
|
||||||
? turret.id
|
? turret.id
|
||||||
: unlock.type === 'gun'
|
: unlock.type === 'gun'
|
||||||
? gun.id
|
? gun.id
|
||||||
: unlock.type === 'engine'
|
: unlock.type === 'engine'
|
||||||
? engine.id
|
? engine.id
|
||||||
: unlock.type === 'chassis'
|
: unlock.type === 'chassis'
|
||||||
? track.id
|
? track.id
|
||||||
: -1) === unlock.id
|
: -1) === unlock.id
|
||||||
}
|
}
|
||||||
top={
|
top={
|
||||||
unlock.type === 'turret'
|
unlock.type === 'turret'
|
||||||
? module.id == topTurret.id
|
? module.id == topTurret.id
|
||||||
: unlock.type === 'gun'
|
: unlock.type === 'gun'
|
||||||
? module.id === topGun.id
|
? module.id === topGun.id
|
||||||
: unlock.type === 'engine'
|
: unlock.type === 'engine'
|
||||||
? module.id === topEngine.id
|
? module.id === topEngine.id
|
||||||
: unlock.type === 'chassis'
|
: unlock.type === 'chassis'
|
||||||
? module.id === topTrack.id
|
? module.id === topTrack.id
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
onClick={() => setByUnlock(unlock)}
|
onClick={() => setByUnlock(unlock)}
|
||||||
/>
|
/>
|
||||||
+4
-4
@@ -1,15 +1,15 @@
|
|||||||
import { availableProvisions, provisionDefinitions } from '@blitzkit/core';
|
import { availableProvisions, provisionDefinitions } from '@blitzkit/core';
|
||||||
import { Button, Flex, Heading } from '@radix-ui/themes';
|
import { Button, Flex, Heading } from '@radix-ui/themes';
|
||||||
import { use } from 'react';
|
import { Duel } from '../../../../stores/duel';
|
||||||
import { ProvisionsManager } from '../../../../../../../components/ProvisionsManager';
|
import { ProvisionsManager } from '../../../ProvisionsManager';
|
||||||
import * as Duel from '../../../../../../../stores/duel';
|
|
||||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
|
|
||||||
|
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||||
|
|
||||||
export function Provisions() {
|
export function Provisions() {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const { tank, gun } = Duel.use((state) => state.protagonist);
|
const { tank, gun } = Duel.use((state) => state.protagonist);
|
||||||
const provisions = Duel.use((state) => state.protagonist.provisions);
|
const provisions = Duel.use((state) => state.protagonist.provisions);
|
||||||
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
|
||||||
const provisionsList = availableProvisions(
|
const provisionsList = availableProvisions(
|
||||||
tank,
|
tank,
|
||||||
gun,
|
gun,
|
||||||
+5
-5
@@ -9,13 +9,13 @@ import {
|
|||||||
Popover,
|
Popover,
|
||||||
Text,
|
Text,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { use } from 'react';
|
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
|
||||||
import { CrewSkillManager } from '../../../../../../../../components/CrewSkillManager';
|
import { CrewSkillManager } from '../../../CrewSkillManager';
|
||||||
import * as TankopediaEphemeral from '../../../../../../../../stores/tankopediaEphemeral';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
import { ConfigurationChildWrapper } from '../ConfigurationChildWrapper';
|
|
||||||
|
const awaitedSkillDefinitions = await skillDefinitions;
|
||||||
|
|
||||||
export function Skills() {
|
export function Skills() {
|
||||||
const awaitedSkillDefinitions = use(skillDefinitions);
|
|
||||||
const skillLevels = TankopediaEphemeral.use((state) => state.skills);
|
const skillLevels = TankopediaEphemeral.use((state) => state.skills);
|
||||||
const mutateTankopediaTemporary = TankopediaEphemeral.useMutation();
|
const mutateTankopediaTemporary = TankopediaEphemeral.useMutation();
|
||||||
|
|
||||||
+7
-7
@@ -1,11 +1,11 @@
|
|||||||
import { Flex, Heading } from '@radix-ui/themes';
|
import { Flex, Heading } from '@radix-ui/themes';
|
||||||
import { Characteristics } from '../../../../website-legacy/src/app/tools/tankopedia/[id]/components/Characteristics';
|
import { Characteristics } from './components/Characteristics';
|
||||||
import { Consumables } from '../../../../website-legacy/src/app/tools/tankopedia/[id]/components/Characteristics/components/Consumables';
|
import { Consumables } from './components/Consumables';
|
||||||
import { Equipment } from '../../../../website-legacy/src/app/tools/tankopedia/[id]/components/Characteristics/components/Equipment';
|
import { Equipment } from './components/Equipment';
|
||||||
import { Miscellaneous } from '../../../../website-legacy/src/app/tools/tankopedia/[id]/components/Characteristics/components/Miscellaneous';
|
import { Miscellaneous } from './components/Miscellaneous';
|
||||||
import { Modules } from '../../../../website-legacy/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules';
|
import { Modules } from './components/Modules';
|
||||||
import { Provisions } from '../../../../website-legacy/src/app/tools/tankopedia/[id]/components/Characteristics/components/Provisions';
|
import { Provisions } from './components/Provisions';
|
||||||
import { Skills } from '../../../../website-legacy/src/app/tools/tankopedia/[id]/components/Characteristics/components/Skills';
|
import { Skills } from './components/Skills';
|
||||||
|
|
||||||
export function CharacteristicsSection() {
|
export function CharacteristicsSection() {
|
||||||
return (
|
return (
|
||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
provisionDefinitions,
|
provisionDefinitions,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
||||||
import * as Duel from '../../../../../../stores/duel';
|
import { Duel } from '../../stores/duel';
|
||||||
|
|
||||||
const awaitedGameDefinitions = await gameDefinitions;
|
const awaitedGameDefinitions = await gameDefinitions;
|
||||||
const awaitedConsumableDefinitions = await consumableDefinitions;
|
const awaitedConsumableDefinitions = await consumableDefinitions;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Heading, Text } from '@radix-ui/themes';
|
import { Heading, Text } from '@radix-ui/themes';
|
||||||
import { PageWrapper } from '../../../../../components/PageWrapper';
|
import { Duel } from '../../stores/duel';
|
||||||
import * as Duel from '../../../../../stores/duel';
|
import { PageWrapper } from '../PageWrapper';
|
||||||
|
|
||||||
export function HistorySection() {
|
export function HistorySection() {
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Flex, Text, TextProps } from '@radix-ui/themes';
|
import { Flex, Text, type TextProps } from '@radix-ui/themes';
|
||||||
|
|
||||||
type ListingProps = TextProps & {
|
type ListingProps = TextProps & {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { StarsInt } from '@blitzkit/core';
|
import type { StarsInt } from '@blitzkit/core';
|
||||||
import { Flex, Text, TextProps } from '@radix-ui/themes';
|
import { type TextProps, Flex, Text } from '@radix-ui/themes';
|
||||||
import { times } from 'lodash-es';
|
import { times } from 'lodash-es';
|
||||||
import { PartialStar } from './components/PartialStar';
|
import { PartialStar } from './components/PartialStar';
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function MetaSection() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{assertSecret(process.env.PUBLIC_PROMOTE_OPENTEST) === 'true' && (
|
{assertSecret(import.meta.env.PUBLIC_PROMOTE_OPENTEST) === 'true' && (
|
||||||
<Link
|
<Link
|
||||||
href={`https://opentest.blitzkit.app/tools/tankopedia/${protagonist.id}`}
|
href={`https://opentest.blitzkit.app/tools/tankopedia/${protagonist.id}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Flex } from '@radix-ui/themes';
|
import { Flex } from '@radix-ui/themes';
|
||||||
import { ShotDisplayCard } from '../../../../../components/Armor/components/ShotDisplayCard';
|
import { TankopediaEphemeral } from '../../stores/tankopediaEphemeral';
|
||||||
import * as TankEphemeral from '../../../../../stores/tankopediaEphemeral';
|
import { ShotDisplayCard } from '../Armor/components/ShotDisplayCard';
|
||||||
|
|
||||||
export function ShotDisplaySection() {
|
export function ShotDisplaySection() {
|
||||||
const shot = TankEphemeral.use((state) => state.shot);
|
const shot = TankopediaEphemeral.use((state) => state.shot);
|
||||||
|
|
||||||
if (!shot) return null;
|
if (!shot) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import {
|
|||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import { Box, Flex, Text } from '@radix-ui/themes';
|
import { Box, Flex, Link, Text } from '@radix-ui/themes';
|
||||||
import Link from 'next/link';
|
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
|
||||||
import * as TankopediaEphemeral from '../../../../../../../../stores/tankopediaEphemeral';
|
|
||||||
|
|
||||||
interface NodeProps {
|
interface NodeProps {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { asset, imgur, tankDefinitions } from '@blitzkit/core';
|
|||||||
import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
|
import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
|
import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
|
||||||
import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
|
import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import * as Duel from '../../../../../../../stores/duel';
|
import { Duel } from '../../../stores/duel';
|
||||||
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||||
import { Arrow } from './components/Arrow';
|
import { Arrow } from './components/Arrow';
|
||||||
import { Node } from './components/Node';
|
import { Node } from './components/Node';
|
||||||
|
|
||||||
@@ -168,6 +168,7 @@ export function TechTreeSection() {
|
|||||||
return (
|
return (
|
||||||
<Fragment key={id}>
|
<Fragment key={id}>
|
||||||
{index > 0 && <Arrow key={`${id}-arrow`} />}
|
{index > 0 && <Arrow key={`${id}-arrow`} />}
|
||||||
|
|
||||||
<Node
|
<Node
|
||||||
key={`${id}-node`}
|
key={`${id}-node`}
|
||||||
id={id}
|
id={id}
|
||||||
|
|||||||
+7
-8
@@ -1,15 +1,14 @@
|
|||||||
import { reviews, youtubers } from '@blitzkit/core';
|
import { reviews, youtubers } from '@blitzkit/core';
|
||||||
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
import { Box, Flex, Heading, Link, Text } from '@radix-ui/themes';
|
||||||
import { use } from 'react';
|
import { Var } from '../../core/radix/var';
|
||||||
import { Link } from '../../../../../components/Link';
|
import { Duel } from '../../stores/duel';
|
||||||
import { PageWrapper } from '../../../../../components/PageWrapper';
|
import { PageWrapper } from '../PageWrapper';
|
||||||
import { VerifiedIcon } from '../../../../../components/VerifiedIcon';
|
import { VerifiedIcon } from '../VerifiedIcon';
|
||||||
import { Var } from '../../../../../core/radix/var';
|
|
||||||
import * as Duel from '../../../../../stores/duel';
|
const awaitedReviews = await reviews;
|
||||||
|
|
||||||
export function VideoSection() {
|
export function VideoSection() {
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const awaitedReviews = use(reviews);
|
|
||||||
const videos = awaitedReviews.reviews[tank.id]?.videos;
|
const videos = awaitedReviews.reviews[tank.id]?.videos;
|
||||||
|
|
||||||
if (!videos) return null;
|
if (!videos) return null;
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
coefficient,
|
|
||||||
degressiveStat,
|
degressiveStat,
|
||||||
EngineDefinition,
|
|
||||||
EquipmentDefinitions,
|
|
||||||
GunDefinition,
|
|
||||||
isExplosive,
|
isExplosive,
|
||||||
ModelDefinition,
|
|
||||||
normalizeBoundingBox,
|
normalizeBoundingBox,
|
||||||
progressiveStat,
|
progressiveStat,
|
||||||
ProvisionDefinitions,
|
|
||||||
resolveDpm,
|
resolveDpm,
|
||||||
resolveNearPenetration,
|
resolveNearPenetration,
|
||||||
resolvePenetrationCoefficient,
|
resolvePenetrationCoefficient,
|
||||||
ShellDefinition,
|
|
||||||
sum,
|
sum,
|
||||||
TankDefinition,
|
|
||||||
TrackDefinition,
|
|
||||||
TurretDefinition,
|
|
||||||
unionBoundingBox,
|
unionBoundingBox,
|
||||||
|
type EngineDefinition,
|
||||||
|
type EquipmentDefinitions,
|
||||||
|
type GunDefinition,
|
||||||
|
type ModelDefinition,
|
||||||
|
type ProvisionDefinitions,
|
||||||
|
type ShellDefinition,
|
||||||
|
type TankDefinition,
|
||||||
|
type TrackDefinition,
|
||||||
|
type TurretDefinition,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import { EquipmentMatrix } from '../../stores/duel';
|
import { coefficient } from '@blitzkit/core/src/blitzkit/coefficient';
|
||||||
|
import type { EquipmentMatrix } from '../../stores/duel';
|
||||||
|
|
||||||
export type TankCharacteristics = ReturnType<typeof tankCharacteristics>;
|
export type TankCharacteristics = ReturnType<typeof tankCharacteristics>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { $patreonLogin } from '../stores/patreonLogin';
|
import { $patreonLogin } from '../stores/patreonLogin';
|
||||||
|
|
||||||
let cache: Record<string, boolean | Promise<boolean>> = {};
|
let cache: Record<string, boolean | Promise<boolean>> = {};
|
||||||
@@ -8,7 +8,7 @@ export function useAdExempt() {
|
|||||||
const patreon = useStore($patreonLogin);
|
const patreon = useStore($patreonLogin);
|
||||||
const [exempt, setExempt] = useState(false);
|
const [exempt, setExempt] = useState(false);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (!patreon.token) return setExempt(false);
|
if (!patreon.token) return setExempt(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useLayoutEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export function useFullscreenAvailability() {
|
export function useFullscreenAvailability() {
|
||||||
const [available, setAvailable] = useState(false);
|
const [available, setAvailable] = useState(false);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useEffect(() => {
|
||||||
setAvailable(document.fullscreenEnabled);
|
setAvailable(document.fullscreenEnabled);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type RefObject, useLayoutEffect, useRef } from 'react';
|
import { type RefObject, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
interface UseIntersectionOptions extends IntersectionObserverInit {
|
interface UseIntersectionOptions extends IntersectionObserverInit {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
@@ -11,7 +11,7 @@ export function useIntersection(
|
|||||||
) {
|
) {
|
||||||
const observer = useRef<IntersectionObserver>();
|
const observer = useRef<IntersectionObserver>();
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useEffect(() => {
|
||||||
if (options?.disabled || !ref.current) return;
|
if (options?.disabled || !ref.current) return;
|
||||||
|
|
||||||
const rect = ref.current.getBoundingClientRect();
|
const rect = ref.current.getBoundingClientRect();
|
||||||
|
|||||||
@@ -4,7 +4,15 @@ import {
|
|||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import { PageWrapper } from '../../../../components/PageWrapper';
|
import { PageWrapper } from '../../../../components/PageWrapper';
|
||||||
|
import { CalloutsSection } from '../../../../components/Tankopedia/CalloutsSection';
|
||||||
|
import { CharacteristicsSection } from '../../../../components/Tankopedia/CharacteristicsSection';
|
||||||
|
import { GameModeSection } from '../../../../components/Tankopedia/GameModeSection';
|
||||||
import { HeroSection } from '../../../../components/Tankopedia/HeroSection';
|
import { HeroSection } from '../../../../components/Tankopedia/HeroSection';
|
||||||
|
import { HistorySection } from '../../../../components/Tankopedia/HistorySection';
|
||||||
|
import { MetaSection } from '../../../../components/Tankopedia/MetaSection';
|
||||||
|
import { ShotDisplaySection } from '../../../../components/Tankopedia/ShotDisplaySection';
|
||||||
|
import { TechTreeSection } from '../../../../components/Tankopedia/TechTreeSection';
|
||||||
|
import { VideoSection } from '../../../../components/Tankopedia/VideoSection';
|
||||||
import { App } from '../../../../stores/app';
|
import { App } from '../../../../stores/app';
|
||||||
import { Duel } from '../../../../stores/duel';
|
import { Duel } from '../../../../stores/duel';
|
||||||
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
|
||||||
@@ -31,17 +39,17 @@ export function Page({ id }: PageProps) {
|
|||||||
>
|
>
|
||||||
<PageWrapper p="0" noMaxWidth color="purple" size={1600} gap="9">
|
<PageWrapper p="0" noMaxWidth color="purple" size={1600} gap="9">
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
{/* <ShotDisplaySection />
|
<ShotDisplaySection />
|
||||||
<MetaSection />
|
<CalloutsSection />
|
||||||
<TechTreeSection />
|
<MetaSection />
|
||||||
{!exempt && <AdMidSectionResponsive />}
|
<TechTreeSection />
|
||||||
<CalloutsSection />
|
{/* {!exempt && <AdMidSectionResponsive />} */}
|
||||||
<CharacteristicsSection />
|
<CharacteristicsSection />
|
||||||
{!exempt && <AdMidSectionResponsive />}
|
{/* {!exempt && <AdMidSectionResponsive />} */}
|
||||||
<GameModeSection />
|
<GameModeSection />
|
||||||
<VideoSection />
|
<VideoSection />
|
||||||
<HistorySection />
|
<HistorySection />
|
||||||
{!exempt && <AdMidSectionResponsive mb="6" />} */}
|
{/* {!exempt && <AdMidSectionResponsive mb="6" />} */}
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</Duel.Provider>
|
</Duel.Provider>
|
||||||
</TankopediaPersistent.Provider>
|
</TankopediaPersistent.Provider>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { TextProps } from '@radix-ui/themes';
|
import type { TextProps } from '@radix-ui/themes';
|
||||||
import { EmbedConfig } from '.';
|
import type { EmbedConfig } from '.';
|
||||||
import * as EmbedState from './';
|
import type { EmbedConfigItemType, EmbedItemType } from './constants';
|
||||||
import { EmbedConfigItemType, EmbedItemType } from './constants';
|
|
||||||
|
|
||||||
export function useEmbedStateCurry<Config extends EmbedConfig>() {
|
export function useEmbedStateCurry<Config extends EmbedConfig>() {
|
||||||
function useState<Key extends keyof Config>(key: Key) {
|
function useState<Key extends keyof Config>(key: Key) {
|
||||||
|
|||||||
Reference in new issue
Block a user