diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx index 3a2fd22be..6fb26be21 100644 --- a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Modules.tsx @@ -1,16 +1,288 @@ -import { Button, Flex, Heading } from '@radix-ui/themes'; -import { ModuleManager } from '../../../../../../../components/ModuleManager'; +import { + 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 { mutateTankopediaTemporary } from '../../../../../../../stores/tankopedia'; 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 = ( + + + {TIER_ROMAN_NUMERALS[tier]} + + + {unlock.cost.value > 0 && ( + + + {numberFormat.format(unlock.cost.value)} + + + + + )} + + + + ); + + return unlock.type === 'vehicle' ? ( + {button} + ) : ( + button + ); +} + export function Modules() { - const protagonist = useDuel((state) => state.protagonist!); + const awaitedTankDefinitions = use(tankDefinitions); + const tank = useDuel((state) => state.protagonist!.tank); const hasUpgrades = - protagonist.tank.turrets.length > 1 || - protagonist.tank.turrets[0].guns.length > 1 || - protagonist.tank.engines.length > 1 || - protagonist.tank.tracks.length > 1; + tank.turrets.length > 1 || + tank.turrets[0].guns.length > 1 || + tank.engines.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 ( + + {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 ( + + {unlock.cost.value > 0 && ( + <> + {unlocks.length > 1 && ( +
+ )} + + +
+ + + + )} + + setByUnlock(unlock)} + /> + + {(module as ModuleDefinition).unlocks && + (module as ModuleDefinition).unlocks!.length > 1 && ( +
+ )} + + {(module as ModuleDefinition).unlocks && + tree(type, (module as ModuleDefinition).unlocks!)} + + ); + })} + + ); + } return ( @@ -58,22 +330,36 @@ export function Modules() { )} - { - mutateDuel((draft) => { - draft.protagonist = { ...draft.protagonist!, ...modules }; - }); - mutateTankopediaTemporary((draft) => { - draft.shot = undefined; - }); - }} - /> + + {tree('engine', [ + { + cost: { type: 'xp', value: 0 }, + id: engine0.id, + type: 'engine', + }, + ])} + {tree('chassis', [ + { + cost: { type: 'xp', value: 0 }, + id: track0.id, + 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', + }, + ])} + ); } diff --git a/src/app/tools/tankopedia/[id]/components/Model/CharacteristicsSection.tsx b/src/app/tools/tankopedia/[id]/components/Model/CharacteristicsSection.tsx index 1215e39a7..285327f68 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/CharacteristicsSection.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/CharacteristicsSection.tsx @@ -13,9 +13,9 @@ export function CharacteristicsSection() { const wideFormat = useWideFormat(720); return ( - + diff --git a/src/app/tools/tankopedia/[id]/components/Model/ModulesSection.tsx b/src/app/tools/tankopedia/[id]/components/Model/ModulesSection.tsx deleted file mode 100644 index 8a60d112a..000000000 --- a/src/app/tools/tankopedia/[id]/components/Model/ModulesSection.tsx +++ /dev/null @@ -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 = ( - - - {TIER_ROMAN_NUMERALS[tier]} - - - {unlock.cost.value > 0 && ( - - - {numberFormat.format(unlock.cost.value)} - - - - - )} - - - - ); - - return unlock.type === 'vehicle' ? ( - {button} - ) : ( - 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 ( - - {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 ( - - {unlock.cost.value > 0 && ( - <> - {unlocks.length > 1 && ( -
0 ? 0 : undefined, - transform: `translateX(${direction * -4}px)`, - }} - /> - )} - - -
- - - - )} - - setByUnlock(unlock)} - /> - - {(module as ModuleDefinition).unlocks && - (module as ModuleDefinition).unlocks!.length > 1 && ( -
- )} - - {(module as ModuleDefinition).unlocks && - tree(type, (module as ModuleDefinition).unlocks!)} - - ); - })} - - ); - } - - return ( - - Modules - - - {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', - }, - ])} - - - ); -} diff --git a/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection.tsx b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection.tsx index 5f8d456e0..970c121c6 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection.tsx @@ -1,5 +1,5 @@ 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 { ComponentProps, use } from 'react'; import PageWrapper from '../../../../../../components/PageWrapper'; @@ -121,10 +121,39 @@ export function TechTreeSection() { } return ( - - Tech tree + + - + {/* + Tech tree + */} + + {tank.ancestors && ( <> )} - + - - {tank.successors && ( diff --git a/src/app/tools/tankopedia/[id]/page.tsx b/src/app/tools/tankopedia/[id]/page.tsx index 518b964bd..1287a19c7 100644 --- a/src/app/tools/tankopedia/[id]/page.tsx +++ b/src/app/tools/tankopedia/[id]/page.tsx @@ -5,9 +5,9 @@ import PageWrapper from '../../../../components/PageWrapper'; import { assignDuelMember } from '../../../../core/blitzkit/assignDuelMember'; import { mutateDuel, useDuel } from '../../../../stores/duel'; import { mutateTankopediaTemporary } from '../../../../stores/tankopedia'; +import { CharacteristicsSection } from './components/Model/CharacteristicsSection'; import { HeroSection } from './components/Model/HeroSection'; import { HistoricalSection } from './components/Model/HistoricalSection'; -import { ModulesSection } from './components/Model/ModulesSection'; import { TechTreeSection } from './components/Model/TechTreeSection'; import { Videos } from './components/Videos'; @@ -62,10 +62,9 @@ export default function Page({ params }: { params: { id: string } }) { <> {/* */} - + - {/* - */} + {/* */} diff --git a/src/components/PageWrapper.tsx b/src/components/PageWrapper.tsx index 8de691c0a..218c40e44 100644 --- a/src/components/PageWrapper.tsx +++ b/src/components/PageWrapper.tsx @@ -1,11 +1,13 @@ import { Flex, FlexProps, Theme } from '@radix-ui/themes'; import { ComponentProps } from 'react'; +import { theme } from '../stitches.config'; type PageWrapperProps = FlexProps & { color?: ComponentProps['accentColor']; size?: number | string; noPadding?: boolean; noMaxWidth?: boolean; + highlight?: boolean; }; export default function PageWrapper({ @@ -15,6 +17,7 @@ export default function PageWrapper({ children, noPadding = false, noMaxWidth = false, + highlight = false, ...props }: PageWrapperProps) { return ( @@ -24,6 +27,9 @@ export default function PageWrapper({ flex: 1, display: 'flex', flexDirection: 'column', + background: highlight + ? `linear-gradient(-90deg, ${theme.colors.appBackground1}, ${theme.colors.appBackground2})` + : undefined, }} >