diff --git a/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection.tsx b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection.tsx
deleted file mode 100644
index 4ad1352df..000000000
--- a/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection.tsx
+++ /dev/null
@@ -1,218 +0,0 @@
-import { CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
-import { Box, Flex, Heading, ScrollArea, Text } from '@radix-ui/themes';
-import Link from 'next/link';
-import { use, useEffect, useMemo, useRef } from 'react';
-import { asset } from '../../../../../../core/blitzkit/asset';
-import {
- TankDefinition,
- tankDefinitions,
-} from '../../../../../../core/blitzkit/tankDefinitions';
-import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkit/tankDefinitions/constants';
-import { formatCompact } from '../../../../../../core/math/formatCompact';
-import * as Duel from '../../../../../../stores/duel';
-
-function Card({ id, highlight }: { id: number; highlight?: boolean }) {
- const awaitedTankDefinitions = use(tankDefinitions);
- const tank = awaitedTankDefinitions[id];
-
- return (
-
-
-
-
-
-
-
-
- {TIER_ROMAN_NUMERALS[tank.tier]}
-
- {tank.name}
-
-
-
-
-
-
- {tank.xp === undefined || tank.tier === 1
- ? 0
- : formatCompact(tank.xp!)}
-
-
-
-
-
-
- {formatCompact(tank.price.value)}
-
-
-
-
-
-
-
- );
-}
-
-function Arrow() {
- return (
-
-
-
- );
-}
-
-export function TechTreeSection() {
- const tank = Duel.use((state) => state.protagonist.tank);
- const awaitedTankDefinitions = use(tankDefinitions);
- const container = useRef(null);
-
- if (
- tank.treeType !== 'researchable' ||
- tank.ancestors === undefined ||
- tank.ancestors.length === 0
- ) {
- return null;
- }
-
- const ancestors = useMemo(() => {
- const treeBackwards: number[] = [];
- let ancestor: TankDefinition | undefined = tank;
-
- while (ancestor !== undefined) {
- if (
- ancestor.tier === 1 ||
- ancestor.ancestors === undefined ||
- ancestor.ancestors.length === 0
- ) {
- ancestor = undefined;
- break;
- } else if (ancestor.ancestors.length === 1) {
- ancestor = awaitedTankDefinitions[ancestor.ancestors[0]];
- } else {
- let bestAncestor = awaitedTankDefinitions[ancestor.ancestors[0]];
-
- ancestor.ancestors.forEach((thisAncestorId) => {
- const possibleAncestor = awaitedTankDefinitions[thisAncestorId];
-
- if (possibleAncestor.xp! < bestAncestor.xp!) {
- bestAncestor = possibleAncestor;
- }
- });
-
- ancestor = bestAncestor as TankDefinition;
- }
-
- treeBackwards.push(ancestor.id);
- }
-
- return treeBackwards.reverse();
- }, []);
-
- useEffect(() => {
- if (!container.current) return;
-
- container.current.scrollLeft = container.current.scrollWidth;
- });
-
- return (
-
-
- Tech tree
-
-
-
-
- {ancestors.map((id, index) => (
- <>
- {index > 0 && }
-
- >
- ))}
-
-
-
-
- {tank.tier < 10 && (
- <>
-
- {tank.successors!.map((id, index) => (
- <>
- {index > 0 && (
-
-
-
- )}
-
- >
- ))}
- >
- )}
-
-
-
- );
-}
diff --git a/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/components/Arrow.tsx b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/components/Arrow.tsx
new file mode 100644
index 000000000..79acdf3f3
--- /dev/null
+++ b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/components/Arrow.tsx
@@ -0,0 +1,10 @@
+import { CaretRightIcon } from '@radix-ui/react-icons';
+import { Text } from '@radix-ui/themes';
+
+export function Arrow() {
+ return (
+
+
+
+ );
+}
diff --git a/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/components/Node.tsx b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/components/Node.tsx
new file mode 100644
index 000000000..be6df1460
--- /dev/null
+++ b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/components/Node.tsx
@@ -0,0 +1,104 @@
+import { Box, Flex, Text } from '@radix-ui/themes';
+import Link from 'next/link';
+import { use } from 'react';
+import { asset } from '../../../../../../../../core/blitzkit/asset';
+import { tankDefinitions } from '../../../../../../../../core/blitzkit/tankDefinitions';
+import { TIER_ROMAN_NUMERALS } from '../../../../../../../../core/blitzkit/tankDefinitions/constants';
+import { formatCompact } from '../../../../../../../../core/math/formatCompact';
+
+export function Node({ id, highlight }: { id: number; highlight?: boolean }) {
+ const awaitedTankDefinitions = use(tankDefinitions);
+ const tank = awaitedTankDefinitions[id];
+
+ return (
+
+
+
+
+
+
+
+
+ {TIER_ROMAN_NUMERALS[tank.tier]}
+
+ {tank.name}
+
+
+
+
+
+
+ {tank.xp === undefined || tank.tier === 1
+ ? 0
+ : formatCompact(tank.xp!)}
+
+
+
+
+
+
+ {formatCompact(tank.price.value)}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/index.tsx b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/index.tsx
new file mode 100644
index 000000000..23796023c
--- /dev/null
+++ b/src/app/tools/tankopedia/[id]/components/Model/TechTreeSection/index.tsx
@@ -0,0 +1,145 @@
+import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
+import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
+import { use, useEffect, useMemo, useRef, useState } from 'react';
+import { tankDefinitions } from '../../../../../../../core/blitzkit/tankDefinitions';
+import * as Duel from '../../../../../../../stores/duel';
+import { Arrow } from './components/Arrow';
+import { Node } from './components/Node';
+
+// function Ancestry({ line }: { line: Line }) {
+// return (
+//
+//
+// {line.map((node) => {
+// if (typeof node === 'number') {
+// return ;
+// } else {
+// return (
+//
+// {node.map((line) => (
+//
+// ))}
+//
+// );
+// }
+// })}
+//
+//
+// );
+// }
+
+type Line = number[];
+
+export function TechTreeSection() {
+ const master = Duel.use((state) => state.protagonist.tank);
+ const awaitedTankDefinitions = use(tankDefinitions);
+ const container = useRef(null);
+ const lines = useMemo(() => {
+ function extend(line: Line): Line[] {
+ const root = awaitedTankDefinitions[line.at(-1)!];
+
+ if (root.ancestors === undefined || root.tier === 1) {
+ return [line];
+ } else {
+ if (root.ancestors.length === 1 || root.tier === 2) {
+ line.push(root.ancestors[0]);
+ return extend(line);
+ } else {
+ return root.ancestors
+ .map((ancestor) => {
+ const newLine = [...line, ancestor];
+ return extend(newLine);
+ })
+ .flat();
+ }
+ }
+ }
+
+ return extend([master.id]);
+ }, [master]);
+ const [lineIndex, setLineIndex] = useState(0);
+ const line = useMemo(
+ () => [...lines[lineIndex]].toReversed(),
+ [master, lineIndex],
+ );
+
+ if (
+ master.treeType !== 'researchable' ||
+ master.ancestors === undefined ||
+ master.ancestors.length === 0
+ ) {
+ return null;
+ }
+
+ useEffect(() => {
+ if (!container.current) return;
+
+ container.current.scrollLeft = container.current.scrollWidth;
+ });
+
+ return (
+
+
+ Tech tree
+
+ {lines.length > 1 && (
+
+
+ setLineIndex((lines.length + lineIndex - 1) % lines.length)
+ }
+ >
+
+
+
+ Route {lineIndex + 1} of {lines.length}
+
+ setLineIndex((lineIndex + 1) % lines.length)}
+ >
+
+
+
+ )}
+
+
+
+
+ {line.map((id, index) => (
+ <>
+ {index > 0 && }
+
+ >
+ ))}
+
+ {master.tier < 10 && (
+ <>
+
+ {master.successors!.map((id, index) => (
+ <>
+ {index > 0 && (
+
+
+
+ )}
+
+ >
+ ))}
+ >
+ )}
+
+
+
+ );
+}