diff --git a/scripts/buildAssets/definitions.ts b/scripts/buildAssets/definitions.ts index 41a7c5f08..1dbd313a1 100644 --- a/scripts/buildAssets/definitions.ts +++ b/scripts/buildAssets/definitions.ts @@ -34,7 +34,6 @@ import { import { DATA, POI } from './constants'; import { TankParameters } from './tankIcons'; -type BlitzTankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank'; interface Strings { [key: string]: string; } @@ -306,19 +305,6 @@ type ConsumablesVehicleFilter = | { name: string } | { extendedTags: string }; -const consumableProvisionEffectSuffixes = [ - 'Increase', - 'Factor', - 'Bias', - 'Coef', - 'Percent', -]; -const blitzTankTypeToBlitzkrieg: Record = { - 'AT-SPG': 'tankDestroyer', - lightTank: 'light', - mediumTank: 'medium', - heavyTank: 'heavy', -}; const blitzShellKindToBLitzkrieg: Record = { ARMOR_PIERCING: 'ap', ARMOR_PIERCING_CR: 'ap_cr', @@ -502,7 +488,7 @@ export async function definitions(production: boolean) { ? 'premium' : 'researchable', tier: tank.level as Tier, - type: blitzTankTypeToBlitzkrieg[tankTags[0] as BlitzTankType], + type: tankTags[0] as TankType, testing: tankTags.includes('testTank') ? true : undefined, price: tankPrice, camouflage: { diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx index b7d4d3e17..dae69588d 100644 --- a/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx @@ -136,12 +136,12 @@ export function Characteristics() { ]); const enginePowerCoefficient = coefficient( [ - hasEngineAccelerator && (tank.type === 'light' || tank.type === 'medium'), + hasEngineAccelerator && (tank.type === 'lightTank' || tank.type === 'mediumTank'), 0.05, ], [ hasEngineAccelerator && - (tank.type === 'heavy' || tank.type === 'tankDestroyer'), + (tank.type === 'heavyTank' || tank.type === 'AT-SPG'), 0.07, ], [hasEnginePowerBoost, 0.2], @@ -163,7 +163,7 @@ export function Characteristics() { const viewRangeCoefficient = coefficient([ hasImprovedOptics, - tank.type === 'tankDestroyer' ? 0.05 : tank.type === 'heavy' ? 0.07 : 0.1, + tank.type === 'AT-SPG' ? 0.05 : tank.type === 'heavyTank' ? 0.07 : 0.1, ]) * coefficient([true, progressiveStat(commanderMastery)]); const fireChanceCoefficient = coefficient([hasProtectiveKit, -0.2]); @@ -180,17 +180,17 @@ export function Characteristics() { const camouflageSumMoving = sum( [ hasCamouflageNet, - tank.type === 'heavy' + tank.type === 'heavyTank' ? 0.03 - : tank.type === 'tankDestroyer' + : tank.type === 'AT-SPG' ? 0.07 : 0.05, ], [ camouflage, - tank.type === 'tankDestroyer' + tank.type === 'AT-SPG' ? 0.04 - : tank.type === 'heavy' + : tank.type === 'heavyTank' ? 0.03 : 0.02, ], @@ -199,17 +199,17 @@ export function Characteristics() { [ hasCamouflageNet, 2 * - (tank.type === 'heavy' + (tank.type === 'heavyTank' ? 0.03 - : tank.type === 'tankDestroyer' + : tank.type === 'AT-SPG' ? 0.07 : 0.05), ], [ camouflage, - tank.type === 'tankDestroyer' + tank.type === 'AT-SPG' ? 0.04 - : tank.type === 'heavy' + : tank.type === 'heavyTank' ? 0.03 : 0.02, ], diff --git a/src/app/tools/tankopedia/components/TankSearch/index.tsx b/src/app/tools/tankopedia/components/TankSearch/index.tsx index 41a6f3a7d..6c5d00a10 100644 --- a/src/app/tools/tankopedia/components/TankSearch/index.tsx +++ b/src/app/tools/tankopedia/components/TankSearch/index.tsx @@ -15,7 +15,7 @@ import { } from '@radix-ui/themes'; import { go } from 'fuzzysort'; import { use, useEffect, useMemo, useRef, useState } from 'react'; -import { TankType, TreeType } from '../../../../../components/Tanks'; +import { TANK_TYPES, TreeType } from '../../../../../components/Tanks'; import { resolveNearPenetration } from '../../../../../core/blitz/resolveNearPenetration'; import { asset } from '../../../../../core/blitzkrieg/asset'; import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions'; @@ -49,7 +49,6 @@ interface TankSearchProps { onSelect?: (tank: TankDefinition) => void; } -const tankTypeOrder: TankType[] = ['light', 'medium', 'heavy', 'tankDestroyer']; const treeTypeOrder: TreeType[] = ['researchable', 'premium', 'collector']; export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) { @@ -70,7 +69,7 @@ export function TankSearch({ compact, onSelect = () => {} }: TankSearchProps) { ) .sort( (a, b) => - (tankTypeOrder.indexOf(a.type) - tankTypeOrder.indexOf(b.type)) * + (TANK_TYPES.indexOf(a.type) - TANK_TYPES.indexOf(b.type)) * (sort.direction === 'descending' ? -1 : 1), ) .sort( diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 9fa3129c4..df7d870d9 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -1,4 +1,4 @@ -import { Locale, SlashCommandBuilder } from 'discord.js'; +import { Locale } from 'discord.js'; import CommandWrapper from '../components/CommandWrapper'; import GenericAllStats from '../components/GenericAllStats'; import NoData from '../components/NoData'; @@ -16,6 +16,7 @@ import autocompleteTanks from '../core/discord/autocompleteTanks'; import autocompleteUsername from '../core/discord/autocompleteUsername'; import { buttonRefresh } from '../core/discord/buttonRefresh'; import commandToURL from '../core/discord/commandToURL'; +import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams'; import { getFiltersFromButton } from '../core/discord/getFiltersFromButton'; import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand'; @@ -73,18 +74,14 @@ async function render( export const fullStatsCommand = new Promise( async (resolve) => { - const command = await addPeriodicFilterOptions( - new SlashCommandBuilder() - .setName('full-stats') - .setDescription('Full in-game statistics'), - (option) => option.addStringOption(addUsernameChoices), - ); - resolve({ inProduction: true, inPublic: true, - command, + command: await addPeriodicFilterOptions( + createLocalizedCommand('full-stats'), + (option) => option.addStringOption(addUsernameChoices), + ), async handler(interaction) { const player = await resolvePlayerFromCommand(interaction); diff --git a/src/commands/today.ts b/src/commands/today.ts index 6e5e27b91..26ee17f9e 100644 --- a/src/commands/today.ts +++ b/src/commands/today.ts @@ -1,5 +1,5 @@ import { SlashCommandBuilder } from 'discord.js'; -import { encyclopediaInfo } from '../core/blitz/encyclopediaInfo'; +import { NATIONS } from '../core/blitzkrieg/tankDefinitions'; import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; import { addFilterOptions } from '../core/discord/addFilterOptions'; import addUsernameChoices from '../core/discord/addUsernameChoices'; @@ -18,12 +18,12 @@ import { CommandRegistry } from '../events/interactionCreate'; import { renderBreakdown } from './breakdown'; export const todayCommand = new Promise(async (resolve) => { - const awaitedEncyclopediaInfo = await encyclopediaInfo; + const nations = await NATIONS; const command = addFilterOptions( new SlashCommandBuilder() .setName('today') .setDescription("Today's played tanks and statistics"), - awaitedEncyclopediaInfo, + nations, ).addStringOption(addUsernameChoices); resolve({ diff --git a/src/components/Tanks/components/Item.tsx b/src/components/Tanks/components/Item.tsx index 870abd89a..886493113 100644 --- a/src/components/Tanks/components/Item.tsx +++ b/src/components/Tanks/components/Item.tsx @@ -6,10 +6,10 @@ import { import { theme } from '../../../stitches.config'; export const TANK_TYPES = [ - 'light', - 'medium', - 'heavy', - 'tankDestroyer', + 'lightTank', + 'mediumTank', + 'heavyTank', + 'AT-SPG', ] as const; export const TREE_TYPES = ['researchable', 'premium', 'collector'] as const; diff --git a/src/core/blitzkrieg/tankDefinitions.ts b/src/core/blitzkrieg/tankDefinitions.ts index 3cadb52a7..fd3d08fb6 100644 --- a/src/core/blitzkrieg/tankDefinitions.ts +++ b/src/core/blitzkrieg/tankDefinitions.ts @@ -181,24 +181,24 @@ export const tankNames = tanksDefinitionsArray.then((tanks) => ); export const TANK_ICONS: Record = { - tankDestroyer: 'https://i.imgur.com/BIHSEH0.png', - light: 'https://i.imgur.com/CSNha5V.png', - medium: 'https://i.imgur.com/wvf3ltm.png', - heavy: 'https://i.imgur.com/ECeqlZa.png', + 'AT-SPG': 'https://i.imgur.com/BIHSEH0.png', + lightTank: 'https://i.imgur.com/CSNha5V.png', + mediumTank: 'https://i.imgur.com/wvf3ltm.png', + heavyTank: 'https://i.imgur.com/ECeqlZa.png', }; export const TANK_ICONS_PREMIUM: Record = { - tankDestroyer: 'https://i.imgur.com/TCu3EdR.png', - light: 'https://i.imgur.com/zdkpTRb.png', - medium: 'https://i.imgur.com/3z7eHX6.png', - heavy: 'https://i.imgur.com/P3vbmyA.png', + 'AT-SPG': 'https://i.imgur.com/TCu3EdR.png', + lightTank: 'https://i.imgur.com/zdkpTRb.png', + mediumTank: 'https://i.imgur.com/3z7eHX6.png', + heavyTank: 'https://i.imgur.com/P3vbmyA.png', }; export const TANK_ICONS_COLLECTOR: Record = { - tankDestroyer: 'https://i.imgur.com/WTjeirB.png', - light: 'https://i.imgur.com/EwhtKkU.png', - medium: 'https://i.imgur.com/u8YDMBh.png', - heavy: 'https://i.imgur.com/8xRf3nc.png', + 'AT-SPG': 'https://i.imgur.com/WTjeirB.png', + lightTank: 'https://i.imgur.com/EwhtKkU.png', + mediumTank: 'https://i.imgur.com/u8YDMBh.png', + heavyTank: 'https://i.imgur.com/8xRf3nc.png', }; export const TIERS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const; diff --git a/src/core/discord/addFilterOptions.ts b/src/core/discord/addFilterOptions.ts index 5523016c9..7e626a232 100644 --- a/src/core/discord/addFilterOptions.ts +++ b/src/core/discord/addFilterOptions.ts @@ -1,60 +1,108 @@ -import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js'; -import { TREE_TYPE_NAMES, TreeType } from '../../components/Tanks'; -import { EncyclopediaInfo } from '../blitz/encyclopediaInfo'; +import { + APIApplicationCommandOptionChoice, + Locale, + SlashCommandBuilder, + SlashCommandSubcommandBuilder, +} from 'discord.js'; +import { TANK_TYPES, TREE_TYPES } from '../../components/Tanks'; +import { translator } from '../localization/translator'; import addTankChoices from './addTankChoices'; import addTierChoices from './addTierChoices'; +import { localizationObject } from './localizationObject'; export function addFilterOptions< Option extends SlashCommandBuilder | SlashCommandSubcommandBuilder, ->(option: Option, encyclopediaInfo: EncyclopediaInfo) { - return option - .addStringOption((option) => - option - .setName('nation') - .setDescription('Nation') - .addChoices( - ...Object.entries(encyclopediaInfo.vehicle_nations).map( - ([type, name]) => ({ name, value: type }), - ), - ) - .setRequired(false), - ) - .addStringOption((option) => addTierChoices(option).setRequired(false)) - .addStringOption((option) => - option - .setName('tank-type') - .setDescription('Tank type') - .addChoices( - ...Object.entries(encyclopediaInfo.vehicle_types).map( - ([type, name]) => ({ - name: name.replace(' Tank', ''), - value: type, - }), - ), - ) - .setRequired(false), - ) - .addStringOption((option) => - option - .setName('tree-type') - .setDescription('Tech tree type') - .addChoices( - { - name: TREE_TYPE_NAMES['researchable'], - value: 'researchable' satisfies TreeType, - }, - { - name: TREE_TYPE_NAMES.premium, - value: 'premium' satisfies TreeType, - }, - { - name: TREE_TYPE_NAMES.collector, - value: 'collector' satisfies TreeType, - }, - ) - .setRequired(false), - ) - .addStringOption((option) => - addTankChoices(option).setRequired(false), - ) as Option; +>(option: Option, nations: string[]) { + const { translate } = translator(Locale.EnglishUS); + + return ( + option + .addStringOption((option) => + option + .setName(translate('bot.common.options.filters.nation')) + .setNameLocalizations( + localizationObject('bot.common.options.filters.nation'), + ) + .setDescription( + translate('bot.common.options.filters.nation.description'), + ) + .setDescriptionLocalizations( + localizationObject('bot.common.options.filters.nation.description'), + ) + .addChoices( + ...nations.map( + (nation) => + ({ + value: nation, + name: translate(`common.nations.${nation}`), + name_localizations: localizationObject( + `common.nations.${nation}`, + ), + }) satisfies APIApplicationCommandOptionChoice, + ), + ) + .setRequired(false), + ) + .addStringOption((option) => addTierChoices(option).setRequired(false)) + .addStringOption((option) => + option + .setName(translate('bot.common.options.filters.tank_type')) + .setNameLocalizations( + localizationObject('bot.common.options.filters.tank_type'), + ) + .setDescription( + translate('bot.common.options.filters.tank_type.description'), + ) + .setDescriptionLocalizations( + localizationObject( + 'bot.common.options.filters.tank_type.description', + ), + ) + .addChoices( + ...TANK_TYPES.map( + (tankType) => + ({ + value: tankType, + name: translate(`common.tank_type_short.${tankType}`), + name_localizations: localizationObject( + `common.tank_type_short.${tankType}`, + ), + }) satisfies APIApplicationCommandOptionChoice, + ), + ) + .setRequired(false), + ) + .addStringOption((option) => + option + .setName(translate('bot.common.options.filters.tree_type')) + .setNameLocalizations( + localizationObject('bot.common.options.filters.tree_type'), + ) + .setDescription( + translate('bot.common.options.filters.tree_type.description'), + ) + .setDescriptionLocalizations( + localizationObject( + 'bot.common.options.filters.tree_type.description', + ), + ) + .addChoices( + ...TREE_TYPES.map( + (treeType) => + ({ + value: treeType, + name: translate(`common.tree_type.${treeType}`), + name_localizations: localizationObject( + `common.tree_type.${treeType}`, + ), + }) satisfies APIApplicationCommandOptionChoice, + ), + ) + .setRequired(false), + ) + // TODO: somehow localize this? + .addStringOption((option) => + addTankChoices(option).setRequired(false), + ) as Option + ); } diff --git a/src/core/discord/addPeriodicFilterOptions.ts b/src/core/discord/addPeriodicFilterOptions.ts index 48188b0cb..e5a9d7f18 100644 --- a/src/core/discord/addPeriodicFilterOptions.ts +++ b/src/core/discord/addPeriodicFilterOptions.ts @@ -3,7 +3,7 @@ import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, } from 'discord.js'; -import { encyclopediaInfo } from '../blitz/encyclopediaInfo'; +import { NATIONS } from '../blitzkrieg/tankDefinitions'; import { addFilterOptions } from './addFilterOptions'; import { addPeriodSubCommands } from './addPeriodSubCommands'; @@ -15,10 +15,10 @@ export default async function addPeriodicFilterOptions< option: SlashCommandSubcommandBuilder, ) => SlashCommandSubcommandBuilder = (option) => option, ): Promise { - const awaitedEncyclopediaInfo = await encyclopediaInfo; - addPeriodSubCommands(option, (option) => { - return addFilterOptions(extra(option), awaitedEncyclopediaInfo); - }); + const nations = await NATIONS; + addPeriodSubCommands(option, (option) => + addFilterOptions(extra(option), nations), + ); return option; } diff --git a/src/core/discord/addTankChoices.ts b/src/core/discord/addTankChoices.ts index 07b8d0e1a..b224fc2a9 100644 --- a/src/core/discord/addTankChoices.ts +++ b/src/core/discord/addTankChoices.ts @@ -1,9 +1,17 @@ -import { SlashCommandStringOption } from 'discord.js'; +import { Locale, SlashCommandStringOption } from 'discord.js'; +import { translator } from '../localization/translator'; +import { localizationObject } from './localizationObject'; export default function addTankChoices(option: SlashCommandStringOption) { + const { translate } = translator(Locale.EnglishUS); + return option - .setName('tank') - .setDescription('The tank to get stats for') + .setName(translate('bot.common.options.tank')) + .setNameLocalizations(localizationObject('bot.common.options.tank')) + .setDescription(translate('bot.common.options.tank.description')) + .setDescriptionLocalizations( + localizationObject('bot.common.options.tank.description'), + ) .setAutocomplete(true) .setRequired(true); } diff --git a/src/core/discord/createLocalizedCommand.ts b/src/core/discord/createLocalizedCommand.ts index dbadcd776..d77866755 100644 --- a/src/core/discord/createLocalizedCommand.ts +++ b/src/core/discord/createLocalizedCommand.ts @@ -23,23 +23,24 @@ export function createLocalizedCommand( | CreateLocalizedCommandSubcommand[] | CreateLocalizedCommandSubcommandGroup[], ) { + const commandPathItem = command.replaceAll('-', '_'); const { translate } = translator(Locale.EnglishUS); const slashCommand = new SlashCommandBuilder() .setName(command) - .setNameLocalizations(localizationObject(`bot.commands.${command}`)) - .setDescription(translate(`bot.commands.${command}.description`)) + .setNameLocalizations(localizationObject(`bot.commands.${commandPathItem}`)) + .setDescription(translate(`bot.commands.${commandPathItem}.description`)) .setDescriptionLocalizations( - localizationObject(`bot.commands.${command}.description`), + localizationObject(`bot.commands.${commandPathItem}.description`), ); if (sub) { sub.map((subItem) => { if ('subcommand' in subItem) { const subcommandNameLocalizations = localizationObject( - `bot.commands.${command}.subcommands.${subItem.subcommand}`, + `bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`, ); const subcommandDescriptionLocalizations = localizationObject( - `bot.commands.${command}.subcommands.${subItem.subcommand}.description`, + `bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`, ); slashCommand.addSubcommand((option) => { @@ -50,17 +51,17 @@ export function createLocalizedCommand( .setNameLocalizations(subcommandNameLocalizations) .setDescription( translate( - `bot.commands.${command}.subcommands.${subItem.subcommand}.description`, + `bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`, ), ) .setDescriptionLocalizations(subcommandDescriptionLocalizations); }); } else { const subcommandNameLocalizations = localizationObject( - `bot.commands.${command}.groups.${subItem.group}`, + `bot.commands.${commandPathItem}.groups.${subItem.group}`, ); const subcommandDescriptionLocalizations = localizationObject( - `bot.commands.${command}.groups.${subItem.group}.description`, + `bot.commands.${commandPathItem}.groups.${subItem.group}.description`, ); slashCommand.addSubcommandGroup((option) => { @@ -71,7 +72,7 @@ export function createLocalizedCommand( .setNameLocalizations(subcommandNameLocalizations) .setDescription( translate( - `bot.commands.${command}.groups.${subItem.group}.description`, + `bot.commands.${commandPathItem}.groups.${subItem.group}.description`, ), ) .setDescriptionLocalizations(subcommandDescriptionLocalizations); diff --git a/src/lang/en-US.json b/src/lang/en-US.json index 959c5812c..2575122f6 100644 --- a/src/lang/en-US.json +++ b/src/lang/en-US.json @@ -84,6 +84,26 @@ "clan": { "$": "clan", "description": "The clan name or tag" + }, + + "tank": { + "$": "tank", + "description": "The tank to get stats for" + }, + + "filters": { + "nation": { + "$": "nation", + "description": "Nation" + }, + "tank_type": { + "$": "tank-type", + "description": "Tank type" + }, + "tree_type": { + "$": "tree-type", + "description": "Tree type" + } } },