From 38b2b6025f3d50e5aed0f5d794f3330d0930f570 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com>
Date: Sat, 22 Jun 2024 16:39:31 -0500
Subject: [PATCH] feat: start of sorting
---
.../tools/tankopedia/components/Results.tsx | 321 +++++++++++++++++-
.../tools/tankopedia/components/SearchBar.tsx | 46 +--
.../{TankSearch/components => }/Sort.tsx | 76 +++--
.../tools/tankopedia/components/TankCard.tsx | 47 ++-
.../components/TankSearch/index.tsx | 2 +-
.../tools/tankopedia/components/TierCard.tsx | 22 +-
src/app/tools/tankopedia/page.tsx | 7 +-
src/stores/tankopediaFilters.ts | 9 +
8 files changed, 455 insertions(+), 75 deletions(-)
rename src/app/tools/tankopedia/components/{TankSearch/components => }/Sort.tsx (83%)
diff --git a/src/app/tools/tankopedia/components/Results.tsx b/src/app/tools/tankopedia/components/Results.tsx
index 59bf5bb5a..52affb9af 100644
--- a/src/app/tools/tankopedia/components/Results.tsx
+++ b/src/app/tools/tankopedia/components/Results.tsx
@@ -2,12 +2,21 @@ import { Callout, Flex } from '@radix-ui/themes';
import { go } from 'fuzzysort';
import { range } from 'lodash';
import { memo, use, useMemo } from 'react';
+import { Vector2Tuple } from 'three';
import { ExperimentIcon } from '../../../../components/ExperimentIcon';
+import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
+import { modelDefinitions } from '../../../../core/blitzkit/modelDefinitions';
+import { normalizeBoundingBox } from '../../../../core/blitzkit/normalizeBoundingBox';
+import { resolveDpm } from '../../../../core/blitzkit/resolveDpm';
+import { resolveReload } from '../../../../core/blitzkit/resolveReload';
import {
+ TankDefinition,
Tier,
tankDefinitions,
tankNames,
+ tanksDefinitionsArray,
} from '../../../../core/blitzkit/tankDefinitions';
+import { unionBoundingBox } from '../../../../core/blitzkit/unionBoundingBox';
import { useTankopediaFilters } from '../../../../stores/tankopediaFilters';
import { SearchBar } from './SearchBar';
import { TankCard } from './TankCard';
@@ -15,20 +24,309 @@ import { TankCardWrapper } from './TankCardWrapper';
import { TierCard } from './TierCard';
export const Results = memo(() => {
+ const awaitedModelDefinitions = use(modelDefinitions);
const awaitedTankDefinitions = use(tankDefinitions);
+ const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray);
const awaitedTankNames = use(tankNames);
const testing = useTankopediaFilters((state) => state.testing);
const search = useTankopediaFilters((state) => state.search);
+ const sort = useTankopediaFilters((state) => state.sort);
const searchedTanks = useMemo(() => {
- if (search === undefined) return undefined;
+ if (search !== undefined) {
+ const searchedRaw = go(search, awaitedTankNames, { key: 'combined' });
+ const searchedTanks = searchedRaw.map(
+ (result) => awaitedTankDefinitions[result.obj.id],
+ );
+ return searchedTanks;
+ }
- const searchedRaw = go(search, awaitedTankNames, { key: 'combined' });
- const searchedTanks = searchedRaw.map(
- (result) => awaitedTankDefinitions[result.obj.id],
- );
+ if (sort.by !== 'meta.tier') {
+ const filtered = awaitedTanksDefinitionsArray;
+ let sorted: TankDefinition[];
- return searchedTanks;
- }, [search]);
+ switch (sort.by) {
+ case 'meta.name':
+ sorted = filtered.sort((a, b) => a.name?.localeCompare(b.name));
+ break;
+
+ case 'survivability.health': {
+ sorted = filtered.sort((a, b) => {
+ const aHealth = a.health + a.turrets.at(-1)!.health;
+ const bHealth = b.health + b.turrets.at(-1)!.health;
+
+ return aHealth - bHealth;
+ });
+ break;
+ }
+
+ case 'survivability.viewRange':
+ sorted = filtered.sort(
+ (a, b) => a.turrets.at(-1)!.viewRange - b.turrets.at(-1)!.viewRange,
+ );
+ break;
+
+ case 'survivability.camouflageStill':
+ sorted = filtered.sort(
+ (a, b) => a.camouflage.still - b.camouflage.still,
+ );
+ break;
+
+ case 'survivability.camouflageMoving':
+ sorted = filtered.sort(
+ (a, b) => a.camouflage.moving - b.camouflage.moving,
+ );
+ break;
+
+ case 'survivability.camouflageShooting':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.turrets.at(-1)!.guns.at(-1)!.camouflageLoss -
+ b.turrets.at(-1)!.guns.at(-1)!.camouflageLoss,
+ );
+ break;
+
+ case 'survivability.volume':
+ sorted = filtered.sort((a, b) => {
+ const aTankModelDefinition = awaitedModelDefinitions[a.id];
+ const bTankModelDefinition = awaitedModelDefinitions[b.id];
+ const aTurretModelDefinition =
+ aTankModelDefinition.turrets[a.turrets.at(-1)!.id];
+ const bTurretModelDefinition =
+ bTankModelDefinition.turrets[b.turrets.at(-1)!.id];
+ const aSize = normalizeBoundingBox(
+ unionBoundingBox(
+ aTankModelDefinition.boundingBox,
+ aTurretModelDefinition.boundingBox,
+ ),
+ );
+ const bSize = normalizeBoundingBox(
+ unionBoundingBox(
+ bTankModelDefinition.boundingBox,
+ bTurretModelDefinition.boundingBox,
+ ),
+ );
+ const aVolume = aSize[0] * aSize[1] * aSize[2];
+ const bVolume = bSize[0] * bSize[1] * bSize[2];
+
+ return aVolume - bVolume;
+ });
+ break;
+
+ case 'survivability.length':
+ sorted = filtered.sort((a, b) => {
+ const aTankModelDefinition = awaitedModelDefinitions[a.id];
+ const bTankModelDefinition = awaitedModelDefinitions[b.id];
+ const aTurretModelDefinition =
+ aTankModelDefinition.turrets[a.turrets.at(-1)!.id];
+ const bTurretModelDefinition =
+ bTankModelDefinition.turrets[b.turrets.at(-1)!.id];
+ const aSize = normalizeBoundingBox(
+ unionBoundingBox(
+ aTankModelDefinition.boundingBox,
+ aTurretModelDefinition.boundingBox,
+ ),
+ );
+ const bSize = normalizeBoundingBox(
+ unionBoundingBox(
+ bTankModelDefinition.boundingBox,
+ bTurretModelDefinition.boundingBox,
+ ),
+ );
+ const aLength = Math.max(aSize[0], aSize[1], aSize[2]);
+ const bLength = Math.max(bSize[0], bSize[1], bSize[2]);
+
+ return aLength - bLength;
+ });
+ break;
+
+ case 'fire.dpm':
+ sorted = filtered.sort(
+ (a, b) =>
+ resolveDpm(
+ a.turrets.at(-1)!.guns.at(-1)!,
+ a.turrets.at(-1)!.guns.at(-1)!.shells[0],
+ ) -
+ resolveDpm(
+ b.turrets.at(-1)!.guns.at(-1)!,
+ b.turrets.at(-1)!.guns.at(-1)!.shells[0],
+ ),
+ );
+ break;
+
+ case 'fire.reload':
+ sorted = filtered.sort(
+ (a, b) =>
+ resolveReload(a.turrets.at(-1)!.guns.at(-1)!) -
+ resolveReload(b.turrets.at(-1)!.guns.at(-1)!),
+ );
+ break;
+
+ case 'fire.caliber':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber -
+ b.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber,
+ );
+ break;
+
+ case 'fire.standardPenetration':
+ sorted = filtered.sort(
+ (a, b) =>
+ resolveNearPenetration(
+ a.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration,
+ ) -
+ resolveNearPenetration(
+ b.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration,
+ ),
+ );
+ break;
+
+ case 'fire.premiumPenetration':
+ sorted = filtered.sort(
+ (a, b) =>
+ resolveNearPenetration(
+ (
+ a.turrets.at(-1)!.guns.at(-1)!.shells[1] ??
+ a.turrets.at(-1)!.guns.at(-1)!.shells[0]
+ ).penetration,
+ ) -
+ resolveNearPenetration(
+ (
+ b.turrets.at(-1)!.guns.at(-1)!.shells[1] ??
+ b.turrets.at(-1)!.guns.at(-1)!.shells[0]
+ ).penetration,
+ ),
+ );
+ break;
+
+ case 'fire.damage':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.turrets.at(-1)!.guns.at(-1)!.shells[0].damage.armor -
+ b.turrets.at(-1)!.guns.at(-1)!.shells[0].damage.armor,
+ );
+ break;
+
+ case 'fire.shellVelocity':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.turrets.at(-1)!.guns.at(-1)!.shells[0].speed -
+ b.turrets.at(-1)!.guns.at(-1)!.shells[0].speed,
+ );
+ break;
+
+ case 'fire.aimTime':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.turrets.at(-1)!.guns.at(-1)!.aimTime -
+ b.turrets.at(-1)!.guns.at(-1)!.aimTime,
+ );
+ break;
+
+ case 'fire.dispersionStill':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.turrets.at(-1)!.guns.at(-1)!.dispersion.base -
+ b.turrets.at(-1)!.guns.at(-1)!.dispersion.base,
+ );
+ break;
+
+ case 'fire.dispersionMoving':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.tracks.at(-1)!.dispersion.move -
+ b.tracks.at(-1)!.dispersion.move,
+ );
+ break;
+
+ case 'fire.gunDepression':
+ sorted = filtered.sort(
+ (a, b) =>
+ awaitedModelDefinitions[a.id].turrets[a.turrets.at(-1)!.id].guns[
+ a.turrets.at(-1)!.guns.at(-1)!.id
+ ].pitch.max -
+ awaitedModelDefinitions[b.id].turrets[b.turrets.at(-1)!.id].guns[
+ b.turrets.at(-1)!.guns.at(-1)!.id
+ ].pitch.max,
+ );
+ break;
+
+ case 'fire.gunElevation':
+ sorted = filtered.sort(
+ (a, b) =>
+ // reversed because of negative values
+ awaitedModelDefinitions[b.id].turrets[b.turrets.at(-1)!.id].guns[
+ b.turrets.at(-1)!.guns.at(-1)!.id
+ ].pitch.min -
+ awaitedModelDefinitions[a.id].turrets[a.turrets.at(-1)!.id].guns[
+ a.turrets.at(-1)!.guns.at(-1)!.id
+ ].pitch.min,
+ );
+ break;
+
+ case 'maneuverability.forwardsSpeed':
+ sorted = filtered.sort((a, b) => a.speed.forwards - b.speed.forwards);
+ break;
+
+ case 'maneuverability.backwardsSpeed':
+ sorted = filtered.sort(
+ (a, b) => a.speed.backwards - b.speed.backwards,
+ );
+ break;
+
+ case 'maneuverability.power':
+ sorted = filtered.sort(
+ (a, b) => a.engines.at(-1)!.power - b.engines.at(-1)!.power,
+ );
+ break;
+
+ case 'maneuverability.powerToWeight':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.engines.at(-1)!.power /
+ (a.weight +
+ a.engines.at(-1)!.weight +
+ a.tracks.at(-1)!.weight +
+ a.turrets.at(-1)!.weight +
+ a.turrets.at(-1)!.guns.at(-1)!.weight) -
+ b.engines.at(-1)!.power /
+ (b.weight +
+ b.engines.at(-1)!.weight +
+ b.tracks.at(-1)!.weight +
+ b.turrets.at(-1)!.weight +
+ b.turrets.at(-1)!.guns.at(-1)!.weight),
+ );
+ break;
+
+ case 'maneuverability.weight':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.weight +
+ a.engines.at(-1)!.weight +
+ a.tracks.at(-1)!.weight +
+ a.turrets.at(-1)!.weight +
+ a.turrets.at(-1)!.guns.at(-1)!.weight -
+ (b.weight +
+ b.engines.at(-1)!.weight +
+ b.tracks.at(-1)!.weight +
+ b.turrets.at(-1)!.weight +
+ b.turrets.at(-1)!.guns.at(-1)!.weight),
+ );
+ break;
+
+ case 'maneuverability.traverseSpeed':
+ sorted = filtered.sort(
+ (a, b) =>
+ a.tracks.at(-1)!.traverseSpeed - b.tracks.at(-1)!.traverseSpeed,
+ );
+ break;
+ }
+
+ return sort.direction === 'ascending' ? sorted : sorted.reverse();
+ }
+
+ return undefined;
+ }, [search, sort]);
return (
@@ -47,9 +345,14 @@ export const Results = memo(() => {
)}
{!search &&
- range(10, 0).map((tier) => )}
+ sort.by === 'meta.tier' &&
+ range(
+ ...(sort.direction === 'descending'
+ ? ([10, 0] as Vector2Tuple)
+ : ([1, 11] as Vector2Tuple)),
+ ).map((tier) => )}
- {search && (
+ {(search || sort.by !== 'meta.tier') && (
{searchedTanks?.map((tank) => )}
diff --git a/src/app/tools/tankopedia/components/SearchBar.tsx b/src/app/tools/tankopedia/components/SearchBar.tsx
index 6d48a4ef4..d7cf815b3 100644
--- a/src/app/tools/tankopedia/components/SearchBar.tsx
+++ b/src/app/tools/tankopedia/components/SearchBar.tsx
@@ -1,11 +1,12 @@
import { CaretRightIcon, MagnifyingGlassIcon } from '@radix-ui/react-icons';
-import { Button, Spinner, TextField } from '@radix-ui/themes';
+import { Button, Flex, Spinner, TextField } from '@radix-ui/themes';
import { debounce } from 'lodash';
import { useRouter } from 'next/navigation';
import { KeyboardEventHandler, useCallback, useRef, useState } from 'react';
import { Link } from '../../../../components/Link';
import { TankDefinition } from '../../../../core/blitzkit/tankDefinitions';
import { useTankopediaFilters } from '../../../../stores/tankopediaFilters';
+import { Sort } from './Sort';
interface SearchBarProps {
topResult?: TankDefinition;
@@ -44,26 +45,31 @@ export function SearchBar({ topResult }: SearchBarProps) {
);
return (
-
-
- {searching ? : }
-
-
- {topResult && (
+
+
-
-
-
+ {searching ? : }
- )}
-
+
+ {topResult && (
+
+
+
+
+
+ )}
+
+
+
+
);
}
diff --git a/src/app/tools/tankopedia/components/TankSearch/components/Sort.tsx b/src/app/tools/tankopedia/components/Sort.tsx
similarity index 83%
rename from src/app/tools/tankopedia/components/TankSearch/components/Sort.tsx
rename to src/app/tools/tankopedia/components/Sort.tsx
index a2a43dc3c..3041a8929 100644
--- a/src/app/tools/tankopedia/components/TankSearch/components/Sort.tsx
+++ b/src/app/tools/tankopedia/components/Sort.tsx
@@ -1,23 +1,25 @@
import { CaretDownIcon } from '@radix-ui/react-icons';
import { Button, DropdownMenu } from '@radix-ui/themes';
-import mutateTankopediaPersistent, {
- TankopediaSortDirection,
- useTankopediaPersistent,
-} from '../../../../../../stores/tankopedia';
+import { TankopediaSortDirection } from '../../../../stores/tankopedia';
+import {
+ mutateTankopediaFilters,
+ useTankopediaFilters,
+} from '../../../../stores/tankopediaFilters';
export function Sort() {
- const sort = useTankopediaPersistent((state) => state.sort);
+ const sort = useTankopediaFilters((state) => state.sort);
return (
-
+
-
-
- By
+
+
+ Sort by
Meta
@@ -25,7 +27,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'meta.tier';
});
}}
@@ -35,7 +37,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'meta.name';
});
}}
@@ -51,7 +53,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.dpm';
});
}}
@@ -61,7 +63,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.reload';
});
}}
@@ -71,7 +73,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.caliber';
});
}}
@@ -81,7 +83,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.standardPenetration';
});
}}
@@ -91,7 +93,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.premiumPenetration';
});
}}
@@ -101,7 +103,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.damage';
});
}}
@@ -111,7 +113,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.shellVelocity';
});
}}
@@ -121,7 +123,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.aimTime';
});
}}
@@ -131,7 +133,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.dispersionStill';
});
}}
@@ -141,7 +143,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.dispersionMoving';
});
}}
@@ -151,7 +153,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.gunDepression';
});
}}
@@ -161,7 +163,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'fire.gunElevation';
});
}}
@@ -177,7 +179,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.forwardsSpeed';
});
}}
@@ -187,7 +189,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.backwardsSpeed';
});
}}
@@ -197,7 +199,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.power';
});
}}
@@ -207,7 +209,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.powerToWeight';
});
}}
@@ -217,7 +219,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.weight';
});
}}
@@ -227,7 +229,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'maneuverability.traverseSpeed';
});
}}
@@ -243,7 +245,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.health';
});
}}
@@ -253,7 +255,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.viewRange';
});
}}
@@ -263,7 +265,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.camouflageStill';
});
}}
@@ -273,7 +275,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.camouflageMoving';
});
}}
@@ -283,7 +285,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.camouflageShooting';
});
}}
@@ -293,7 +295,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.volume';
});
}}
@@ -303,7 +305,7 @@ export function Sort() {
{
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.by = 'survivability.length';
});
}}
@@ -319,7 +321,7 @@ export function Sort() {
- mutateTankopediaPersistent((draft) => {
+ mutateTankopediaFilters((draft) => {
draft.sort.direction = value as TankopediaSortDirection;
})
}
diff --git a/src/app/tools/tankopedia/components/TankCard.tsx b/src/app/tools/tankopedia/components/TankCard.tsx
index e1b08d6f9..44688a6ed 100644
--- a/src/app/tools/tankopedia/components/TankCard.tsx
+++ b/src/app/tools/tankopedia/components/TankCard.tsx
@@ -1,7 +1,9 @@
import { Flex, Link, Text } from '@radix-ui/themes';
-import { useEffect, useRef } from 'react';
+import { use, useEffect, useMemo, useRef } from 'react';
import { classIcons } from '../../../../components/ClassIcon';
import { asset } from '../../../../core/blitzkit/asset';
+import { modelDefinitions } from '../../../../core/blitzkit/modelDefinitions';
+import { resolveDpm } from '../../../../core/blitzkit/resolveDpm';
import { TankDefinition } from '../../../../core/blitzkit/tankDefinitions';
import { tankIcon } from '../../../../core/blitzkit/tankIcon';
import { tankopediaFilterTank } from '../../../../core/blitzkit/tankopediaFilterTank';
@@ -12,8 +14,36 @@ interface TankCardProps {
}
export function TankCard({ tank }: TankCardProps) {
+ const awaitedModelDefinitions = use(modelDefinitions);
const Icon = classIcons[tank.class];
const link = useRef(null);
+ const sortBy = useTankopediaFilters((state) => state.sort.by);
+ const discriminator = useMemo(() => {
+ if (sortBy.startsWith('meta')) return undefined;
+
+ const turret = tank.turrets.at(-1)!;
+ const gun = turret.guns.at(-1)!;
+ const shell = gun.shells[0];
+ const tracks = tank.tracks.at(-1)!;
+ const tankModelDefinition = awaitedModelDefinitions[tank.id];
+
+ switch (sortBy) {
+ case 'fire.aimTime':
+ return `${gun.aimTime.toFixed(2)}s`;
+ case 'fire.caliber':
+ return `${shell.caliber.toFixed(0)}mm`;
+ case 'fire.damage':
+ return `${shell.damage.armor.toFixed(0)}hp`;
+ case 'fire.dispersionMoving':
+ return `+ ${tracks.dispersion.move.toFixed(3)}`;
+ case 'fire.dispersionStill':
+ return gun.dispersion.base.toFixed(3);
+ case 'fire.dpm':
+ return resolveDpm(gun, shell).toFixed(0);
+ case 'fire.gunDepression':
+ return awaitedModelDefinitions;
+ }
+ }, [sortBy]);
useEffect(() => {
const unsubscribe = useTankopediaFilters.subscribe((filters) => {
@@ -89,6 +119,21 @@ export function TankCard({ tank }: TankCardProps) {
{tank.name}
+
+ {discriminator && (
+
+ {discriminator}
+
+ )}
);
}
diff --git a/src/app/tools/tankopedia/components/TankSearch/index.tsx b/src/app/tools/tankopedia/components/TankSearch/index.tsx
index 64c8c605e..e145f67d2 100644
--- a/src/app/tools/tankopedia/components/TankSearch/index.tsx
+++ b/src/app/tools/tankopedia/components/TankSearch/index.tsx
@@ -31,9 +31,9 @@ import mutateTankopediaPersistent, {
useTankopediaPersistent,
} from '../../../../../stores/tankopedia';
import { PageTurner } from '../PageTurner';
+import { Sort } from '../Sort';
import { Options } from './components/Options';
import { Results } from './components/Results';
-import { Sort } from './components/Sort';
import { treeTypeOrder } from './constants';
interface TankSearchProps {
diff --git a/src/app/tools/tankopedia/components/TierCard.tsx b/src/app/tools/tankopedia/components/TierCard.tsx
index a27a3d19c..c110397e6 100644
--- a/src/app/tools/tankopedia/components/TierCard.tsx
+++ b/src/app/tools/tankopedia/components/TierCard.tsx
@@ -1,5 +1,7 @@
import { Flex, Heading, Separator } from '@radix-ui/themes';
import { memo, use, useEffect, useMemo, useRef } from 'react';
+import { TANK_CLASSES } from '../../../../components/Tanks/components/Item/constants';
+import { gameDefinitions } from '../../../../core/blitzkit/gameDefinitions';
import {
Tier,
tanksDefinitionsArray,
@@ -9,6 +11,7 @@ import { tankopediaFilterTank } from '../../../../core/blitzkit/tankopediaFilter
import { useTankopediaFilters } from '../../../../stores/tankopediaFilters';
import { TankCard } from './TankCard';
import { TankCardWrapper } from './TankCardWrapper';
+import { treeTypeOrder } from './TankSearch/constants';
interface TierCardProps {
tier: Tier;
@@ -16,10 +19,27 @@ interface TierCardProps {
export const TierCard = memo(
({ tier }: TierCardProps) => {
+ const awaitedGameDefinitions = use(gameDefinitions);
const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray);
const container = useRef(null);
const tanks = useMemo(
- () => awaitedTanksDefinitionsArray.filter((tank) => tank.tier === tier),
+ () =>
+ awaitedTanksDefinitionsArray
+ .filter((tank) => tank.tier === tier)
+ .sort(
+ (a, b) =>
+ treeTypeOrder.indexOf(a.treeType) -
+ treeTypeOrder.indexOf(b.treeType),
+ )
+ .sort(
+ (a, b) =>
+ TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class),
+ )
+ .sort(
+ (a, b) =>
+ awaitedGameDefinitions.nations.indexOf(a.nation) -
+ awaitedGameDefinitions.nations.indexOf(b.nation),
+ ),
[tier],
);
diff --git a/src/app/tools/tankopedia/page.tsx b/src/app/tools/tankopedia/page.tsx
index a5b96b0c8..5d240ef88 100644
--- a/src/app/tools/tankopedia/page.tsx
+++ b/src/app/tools/tankopedia/page.tsx
@@ -8,12 +8,7 @@ import { Results } from './components/Results';
export default function Page() {
return (
-
+
diff --git a/src/stores/tankopediaFilters.ts b/src/stores/tankopediaFilters.ts
index 13e65ed9f..e9c5a91bf 100644
--- a/src/stores/tankopediaFilters.ts
+++ b/src/stores/tankopediaFilters.ts
@@ -2,6 +2,7 @@ import { produce } from 'immer';
import { create } from 'zustand';
import { TankClass, TreeType } from '../components/Tanks';
import { Tier } from '../core/blitzkit/tankDefinitions';
+import { TankopediaSortBy, TankopediaSortDirection } from './tankopedia';
export interface TankopediaFilters {
tier?: Tier;
@@ -10,10 +11,18 @@ export interface TankopediaFilters {
type?: TreeType;
testing: 'include' | 'exclude' | 'only';
search?: string;
+ sort: {
+ by: TankopediaSortBy;
+ direction: TankopediaSortDirection;
+ };
}
export const useTankopediaFilters = create()(() => ({
testing: 'include',
+ sort: {
+ by: 'meta.tier',
+ direction: 'descending',
+ },
}));
export function mutateTankopediaFilters(