diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 94def8954..4ecf9071a 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -762,6 +762,8 @@ "sorting_by": "Sorting by {name}", "clear": "Clear", "tier": "Tier {tier}", + "passive": "{name} (Passive)", + "active": "{name} (Active)", "no_tanks_found": { "body": "No tanks found.", @@ -928,7 +930,10 @@ "description": "Reveals the tank without showing the name. This round will be considered incorrect and break your streak!", "cancel": "Cancel", "reveal": "Reveal" - } + }, + "clear": "Clear", + "select_all": "Select all", + "tier": "Tier {tier}" }, "tankopedia": { diff --git a/packages/website/src/components/Guesser.tsx b/packages/website/src/components/Guesser.tsx index ce512cffb..5c3b1d0a3 100644 --- a/packages/website/src/components/Guesser.tsx +++ b/packages/website/src/components/Guesser.tsx @@ -9,13 +9,17 @@ import { ArrowRightIcon, EyeOpenIcon, MagnifyingGlassIcon, + MixerVerticalIcon, PaperPlaneIcon, + StarFilledIcon, + TrashIcon, } from "@radix-ui/react-icons"; import { AlertDialog, Box, Button, Card, + DropdownMenu, Flex, IconButton, Spinner, @@ -23,7 +27,7 @@ import { TextField, } from "@radix-ui/themes"; import fuzzysort from "fuzzysort"; -import { debounce } from "lodash-es"; +import { debounce, times } from "lodash-es"; import { useCallback, useEffect, useRef, useState } from "react"; import { awaitableTankDefinitions } from "../core/awaitables/tankDefinitions"; import { awaitableTankNames } from "../core/awaitables/tankNames"; @@ -53,6 +57,7 @@ export function Guesser() { const [searching, setSearching] = useState(false); const [results, setResults] = useState(null); const [selected, setSelected] = useState(null); + const tiers = Guess.use((state) => state.tiers); const requestSearch = useCallback(() => { setSelected(null); @@ -97,58 +102,60 @@ export function Guesser() { width="100%" p="4" maxWidth="25rem" - gap="4" + gap="3" > - {results !== null && ( - - - {results.length === 0 && ( - - - {strings.website.tools.guess.search.no_results} - - - )} + + {results !== null && ( + + + {results.length === 0 && ( + + + {strings.website.tools.guess.search.no_results} + + + )} - - {results.map((result) => { - const Icon = classIcons[result.class]; + + {results.map((result) => { + const Icon = classIcons[result.class]; - return ( - { - if (!input.current) return; + return ( + { + if (!input.current) return; - input.current.value = unwrap(result.name); - setSelected(result); - setResults(null); - }} - text={unwrap(result.name)} - prefix={ - - } - discriminator={ - - - {TIER_ROMAN_NUMERALS[result.tier]} - - } - /> - ); - })} - - - - )} + input.current.value = unwrap(result.name); + setSelected(result); + setResults(null); + }} + text={unwrap(result.name)} + prefix={ + + } + discriminator={ + + + {TIER_ROMAN_NUMERALS[result.tier]} + + } + /> + ); + })} + + + + )} + @@ -161,11 +168,78 @@ export function Guesser() { + + + + + + + + + {times(10, (index) => { + const tier = 10 - index; + const isSelected = tiers.includes(tier); + + return ( + { + event.preventDefault(); + + Guess.mutate((draft) => { + if (isSelected) { + if (draft.tiers.length === 1) return; + draft.tiers = draft.tiers.filter((t) => t !== tier); + } else { + draft.tiers = [...draft.tiers, tier]; + } + + draft.streak = 0; + }); + }} + > + {literals(strings.website.tools.guess.tier, { + tier: TIER_ROMAN_NUMERALS[tier], + })} + + ); + })} + + + + { + event.preventDefault(); + + Guess.mutate((draft) => { + draft.tiers = Guess.initial.tiers; + }); + }} + > + {strings.website.tools.guess.select_all} + + + { + event.preventDefault(); + + Guess.mutate((draft) => { + draft.tiers = []; + }); + }} + > + {strings.website.tools.guess.clear} + + + + { + const tank = tankDefinitions.tanks[Number(id)]; + return tiers.includes(tank.tier); + }); + const id = Number( + filteredIds[Math.floor(Math.random() * filteredIds.length)] + ); const tank = tankDefinitions.tanks[id]; Guess.mutate((draft) => { diff --git a/packages/website/src/components/TankSearch/components/FilterControl.tsx b/packages/website/src/components/TankSearch/components/FilterControl.tsx index fc51ff253..3991366a1 100644 --- a/packages/website/src/components/TankSearch/components/FilterControl.tsx +++ b/packages/website/src/components/TankSearch/components/FilterControl.tsx @@ -155,7 +155,7 @@ const TIERS = times(10, (i) => 10 - i); export function FilterControl() { return ( - + @@ -168,8 +168,7 @@ export function FilterControl() { - - + @@ -1031,22 +1030,33 @@ function ProvisionsFilter() { ); } -function PowersFilter() { +function GameModeAbilitiesFilter() { const { unwrap, strings } = useLocale(); + const rawAbilities = TankFilters.use((state) => state.abilities); const rawPowers = TankFilters.use((state) => state.powers); + + const abilities = + rawAbilities.length === 0 && rawPowers.length === 0 + ? Array.from(allGameModeConsumables.values()) + : rawAbilities; const powers = - rawPowers.length === 0 + rawPowers.length === 0 && rawAbilities.length === 0 ? Array.from(allGameModeProvisions.values()) : rawPowers; + const icons = [ + ...abilities.map((ability) => `icons/consumables/${ability}.webp`), + ...powers.map((power) => `icons/provisions/${power}.webp`), + ]; + return ( - - - - {gameModeRoles.map(({ gameModeId, consumables }) => { - const gameMode = gameDefinitions.gameModes[gameModeId]; - - if (consumables.length === 0) return null; - - return ( - - {unwrap(gameMode.name)} - - {consumables.map((consumable) => { - { - const selected = rawAbilities.includes(consumable); - const abilityDefinition = - consumableDefinitions.consumables[consumable]; - - return ( - { - event.preventDefault(); - - TankFilters.mutate((draft) => { - if (selected) { - draft.abilities = draft.abilities.filter( - (n) => n !== consumable - ); - } else { - draft.abilities = [...draft.abilities, consumable]; - } - }); - }} - checked={selected} - key={consumable} - > - - - {unwrap(abilityDefinition.name)} - - ); - } - })} - - ); - })} - - - - { - event.preventDefault(); - - TankFilters.mutate((draft) => { - draft.abilities = []; - }); - }} - > - - {strings.website.common.tank_search.clear} - - - - ); -} diff --git a/packages/website/src/pages/[...locale]/guess/_index.tsx b/packages/website/src/pages/[...locale]/guess/_index.tsx index 7282e7bbc..d172884ff 100644 --- a/packages/website/src/pages/[...locale]/guess/_index.tsx +++ b/packages/website/src/pages/[...locale]/guess/_index.tsx @@ -1,5 +1,5 @@ import { Box, Flex, Heading } from "@radix-ui/themes"; -import { Suspense } from "react"; +import { Suspense, useEffect } from "react"; import { GuessBackground } from "../../../components/GuessBackground"; import { Guesser } from "../../../components/Guesser"; import { GuessRenderer } from "../../../components/GuessRenderer"; @@ -62,6 +62,31 @@ function Content({ skeleton }: MaybeSkeletonComponentProps) { const name = unwrap(tank.name); const fontSize = `min(48vh, ${55 / name.length}vw)`; const transitionDuration = isRevealed ? "2s" : undefined; + const tiers = Guess.use((state) => state.tiers); + + useEffect(() => { + const isTankAlreadyCompliant = tiers.includes(tank.tier); + + if (isTankAlreadyCompliant) return; + + const filteredIds = ids.filter((id) => { + const tank = tankDefinitions.tanks[Number(id)]; + return tiers.includes(tank.tier); + }); + + if (filteredIds.length === 0) return; + + const id = Number( + filteredIds[Math.floor(Math.random() * filteredIds.length)] + ); + const newTank = tankDefinitions.tanks[id]; + + Guess.mutate((draft) => { + draft.tank = newTank; + draft.guessState = GuessState.NotGuessed; + draft.helpingReveal = false; + }); + }, [tiers]); return ( diff --git a/packages/website/src/stores/guess.ts b/packages/website/src/stores/guess.ts index 22d6a10f0..0a3e94f49 100644 --- a/packages/website/src/stores/guess.ts +++ b/packages/website/src/stores/guess.ts @@ -1,4 +1,5 @@ import type { TankDefinition } from "@blitzkit/core"; +import { times } from "lodash-es"; import { Varuna } from "varuna"; export enum GuessState { @@ -14,6 +15,7 @@ export interface Guess { correctGuesses: number; streak: number; helpingReveal: boolean; + tiers: number[]; } export const Guess = new Varuna((tank) => ({ @@ -23,4 +25,5 @@ export const Guess = new Varuna((tank) => ({ correctGuesses: 0, streak: 0, helpingReveal: false, + tiers: times(10, (index) => index + 1), }));