From 718576d5a99e11a7504ce4f0e87459ab44d24cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Wed, 31 Dec 2025 14:27:48 -0600 Subject: [PATCH] feat: provisions --- docs/changelogs/1.29.9.md | 3 +- .../TankSearch/components/FilterControl.tsx | 132 +++++++++++++++++- packages/website/src/stores/tankFilters.ts | 2 + 3 files changed, 135 insertions(+), 2 deletions(-) diff --git a/docs/changelogs/1.29.9.md b/docs/changelogs/1.29.9.md index 6984cba3d..26740b924 100644 --- a/docs/changelogs/1.29.9.md +++ b/docs/changelogs/1.29.9.md @@ -4,7 +4,8 @@ Thank you to [Mark Greene](https://github.com/Hitjack007) for your contributions ## Features -- [Mark Greene] Added the ability to filter tanks by Consumables and game-mode Abilities +- [Mark Greene] Added the ability to filter tanks by consumables +- Also added the ability to filter tanks by game-mode abilities and provisions ## Fixes diff --git a/packages/website/src/components/TankSearch/components/FilterControl.tsx b/packages/website/src/components/TankSearch/components/FilterControl.tsx index 228156b9e..0b394c6dd 100644 --- a/packages/website/src/components/TankSearch/components/FilterControl.tsx +++ b/packages/website/src/components/TankSearch/components/FilterControl.tsx @@ -28,6 +28,7 @@ import { times } from "lodash-es"; import type { ComponentProps, ReactNode } from "react"; import { awaitableConsumableDefinitions } from "../../../core/awaitables/consumableDefinitions"; import { awaitableGameDefinitions } from "../../../core/awaitables/gameDefinitions"; +import { awaitableProvisionDefinitions } from "../../../core/awaitables/provisionDefinitions"; import { useLocale } from "../../../hooks/useLocale"; import { App } from "../../../stores/app"; import { TankFilters, type CaseType } from "../../../stores/tankFilters"; @@ -42,6 +43,7 @@ import { ScienceOffIcon } from "../../ScienceOffIcon"; const gameDefinitions = await awaitableGameDefinitions; const consumableDefinitions = await awaitableConsumableDefinitions; +const provisionDefinitions = await awaitableProvisionDefinitions; const consumablesArray = Object.values(consumableDefinitions.consumables) .filter( @@ -52,7 +54,38 @@ const consumablesArray = Object.values(consumableDefinitions.consumables) .map((consumable) => consumable.id); const abilitiesArray = Array.from( Object.values(consumableDefinitions.consumables) - .filter((c) => c.game_mode_exclusive) + .filter((ability) => ability.game_mode_exclusive) + .reduce((uniqueMap, consumable) => { + if (!uniqueMap.has(consumable.name.locales[locales.default])) { + uniqueMap.set(consumable.name.locales[locales.default], consumable.id); + } + + return uniqueMap; + }, new Map()) + .values() +); +const provisionsArray = Array.from( + Object.values(provisionDefinitions.provisions) + .filter( + (provision) => + !provision.game_mode_exclusive && + provision.name.locales[locales.default] + ) + .reduce((uniqueMap, consumable) => { + if (!uniqueMap.has(consumable.name.locales[locales.default])) { + uniqueMap.set(consumable.name.locales[locales.default], consumable.id); + } + + return uniqueMap; + }, new Map()) + .values() +); +const powersArray = Array.from( + Object.values(provisionDefinitions.provisions) + .filter( + (provision) => + provision.game_mode_exclusive && provision.name.locales[locales.default] + ) .reduce((uniqueMap, consumable) => { if (!uniqueMap.has(consumable.name.locales[locales.default])) { uniqueMap.set(consumable.name.locales[locales.default], consumable.id); @@ -91,6 +124,7 @@ const GUN_TYPES = Object.keys( const MAX_TIERS = 4; const MAX_CONSUMABLES = 4; +const MAX_PROVISIONS = 4; const MAX_ABILITIES = 4; const TIERS = times(10, (i) => 10 - i); @@ -109,7 +143,9 @@ export function FilterControl() { + + @@ -881,6 +917,100 @@ function ConsumablesFilter() { ); } +function ProvisionsFilter() { + const { unwrap, strings } = useLocale(); + const rawProvisions = TankFilters.use((state) => state.provisions); + const provisions = + rawProvisions.length === 0 ? provisionsArray : rawProvisions; + + return ( + + + + + + + {provisionsArray.map((provision) => { + const selected = rawProvisions.includes(provision); + const provisionDefinition = + provisionDefinitions.provisions[provision]; + + return ( + { + event.preventDefault(); + + TankFilters.mutate((draft) => { + if (selected) { + draft.provisions = draft.provisions.filter( + (n) => n !== provision + ); + } else { + draft.provisions = [...draft.provisions, provision]; + } + }); + }} + checked={selected} + key={provision} + > + + + {unwrap(provisionDefinition.name)} + + ); + })} + + + + { + event.preventDefault(); + + TankFilters.mutate((draft) => { + draft.provisions = []; + }); + }} + > + + {strings.website.common.tank_search.clear} + + + + ); +} + function AbilitiesFilter() { const { unwrap, strings } = useLocale(); const rawAbilities = TankFilters.use((state) => state.abilities); diff --git a/packages/website/src/stores/tankFilters.ts b/packages/website/src/stores/tankFilters.ts index 7b23af850..76475c1d1 100644 --- a/packages/website/src/stores/tankFilters.ts +++ b/packages/website/src/stores/tankFilters.ts @@ -22,6 +22,7 @@ export interface TankFilters { gunType: CaseType[]; shells: [ShellType | null, ShellType | null, ShellType | null]; consumables: number[]; + provisions: number[]; abilities: number[]; showTesting: boolean; @@ -40,6 +41,7 @@ export const TankFilters = new Varuna({ gunType: [], shells: [null, null, null], consumables: [], + provisions: [], abilities: [], showTesting: true,