mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
feat: bring back stats
This commit is contained in:
1 parent
42076c1317
commit
299ccef579
6 files changed
+355
-370
No files matched your search
@@ -1,16 +1,288 @@
|
|||||||
import { Button, Flex, Heading } from '@radix-ui/themes';
|
import {
|
||||||
import { ModuleManager } from '../../../../../../../components/ModuleManager';
|
Button,
|
||||||
|
ChevronDownIcon,
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
IconButton,
|
||||||
|
Link,
|
||||||
|
Text,
|
||||||
|
} from '@radix-ui/themes';
|
||||||
|
import { use } from 'react';
|
||||||
|
import { asset } from '../../../../../../../core/blitzkit/asset';
|
||||||
|
import {
|
||||||
|
ModuleDefinition,
|
||||||
|
ModuleType,
|
||||||
|
TankDefinition,
|
||||||
|
tankDefinitions,
|
||||||
|
Tier,
|
||||||
|
Unlock,
|
||||||
|
} from '../../../../../../../core/blitzkit/tankDefinitions';
|
||||||
|
import { TIER_ROMAN_NUMERALS } from '../../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||||
|
import { tankIcon } from '../../../../../../../core/blitzkit/tankIcon';
|
||||||
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
|
||||||
import { mutateTankopediaTemporary } from '../../../../../../../stores/tankopedia';
|
|
||||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||||
|
|
||||||
|
function ModuleButton({
|
||||||
|
tier,
|
||||||
|
selected,
|
||||||
|
unlock,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
unlock: Unlock;
|
||||||
|
tier: Tier;
|
||||||
|
selected: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}) {
|
||||||
|
const numberFormat = Intl.NumberFormat(undefined, {
|
||||||
|
notation: 'compact',
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
});
|
||||||
|
const button = (
|
||||||
|
<IconButton
|
||||||
|
size="4"
|
||||||
|
radius="small"
|
||||||
|
variant={selected ? 'surface' : 'soft'}
|
||||||
|
color={selected ? undefined : 'gray'}
|
||||||
|
onClick={onClick}
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
size="1"
|
||||||
|
color="gray"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: 4,
|
||||||
|
top: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{TIER_ROMAN_NUMERALS[tier]}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{unlock.cost.value > 0 && (
|
||||||
|
<Flex
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: '50%',
|
||||||
|
bottom: 4,
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
}}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<Text size="1" color="gray">
|
||||||
|
{numberFormat.format(unlock.cost.value)}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<img
|
||||||
|
src={asset(`icons/currencies/${unlock.cost.type}.webp`)}
|
||||||
|
style={{
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<img
|
||||||
|
src={
|
||||||
|
unlock.type === 'vehicle'
|
||||||
|
? tankIcon(unlock.id)
|
||||||
|
: asset(`icons/modules/${unlock.type}.webp`)
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
return unlock.type === 'vehicle' ? (
|
||||||
|
<Link href={`/tools/tankopedia/${unlock.id}`}>{button}</Link>
|
||||||
|
) : (
|
||||||
|
button
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function Modules() {
|
export function Modules() {
|
||||||
const protagonist = useDuel((state) => state.protagonist!);
|
const awaitedTankDefinitions = use(tankDefinitions);
|
||||||
|
const tank = useDuel((state) => state.protagonist!.tank);
|
||||||
const hasUpgrades =
|
const hasUpgrades =
|
||||||
protagonist.tank.turrets.length > 1 ||
|
tank.turrets.length > 1 ||
|
||||||
protagonist.tank.turrets[0].guns.length > 1 ||
|
tank.turrets[0].guns.length > 1 ||
|
||||||
protagonist.tank.engines.length > 1 ||
|
tank.engines.length > 1 ||
|
||||||
protagonist.tank.tracks.length > 1;
|
tank.tracks.length > 1;
|
||||||
|
const turret = useDuel((state) => state.protagonist!.turret);
|
||||||
|
const gun = useDuel((state) => state.protagonist!.gun);
|
||||||
|
const engine = useDuel((state) => state.protagonist!.engine);
|
||||||
|
const track = useDuel((state) => state.protagonist!.track);
|
||||||
|
const turret0 = tank.turrets[0];
|
||||||
|
const gun0 = turret0.guns[0];
|
||||||
|
const engine0 = tank.engines[0];
|
||||||
|
const track0 = tank.tracks[0];
|
||||||
|
|
||||||
|
function setByUnlock(unlock: Unlock) {
|
||||||
|
mutateDuel((draft) => {
|
||||||
|
if (unlock.type === 'turret') {
|
||||||
|
draft.protagonist!.turret = draft.protagonist!.tank.turrets.find(
|
||||||
|
(turret) => turret.id === unlock.id,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!draft.protagonist!.turret.guns.some(
|
||||||
|
(gun) => gun.id === draft.protagonist!.gun.id,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
draft.protagonist!.gun = draft.protagonist!.turret.guns.at(-1)!;
|
||||||
|
draft.protagonist!.shell = draft.protagonist!.gun.shells.at(-1)!;
|
||||||
|
}
|
||||||
|
} else if (unlock.type === 'gun') {
|
||||||
|
const gunInTurret = draft.protagonist!.turret.guns.find(
|
||||||
|
(gun) => gun.id === unlock.id,
|
||||||
|
);
|
||||||
|
if (gunInTurret) {
|
||||||
|
draft.protagonist!.gun = gunInTurret;
|
||||||
|
draft.protagonist!.shell = gunInTurret.shells.at(-1)!;
|
||||||
|
} else {
|
||||||
|
// TODO: warn somehow?
|
||||||
|
const suitableTurret = draft.protagonist!.tank.turrets.find(
|
||||||
|
(turret) => turret.guns.some((gun) => gun.id === unlock.id),
|
||||||
|
)!;
|
||||||
|
const gunInSuitableTurret = suitableTurret.guns.find(
|
||||||
|
(gun) => gun.id === unlock.id,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
draft.protagonist!.turret = suitableTurret;
|
||||||
|
draft.protagonist!.gun = gunInSuitableTurret;
|
||||||
|
draft.protagonist!.shell = gunInSuitableTurret.shells.at(-1)!;
|
||||||
|
}
|
||||||
|
} else if (unlock.type === 'engine') {
|
||||||
|
draft.protagonist!.engine = draft.protagonist!.tank.engines.find(
|
||||||
|
(engine) => engine.id === unlock.id,
|
||||||
|
)!;
|
||||||
|
} else if (unlock.type === 'chassis') {
|
||||||
|
draft.protagonist!.track = draft.protagonist!.tank.tracks.find(
|
||||||
|
(track) => track.id === unlock.id,
|
||||||
|
)!;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function tree(type: ModuleType, unlocks: Unlock[]) {
|
||||||
|
return (
|
||||||
|
<Flex gap="2">
|
||||||
|
{unlocks.map((unlock, index) => {
|
||||||
|
const first = index === 0;
|
||||||
|
const last = index === unlocks.length - 1;
|
||||||
|
const central = !first && !last;
|
||||||
|
let module: ModuleDefinition | TankDefinition | undefined = undefined;
|
||||||
|
|
||||||
|
if (unlock.type === 'chassis') {
|
||||||
|
module = tank.tracks.find((track) => track.id === unlock.id)!;
|
||||||
|
} else if (unlock.type === 'engine') {
|
||||||
|
module = tank.engines.find((engine) => engine.id === unlock.id)!;
|
||||||
|
} else if (unlock.type === 'turret') {
|
||||||
|
module = tank.turrets.find((turret) => turret.id === unlock.id)!;
|
||||||
|
} else if (unlock.type === 'gun') {
|
||||||
|
module = tank.turrets
|
||||||
|
.find((turret) =>
|
||||||
|
turret.guns.some((gun) => gun.id === unlock.id),
|
||||||
|
)!
|
||||||
|
.guns.find((gun) => gun.id === unlock.id)!;
|
||||||
|
} else if (unlock.type === 'vehicle') {
|
||||||
|
module = awaitedTankDefinitions[unlock.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module === undefined) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
align="center"
|
||||||
|
direction="column"
|
||||||
|
gap="2"
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{unlock.cost.value > 0 && (
|
||||||
|
<>
|
||||||
|
{unlocks.length > 1 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'currentcolor',
|
||||||
|
width: `calc(${central ? 100 : 50}% + ${central ? 8 : 4}px)`,
|
||||||
|
height: 1,
|
||||||
|
position: 'absolute',
|
||||||
|
right: first ? 0 : undefined,
|
||||||
|
left: last ? 0 : undefined,
|
||||||
|
transform: `translateX(${first ? 4 : last ? -4 : 0}px)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
direction="column"
|
||||||
|
align="center"
|
||||||
|
style={{
|
||||||
|
marginBottom: -7,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'currentcolor',
|
||||||
|
width: 1,
|
||||||
|
height: 8,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ChevronDownIcon
|
||||||
|
style={{
|
||||||
|
transform: 'translateY(-7px)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ModuleButton
|
||||||
|
unlock={unlock}
|
||||||
|
tier={module.tier}
|
||||||
|
selected={
|
||||||
|
(unlock.type === 'turret'
|
||||||
|
? turret.id
|
||||||
|
: unlock.type === 'gun'
|
||||||
|
? gun.id
|
||||||
|
: unlock.type === 'engine'
|
||||||
|
? engine.id
|
||||||
|
: unlock.type === 'chassis'
|
||||||
|
? track.id
|
||||||
|
: -1) === unlock.id
|
||||||
|
}
|
||||||
|
onClick={() => setByUnlock(unlock)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{(module as ModuleDefinition).unlocks &&
|
||||||
|
(module as ModuleDefinition).unlocks!.length > 1 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'currentcolor',
|
||||||
|
width: 1,
|
||||||
|
height: 8,
|
||||||
|
marginBottom: -8,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(module as ModuleDefinition).unlocks &&
|
||||||
|
tree(type, (module as ModuleDefinition).unlocks!)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigurationChildWrapper>
|
<ConfigurationChildWrapper>
|
||||||
@@ -58,22 +330,36 @@ export function Modules() {
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<ModuleManager
|
<Flex gap="2">
|
||||||
tank={protagonist.tank}
|
{tree('engine', [
|
||||||
turret={protagonist.turret}
|
{
|
||||||
gun={protagonist.gun}
|
cost: { type: 'xp', value: 0 },
|
||||||
shell={protagonist.shell}
|
id: engine0.id,
|
||||||
engine={protagonist.engine}
|
type: 'engine',
|
||||||
track={protagonist.track}
|
},
|
||||||
onChange={(modules) => {
|
])}
|
||||||
mutateDuel((draft) => {
|
{tree('chassis', [
|
||||||
draft.protagonist = { ...draft.protagonist!, ...modules };
|
{
|
||||||
});
|
cost: { type: 'xp', value: 0 },
|
||||||
mutateTankopediaTemporary((draft) => {
|
id: track0.id,
|
||||||
draft.shot = undefined;
|
type: 'chassis',
|
||||||
});
|
},
|
||||||
}}
|
])}
|
||||||
/>
|
{tree('turret', [
|
||||||
|
{
|
||||||
|
cost: { type: 'xp', value: 0 },
|
||||||
|
id: turret0.id,
|
||||||
|
type: 'turret',
|
||||||
|
},
|
||||||
|
])}
|
||||||
|
{tree('gun', [
|
||||||
|
{
|
||||||
|
cost: { type: 'xp', value: 0 },
|
||||||
|
id: gun0.id,
|
||||||
|
type: 'gun',
|
||||||
|
},
|
||||||
|
])}
|
||||||
|
</Flex>
|
||||||
</ConfigurationChildWrapper>
|
</ConfigurationChildWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -13,9 +13,9 @@ export function CharacteristicsSection() {
|
|||||||
const wideFormat = useWideFormat(720);
|
const wideFormat = useWideFormat(720);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<PageWrapper >
|
||||||
<Flex
|
<Flex
|
||||||
gap={wideFormat ? '4' : '6'}
|
gap="6"
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
direction={wideFormat ? 'row' : 'column'}
|
direction={wideFormat ? 'row' : 'column'}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,315 +0,0 @@
|
|||||||
import {
|
|
||||||
ChevronDownIcon,
|
|
||||||
Flex,
|
|
||||||
Heading,
|
|
||||||
IconButton,
|
|
||||||
Text,
|
|
||||||
} from '@radix-ui/themes';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { use } from 'react';
|
|
||||||
import PageWrapper from '../../../../../../components/PageWrapper';
|
|
||||||
import { asset } from '../../../../../../core/blitzkit/asset';
|
|
||||||
import {
|
|
||||||
ModuleDefinition,
|
|
||||||
ModuleType,
|
|
||||||
TankDefinition,
|
|
||||||
tankDefinitions,
|
|
||||||
Tier,
|
|
||||||
Unlock,
|
|
||||||
} from '../../../../../../core/blitzkit/tankDefinitions';
|
|
||||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
|
||||||
import { tankIcon } from '../../../../../../core/blitzkit/tankIcon';
|
|
||||||
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
|
||||||
|
|
||||||
function ModuleButton({
|
|
||||||
tier,
|
|
||||||
selected,
|
|
||||||
unlock,
|
|
||||||
onClick,
|
|
||||||
}: {
|
|
||||||
unlock: Unlock;
|
|
||||||
tier: Tier;
|
|
||||||
selected: boolean;
|
|
||||||
onClick: () => void;
|
|
||||||
}) {
|
|
||||||
const numberFormat = Intl.NumberFormat(undefined, {
|
|
||||||
notation: 'compact',
|
|
||||||
maximumFractionDigits: 0,
|
|
||||||
});
|
|
||||||
const button = (
|
|
||||||
<IconButton
|
|
||||||
size="4"
|
|
||||||
radius="small"
|
|
||||||
variant={selected ? 'surface' : 'soft'}
|
|
||||||
color={selected ? undefined : 'gray'}
|
|
||||||
onClick={onClick}
|
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
size="1"
|
|
||||||
color="gray"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 4,
|
|
||||||
top: 4,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{TIER_ROMAN_NUMERALS[tier]}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{unlock.cost.value > 0 && (
|
|
||||||
<Flex
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: '50%',
|
|
||||||
bottom: 4,
|
|
||||||
transform: 'translateX(-50%)',
|
|
||||||
}}
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<Text size="1" color="gray">
|
|
||||||
{numberFormat.format(unlock.cost.value)}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<img
|
|
||||||
src={asset(`icons/currencies/${unlock.cost.type}.webp`)}
|
|
||||||
style={{
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
objectFit: 'contain',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<img
|
|
||||||
src={
|
|
||||||
unlock.type === 'vehicle'
|
|
||||||
? tankIcon(unlock.id)
|
|
||||||
: asset(`icons/modules/${unlock.type}.webp`)
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
objectFit: 'contain',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
|
|
||||||
return unlock.type === 'vehicle' ? (
|
|
||||||
<Link href={`/tools/tankopedia/${unlock.id}`}>{button}</Link>
|
|
||||||
) : (
|
|
||||||
button
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ModulesSection() {
|
|
||||||
const awaitedTankDefinitions = use(tankDefinitions);
|
|
||||||
const tank = useDuel((state) => state.protagonist!.tank);
|
|
||||||
const turret = useDuel((state) => state.protagonist!.turret);
|
|
||||||
const gun = useDuel((state) => state.protagonist!.gun);
|
|
||||||
const engine = useDuel((state) => state.protagonist!.engine);
|
|
||||||
const track = useDuel((state) => state.protagonist!.track);
|
|
||||||
const turret0 = tank.turrets[0];
|
|
||||||
const gun0 = turret0.guns[0];
|
|
||||||
const engine0 = tank.engines[0];
|
|
||||||
const track0 = tank.tracks[0];
|
|
||||||
|
|
||||||
function setByUnlock(unlock: Unlock) {
|
|
||||||
mutateDuel((draft) => {
|
|
||||||
if (unlock.type === 'turret') {
|
|
||||||
draft.protagonist!.turret = draft.protagonist!.tank.turrets.find(
|
|
||||||
(turret) => turret.id === unlock.id,
|
|
||||||
)!;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!draft.protagonist!.turret.guns.some(
|
|
||||||
(gun) => gun.id === draft.protagonist!.gun.id,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
draft.protagonist!.gun = draft.protagonist!.turret.guns.at(-1)!;
|
|
||||||
draft.protagonist!.shell = draft.protagonist!.gun.shells.at(-1)!;
|
|
||||||
}
|
|
||||||
} else if (unlock.type === 'gun') {
|
|
||||||
const gunInTurret = draft.protagonist!.turret.guns.find(
|
|
||||||
(gun) => gun.id === unlock.id,
|
|
||||||
);
|
|
||||||
if (gunInTurret) {
|
|
||||||
draft.protagonist!.gun = gunInTurret;
|
|
||||||
draft.protagonist!.shell = gunInTurret.shells.at(-1)!;
|
|
||||||
} else {
|
|
||||||
// TODO: warn somehow?
|
|
||||||
const suitableTurret = draft.protagonist!.tank.turrets.find(
|
|
||||||
(turret) => turret.guns.some((gun) => gun.id === unlock.id),
|
|
||||||
)!;
|
|
||||||
const gunInSuitableTurret = suitableTurret.guns.find(
|
|
||||||
(gun) => gun.id === unlock.id,
|
|
||||||
)!;
|
|
||||||
|
|
||||||
draft.protagonist!.turret = suitableTurret;
|
|
||||||
draft.protagonist!.gun = gunInSuitableTurret;
|
|
||||||
draft.protagonist!.shell = gunInSuitableTurret.shells.at(-1)!;
|
|
||||||
}
|
|
||||||
} else if (unlock.type === 'engine') {
|
|
||||||
draft.protagonist!.engine = draft.protagonist!.tank.engines.find(
|
|
||||||
(engine) => engine.id === unlock.id,
|
|
||||||
)!;
|
|
||||||
} else if (unlock.type === 'chassis') {
|
|
||||||
draft.protagonist!.track = draft.protagonist!.tank.tracks.find(
|
|
||||||
(track) => track.id === unlock.id,
|
|
||||||
)!;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function tree(type: ModuleType, unlocks: Unlock[]) {
|
|
||||||
return (
|
|
||||||
<Flex gap="2">
|
|
||||||
{unlocks.map((unlock, index) => {
|
|
||||||
const direction = index < Math.floor(unlocks.length / 2) ? -1 : 1;
|
|
||||||
let module: ModuleDefinition | TankDefinition | undefined = undefined;
|
|
||||||
|
|
||||||
if (unlock.type === 'chassis') {
|
|
||||||
module = tank.tracks.find((track) => track.id === unlock.id)!;
|
|
||||||
} else if (unlock.type === 'engine') {
|
|
||||||
module = tank.engines.find((engine) => engine.id === unlock.id)!;
|
|
||||||
} else if (unlock.type === 'turret') {
|
|
||||||
module = tank.turrets.find((turret) => turret.id === unlock.id)!;
|
|
||||||
} else if (unlock.type === 'gun') {
|
|
||||||
module = tank.turrets
|
|
||||||
.find((turret) =>
|
|
||||||
turret.guns.some((gun) => gun.id === unlock.id),
|
|
||||||
)!
|
|
||||||
.guns.find((gun) => gun.id === unlock.id)!;
|
|
||||||
} else if (unlock.type === 'vehicle') {
|
|
||||||
module = awaitedTankDefinitions[unlock.id];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (module === undefined) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
direction="column"
|
|
||||||
gap="2"
|
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{unlock.cost.value > 0 && (
|
|
||||||
<>
|
|
||||||
{unlocks.length > 1 && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
backgroundColor: 'currentcolor',
|
|
||||||
width: 'calc(50% + 4px)',
|
|
||||||
height: 1,
|
|
||||||
position: 'absolute',
|
|
||||||
right: direction < 0 ? 0 : undefined,
|
|
||||||
left: direction > 0 ? 0 : undefined,
|
|
||||||
transform: `translateX(${direction * -4}px)`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
style={{
|
|
||||||
marginBottom: -7,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
backgroundColor: 'currentcolor',
|
|
||||||
width: 1,
|
|
||||||
height: 8,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ChevronDownIcon
|
|
||||||
style={{
|
|
||||||
transform: 'translateY(-7px)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ModuleButton
|
|
||||||
unlock={unlock}
|
|
||||||
tier={module.tier}
|
|
||||||
selected={
|
|
||||||
(unlock.type === 'turret'
|
|
||||||
? turret.id
|
|
||||||
: unlock.type === 'gun'
|
|
||||||
? gun.id
|
|
||||||
: unlock.type === 'engine'
|
|
||||||
? engine.id
|
|
||||||
: unlock.type === 'chassis'
|
|
||||||
? track.id
|
|
||||||
: -1) === unlock.id
|
|
||||||
}
|
|
||||||
onClick={() => setByUnlock(unlock)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{(module as ModuleDefinition).unlocks &&
|
|
||||||
(module as ModuleDefinition).unlocks!.length > 1 && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
backgroundColor: 'currentcolor',
|
|
||||||
width: 1,
|
|
||||||
height: 8,
|
|
||||||
marginBottom: -8,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(module as ModuleDefinition).unlocks &&
|
|
||||||
tree(type, (module as ModuleDefinition).unlocks!)}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageWrapper>
|
|
||||||
<Heading>Modules</Heading>
|
|
||||||
|
|
||||||
<Flex gap="2">
|
|
||||||
{tree('turret', [
|
|
||||||
{
|
|
||||||
cost: { type: 'xp', value: 0 },
|
|
||||||
id: turret0.id,
|
|
||||||
type: 'turret',
|
|
||||||
},
|
|
||||||
])}
|
|
||||||
{tree('gun', [
|
|
||||||
{
|
|
||||||
cost: { type: 'xp', value: 0 },
|
|
||||||
id: gun0.id,
|
|
||||||
type: 'gun',
|
|
||||||
},
|
|
||||||
])}
|
|
||||||
{tree('engine', [
|
|
||||||
{
|
|
||||||
cost: { type: 'xp', value: 0 },
|
|
||||||
id: engine0.id,
|
|
||||||
type: 'engine',
|
|
||||||
},
|
|
||||||
])}
|
|
||||||
{tree('chassis', [
|
|
||||||
{
|
|
||||||
cost: { type: 'xp', value: 0 },
|
|
||||||
id: track0.id,
|
|
||||||
type: 'chassis',
|
|
||||||
},
|
|
||||||
])}
|
|
||||||
</Flex>
|
|
||||||
</PageWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ChevronRightIcon } from '@radix-ui/react-icons';
|
import { ChevronRightIcon } from '@radix-ui/react-icons';
|
||||||
import { ChevronDownIcon, Flex, Heading, Text } from '@radix-ui/themes';
|
import { ChevronDownIcon, Flex, Text } from '@radix-ui/themes';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { ComponentProps, use } from 'react';
|
import { ComponentProps, use } from 'react';
|
||||||
import PageWrapper from '../../../../../../components/PageWrapper';
|
import PageWrapper from '../../../../../../components/PageWrapper';
|
||||||
@@ -121,10 +121,39 @@ export function TechTreeSection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<PageWrapper
|
||||||
<Heading>Tech tree</Heading>
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
size={tank.ancestors && tank.successors ? 880 : 480}
|
||||||
|
gap="0"
|
||||||
|
highlight
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={asset(`flags/scratched/${tank.nation}.webp`)}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
height: '200%',
|
||||||
|
transform: 'translate(-40%, -48%)',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
opacity: 1 / 4,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<Flex align="center" gap="6">
|
{/* <Flex justify="center">
|
||||||
|
<Heading>Tech tree</Heading>
|
||||||
|
</Flex> */}
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
align="center"
|
||||||
|
gap="6"
|
||||||
|
style={{
|
||||||
|
zIndex: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{tank.ancestors && (
|
{tank.ancestors && (
|
||||||
<>
|
<>
|
||||||
<Flex
|
<Flex
|
||||||
@@ -146,28 +175,8 @@ export function TechTreeSection() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Flex
|
<Flex direction="column">
|
||||||
direction="column"
|
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Card id={tank.id} />
|
<Card id={tank.id} />
|
||||||
|
|
||||||
<img
|
|
||||||
src={asset(`flags/scratched/${tank.nation}.webp`)}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
height: 180,
|
|
||||||
transform: 'translate(-42%, -50%)',
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
zIndex: -1,
|
|
||||||
opacity: 1 / 4,
|
|
||||||
filter: 'blur(4px)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{tank.successors && (
|
{tank.successors && (
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import PageWrapper from '../../../../components/PageWrapper';
|
|||||||
import { assignDuelMember } from '../../../../core/blitzkit/assignDuelMember';
|
import { assignDuelMember } from '../../../../core/blitzkit/assignDuelMember';
|
||||||
import { mutateDuel, useDuel } from '../../../../stores/duel';
|
import { mutateDuel, useDuel } from '../../../../stores/duel';
|
||||||
import { mutateTankopediaTemporary } from '../../../../stores/tankopedia';
|
import { mutateTankopediaTemporary } from '../../../../stores/tankopedia';
|
||||||
|
import { CharacteristicsSection } from './components/Model/CharacteristicsSection';
|
||||||
import { HeroSection } from './components/Model/HeroSection';
|
import { HeroSection } from './components/Model/HeroSection';
|
||||||
import { HistoricalSection } from './components/Model/HistoricalSection';
|
import { HistoricalSection } from './components/Model/HistoricalSection';
|
||||||
import { ModulesSection } from './components/Model/ModulesSection';
|
|
||||||
import { TechTreeSection } from './components/Model/TechTreeSection';
|
import { TechTreeSection } from './components/Model/TechTreeSection';
|
||||||
import { Videos } from './components/Videos';
|
import { Videos } from './components/Videos';
|
||||||
|
|
||||||
@@ -62,10 +62,9 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
<>
|
<>
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
{/* <TankopediaPlug /> */}
|
{/* <TankopediaPlug /> */}
|
||||||
<ModulesSection />
|
<CharacteristicsSection />
|
||||||
<TechTreeSection />
|
<TechTreeSection />
|
||||||
{/* <CharacteristicsSection />
|
{/* <TankopediaSeparator /> */}
|
||||||
<TankopediaSeparator /> */}
|
|
||||||
<Videos />
|
<Videos />
|
||||||
<HistoricalSection />
|
<HistoricalSection />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Flex, FlexProps, Theme } from '@radix-ui/themes';
|
import { Flex, FlexProps, Theme } from '@radix-ui/themes';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps } from 'react';
|
||||||
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
type PageWrapperProps = FlexProps & {
|
type PageWrapperProps = FlexProps & {
|
||||||
color?: ComponentProps<typeof Theme>['accentColor'];
|
color?: ComponentProps<typeof Theme>['accentColor'];
|
||||||
size?: number | string;
|
size?: number | string;
|
||||||
noPadding?: boolean;
|
noPadding?: boolean;
|
||||||
noMaxWidth?: boolean;
|
noMaxWidth?: boolean;
|
||||||
|
highlight?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PageWrapper({
|
export default function PageWrapper({
|
||||||
@@ -15,6 +17,7 @@ export default function PageWrapper({
|
|||||||
children,
|
children,
|
||||||
noPadding = false,
|
noPadding = false,
|
||||||
noMaxWidth = false,
|
noMaxWidth = false,
|
||||||
|
highlight = false,
|
||||||
...props
|
...props
|
||||||
}: PageWrapperProps) {
|
}: PageWrapperProps) {
|
||||||
return (
|
return (
|
||||||
@@ -24,6 +27,9 @@ export default function PageWrapper({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
background: highlight
|
||||||
|
? `linear-gradient(-90deg, ${theme.colors.appBackground1}, ${theme.colors.appBackground2})`
|
||||||
|
: undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
|
|||||||
Reference in new issue
Block a user