feat: provisions

This commit is contained in:
TrèsAbhi committed 2025-12-31 14:27:48 -06:00
1 parent f885fba4bd
commit 718576d5a9
3 files changed
+135 -2

No files matched your search

+2 -1
View File
@@ -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
@@ -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<string, number>())
.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<string, number>())
.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() {
<GunTypeFilter />
<ShellFilter />
<ConsumablesFilter />
<ProvisionsFilter />
<AbilitiesFilter />
<PowersFilter />
<ClearButton />
</Flex>
@@ -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 (
<DropdownMenu.Root modal={false}>
<DropdownMenu.Trigger>
<Button color="gray" variant="surface">
<Flex>
{provisions.slice(0, MAX_PROVISIONS).map((provision, index) => (
<img
key={provision}
style={{
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
marginLeft: index > 0 ? "-0.5em" : undefined,
width: "1.25em",
height: "1.25em",
objectFit: "contain",
}}
src={asset(`icons/provisions/${provision}.webp`)}
/>
))}
{provisions.length > MAX_CONSUMABLES && (
<Text size="1" ml="1">
{literals(strings.common.units.plus, {
value: provisions.length - MAX_CONSUMABLES,
})}
</Text>
)}
</Flex>
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{provisionsArray.map((provision) => {
const selected = rawProvisions.includes(provision);
const provisionDefinition =
provisionDefinitions.provisions[provision];
return (
<DropdownMenu.CheckboxItem
onClick={(event) => {
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}
>
<img
style={{
width: "1.25em",
height: "1.25em",
objectFit: "contain",
}}
src={asset(`icons/provisions/${provision}.webp`)}
/>
{unwrap(provisionDefinition.name)}
</DropdownMenu.CheckboxItem>
);
})}
<DropdownMenu.Separator />
<DropdownMenu.Item
color="red"
onClick={(event) => {
event.preventDefault();
TankFilters.mutate((draft) => {
draft.provisions = [];
});
}}
>
<TrashIcon />
{strings.website.common.tank_search.clear}
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
);
}
function AbilitiesFilter() {
const { unwrap, strings } = useLocale();
const rawAbilities = TankFilters.use((state) => state.abilities);
@@ -22,6 +22,7 @@ export interface TankFilters {
gunType: CaseType<GunDefinition>[];
shells: [ShellType | null, ShellType | null, ShellType | null];
consumables: number[];
provisions: number[];
abilities: number[];
showTesting: boolean;
@@ -40,6 +41,7 @@ export const TankFilters = new Varuna<TankFilters>({
gunType: [],
shells: [null, null, null],
consumables: [],
provisions: [],
abilities: [],
showTesting: true,