mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: sort options
This commit is contained in:
1 parent
ded71bf4f9
commit
e7525006b5
5 files changed
+655
-76
No files matched your search
@@ -3,6 +3,7 @@ import { use } from 'react';
|
|||||||
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
||||||
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
|
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
import { normalizeBoundingBox } from '../../../../../../core/blitzkrieg/normalizeBoundingBox';
|
import { normalizeBoundingBox } from '../../../../../../core/blitzkrieg/normalizeBoundingBox';
|
||||||
|
import { resolveDpm } from '../../../../../../core/blitzkrieg/resolveDpm';
|
||||||
import {
|
import {
|
||||||
GUN_TYPE_NAMES,
|
GUN_TYPE_NAMES,
|
||||||
SHELL_NAMES,
|
SHELL_NAMES,
|
||||||
@@ -40,23 +41,6 @@ export function Characteristics() {
|
|||||||
stockTurret.weight +
|
stockTurret.weight +
|
||||||
stockGun.weight;
|
stockGun.weight;
|
||||||
|
|
||||||
let dpm: number;
|
|
||||||
|
|
||||||
if (gun.type === 'regular') {
|
|
||||||
dpm = (shell.damage.armor / gun.reload) * 60;
|
|
||||||
} else if (gun.type === 'autoLoader') {
|
|
||||||
dpm =
|
|
||||||
((shell.damage.armor * gun.count) /
|
|
||||||
(gun.reload + (gun.count - 1) * gun.interClip)) *
|
|
||||||
60;
|
|
||||||
} else {
|
|
||||||
dpm =
|
|
||||||
((shell.damage.armor * gun.count) /
|
|
||||||
(gun.reload.reduce((a, b) => a + b, 0) +
|
|
||||||
(gun.count - 1) * gun.interClip)) *
|
|
||||||
60;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="4" style={{ width: '100%' }}>
|
<Flex direction="column" gap="4" style={{ width: '100%' }}>
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
@@ -95,7 +79,7 @@ export function Characteristics() {
|
|||||||
<Heading size="5">Fire</Heading>
|
<Heading size="5">Fire</Heading>
|
||||||
<Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
|
<Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
|
||||||
<Info name="Damage per minute" unit="hp / min">
|
<Info name="Damage per minute" unit="hp / min">
|
||||||
{dpm.toFixed(0)}
|
{resolveDpm(gun, shell).toFixed(0)}
|
||||||
</Info>
|
</Info>
|
||||||
{gun.type === 'autoReloader' && (
|
{gun.type === 'autoReloader' && (
|
||||||
<>
|
<>
|
||||||
@@ -206,10 +190,10 @@ export function Characteristics() {
|
|||||||
</Info>
|
</Info>
|
||||||
<Info name="Gun flexibility" unit="°" />
|
<Info name="Gun flexibility" unit="°" />
|
||||||
<Info indent name="Depression">
|
<Info indent name="Depression">
|
||||||
{gunModelDefinition.pitch.max}
|
{gunModelDefinition.pitch.max.toFixed(1)}
|
||||||
</Info>
|
</Info>
|
||||||
<Info indent name="Elevation">
|
<Info indent name="Elevation">
|
||||||
{-gunModelDefinition.pitch.min}
|
{(-gunModelDefinition.pitch.min).toFixed(1)}
|
||||||
</Info>
|
</Info>
|
||||||
{gunModelDefinition.pitch.front && (
|
{gunModelDefinition.pitch.front && (
|
||||||
<>
|
<>
|
||||||
@@ -242,10 +226,13 @@ export function Characteristics() {
|
|||||||
<Heading size="5">Maneuverability</Heading>
|
<Heading size="5">Maneuverability</Heading>
|
||||||
<Info name="Speed" unit="km/hr" />
|
<Info name="Speed" unit="km/hr" />
|
||||||
<Info indent name="Forwards">
|
<Info indent name="Forwards">
|
||||||
{tank.speed.forwards}
|
{tank.speed.forwards.toFixed(0)}
|
||||||
</Info>
|
</Info>
|
||||||
<Info indent name="Average">
|
<Info indent name="Backwards">
|
||||||
{tank.speed.backwards}
|
{tank.speed.backwards.toFixed(0)}
|
||||||
|
</Info>
|
||||||
|
<Info name="Power" unit="hp">
|
||||||
|
{engine.power}
|
||||||
</Info>
|
</Info>
|
||||||
<Info name="Power to weight ratio" unit="hp/mt" />
|
<Info name="Power to weight ratio" unit="hp/mt" />
|
||||||
<Info indent name="On hard terrain">
|
<Info indent name="On hard terrain">
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ import {
|
|||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import { use, useEffect, useMemo, useRef, useState } from 'react';
|
import { use, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { TankType, TreeType } from '../../../../../components/Tanks';
|
import { TankType, TreeType } from '../../../../../components/Tanks';
|
||||||
|
import { resolveNearPenetration } from '../../../../../core/blitz/resolveNearPenetration';
|
||||||
import { asset } from '../../../../../core/blitzkrieg/asset';
|
import { asset } from '../../../../../core/blitzkrieg/asset';
|
||||||
|
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
|
import { normalizeBoundingBox } from '../../../../../core/blitzkrieg/normalizeBoundingBox';
|
||||||
|
import { resolveDpm } from '../../../../../core/blitzkrieg/resolveDpm';
|
||||||
|
import { resolveReload } from '../../../../../core/blitzkrieg/resolveReload';
|
||||||
import {
|
import {
|
||||||
NATIONS,
|
NATIONS,
|
||||||
TANK_ICONS,
|
TANK_ICONS,
|
||||||
@@ -27,9 +32,9 @@ import {
|
|||||||
tanksDefinitionsArray,
|
tanksDefinitionsArray,
|
||||||
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
} from '../../../../../core/blitzkrieg/tankDefinitions';
|
||||||
import { tankIcon } from '../../../../../core/blitzkrieg/tankIcon';
|
import { tankIcon } from '../../../../../core/blitzkrieg/tankIcon';
|
||||||
|
import { unionBoundingBox } from '../../../../../core/blitzkrieg/unionBoundingBox';
|
||||||
import { theme } from '../../../../../stitches.config';
|
import { theme } from '../../../../../stitches.config';
|
||||||
import mutateTankopediaPersistent, {
|
import mutateTankopediaPersistent, {
|
||||||
TankopediaSortBy,
|
|
||||||
TankopediaSortDirection,
|
TankopediaSortDirection,
|
||||||
useTankopediaPersistent,
|
useTankopediaPersistent,
|
||||||
} from '../../../../../stores/tankopedia';
|
} from '../../../../../stores/tankopedia';
|
||||||
@@ -52,6 +57,7 @@ export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) {
|
|||||||
const filters = useTankopediaPersistent((state) => state.filters);
|
const filters = useTankopediaPersistent((state) => state.filters);
|
||||||
const sort = useTankopediaPersistent((state) => state.sort);
|
const sort = useTankopediaPersistent((state) => state.sort);
|
||||||
const awaitedTanks = use(tanksDefinitionsArray);
|
const awaitedTanks = use(tanksDefinitionsArray);
|
||||||
|
const awaitedModelDefinitions = use(modelDefinitions);
|
||||||
const defaultSortedTanks = useMemo(
|
const defaultSortedTanks = useMemo(
|
||||||
() =>
|
() =>
|
||||||
awaitedTanks
|
awaitedTanks
|
||||||
@@ -68,43 +74,296 @@ export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const input = useRef<HTMLInputElement>(null);
|
const input = useRef<HTMLInputElement>(null);
|
||||||
const searchableTanks = useMemo(
|
const searchableTanks = useMemo(() => {
|
||||||
() =>
|
const filtered = defaultSortedTanks.filter(
|
||||||
defaultSortedTanks
|
(tank) =>
|
||||||
.filter(
|
(filters.tiers.length === 0
|
||||||
(tank) =>
|
? true
|
||||||
(filters.tiers.length === 0
|
: filters.tiers.includes(tank.tier)) &&
|
||||||
? true
|
(filters.types.length === 0
|
||||||
: filters.tiers.includes(tank.tier)) &&
|
? true
|
||||||
(filters.types.length === 0
|
: filters.types.includes(tank.type)) &&
|
||||||
? true
|
(filters.treeTypes.length === 0
|
||||||
: filters.types.includes(tank.type)) &&
|
? true
|
||||||
(filters.treeTypes.length === 0
|
: filters.treeTypes.includes(tank.treeType)) &&
|
||||||
? true
|
(filters.nations.length === 0
|
||||||
: filters.treeTypes.includes(tank.treeType)) &&
|
? true
|
||||||
(filters.nations.length === 0
|
: filters.nations.includes(tank.nation)) &&
|
||||||
? true
|
(filters.test === 'include'
|
||||||
: filters.nations.includes(tank.nation)) &&
|
? true
|
||||||
(filters.test === 'include'
|
: filters.test === 'exclude'
|
||||||
? true
|
? !tank.testing
|
||||||
: filters.test === 'exclude'
|
: tank.testing),
|
||||||
? !tank.testing
|
);
|
||||||
: tank.testing),
|
let sorted: TankDefinition[] = [];
|
||||||
)
|
|
||||||
.sort((a, b) => {
|
|
||||||
let diff = 0;
|
|
||||||
|
|
||||||
if (sort.by === 'tier') {
|
// .sort((a, b) => {
|
||||||
diff = a.tier - b.tier;
|
// let diff = 0;
|
||||||
}
|
|
||||||
if (sort.by === 'name') {
|
|
||||||
diff = a.name.localeCompare(b.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sort.direction === 'ascending' ? diff : -diff;
|
// if (sort.by === 'meta.tier') {
|
||||||
}),
|
// diff = a.tier - b.tier;
|
||||||
[filters, sort],
|
// }
|
||||||
);
|
// if (sort.by === 'meta.name') {
|
||||||
|
// diff = a.name?.localeCompare(b.name);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return sort.direction === 'ascending' ? diff : -diff;
|
||||||
|
// });
|
||||||
|
|
||||||
|
switch (sort.by) {
|
||||||
|
case 'meta.tier':
|
||||||
|
sorted = filtered.sort((a, b) => a.tier - b.tier);
|
||||||
|
break;
|
||||||
|
|
||||||
|
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.size':
|
||||||
|
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 '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();
|
||||||
|
}, [filters, sort]);
|
||||||
const [searchResults, setSearchedList] = useState(searchableTanks);
|
const [searchResults, setSearchedList] = useState(searchableTanks);
|
||||||
const page = useTankopediaPersistent((state) => state.filters.page);
|
const page = useTankopediaPersistent((state) => state.filters.page);
|
||||||
const chunkSize =
|
const chunkSize =
|
||||||
@@ -118,6 +377,9 @@ export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) {
|
|||||||
const firstChunk = searchResultsPageSlice.slice(0, chunkSize);
|
const firstChunk = searchResultsPageSlice.slice(0, chunkSize);
|
||||||
const secondChunk = searchResultsPageSlice.slice(chunkSize);
|
const secondChunk = searchResultsPageSlice.slice(chunkSize);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSearchedList(searchableTanks);
|
||||||
|
}, [searchableTanks]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
mutateTankopediaPersistent((draft) => {
|
mutateTankopediaPersistent((draft) => {
|
||||||
draft.filters.page = Math.min(
|
draft.filters.page = Math.min(
|
||||||
@@ -166,21 +428,292 @@ export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) {
|
|||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
<DropdownMenu.Label>By</DropdownMenu.Label>
|
<DropdownMenu.Label>By</DropdownMenu.Label>
|
||||||
<DropdownMenu.RadioGroup
|
|
||||||
value={sort.by}
|
<DropdownMenu.Sub>
|
||||||
onValueChange={(value) =>
|
<DropdownMenu.SubTrigger>Meta</DropdownMenu.SubTrigger>
|
||||||
mutateTankopediaPersistent((draft) => {
|
<DropdownMenu.SubContent>
|
||||||
draft.sort.by = value as TankopediaSortBy;
|
<DropdownMenu.CheckboxItem
|
||||||
})
|
checked={sort.by === 'meta.tier'}
|
||||||
}
|
onClick={() => {
|
||||||
>
|
mutateTankopediaPersistent((draft) => {
|
||||||
<DropdownMenu.RadioItem value="tier">
|
draft.sort.by = 'meta.tier';
|
||||||
Tier
|
});
|
||||||
</DropdownMenu.RadioItem>
|
}}
|
||||||
<DropdownMenu.RadioItem value="name">
|
>
|
||||||
Name
|
Tier
|
||||||
</DropdownMenu.RadioItem>
|
</DropdownMenu.CheckboxItem>
|
||||||
</DropdownMenu.RadioGroup>
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'meta.name'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'meta.name';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
|
||||||
|
<DropdownMenu.Sub>
|
||||||
|
<DropdownMenu.SubTrigger>Survivability</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'survivability.health'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'survivability.health';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Health
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'survivability.viewRange'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'survivability.viewRange';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
View range
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'survivability.camouflageStill'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'survivability.camouflageStill';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Camouflage
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'survivability.camouflageMoving'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'survivability.camouflageMoving';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Camouflage moving
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'survivability.camouflageShooting'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'survivability.camouflageShooting';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Camouflage shooting
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'survivability.size'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'survivability.size';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Size
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
|
||||||
|
<DropdownMenu.Sub>
|
||||||
|
<DropdownMenu.SubTrigger>Fire</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.dpm'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.dpm';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
DPM
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.reload'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.reload';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reload
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.caliber'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.caliber';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Caliber
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.standardPenetration'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.standardPenetration';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Standard penetration
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.premiumPenetration'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.premiumPenetration';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Premium penetration
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.damage'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.damage';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Damage
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.shellVelocity'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.shellVelocity';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Shell velocity
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.aimTime'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.aimTime';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Aim time
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.dispersionStill'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.dispersionStill';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Dispersion still
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.dispersionMoving'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.dispersionMoving';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Dispersion moving
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.gunDepression'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.gunDepression';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Gun depression
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'fire.gunElevation'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'fire.gunElevation';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Gun elevation
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
|
||||||
|
<DropdownMenu.Sub>
|
||||||
|
<DropdownMenu.SubTrigger>
|
||||||
|
Maneuverability
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'maneuverability.forwardsSpeed'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'maneuverability.forwardsSpeed';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Forwards speed
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'maneuverability.backwardsSpeed'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'maneuverability.backwardsSpeed';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Backwards speed
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'maneuverability.power'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'maneuverability.power';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Power
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'maneuverability.powerToWeight'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'maneuverability.powerToWeight';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Power to weight
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'maneuverability.weight'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'maneuverability.weight';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Weight
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
checked={sort.by === 'maneuverability.traverseSpeed'}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaPersistent((draft) => {
|
||||||
|
draft.sort.by = 'maneuverability.traverseSpeed';
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Traverse speed
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
|
||||||
<DropdownMenu.Separator />
|
<DropdownMenu.Separator />
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { GunDefinition, ShellDefinition } from './tankDefinitions';
|
||||||
|
|
||||||
|
export function resolveDpm(gun: GunDefinition, shell: ShellDefinition) {
|
||||||
|
if (gun.type === 'regular') {
|
||||||
|
return (shell.damage.armor / gun.reload) * 60;
|
||||||
|
} else if (gun.type === 'autoLoader') {
|
||||||
|
return (
|
||||||
|
((shell.damage.armor * gun.count) /
|
||||||
|
(gun.reload + (gun.count - 1) * gun.interClip)) *
|
||||||
|
60
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
((shell.damage.armor * gun.count) /
|
||||||
|
(gun.reload.reduce((a, b) => a + b, 0) +
|
||||||
|
(gun.count - 1) * gun.interClip)) *
|
||||||
|
60
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { GunDefinition } from './tankDefinitions';
|
||||||
|
|
||||||
|
export function resolveReload(gun: GunDefinition) {
|
||||||
|
if (gun.type === 'regular') {
|
||||||
|
return gun.reload;
|
||||||
|
} else if (gun.type === 'autoLoader') {
|
||||||
|
return gun.reload + (gun.count - 1) * gun.interClip;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
gun.reload.reduce((a, b) => a + b, 0) + (gun.count - 1) * gun.interClip
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,33 @@ import {
|
|||||||
TurretDefinition,
|
TurretDefinition,
|
||||||
} from '../core/blitzkrieg/tankDefinitions';
|
} from '../core/blitzkrieg/tankDefinitions';
|
||||||
|
|
||||||
export type TankopediaSortBy = 'tier' | 'name';
|
export type TankopediaSortBy =
|
||||||
|
| 'meta.tier'
|
||||||
|
| 'meta.name'
|
||||||
|
| 'survivability.health'
|
||||||
|
| 'survivability.viewRange'
|
||||||
|
| 'survivability.camouflageStill'
|
||||||
|
| 'survivability.camouflageMoving'
|
||||||
|
| 'survivability.camouflageShooting'
|
||||||
|
| 'survivability.size'
|
||||||
|
| 'fire.dpm'
|
||||||
|
| 'fire.reload'
|
||||||
|
| 'fire.caliber'
|
||||||
|
| 'fire.standardPenetration'
|
||||||
|
| 'fire.premiumPenetration'
|
||||||
|
| 'fire.damage'
|
||||||
|
| 'fire.shellVelocity'
|
||||||
|
| 'fire.aimTime'
|
||||||
|
| 'fire.dispersionStill'
|
||||||
|
| 'fire.dispersionMoving'
|
||||||
|
| 'fire.gunDepression'
|
||||||
|
| 'fire.gunElevation'
|
||||||
|
| 'maneuverability.forwardsSpeed'
|
||||||
|
| 'maneuverability.backwardsSpeed'
|
||||||
|
| 'maneuverability.power'
|
||||||
|
| 'maneuverability.powerToWeight'
|
||||||
|
| 'maneuverability.weight'
|
||||||
|
| 'maneuverability.traverseSpeed';
|
||||||
export type TankopediaSortDirection = 'ascending' | 'descending';
|
export type TankopediaSortDirection = 'ascending' | 'descending';
|
||||||
export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only';
|
export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only';
|
||||||
export type TankopediaMode = 'model' | 'armor';
|
export type TankopediaMode = 'model' | 'armor';
|
||||||
@@ -116,7 +142,7 @@ export const useTankopediaPersistent = create<TankopediaPersistent>()(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
sort: {
|
sort: {
|
||||||
by: 'tier',
|
by: 'meta.tier',
|
||||||
direction: 'descending',
|
direction: 'descending',
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
|
|||||||
Reference in new issue
Block a user