mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: localize game assets
This commit is contained in:
1 parent
d8459d373c
commit
ef9778ea96
90 files changed
+336
-251
No files matched your search
Vendored
+1
@@ -189,6 +189,7 @@
|
|||||||
"unpickled",
|
"unpickled",
|
||||||
"Unregistering",
|
"Unregistering",
|
||||||
"unrotate",
|
"unrotate",
|
||||||
|
"unwrapper",
|
||||||
"vercel",
|
"vercel",
|
||||||
"vickers",
|
"vickers",
|
||||||
"vitepress",
|
"vitepress",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"types": "src/types.d.ts",
|
"types": "src/types.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blitzkit/core": "workspace:*",
|
"@blitzkit/core": "workspace:*",
|
||||||
|
"@blitzkit/i18n": "workspace:*",
|
||||||
"satori": "^0.10.14",
|
"satori": "^0.10.14",
|
||||||
"@resvg/resvg-js": "^2.6.2",
|
"@resvg/resvg-js": "^2.6.2",
|
||||||
"discord.js": "^14.16.3",
|
"discord.js": "^14.16.3",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export async function renderBreakdown(
|
|||||||
filters: StatFilters,
|
filters: StatFilters,
|
||||||
locale: Locale,
|
locale: Locale,
|
||||||
) {
|
) {
|
||||||
const { t, translate } = translator(locale);
|
const { t, translate, unwrap } = translator(locale);
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
const awaitedTankAverages = await blitzStarsTankAverages;
|
const awaitedTankAverages = await blitzStarsTankAverages;
|
||||||
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
|
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
|
||||||
@@ -190,8 +190,9 @@ export async function renderBreakdown(
|
|||||||
class={tankDefinition?.class}
|
class={tankDefinition?.class}
|
||||||
type={tankDefinition?.type}
|
type={tankDefinition?.type}
|
||||||
title={
|
title={
|
||||||
tankDefinition?.name ??
|
tankDefinition
|
||||||
translate('bot.commands.breakdown.body.unknown_tank', [`${id}`])
|
? unwrap(tankDefinition.name)
|
||||||
|
: translate('bot.commands.breakdown.body.unknown_tank', [`${id}`])
|
||||||
}
|
}
|
||||||
stats={[
|
stats={[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
.addStringOption(addUsernameChoices),
|
.addStringOption(addUsernameChoices),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { t, translate } = translator(interaction.locale);
|
const { t, translate, unwrap } = translator(interaction.locale);
|
||||||
const tier = Number(interaction.options.getString('tier'));
|
const tier = Number(interaction.options.getString('tier'));
|
||||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||||
const accountInfo = await getAccountInfo(region, id);
|
const accountInfo = await getAccountInfo(region, id);
|
||||||
@@ -77,7 +77,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
(tank) =>
|
(tank) =>
|
||||||
`[${flags[tank.nation]} ${markdownEscape(tank.name)}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
|
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
|
||||||
);
|
);
|
||||||
const lines = [title, ...tankList];
|
const lines = [title, ...tankList];
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
tankIcon,
|
tankIcon,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
|
import { Locale } from 'discord.js';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import { CommandWrapper } from '../components/CommandWrapper';
|
import { CommandWrapper } from '../components/CommandWrapper';
|
||||||
import { TitleBar } from '../components/TitleBar';
|
import { TitleBar } from '../components/TitleBar';
|
||||||
@@ -18,6 +19,7 @@ import { RenderConfiguration } from '../core/blitzkit/renderConfiguration';
|
|||||||
import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
|
import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
|
||||||
import { buttonLink } from '../core/discord/buttonLink';
|
import { buttonLink } from '../core/discord/buttonLink';
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
|
import { translator } from '../core/localization/translator';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { theme } from '../stitches.config';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
@@ -218,11 +220,13 @@ function stat(value: string | number, flex = 2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function playerListing(
|
async function playerListing(
|
||||||
|
locale: Locale,
|
||||||
player: WotInspectorReplayPlayerData,
|
player: WotInspectorReplayPlayerData,
|
||||||
protagonistTeam: boolean,
|
protagonistTeam: boolean,
|
||||||
winningTeam: number,
|
winningTeam: number,
|
||||||
recordingPlayer = false,
|
recordingPlayer = false,
|
||||||
) {
|
) {
|
||||||
|
const { unwrap } = translator(locale);
|
||||||
const awaitedTankAverages = await blitzStarsTankAverages;
|
const awaitedTankAverages = await blitzStarsTankAverages;
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
const tank = awaitedTankDefinitions.tanks[player.vehicle_descr];
|
const tank = awaitedTankDefinitions.tanks[player.vehicle_descr];
|
||||||
@@ -275,7 +279,7 @@ async function playerListing(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={tank.name}
|
alt={unwrap(tank.name)}
|
||||||
src={await iconPng(tankIcon(tank.id))}
|
src={await iconPng(tankIcon(tank.id))}
|
||||||
style={{
|
style={{
|
||||||
width: 32,
|
width: 32,
|
||||||
@@ -312,7 +316,7 @@ async function playerListing(
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tank.name}
|
{unwrap(tank.name)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -530,6 +534,7 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
)
|
)
|
||||||
.map((player) =>
|
.map((player) =>
|
||||||
playerListing(
|
playerListing(
|
||||||
|
interaction.locale,
|
||||||
player,
|
player,
|
||||||
true,
|
true,
|
||||||
replayData.winner_team,
|
replayData.winner_team,
|
||||||
@@ -553,7 +558,12 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
players
|
players
|
||||||
.filter((player) => player.team === antagonistTeamId)
|
.filter((player) => player.team === antagonistTeamId)
|
||||||
.map((player) =>
|
.map((player) =>
|
||||||
playerListing(player, false, replayData.winner_team),
|
playerListing(
|
||||||
|
interaction.locale,
|
||||||
|
player,
|
||||||
|
false,
|
||||||
|
replayData.winner_team,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
.addStringOption(addUsernameChoices),
|
.addStringOption(addUsernameChoices),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { t, translate } = translator(interaction.locale);
|
const { t, translate, unwrap } = translator(interaction.locale);
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
const targetTankId = await resolveTankId(
|
const targetTankId = await resolveTankId(
|
||||||
interaction.options.getString('target-tank', true),
|
interaction.options.getString('target-tank', true),
|
||||||
@@ -99,7 +99,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
if (targetTankId === startingTankId) {
|
if (targetTankId === startingTankId) {
|
||||||
return translate('bot.commands.research.errors.start_end_equal', [
|
return translate('bot.commands.research.errors.start_end_equal', [
|
||||||
escapeMarkdown(targetTank.name),
|
escapeMarkdown(unwrap(targetTank.name)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,28 +107,28 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
if (startingTank.tier > targetTank.tier) {
|
if (startingTank.tier > targetTank.tier) {
|
||||||
return translate('bot.commands.research.errors.unordered', [
|
return translate('bot.commands.research.errors.unordered', [
|
||||||
escapeMarkdown(startingTank.name),
|
escapeMarkdown(unwrap(startingTank.name)),
|
||||||
escapeMarkdown(targetTank.name),
|
escapeMarkdown(unwrap(targetTank.name)),
|
||||||
escapeMarkdown(targetTank.name),
|
escapeMarkdown(unwrap(targetTank.name)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetTankAncestry.includes(startingTankId)) {
|
if (!targetTankAncestry.includes(startingTankId)) {
|
||||||
return translate('bot.commands.research.errors.tanks_not_on_line', [
|
return translate('bot.commands.research.errors.tanks_not_on_line', [
|
||||||
escapeMarkdown(targetTank.name),
|
escapeMarkdown(unwrap(targetTank.name)),
|
||||||
escapeMarkdown(startingTank.name),
|
escapeMarkdown(unwrap(startingTank.name)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (targetTank.type !== TankType.RESEARCHABLE) {
|
if (targetTank.type !== TankType.RESEARCHABLE) {
|
||||||
return translate('bot.commands.research.errors.non_tech_tree', [
|
return translate('bot.commands.research.errors.non_tech_tree', [
|
||||||
escapeMarkdown(targetTank.name),
|
escapeMarkdown(unwrap(targetTank.name)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetTank.tier === 1) {
|
if (targetTank.tier === 1) {
|
||||||
return translate('bot.commands.research.errors.tier_1', [
|
return translate('bot.commands.research.errors.tier_1', [
|
||||||
escapeMarkdown(targetTank.name),
|
escapeMarkdown(unwrap(targetTank.name)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
|
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
|
||||||
return translate('bot.commands.research.errors.already_researched', [
|
return translate('bot.commands.research.errors.already_researched', [
|
||||||
escapeMarkdown(targetTank.name),
|
escapeMarkdown(unwrap(targetTank.name)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +452,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={tank.name}
|
alt={unwrap(tank.name)}
|
||||||
src={TANK_ICONS[tank.class]}
|
src={TANK_ICONS[tank.class]}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
@@ -463,7 +463,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tank.name}
|
{unwrap(tank.name)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import { fetchTankNames, TankType } from '@blitzkit/core';
|
import { fetchTankNames, TankType } from '@blitzkit/core';
|
||||||
|
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||||
import {
|
import {
|
||||||
ApplicationCommandOptionChoiceData,
|
ApplicationCommandOptionChoiceData,
|
||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
|
import { tankNames } from '../blitzkit/nonBlockingPromises';
|
||||||
|
import { translator } from '../localization/translator';
|
||||||
import {
|
import {
|
||||||
DISCORD_CHOICES_MAX_NAME_SIZE,
|
DISCORD_CHOICES_MAX_NAME_SIZE,
|
||||||
OVERFLOW_SUFFIX,
|
OVERFLOW_SUFFIX,
|
||||||
} from './autocompleteClan/constants';
|
} from './autocompleteClan/constants';
|
||||||
import { tankNames } from '../blitzkit/nonBlockingPromises';
|
|
||||||
|
|
||||||
export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
|
export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
|
||||||
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE),
|
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE),
|
||||||
@@ -20,6 +22,7 @@ export async function autocompleteTanks(
|
|||||||
techTreeOnly = false,
|
techTreeOnly = false,
|
||||||
fields = ['tank'],
|
fields = ['tank'],
|
||||||
) {
|
) {
|
||||||
|
const { unwrap } = translator(interaction.locale);
|
||||||
const focusedOption = interaction.options.getFocused(true);
|
const focusedOption = interaction.options.getFocused(true);
|
||||||
if (!fields.includes(focusedOption.name)) return;
|
if (!fields.includes(focusedOption.name)) return;
|
||||||
|
|
||||||
@@ -30,18 +33,24 @@ export async function autocompleteTanks(
|
|||||||
focusedOption.value,
|
focusedOption.value,
|
||||||
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
|
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
|
||||||
{
|
{
|
||||||
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
|
keys: [
|
||||||
|
...SUPPORTED_LOCALES.map((locale) => [
|
||||||
|
`searchableName.${locale}`,
|
||||||
|
`searchableNameDeburr.${locale}`,
|
||||||
|
]).flat(),
|
||||||
|
'camouflages',
|
||||||
|
],
|
||||||
limit: 10,
|
limit: 10,
|
||||||
},
|
},
|
||||||
).map(async (item) => {
|
).map(async (item) => {
|
||||||
let name = item.obj.searchableName;
|
let name = unwrap(item.obj.searchableName);
|
||||||
|
|
||||||
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
||||||
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
||||||
|
|
||||||
name = `${item.obj.searchableName.slice(
|
name = `${unwrap(item.obj.searchableName).slice(
|
||||||
0,
|
0,
|
||||||
item.obj.searchableName.length -
|
unwrap(item.obj.searchableName).length -
|
||||||
overSize -
|
overSize -
|
||||||
OVERFLOW_SUFFIX.length,
|
OVERFLOW_SUFFIX.length,
|
||||||
)}${OVERFLOW_SUFFIX}`;
|
)}${OVERFLOW_SUFFIX}`;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/core';
|
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/i18n';
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
|
|
||||||
const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
|
const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import { unwrapper } from '@blitzkit/i18n';
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import { translations } from './strings';
|
import { translations } from './strings';
|
||||||
import {
|
import {
|
||||||
DEFAULT_LOCALE_DISCORD,
|
DEFAULT_LOCALE_DISCORD,
|
||||||
SUPPORTED_LOCALES_DISCORD,
|
SUPPORTED_LOCALES_DISCORD,
|
||||||
|
SUPPORTED_LOCALES_DISCORD_MAP_INVERSE,
|
||||||
} from './strings/constants';
|
} from './strings/constants';
|
||||||
|
|
||||||
export function translator(localeRaw: Locale) {
|
export function translator(localeRaw: Locale) {
|
||||||
@@ -74,5 +76,7 @@ export function translator(localeRaw: Locale) {
|
|||||||
.join('');
|
.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { locale, translate, t };
|
const unwrap = unwrapper(SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]);
|
||||||
|
|
||||||
|
return { locale, translate, t, unwrap };
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"include": ["src/**/*", "../core/src/blitzkit/supportedLocales.ts"]
|
"include": ["src/**/*", "../core/src/blitzkit/supportedLocales.ts", "../i18n/src/unwrapper.ts"]
|
||||||
}
|
}
|
||||||
@@ -46,5 +46,4 @@ export * from './votes';
|
|||||||
export * from './createDefaultProvisions';
|
export * from './createDefaultProvisions';
|
||||||
export * from './metaSortTank';
|
export * from './metaSortTank';
|
||||||
export * from './romanize';
|
export * from './romanize';
|
||||||
export * from './supportedLocales';
|
|
||||||
export * from './youtubers';
|
export * from './youtubers';
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { deburr } from 'lodash-es';
|
import { deburr } from 'lodash-es';
|
||||||
|
import { I18nString } from '../protos';
|
||||||
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
|
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
|
||||||
import { fetchTankDefinitions } from './tankDefinitions';
|
import { fetchTankDefinitions } from './tankDefinitions';
|
||||||
|
|
||||||
@@ -11,12 +12,18 @@ export async function fetchTankNames() {
|
|||||||
|
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
tankDefinitionsArray.map(async (tank) => {
|
tankDefinitionsArray.map(async (tank) => {
|
||||||
|
const searchableNameDeburr: I18nString = { locales: {} };
|
||||||
|
|
||||||
|
Object.entries(tank.name_full ?? tank.name).forEach(([key, value]) => {
|
||||||
|
searchableNameDeburr.locales[key] = deburr(value);
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: tank.id,
|
id: tank.id,
|
||||||
name: tank.name,
|
name: tank.name,
|
||||||
nameFull: tank.name_full,
|
nameFull: tank.name_full,
|
||||||
searchableName: tank.name_full ?? tank.name,
|
searchableName: tank.name_full ?? tank.name,
|
||||||
searchableNameDeburr: deburr(tank.name_full ?? tank.name),
|
searchableNameDeburr,
|
||||||
camouflages: tank.camouflages
|
camouflages: tank.camouflages
|
||||||
?.map((id) => camouflageDefinitions.camouflages[id]?.name)
|
?.map((id) => camouflageDefinitions.camouflages[id]?.name)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from './blitz';
|
export * from './blitz';
|
||||||
export * from './blitzkit';
|
export * from './blitzkit';
|
||||||
export * from './databases';
|
export * from './databases';
|
||||||
|
export * from './imgur';
|
||||||
export * from './math';
|
export * from './math';
|
||||||
export * from './patreon';
|
export * from './patreon';
|
||||||
export * from './protobuf';
|
export * from './protobuf';
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
import { I18nString } from "./i18n";
|
import { I18nString, createBaseI18nString } from "./i18n";
|
||||||
|
|
||||||
export const protobufPackage = "blitzkit";
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
import { I18nString } from "./i18n";
|
import { I18nString, createBaseI18nString } from "./i18n";
|
||||||
|
|
||||||
export const protobufPackage = "blitzkit";
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
import { I18nString } from "./i18n";
|
import { I18nString, createBaseI18nString } from "./i18n";
|
||||||
|
|
||||||
export const protobufPackage = "blitzkit";
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
import { I18nString } from "./i18n";
|
import { I18nString, createBaseI18nString } from "./i18n";
|
||||||
|
|
||||||
export const protobufPackage = "blitzkit";
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export interface I18nString_LocalesEntry {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBaseI18nString(): I18nString {
|
export function createBaseI18nString(): I18nString {
|
||||||
return { locales: {} };
|
return { locales: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
import { I18nString } from "./i18n";
|
import { I18nString, createBaseI18nString } from "./i18n";
|
||||||
|
|
||||||
export const protobufPackage = "blitzkit";
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
import { ConsumableTankFilter } from "./consumable_definitions";
|
import { ConsumableTankFilter } from "./consumable_definitions";
|
||||||
import { I18nString } from "./i18n";
|
import { I18nString, createBaseI18nString } from "./i18n";
|
||||||
|
|
||||||
export const protobufPackage = "blitzkit";
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||||
import { I18nString } from "./i18n";
|
import { I18nString, createBaseI18nString } from "./i18n";
|
||||||
|
|
||||||
export const protobufPackage = "blitzkit";
|
export const protobufPackage = "blitzkit";
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,6 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
},
|
"devDependencies": {}
|
||||||
"devDependencies": {
|
|
||||||
"@types/adm-zip": "^0.5.5",
|
|
||||||
"@types/lz4js": "^0.2.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1 +1,3 @@
|
|||||||
export * from './strings';
|
export * from './strings';
|
||||||
|
export * from './supportedLocales';
|
||||||
|
export * from './unwrapper';
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/core/src/blitzkit';
|
|
||||||
import { merge } from 'lodash-es';
|
import { merge } from 'lodash-es';
|
||||||
import type en from '../strings/en.json';
|
import type en from '../strings/en.json';
|
||||||
|
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from './supportedLocales';
|
||||||
|
|
||||||
export type TranslationTree = { [key: string]: TranslationNode };
|
export type TranslationTree = { [key: string]: TranslationNode };
|
||||||
|
|
||||||
export type TranslationNode = string | TranslationTree;
|
export type TranslationNode = string | TranslationTree;
|
||||||
|
|
||||||
const files = import.meta.glob('../../lang/*.json', { eager: true });
|
const files = import.meta.glob('../strings/*.json', { eager: true });
|
||||||
export const localizedStrings: Record<string, typeof en> = {};
|
export const localizedStrings: Record<string, typeof en> = {};
|
||||||
|
|
||||||
function nuke(object: TranslationTree) {
|
function nuke(object: TranslationTree) {
|
||||||
@@ -21,8 +21,8 @@ function nuke(object: TranslationTree) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SUPPORTED_LOCALES.forEach((locale) => {
|
SUPPORTED_LOCALES.forEach((locale) => {
|
||||||
const defaultStrings = files[`../../lang/${DEFAULT_LOCALE}.json`];
|
const defaultStrings = files[`../strings/${DEFAULT_LOCALE}.json`];
|
||||||
const strings = files[`../../lang/${locale}.json`];
|
const strings = files[`../strings/${locale}.json`];
|
||||||
const mergedStrings = merge({}, defaultStrings, strings);
|
const mergedStrings = merge({}, defaultStrings, strings);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
File renamed without changes.
@@ -0,0 +1,12 @@
|
|||||||
|
import { I18nString } from '@blitzkit/core';
|
||||||
|
import { DEFAULT_LOCALE } from './supportedLocales';
|
||||||
|
|
||||||
|
export function unwrapper(locale: string) {
|
||||||
|
return function (i18nString: I18nString) {
|
||||||
|
if (locale in i18nString.locales) {
|
||||||
|
return i18nString.locales[locale];
|
||||||
|
}
|
||||||
|
|
||||||
|
return i18nString.locales[DEFAULT_LOCALE];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "prate",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"main": "src/index.ts",
|
|
||||||
"devDependencies": {
|
|
||||||
"@blitzkit/core": "workspace:*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ import { DATA } from './buildAssets/constants';
|
|||||||
import { readYAMLDVPL } from './core/blitz/readYAMLDVPL';
|
import { readYAMLDVPL } from './core/blitz/readYAMLDVPL';
|
||||||
import { writeDVPL } from './core/blitz/writeDVPL';
|
import { writeDVPL } from './core/blitz/writeDVPL';
|
||||||
|
|
||||||
const versionTextFile = await readStringDVPL(`${DATA}/version.txt.dvpl`);
|
const versionTextFile = await readStringDVPL(`${DATA}/version.txt`);
|
||||||
const currentVersion = versionTextFile
|
const currentVersion = versionTextFile
|
||||||
.split(' ')[0]
|
.split(' ')[0]
|
||||||
.split('.')
|
.split('.')
|
||||||
@@ -51,7 +51,7 @@ while (true) {
|
|||||||
|
|
||||||
await mkdir(`${DATA}/${dir}`, { recursive: true });
|
await mkdir(`${DATA}/${dir}`, { recursive: true });
|
||||||
await writeFile(
|
await writeFile(
|
||||||
`${DATA}/${path}.dvpl`,
|
`${DATA}/${path}`,
|
||||||
new Uint8Array(writeDVPL(Buffer.from(data))),
|
new Uint8Array(writeDVPL(Buffer.from(data))),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -69,13 +69,13 @@ while (true) {
|
|||||||
);
|
);
|
||||||
const newStrings = parseYaml(await localizationsResponse.text());
|
const newStrings = parseYaml(await localizationsResponse.text());
|
||||||
const oldStrings = await readYAMLDVPL<Record<string, string>>(
|
const oldStrings = await readYAMLDVPL<Record<string, string>>(
|
||||||
`${DATA}/Strings/en.yaml.dvpl`,
|
`${DATA}/Strings/en.yaml`,
|
||||||
);
|
);
|
||||||
const patchedStrings = { ...oldStrings, ...newStrings };
|
const patchedStrings = { ...oldStrings, ...newStrings };
|
||||||
const patchedContent = stringifyYaml(patchedStrings);
|
const patchedContent = stringifyYaml(patchedStrings);
|
||||||
|
|
||||||
await writeFile(
|
await writeFile(
|
||||||
`${DATA}/Strings/en.yaml.dvpl`,
|
`${DATA}/Strings/en.yaml`,
|
||||||
new Uint8Array(writeDVPL(Buffer.from(patchedContent))),
|
new Uint8Array(writeDVPL(Buffer.from(patchedContent))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import { DATA } from './constants';
|
|||||||
|
|
||||||
export async function boosterIcons() {
|
export async function boosterIcons() {
|
||||||
const boosterFiles = (await readdir(`${DATA}/Gfx/Shared/boosters`)).filter(
|
const boosterFiles = (await readdir(`${DATA}/Gfx/Shared/boosters`)).filter(
|
||||||
(file) => !file.endsWith('@2x.txt.dvpl') && !file.startsWith('texture0'),
|
(file) => !file.includes('@2x.txt') && !file.startsWith('texture0'),
|
||||||
);
|
);
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/Gfx/Shared/boosters/texture0.packed.webp.dvpl`),
|
await readDVPLFile(`${DATA}/Gfx/Shared/boosters/texture0.packed.webp`),
|
||||||
);
|
);
|
||||||
const changes: FileChange[] = [];
|
const changes: FileChange[] = [];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
boosterFiles.map(async (file) => {
|
boosterFiles.map(async (file) => {
|
||||||
const name = file.match(/booster_(.+).txt.dvpl/)?.[1];
|
const name = file.match(/booster_(.+).txt/)?.[1];
|
||||||
|
|
||||||
if (name === undefined) return;
|
if (name === undefined) return;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { DATA } from './constants';
|
|||||||
export async function camouflageIcons() {
|
export async function camouflageIcons() {
|
||||||
console.log('Building camouflage icons...');
|
console.log('Building camouflage icons...');
|
||||||
const content = await readDVPLFile(
|
const content = await readDVPLFile(
|
||||||
`${DATA}/Gfx/UI/Hangar/IconCamouflage.packed.webp.dvpl`,
|
`${DATA}/Gfx/UI/Hangar/IconCamouflage.packed.webp`,
|
||||||
);
|
);
|
||||||
const changes: FileChange[] = [
|
const changes: FileChange[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,33 @@
|
|||||||
import { readdir } from 'fs/promises';
|
import { readdir } from 'fs/promises';
|
||||||
import { argv } from 'process';
|
import { argv } from 'process';
|
||||||
|
|
||||||
|
const WINDOWS_APPS = 'C:/Program Files/WindowsApps';
|
||||||
|
const wotbPackageRegex = /7458BE2C\..+_\d+(\.\d+)+_x64__.+/;
|
||||||
|
|
||||||
const isDepot = argv.includes('--depot');
|
const isDepot = argv.includes('--depot');
|
||||||
|
|
||||||
export const DATA = isDepot
|
let data: string;
|
||||||
? await (async () => {
|
let compressed: boolean;
|
||||||
const [installationVersion] = await readdir(`../../depots/444202`);
|
|
||||||
return `../../depots/444202/${installationVersion}/Data`;
|
if (isDepot) {
|
||||||
})()
|
const [installationVersion] = await readdir(`../../depots/444202`);
|
||||||
: 'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data';
|
data = `../../depots/444202/${installationVersion}/Data`;
|
||||||
|
compressed = true;
|
||||||
|
} else {
|
||||||
|
const directories = await readdir(WINDOWS_APPS).then((files) =>
|
||||||
|
files.filter((file) => wotbPackageRegex.test(file)),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (directories.length !== 1) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected 1 directory for WoTB Windows App, got ${directories.length}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [windowsApp] = directories;
|
||||||
|
data = `${WINDOWS_APPS}/${windowsApp}/Data`;
|
||||||
|
compressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DATA = data;
|
||||||
|
export const DATA_COMPRESSED = compressed;
|
||||||
@@ -34,21 +34,19 @@ export async function consumableProvisionIcons() {
|
|||||||
const styleSheets = await Promise.all(
|
const styleSheets = await Promise.all(
|
||||||
styles.map(async (path) =>
|
styles.map(async (path) =>
|
||||||
Object.values(
|
Object.values(
|
||||||
(await readYAMLDVPL<Mappings>(`${DATA}/${path}.dvpl`)).StyleSheets,
|
(await readYAMLDVPL<Mappings>(`${DATA}/${path}`)).StyleSheets,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((array) => array.flat());
|
).then((array) => array.flat());
|
||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await readStringDVPL(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/consumables/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/consumables/list.xml`,
|
||||||
)
|
)
|
||||||
).matchAll(listItemsPattern)) {
|
).matchAll(listItemsPattern)) {
|
||||||
const consumablesCommon = await readXMLDVPL<{
|
const consumablesCommon = await readXMLDVPL<{
|
||||||
root: ConsumablesCommon;
|
root: ConsumablesCommon;
|
||||||
}>(
|
}>(`${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`);
|
||||||
`${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml.dvpl`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.values(consumablesCommon.root).map(async (consumable) => {
|
Object.values(consumablesCommon.root).map(async (consumable) => {
|
||||||
@@ -70,9 +68,9 @@ export async function consumableProvisionIcons() {
|
|||||||
.replace('.psd', '')
|
.replace('.psd', '')
|
||||||
.replace('.txt', '');
|
.replace('.txt', '');
|
||||||
|
|
||||||
if (existsSync(`${DATA}/${configPath}.packed.webp.dvpl`)) {
|
if (existsSync(`${DATA}/${configPath}.packed.webp`)) {
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/${configPath}.packed.webp.dvpl`),
|
await readDVPLFile(`${DATA}/${configPath}.packed.webp`),
|
||||||
);
|
);
|
||||||
const content = await image.trim({ threshold: 100 }).toBuffer();
|
const content = await image.trim({ threshold: 100 }).toBuffer();
|
||||||
|
|
||||||
@@ -83,10 +81,10 @@ export async function consumableProvisionIcons() {
|
|||||||
} else {
|
} else {
|
||||||
const consumablesTexture = sharp(
|
const consumablesTexture = sharp(
|
||||||
await readDVPLFile(
|
await readDVPLFile(
|
||||||
`${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp.dvpl`,
|
`${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt.dvpl`))
|
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt`))
|
||||||
.split('\n')[4]
|
.split('\n')[4]
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.map(Number);
|
.map(Number);
|
||||||
@@ -111,12 +109,12 @@ export async function consumableProvisionIcons() {
|
|||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await readStringDVPL(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/provisions/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/provisions/list.xml`,
|
||||||
)
|
)
|
||||||
).matchAll(listItemsPattern)) {
|
).matchAll(listItemsPattern)) {
|
||||||
const provisionsCommon = await readXMLDVPL<{
|
const provisionsCommon = await readXMLDVPL<{
|
||||||
root: ProvisionsCommon;
|
root: ProvisionsCommon;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml.dvpl`);
|
}>(`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.values(provisionsCommon.root).map(async (provision) => {
|
Object.values(provisionsCommon.root).map(async (provision) => {
|
||||||
const styleSheet = styleSheets.find((styleSheet) =>
|
const styleSheet = styleSheets.find((styleSheet) =>
|
||||||
@@ -137,9 +135,9 @@ export async function consumableProvisionIcons() {
|
|||||||
.replace('.psd', '')
|
.replace('.psd', '')
|
||||||
.replace('.txt', '');
|
.replace('.txt', '');
|
||||||
|
|
||||||
if (existsSync(`${DATA}/${configPath}.packed.webp.dvpl`)) {
|
if (existsSync(`${DATA}/${configPath}.packed.webp`)) {
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/${configPath}.packed.webp.dvpl`),
|
await readDVPLFile(`${DATA}/${configPath}.packed.webp`),
|
||||||
);
|
);
|
||||||
const content = await image.trim({ threshold: 100 }).toBuffer();
|
const content = await image.trim({ threshold: 100 }).toBuffer();
|
||||||
|
|
||||||
@@ -150,10 +148,10 @@ export async function consumableProvisionIcons() {
|
|||||||
} else {
|
} else {
|
||||||
const provisionsTexture = sharp(
|
const provisionsTexture = sharp(
|
||||||
await readDVPLFile(
|
await readDVPLFile(
|
||||||
`${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp.dvpl`,
|
`${`${DATA}/${configPath}`.split('/').slice(0, -1).join('/')}/texture0.packed.webp`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt.dvpl`))
|
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt`))
|
||||||
.split('\n')[4]
|
.split('\n')[4]
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.map(Number);
|
.map(Number);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export async function currencies() {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
ICONS.map(async ([file, name]) => {
|
ICONS.map(async ([file, name]) => {
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
await readDVPLFile(`${DATA}/Gfx/Lobby/currency/${file}.dvpl`),
|
await readDVPLFile(`${DATA}/Gfx/Lobby/currency/${file}`),
|
||||||
)
|
)
|
||||||
.trim()
|
.trim()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
ConsumableTankCategoryFilterCategory,
|
ConsumableTankCategoryFilterCategory,
|
||||||
Crew,
|
Crew,
|
||||||
CrewType,
|
CrewType,
|
||||||
DEFAULT_LOCALE,
|
|
||||||
encodePBBuffer,
|
encodePBBuffer,
|
||||||
EquipmentDefinitions,
|
EquipmentDefinitions,
|
||||||
EquipmentSlot,
|
EquipmentSlot,
|
||||||
@@ -23,8 +22,6 @@ import {
|
|||||||
ResearchCost,
|
ResearchCost,
|
||||||
ShellType,
|
ShellType,
|
||||||
SkillDefinitions,
|
SkillDefinitions,
|
||||||
SUPPORTED_LOCALE_BLITZ_MAP,
|
|
||||||
SUPPORTED_LOCALES,
|
|
||||||
TankClass,
|
TankClass,
|
||||||
TankDefinitions,
|
TankDefinitions,
|
||||||
TankPrice,
|
TankPrice,
|
||||||
@@ -34,6 +31,11 @@ import {
|
|||||||
Unlock,
|
Unlock,
|
||||||
Vector3,
|
Vector3,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
|
import {
|
||||||
|
DEFAULT_LOCALE,
|
||||||
|
SUPPORTED_LOCALE_BLITZ_MAP,
|
||||||
|
SUPPORTED_LOCALES,
|
||||||
|
} from '@blitzkit/i18n';
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from 'fs/promises';
|
||||||
import { parse as parsePath } from 'path';
|
import { parse as parsePath } from 'path';
|
||||||
import { Vector3Tuple } from 'three';
|
import { Vector3Tuple } from 'three';
|
||||||
@@ -513,11 +515,9 @@ export async function definitions() {
|
|||||||
console.log('Building definitions...');
|
console.log('Building definitions...');
|
||||||
|
|
||||||
const gameDefinitions: GameDefinitions = {
|
const gameDefinitions: GameDefinitions = {
|
||||||
version: (await readStringDVPL(`${DATA}/version.txt.dvpl`)).split(' ')[0],
|
version: (await readStringDVPL(`${DATA}/version.txt`)).split(' ')[0],
|
||||||
nations: (
|
nations: (
|
||||||
await readYAMLDVPL<AvailableNationsYaml>(
|
await readYAMLDVPL<AvailableNationsYaml>(`${DATA}/available_nations.yaml`)
|
||||||
`${DATA}/available_nations.yaml.dvpl`,
|
|
||||||
)
|
|
||||||
).available_nations,
|
).available_nations,
|
||||||
gameModes: {},
|
gameModes: {},
|
||||||
roles: {},
|
roles: {},
|
||||||
@@ -550,26 +550,30 @@ export async function definitions() {
|
|||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
.then((string) => parseYaml(string) as BlitzStrings);
|
.then((string) => parseYaml(string) as BlitzStrings);
|
||||||
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
|
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/optional_devices.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/optional_devices.xml`,
|
||||||
);
|
);
|
||||||
const stringsI18n: Record<string, Record<string, string>> = {};
|
const stringsI18n: Record<string, Record<string, string>> = {};
|
||||||
|
|
||||||
for (const locale of SUPPORTED_LOCALES) {
|
await Promise.all(
|
||||||
const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale];
|
SUPPORTED_LOCALES.map(async (locale) => {
|
||||||
const cache = await fetch(
|
const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale];
|
||||||
`https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`,
|
const cache = await fetch(
|
||||||
)
|
`https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`,
|
||||||
.then((response) => response.text())
|
)
|
||||||
.then((string) => parseYaml(string) as BlitzStrings);
|
.then((response) => response.text())
|
||||||
const preInstalled = await readYAMLDVPL<BlitzStrings>(
|
.then((string) => parseYaml(string) as BlitzStrings);
|
||||||
`${DATA}/Strings/${blitzLocale}.yaml.dvpl`,
|
const preInstalled = await readYAMLDVPL<BlitzStrings>(
|
||||||
);
|
`${DATA}/Strings/${blitzLocale}.yaml`,
|
||||||
|
);
|
||||||
|
|
||||||
stringsI18n[locale] = {
|
stringsI18n[locale] = {
|
||||||
...cache,
|
...cache,
|
||||||
...preInstalled,
|
...preInstalled,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
console.log(`Loaded strings for ${locale}`);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
function getString(name: string) {
|
function getString(name: string) {
|
||||||
const locales: Record<string, string> = {
|
const locales: Record<string, string> = {
|
||||||
@@ -594,13 +598,13 @@ export async function definitions() {
|
|||||||
|
|
||||||
const optionalDeviceSlots = await readXMLDVPL<{
|
const optionalDeviceSlots = await readXMLDVPL<{
|
||||||
root: OptionalDeviceSlots;
|
root: OptionalDeviceSlots;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/common/optional_device_slots.xml.dvpl`);
|
}>(`${DATA}/XML/item_defs/vehicles/common/optional_device_slots.xml`);
|
||||||
const consumables: ConsumablesCommon = {};
|
const consumables: ConsumablesCommon = {};
|
||||||
const provisions: ProvisionsCommon = {};
|
const provisions: ProvisionsCommon = {};
|
||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await readStringDVPL(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/consumables/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/consumables/list.xml`,
|
||||||
)
|
)
|
||||||
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
||||||
if (match[1] === 'prototypes') continue;
|
if (match[1] === 'prototypes') continue;
|
||||||
@@ -609,7 +613,7 @@ export async function definitions() {
|
|||||||
consumables,
|
consumables,
|
||||||
(
|
(
|
||||||
await readXMLDVPL<{ root: ConsumablesCommon }>(
|
await readXMLDVPL<{ root: ConsumablesCommon }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`,
|
||||||
)
|
)
|
||||||
).root,
|
).root,
|
||||||
);
|
);
|
||||||
@@ -617,7 +621,7 @@ export async function definitions() {
|
|||||||
|
|
||||||
for (const match of (
|
for (const match of (
|
||||||
await readStringDVPL(
|
await readStringDVPL(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/provisions/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/provisions/list.xml`,
|
||||||
)
|
)
|
||||||
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
).matchAll(/<items path="(.+)\.xml"\/>/g)) {
|
||||||
if (match[1] === 'prototypes') continue;
|
if (match[1] === 'prototypes') continue;
|
||||||
@@ -626,33 +630,33 @@ export async function definitions() {
|
|||||||
provisions,
|
provisions,
|
||||||
(
|
(
|
||||||
await readXMLDVPL<{ root: ConsumablesCommon }>(
|
await readXMLDVPL<{ root: ConsumablesCommon }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`,
|
||||||
)
|
)
|
||||||
).root,
|
).root,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatar = await readXMLDVPL<{ root: Avatar }>(
|
const avatar = await readXMLDVPL<{ root: Avatar }>(
|
||||||
`${DATA}/XML/item_defs/tankmen/avatar.xml.dvpl`,
|
`${DATA}/XML/item_defs/tankmen/avatar.xml`,
|
||||||
);
|
);
|
||||||
const maps = await readYAMLDVPL<Maps>(`${DATA}/maps.yaml.dvpl`);
|
const maps = await readYAMLDVPL<Maps>(`${DATA}/maps.yaml`);
|
||||||
const tankXps = new Map<number, ResearchCost>();
|
const tankXps = new Map<number, ResearchCost>();
|
||||||
const camouflagesXml = await readXMLDVPL<{ root: CamouflagesXML }>(
|
const camouflagesXml = await readXMLDVPL<{ root: CamouflagesXML }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/camouflages.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/camouflages.xml`,
|
||||||
);
|
);
|
||||||
const camouflagesYaml = await readYAMLDVPL<CamouflagesYaml>(
|
const camouflagesYaml = await readYAMLDVPL<CamouflagesYaml>(
|
||||||
`${DATA}/camouflages.yaml.dvpl`,
|
`${DATA}/camouflages.yaml`,
|
||||||
);
|
);
|
||||||
const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages);
|
const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages);
|
||||||
const squadBattleTypeStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const squadBattleTypeStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml.dvpl`,
|
`${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`,
|
||||||
);
|
);
|
||||||
const gameTypeSelectorStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const gameTypeSelectorStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml.dvpl`,
|
`${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`,
|
||||||
);
|
);
|
||||||
const gameModeNativeNames: Record<string, number> = {};
|
const gameModeNativeNames: Record<string, number> = {};
|
||||||
const combatRoles = await readYAMLDVPL<CombatRolesYaml>(
|
const combatRoles = await readYAMLDVPL<CombatRolesYaml>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/combat_roles.yaml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/combat_roles.yaml`,
|
||||||
);
|
);
|
||||||
const consumableNativeNames: Record<string, number> = {};
|
const consumableNativeNames: Record<string, number> = {};
|
||||||
const provisionNativeNames: Record<string, number> = {};
|
const provisionNativeNames: Record<string, number> = {};
|
||||||
@@ -702,29 +706,23 @@ export async function definitions() {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
nations.map(async (nation) => {
|
nations.map(async (nation) => {
|
||||||
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
||||||
);
|
);
|
||||||
const turretList = await readXMLDVPL<{
|
const turretList = await readXMLDVPL<{
|
||||||
root: TurretDefinitionsList;
|
root: TurretDefinitionsList;
|
||||||
}>(
|
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/turrets.xml`);
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/components/turrets.xml.dvpl`,
|
|
||||||
);
|
|
||||||
const gunList = await readXMLDVPL<{
|
const gunList = await readXMLDVPL<{
|
||||||
root: GunDefinitionsList;
|
root: GunDefinitionsList;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/guns.xml.dvpl`);
|
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/guns.xml`);
|
||||||
const shellList = await readXMLDVPL<{
|
const shellList = await readXMLDVPL<{
|
||||||
root: ShellDefinitionsList;
|
root: ShellDefinitionsList;
|
||||||
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/shells.xml.dvpl`);
|
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/shells.xml`);
|
||||||
const enginesList = await readXMLDVPL<{
|
const enginesList = await readXMLDVPL<{
|
||||||
root: EngineDefinitionsList;
|
root: EngineDefinitionsList;
|
||||||
}>(
|
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/engines.xml`);
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/components/engines.xml.dvpl`,
|
|
||||||
);
|
|
||||||
const chassisList = await readXMLDVPL<{
|
const chassisList = await readXMLDVPL<{
|
||||||
root: ChassisDefinitionsList;
|
root: ChassisDefinitionsList;
|
||||||
}>(
|
}>(`${DATA}/XML/item_defs/vehicles/${nation}/components/chassis.xml`);
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/components/chassis.xml.dvpl`,
|
|
||||||
);
|
|
||||||
|
|
||||||
function resolveUnlocks(unlocks?: BlitzModuleType) {
|
function resolveUnlocks(unlocks?: BlitzModuleType) {
|
||||||
if (!unlocks) return [];
|
if (!unlocks) return [];
|
||||||
@@ -776,10 +774,10 @@ export async function definitions() {
|
|||||||
const tank = tankList.root[tankKey];
|
const tank = tankList.root[tankKey];
|
||||||
let tankPrice: TankPrice;
|
let tankPrice: TankPrice;
|
||||||
const tankDefinition = await readXMLDVPL<{ root: VehicleDefinitions }>(
|
const tankDefinition = await readXMLDVPL<{ root: VehicleDefinitions }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/${tankKey}.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/${nation}/${tankKey}.xml`,
|
||||||
);
|
);
|
||||||
const tankParameters = await readYAMLDVPL<TankParameters>(
|
const tankParameters = await readYAMLDVPL<TankParameters>(
|
||||||
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml.dvpl`,
|
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
|
||||||
);
|
);
|
||||||
const turretOrigin = tankDefinition.root.hull.turretPositions.turret
|
const turretOrigin = tankDefinition.root.hull.turretPositions.turret
|
||||||
.split(' ')
|
.split(' ')
|
||||||
@@ -1667,8 +1665,6 @@ export async function definitions() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
await commitAssets('definitions', [
|
await commitAssets('definitions', [
|
||||||
{
|
{
|
||||||
content: encodePBBuffer(GameDefinitions, gameDefinitions),
|
content: encodePBBuffer(GameDefinitions, gameDefinitions),
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ export async function equipmentIcons() {
|
|||||||
|
|
||||||
const changes: FileChange[] = [];
|
const changes: FileChange[] = [];
|
||||||
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
|
const optionalDevices = await readXMLDVPL<{ root: OptionalDevices }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/common/optional_devices.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/common/optional_devices.xml`,
|
||||||
);
|
);
|
||||||
const mappings = await readYAMLDVPL<Mappings>(
|
const mappings = await readYAMLDVPL<Mappings>(
|
||||||
`${DATA}/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml.dvpl`,
|
`${DATA}/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`,
|
||||||
);
|
);
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(
|
await readDVPLFile(
|
||||||
`${DATA}/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp.dvpl`,
|
`${DATA}/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ export async function equipmentIcons() {
|
|||||||
if (configPath.startsWith('Gfx/Lobby')) {
|
if (configPath.startsWith('Gfx/Lobby')) {
|
||||||
const configPathWebp = configPath.replace('.txt', '');
|
const configPathWebp = configPath.replace('.txt', '');
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(`${DATA}/${configPathWebp}.packed.webp.dvpl`),
|
await readDVPLFile(`${DATA}/${configPathWebp}.packed.webp`),
|
||||||
);
|
);
|
||||||
const content = await image.trim().toBuffer();
|
const content = await image.trim().toBuffer();
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ export async function equipmentIcons() {
|
|||||||
content,
|
content,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const config = await readStringDVPL(`${DATA}/${configPath}.dvpl`);
|
const config = await readStringDVPL(`${DATA}/${configPath}`);
|
||||||
const sizes = config.split('\n')[4].split(' ').map(Number);
|
const sizes = config.split('\n')[4].split(' ').map(Number);
|
||||||
const content = await image
|
const content = await image
|
||||||
.clone()
|
.clone()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export async function flags() {
|
|||||||
.filter(
|
.filter(
|
||||||
(flag) =>
|
(flag) =>
|
||||||
flag.startsWith('flag_profile-stat_') &&
|
flag.startsWith('flag_profile-stat_') &&
|
||||||
!flag.endsWith('@2x.packed.webp.dvpl'),
|
!flag.endsWith('@2x.packed.webp'),
|
||||||
)
|
)
|
||||||
.map(async (flag) => {
|
.map(async (flag) => {
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
@@ -34,7 +34,7 @@ export async function flags() {
|
|||||||
.filter(
|
.filter(
|
||||||
(flag) =>
|
(flag) =>
|
||||||
flag.startsWith('flag_tutor-tank_') &&
|
flag.startsWith('flag_tutor-tank_') &&
|
||||||
!flag.endsWith('@2x.packed.webp.dvpl'),
|
!flag.endsWith('@2x.packed.webp'),
|
||||||
)
|
)
|
||||||
.map(async (flag) => {
|
.map(async (flag) => {
|
||||||
const content = await readDVPLFile(`${DATA}/Gfx/Lobby/flags/${flag}`);
|
const content = await readDVPLFile(`${DATA}/Gfx/Lobby/flags/${flag}`);
|
||||||
@@ -50,8 +50,7 @@ export async function flags() {
|
|||||||
(await readdir(`${DATA}/Gfx/Lobby/flags`))
|
(await readdir(`${DATA}/Gfx/Lobby/flags`))
|
||||||
.filter(
|
.filter(
|
||||||
(flag) =>
|
(flag) =>
|
||||||
flag.startsWith('flag_filter_') &&
|
flag.startsWith('flag_filter_') && flag.endsWith('@2x.packed.webp'),
|
||||||
flag.endsWith('@2x.packed.webp.dvpl'),
|
|
||||||
)
|
)
|
||||||
.map(async (flag) => {
|
.map(async (flag) => {
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ export async function gameModeBanners() {
|
|||||||
|
|
||||||
const changes: FileChange[] = [];
|
const changes: FileChange[] = [];
|
||||||
const gameTypeSelectorStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const gameTypeSelectorStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml.dvpl`,
|
`${DATA}/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`,
|
||||||
);
|
);
|
||||||
const squadBattleTypeStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
const squadBattleTypeStyles = await readYAMLDVPL<SquadBattleTypeStyles>(
|
||||||
`${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml.dvpl`,
|
`${DATA}/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`,
|
||||||
);
|
);
|
||||||
const bannerMatches: { name: string; path: string }[] = [];
|
const bannerMatches: { name: string; path: string }[] = [];
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ export async function gameModeBanners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
await readDVPLFile(`${DATA}${path}.packed.webp.dvpl`),
|
await readDVPLFile(`${DATA}${path}.packed.webp`),
|
||||||
)
|
)
|
||||||
.trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
.trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ export async function moduleIcons() {
|
|||||||
(await readdir(`${DATA}/Gfx/UI/ModulesTechTree`))
|
(await readdir(`${DATA}/Gfx/UI/ModulesTechTree`))
|
||||||
.filter(
|
.filter(
|
||||||
(file) =>
|
(file) =>
|
||||||
!file.endsWith('@2x.packed.webp.dvpl') && file.startsWith('vehicle'),
|
!file.endsWith('@2x.packed.webp') && file.startsWith('vehicle'),
|
||||||
)
|
)
|
||||||
.map(async (file) => {
|
.map(async (file) => {
|
||||||
const nameRaw = file.match(/vehicle(.+)\.packed\.webp\.dvpl/)![1];
|
const nameRaw = file.match(/vehicle(.+)\.packed\.webp/)![1];
|
||||||
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
|
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
|
||||||
const content = await sharp(
|
const content = await sharp(
|
||||||
await readDVPLFile(`${DATA}/Gfx/UI/ModulesTechTree/${file}`),
|
await readDVPLFile(`${DATA}/Gfx/UI/ModulesTechTree/${file}`),
|
||||||
|
|||||||
@@ -11,15 +11,15 @@ export async function shellIcons() {
|
|||||||
|
|
||||||
const image = sharp(
|
const image = sharp(
|
||||||
await readDVPLFile(
|
await readDVPLFile(
|
||||||
`${DATA}/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp.dvpl`,
|
`${DATA}/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const changes = await Promise.all(
|
const changes = await Promise.all(
|
||||||
(await readdir(`${DATA}/Gfx/Shared/tank-supply/ammunition/big`))
|
(await readdir(`${DATA}/Gfx/Shared/tank-supply/ammunition/big`))
|
||||||
.filter((file) => file.endsWith('_l.txt.dvpl'))
|
.filter((file) => file.endsWith('_l.txt'))
|
||||||
.map(async (file) => {
|
.map(async (file) => {
|
||||||
const name = file.match(/(.+)_l\.txt\.dvpl/)![1];
|
const name = file.match(/(.+)_l\.txt/)![1];
|
||||||
const sizes = (
|
const sizes = (
|
||||||
await readStringDVPL(
|
await readStringDVPL(
|
||||||
`${DATA}/Gfx/Shared/tank-supply/ammunition/big/${file}`,
|
`${DATA}/Gfx/Shared/tank-supply/ammunition/big/${file}`,
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ export async function skillIcons() {
|
|||||||
console.log('Building skill icons...');
|
console.log('Building skill icons...');
|
||||||
|
|
||||||
const avatar = await readXMLDVPL<{ root: Avatar }>(
|
const avatar = await readXMLDVPL<{ root: Avatar }>(
|
||||||
`${DATA}/XML/item_defs/tankmen/avatar.xml.dvpl`,
|
`${DATA}/XML/item_defs/tankmen/avatar.xml`,
|
||||||
);
|
);
|
||||||
const changes = await Promise.all(
|
const changes = await Promise.all(
|
||||||
Object.values(avatar.root.skills).map(async (skill) => {
|
Object.values(avatar.root.skills).map(async (skill) => {
|
||||||
const icon = Array.isArray(skill.icon) ? skill.icon[0] : skill.icon;
|
const icon = Array.isArray(skill.icon) ? skill.icon[0] : skill.icon;
|
||||||
const name = icon.name.split('/').at(-1)!.replace(/_\d$/, '');
|
const name = icon.name.split('/').at(-1)!.replace(/_\d$/, '');
|
||||||
const path = `${DATA}${icon.name.replace('~res:', '')}.packed.webp.dvpl`;
|
const path = `${DATA}${icon.name.replace('~res:', '')}.packed.webp`;
|
||||||
const image = sharp(await readDVPLFile(path)).trim();
|
const image = sharp(await readDVPLFile(path)).trim();
|
||||||
const content = await image.toBuffer();
|
const content = await image.toBuffer();
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export async function tankArmors() {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
nations.map(async (nation) => {
|
nations.map(async (nation) => {
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export async function tankIcons() {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
nations.map(async (nation) => {
|
nations.map(async (nation) => {
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@@ -63,14 +63,14 @@ export async function tankIcons() {
|
|||||||
const nationVehicleId = tank.id;
|
const nationVehicleId = tank.id;
|
||||||
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
||||||
const parameters = await readYAMLDVPL<TankParameters>(
|
const parameters = await readYAMLDVPL<TankParameters>(
|
||||||
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml.dvpl`,
|
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
|
||||||
);
|
);
|
||||||
const smallPath = `${DATA}/${parameters.resourcesPath.smallIconPath
|
const smallPath = `${DATA}/${parameters.resourcesPath.smallIconPath
|
||||||
.replace(/~res:\//, '')
|
.replace(/~res:\//, '')
|
||||||
.replace(/\..+/, '')}.packed.webp.dvpl`;
|
.replace(/\..+/, '')}.packed.webp`;
|
||||||
const bigPath = `${DATA}/${parameters.resourcesPath.bigIconPath
|
const bigPath = `${DATA}/${parameters.resourcesPath.bigIconPath
|
||||||
.replace(/~res:\//, '')
|
.replace(/~res:\//, '')
|
||||||
.replace(/\..+/, '')}.packed.webp.dvpl`;
|
.replace(/\..+/, '')}.packed.webp`;
|
||||||
const big = await sharp(await readDVPLFile(bigPath))
|
const big = await sharp(await readDVPLFile(bigPath))
|
||||||
.trim()
|
.trim()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export async function tankModels() {
|
|||||||
const nation = nations[nationIndex];
|
const nation = nations[nationIndex];
|
||||||
const changes: FileChange[] = [];
|
const changes: FileChange[] = [];
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml.dvpl`,
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(`Building models for ${nation}`);
|
console.log(`Building models for ${nation}`);
|
||||||
@@ -35,7 +35,7 @@ export async function tankModels() {
|
|||||||
const id = toUniqueId(nation, tank.id);
|
const id = toUniqueId(nation, tank.id);
|
||||||
|
|
||||||
const parameters = await readYAMLDVPL<TankParameters>(
|
const parameters = await readYAMLDVPL<TankParameters>(
|
||||||
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml.dvpl`,
|
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
|
||||||
);
|
);
|
||||||
const model = await extractModel(
|
const model = await extractModel(
|
||||||
DATA,
|
DATA,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
import { readDVPLFile } from './readDVPLFile';
|
import { readDVPLFile } from './readDVPLFile';
|
||||||
|
|
||||||
export async function extractArmor(data: string, fileName: string) {
|
export async function extractArmor(data: string, fileName: string) {
|
||||||
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2.dvpl`;
|
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2`;
|
||||||
const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg.dvpl`;
|
const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg`;
|
||||||
const sc2 = new Sc2ReadStream(
|
const sc2 = new Sc2ReadStream(
|
||||||
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer,
|
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer,
|
||||||
).sc2();
|
).sc2();
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ const omitMeshNames = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function extractModel(data: string, path: string) {
|
export async function extractModel(data: string, path: string) {
|
||||||
const sc2Path = `${data}/3d/${path}.sc2.dvpl`;
|
const sc2Path = `${data}/3d/${path}.sc2`;
|
||||||
const scgPath = `${data}/3d/${path}.scg.dvpl`;
|
const scgPath = `${data}/3d/${path}.scg`;
|
||||||
const sc2 = new Sc2ReadStream(
|
const sc2 = new Sc2ReadStream(
|
||||||
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer,
|
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer,
|
||||||
).sc2();
|
).sc2();
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { readFile } from 'fs/promises';
|
import { readFile } from 'fs/promises';
|
||||||
|
import { DATA_COMPRESSED } from '../../buildAssets/constants';
|
||||||
import { readDVPL } from './readDVPL';
|
import { readDVPL } from './readDVPL';
|
||||||
|
|
||||||
export async function readDVPLFile(file: string) {
|
export async function readDVPLFile(file: string) {
|
||||||
return readDVPL(await readFile(file));
|
if (DATA_COMPRESSED) {
|
||||||
|
return readDVPL(await readFile(`${file}.dvpl`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return readFile(file);
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import { readDVPLFile } from '../readDVPLFile';
|
|||||||
import { TextureMutation } from './constants';
|
import { TextureMutation } from './constants';
|
||||||
|
|
||||||
export async function readTexture(path: string, mutation?: TextureMutation) {
|
export async function readTexture(path: string, mutation?: TextureMutation) {
|
||||||
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
const ddsTexturePath = path.replace('.tex', '.dx11.dds');
|
||||||
const isDds = existsSync(ddsTexturePath);
|
const isDds = existsSync(ddsTexturePath);
|
||||||
const resolvedTexturePath = isDds
|
const resolvedTexturePath = isDds
|
||||||
? ddsTexturePath
|
? ddsTexturePath
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react';
|
||||||
import sitemap from '@astrojs/sitemap';
|
import sitemap from '@astrojs/sitemap';
|
||||||
import { SUPPORTED_LOCALES } from '@blitzkit/core';
|
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: SUPPORTED_LOCALES,
|
locales: [...SUPPORTED_LOCALES],
|
||||||
routing: {
|
routing: {
|
||||||
prefixDefaultLocale: false,
|
prefixDefaultLocale: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { asset } from '@blitzkit/core';
|
import { asset } from '@blitzkit/core';
|
||||||
import { Flex, Heading, IconButton, Table } from '@radix-ui/themes';
|
import { Flex, Heading, IconButton, Table } from '@radix-ui/themes';
|
||||||
import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics';
|
import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
||||||
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell';
|
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell';
|
||||||
import { CompareRow } from './CompareRow';
|
import { CompareRow } from './CompareRow';
|
||||||
@@ -15,6 +16,7 @@ export function Body({ stats }: BodyProps) {
|
|||||||
const hasNonRegularGun = members.some(
|
const hasNonRegularGun = members.some(
|
||||||
({ gun }) => gun.gun_type!.$case !== 'regular',
|
({ gun }) => gun.gun_type!.$case !== 'regular',
|
||||||
);
|
);
|
||||||
|
const { unwrap } = useLocale();
|
||||||
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -60,7 +62,7 @@ export function Body({ stats }: BodyProps) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={thisShell.name}
|
alt={unwrap(thisShell.name)}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDe
|
|||||||
import { awaitableSkillDefinitions } from '../../core/awaitables/skillDefinitions';
|
import { awaitableSkillDefinitions } from '../../core/awaitables/skillDefinitions';
|
||||||
import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics';
|
import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics';
|
||||||
import { Var } from '../../core/radix/var';
|
import { Var } from '../../core/radix/var';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
||||||
import type { EquipmentMatrix } from '../../stores/duel';
|
import type { EquipmentMatrix } from '../../stores/duel';
|
||||||
import { BlitzkitButtonGrayIcon } from '../BlitzkitButtonGrayIcon';
|
import { BlitzkitButtonGrayIcon } from '../BlitzkitButtonGrayIcon';
|
||||||
@@ -43,6 +44,7 @@ export function CompareTable({ stats }: CompareTableProps) {
|
|||||||
const crewSkills = CompareEphemeral.use((state) => state.crewSkills);
|
const crewSkills = CompareEphemeral.use((state) => state.crewSkills);
|
||||||
const members = CompareEphemeral.use((state) => state.members);
|
const members = CompareEphemeral.use((state) => state.members);
|
||||||
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StickyTableRoot
|
<StickyTableRoot
|
||||||
@@ -293,10 +295,10 @@ export function CompareTable({ stats }: CompareTableProps) {
|
|||||||
<Flex direction="column" justify="center">
|
<Flex direction="column" justify="center">
|
||||||
{provisions.map((provision, index) => (
|
{provisions.map((provision, index) => (
|
||||||
<img
|
<img
|
||||||
alt={
|
alt={unwrap(
|
||||||
provisionDefinitions.provisions[provision]
|
provisionDefinitions.provisions[provision]
|
||||||
.name
|
.name,
|
||||||
}
|
)}
|
||||||
key={provision}
|
key={provision}
|
||||||
src={asset(
|
src={asset(
|
||||||
`/icons/provisions/${provision}.webp`,
|
`/icons/provisions/${provision}.webp`,
|
||||||
@@ -473,11 +475,11 @@ export function CompareTable({ stats }: CompareTableProps) {
|
|||||||
<Flex direction="column" justify="center">
|
<Flex direction="column" justify="center">
|
||||||
{consumables.map((consumable, index) => (
|
{consumables.map((consumable, index) => (
|
||||||
<img
|
<img
|
||||||
alt={
|
alt={unwrap(
|
||||||
consumableDefinitions.consumables[
|
consumableDefinitions.consumables[
|
||||||
consumable
|
consumable
|
||||||
].name
|
].name,
|
||||||
}
|
)}
|
||||||
key={consumable}
|
key={consumable}
|
||||||
src={asset(
|
src={asset(
|
||||||
`/icons/consumables/${consumable}.webp`,
|
`/icons/consumables/${consumable}.webp`,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { type TankDefinition, tankIcon } from '@blitzkit/core';
|
|||||||
import { Flex, Text } from '@radix-ui/themes';
|
import { Flex, Text } from '@radix-ui/themes';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { Vector2 } from 'three';
|
import { Vector2 } from 'three';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
||||||
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell';
|
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell';
|
||||||
import { InsertionMarker } from './IntersectionMarker';
|
import { InsertionMarker } from './IntersectionMarker';
|
||||||
@@ -16,6 +17,7 @@ interface TankCardProps {
|
|||||||
export function TankCard({ index, tank }: TankCardProps) {
|
export function TankCard({ index, tank }: TankCardProps) {
|
||||||
const draggable = useRef<HTMLDivElement>(null);
|
const draggable = useRef<HTMLDivElement>(null);
|
||||||
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initial = new Vector2();
|
const initial = new Vector2();
|
||||||
@@ -118,7 +120,7 @@ export function TankCard({ index, tank }: TankCardProps) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={tank.name}
|
alt={unwrap(tank.name)}
|
||||||
src={tankIcon(tank.id)}
|
src={tankIcon(tank.id)}
|
||||||
width={64}
|
width={64}
|
||||||
height={64}
|
height={64}
|
||||||
@@ -136,7 +138,7 @@ export function TankCard({ index, tank }: TankCardProps) {
|
|||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tank.name}
|
{unwrap(tank.name)}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
type AccountListItem,
|
type AccountListItem,
|
||||||
type AccountListWithServer,
|
type AccountListWithServer,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import strings from '@blitzkit/core/lang/en.json';
|
import strings from '@blitzkit/i18n/strings/en.json';
|
||||||
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
|
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
sumCompositeStats,
|
sumCompositeStats,
|
||||||
type IndividualTankStats,
|
type IndividualTankStats,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import strings from '@blitzkit/core/lang/en.json';
|
import strings from '@blitzkit/i18n/strings/en.json';
|
||||||
import { ReloadIcon } from '@radix-ui/react-icons';
|
import { ReloadIcon } from '@radix-ui/react-icons';
|
||||||
import { ContextMenu } from '@radix-ui/themes';
|
import { ContextMenu } from '@radix-ui/themes';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||||
import { GlobeIcon } from '@radix-ui/react-icons';
|
import { GlobeIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, Text } from '@radix-ui/themes';
|
import { Flex, Text } from '@radix-ui/themes';
|
||||||
import type { LocaleAcceptorProps } from '../../hooks/useLocale';
|
import type { LocaleAcceptorProps } from '../../hooks/useLocale';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||||
import { Link, type LinkProps } from '@radix-ui/themes';
|
import { Link, type LinkProps } from '@radix-ui/themes';
|
||||||
import type { LocaleAcceptorProps } from '../hooks/useLocale';
|
import type { LocaleAcceptorProps } from '../hooks/useLocale';
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
import {} from '@blitzkit/core';
|
||||||
import {
|
import {
|
||||||
DEFAULT_LOCALE,
|
DEFAULT_LOCALE,
|
||||||
localizedStrings,
|
|
||||||
SUPPORTED_LOCALE_FLAGS,
|
|
||||||
SUPPORTED_LOCALES,
|
SUPPORTED_LOCALES,
|
||||||
} from '@blitzkit/core';
|
SUPPORTED_LOCALE_FLAGS,
|
||||||
|
localizedStrings,
|
||||||
|
} from '@blitzkit/i18n';
|
||||||
import { Select } from '@radix-ui/themes';
|
import { Select } from '@radix-ui/themes';
|
||||||
import type { LocaleAcceptorProps } from '../hooks/useLocale';
|
import type { LocaleAcceptorProps } from '../hooks/useLocale';
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ClockIcon, ReloadIcon } from '@radix-ui/react-icons';
|
|||||||
import { Flex, Text } from '@radix-ui/themes';
|
import { Flex, Text } from '@radix-ui/themes';
|
||||||
import { awaitableConsumableDefinitions } from '../../core/awaitables/consumableDefinitions';
|
import { awaitableConsumableDefinitions } from '../../core/awaitables/consumableDefinitions';
|
||||||
import { useDelta } from '../../hooks/useDelta';
|
import { useDelta } from '../../hooks/useDelta';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||||
import type { TankComponentButtonProps } from './TankComponentButton';
|
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||||
|
|
||||||
@@ -22,11 +23,12 @@ export function ConsumableButton({
|
|||||||
}: ConsumableButtonProps) {
|
}: ConsumableButtonProps) {
|
||||||
const cooldownDelta = useDelta(cooldown ?? 0);
|
const cooldownDelta = useDelta(cooldown ?? 0);
|
||||||
const durationDelta = useDelta(duration ?? 0);
|
const durationDelta = useDelta(duration ?? 0);
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericTankComponentButton
|
<GenericTankComponentButton
|
||||||
style={{ width: '6rem' }}
|
style={{ width: '6rem' }}
|
||||||
tooltip={consumableDefinitions.consumables[consumable].name}
|
tooltip={unwrap(consumableDefinitions.consumables[consumable].name)}
|
||||||
icon={asset(`icons/consumables/${consumable}.webp`)}
|
icon={asset(`icons/consumables/${consumable}.webp`)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { asset } from '@blitzkit/core';
|
import { asset } from '@blitzkit/core';
|
||||||
import { awaitableEquipmentDefinitions } from '../../core/awaitables/equipmentDefinitions';
|
import { awaitableEquipmentDefinitions } from '../../core/awaitables/equipmentDefinitions';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||||
import type { TankComponentButtonProps } from './TankComponentButton';
|
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||||
|
|
||||||
@@ -10,9 +11,11 @@ interface EquipmentButtonProps extends TankComponentButtonProps {
|
|||||||
const equipmentDefinitions = await awaitableEquipmentDefinitions;
|
const equipmentDefinitions = await awaitableEquipmentDefinitions;
|
||||||
|
|
||||||
export function EquipmentButton({ equipment, ...props }: EquipmentButtonProps) {
|
export function EquipmentButton({ equipment, ...props }: EquipmentButtonProps) {
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericTankComponentButton
|
<GenericTankComponentButton
|
||||||
tooltip={equipmentDefinitions.equipments[equipment].name}
|
tooltip={unwrap(equipmentDefinitions.equipments[equipment].name)}
|
||||||
icon={asset(`icons/equipment/${equipment}.webp`)}
|
icon={asset(`icons/equipment/${equipment}.webp`)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { asset } from '@blitzkit/core';
|
import { asset } from '@blitzkit/core';
|
||||||
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||||
import type { TankComponentButtonProps } from './TankComponentButton';
|
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||||
|
|
||||||
@@ -10,9 +11,11 @@ interface ProvisionButtonProps extends TankComponentButtonProps {
|
|||||||
const provisionDefinitions = await awaitableProvisionDefinitions;
|
const provisionDefinitions = await awaitableProvisionDefinitions;
|
||||||
|
|
||||||
export function ProvisionButton({ provision, ...props }: ProvisionButtonProps) {
|
export function ProvisionButton({ provision, ...props }: ProvisionButtonProps) {
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericTankComponentButton
|
<GenericTankComponentButton
|
||||||
tooltip={provisionDefinitions.provisions[provision].name}
|
tooltip={unwrap(provisionDefinitions.provisions[provision].name)}
|
||||||
icon={asset(`icons/provisions/${provision}.webp`)}
|
icon={asset(`icons/provisions/${provision}.webp`)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import { DropdownMenu, Flex } from '@radix-ui/themes';
|
import { DropdownMenu, Flex } from '@radix-ui/themes';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { useLocale } from '../hooks/useLocale';
|
||||||
import { ModuleButton } from './ModuleButtons/ModuleButton';
|
import { ModuleButton } from './ModuleButtons/ModuleButton';
|
||||||
|
|
||||||
interface ModuleManagerProps {
|
interface ModuleManagerProps {
|
||||||
@@ -35,6 +36,7 @@ export function ModuleManager({
|
|||||||
const [gunMenuOpen, setGunMenuOpen] = useState(false);
|
const [gunMenuOpen, setGunMenuOpen] = useState(false);
|
||||||
const [engineMenuOpen, setEngineMenuOpen] = useState(false);
|
const [engineMenuOpen, setEngineMenuOpen] = useState(false);
|
||||||
const [trackMenuOpen, setTrackMenuOpen] = useState(false);
|
const [trackMenuOpen, setTrackMenuOpen] = useState(false);
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap="1" wrap="wrap">
|
<Flex gap="1" wrap="wrap">
|
||||||
@@ -71,7 +73,7 @@ export function ModuleManager({
|
|||||||
module="turret"
|
module="turret"
|
||||||
discriminator={TIER_ROMAN_NUMERALS[turret.tier]}
|
discriminator={TIER_ROMAN_NUMERALS[turret.tier]}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
{turret.name}
|
{unwrap(turret.name)}
|
||||||
</DropdownMenu.RadioItem>
|
</DropdownMenu.RadioItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -117,7 +119,7 @@ export function ModuleManager({
|
|||||||
TIER_ROMAN_NUMERALS[gun.gun_type!.value.base.tier]
|
TIER_ROMAN_NUMERALS[gun.gun_type!.value.base.tier]
|
||||||
}
|
}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
{gun.gun_type!.value.base.name}
|
{unwrap(gun.gun_type!.value.base.name)}
|
||||||
</DropdownMenu.RadioItem>
|
</DropdownMenu.RadioItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -156,7 +158,7 @@ export function ModuleManager({
|
|||||||
module="engine"
|
module="engine"
|
||||||
discriminator={TIER_ROMAN_NUMERALS[engine.tier]}
|
discriminator={TIER_ROMAN_NUMERALS[engine.tier]}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
{engine.name}
|
{unwrap(engine.name)}
|
||||||
</DropdownMenu.RadioItem>
|
</DropdownMenu.RadioItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -195,7 +197,7 @@ export function ModuleManager({
|
|||||||
module="chassis"
|
module="chassis"
|
||||||
discriminator={TIER_ROMAN_NUMERALS[track.tier]}
|
discriminator={TIER_ROMAN_NUMERALS[track.tier]}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
{track.name}
|
{unwrap(track.name)}
|
||||||
</DropdownMenu.RadioItem>
|
</DropdownMenu.RadioItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
|||||||
}: TankCardProps,
|
}: TankCardProps,
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const { locale } = useLocale();
|
const { unwrap, locale } = useLocale();
|
||||||
const provideLink = !noLink && onSelect === undefined;
|
const provideLink = !noLink && onSelect === undefined;
|
||||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||||
const Icon = classIcons[tank.class];
|
const Icon = classIcons[tank.class];
|
||||||
@@ -73,7 +73,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={tank.name}
|
alt={unwrap(tank.name)}
|
||||||
src={asset(`icons/tanks/blitzkit/${tank.id}.webp`)}
|
src={asset(`icons/tanks/blitzkit/${tank.id}.webp`)}
|
||||||
className="image"
|
className="image"
|
||||||
draggable={false}
|
draggable={false}
|
||||||
@@ -89,7 +89,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
|||||||
>
|
>
|
||||||
<Icon className="class-icon" />
|
<Icon className="class-icon" />
|
||||||
<Text align="center" className="name">
|
<Text align="center" className="name">
|
||||||
{tank.name}
|
{unwrap(tank.name)}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface TankRowHeaderCellProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) {
|
export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) {
|
||||||
const { locale } = useLocale();
|
const { locale, unwrap } = useLocale();
|
||||||
const Icon = classIcons[tank.class];
|
const Icon = classIcons[tank.class];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -24,7 +24,7 @@ export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) {
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={tank.name}
|
alt={unwrap(tank.name)}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
src={tankIcon(tank.id)}
|
src={tankIcon(tank.id)}
|
||||||
style={{
|
style={{
|
||||||
@@ -70,7 +70,7 @@ export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) {
|
|||||||
<ExperimentIcon style={{ width: '1em', height: '1em' }} />
|
<ExperimentIcon style={{ width: '1em', height: '1em' }} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tank.name}
|
{unwrap(tank.name)}
|
||||||
</LinkI18n>
|
</LinkI18n>
|
||||||
</StickyRowHeaderCell>
|
</StickyRowHeaderCell>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ interface QuickLinkProps {
|
|||||||
|
|
||||||
export function QuickLink({ topResult }: QuickLinkProps) {
|
export function QuickLink({ topResult }: QuickLinkProps) {
|
||||||
const tankFilters = useStore($tankFilters);
|
const tankFilters = useStore($tankFilters);
|
||||||
const { locale } = useLocale();
|
const { locale, unwrap } = useLocale();
|
||||||
|
|
||||||
if (!tankFilters.search || !topResult || tankFilters.searching) return null;
|
if (!tankFilters.search || !topResult || tankFilters.searching) return null;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export function QuickLink({ topResult }: QuickLinkProps) {
|
|||||||
<TextField.Slot>
|
<TextField.Slot>
|
||||||
<LinkI18n locale={locale} href={`/tools/tankopedia/${topResult.id}`}>
|
<LinkI18n locale={locale} href={`/tools/tankopedia/${topResult.id}`}>
|
||||||
<Button variant="ghost">
|
<Button variant="ghost">
|
||||||
{topResult.name} <CaretRightIcon />
|
{unwrap(topResult.name)} <CaretRightIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</LinkI18n>
|
</LinkI18n>
|
||||||
</TextField.Slot>
|
</TextField.Slot>
|
||||||
|
|||||||
+4
-2
@@ -27,6 +27,7 @@ import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/pr
|
|||||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||||
|
import { useLocale } from '../../../../../hooks/useLocale';
|
||||||
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||||
import { Duel } from '../../../../../stores/duel';
|
import { Duel } from '../../../../../stores/duel';
|
||||||
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||||
@@ -39,6 +40,7 @@ const [equipmentDefinitions, provisionDefinitions] = await Promise.all([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export function Characteristics() {
|
export function Characteristics() {
|
||||||
|
const { unwrap } = useLocale();
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||||
@@ -146,7 +148,7 @@ export function Characteristics() {
|
|||||||
|
|
||||||
<Flex>
|
<Flex>
|
||||||
{gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => (
|
{gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => (
|
||||||
<Tooltip content={thisShell.name} key={thisShell.id}>
|
<Tooltip content={unwrap(thisShell.name)} key={thisShell.id}>
|
||||||
<IconButton
|
<IconButton
|
||||||
color={thisShell.id === shell.id ? undefined : 'gray'}
|
color={thisShell.id === shell.id ? undefined : 'gray'}
|
||||||
variant="soft"
|
variant="soft"
|
||||||
@@ -172,7 +174,7 @@ export function Characteristics() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={thisShell.name}
|
alt={unwrap(thisShell.name)}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||||
|
|||||||
+1
-1
@@ -103,7 +103,7 @@ function ModuleButton({
|
|||||||
gap={isTank ? '1' : undefined}
|
gap={isTank ? '1' : undefined}
|
||||||
>
|
>
|
||||||
<Text size="1" color="gray">
|
<Text size="1" color="gray">
|
||||||
{formatCompact(unlock.cost.value)}
|
{formatCompact(locale, unlock.cost.value)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
|||||||
import { awaitableConsumableDefinitions } from '../../core/awaitables/consumableDefinitions';
|
import { awaitableConsumableDefinitions } from '../../core/awaitables/consumableDefinitions';
|
||||||
import { awaitableGameDefinitions } from '../../core/awaitables/gameDefinitions';
|
import { awaitableGameDefinitions } from '../../core/awaitables/gameDefinitions';
|
||||||
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { Duel } from '../../stores/duel';
|
import { Duel } from '../../stores/duel';
|
||||||
|
|
||||||
const [gameDefinitions, consumableDefinitions, provisionDefinitions] =
|
const [gameDefinitions, consumableDefinitions, provisionDefinitions] =
|
||||||
@@ -13,6 +14,7 @@ const [gameDefinitions, consumableDefinitions, provisionDefinitions] =
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export function GameModeSection() {
|
export function GameModeSection() {
|
||||||
|
const { unwrap } = useLocale();
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
const tank = Duel.use((state) => state.protagonist.tank);
|
||||||
const roles = Object.entries(tank.roles);
|
const roles = Object.entries(tank.roles);
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ export function GameModeSection() {
|
|||||||
justify="center"
|
justify="center"
|
||||||
>
|
>
|
||||||
<Text size="4" weight="bold">
|
<Text size="4" weight="bold">
|
||||||
{gameMode.name}
|
{unwrap(gameMode.name)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Flex direction="column">
|
<Flex direction="column">
|
||||||
@@ -84,9 +86,9 @@ export function GameModeSection() {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
}}
|
}}
|
||||||
src={asset(`icons/provisions/${id}.webp`)}
|
src={asset(`icons/provisions/${id}.webp`)}
|
||||||
alt={provisions.name}
|
alt={unwrap(provisions.name)}
|
||||||
/>
|
/>
|
||||||
{provisions.name}
|
{unwrap(provisions.name)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
@@ -104,9 +106,9 @@ export function GameModeSection() {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
}}
|
}}
|
||||||
src={asset(`icons/consumables/${id}.webp`)}
|
src={asset(`icons/consumables/${id}.webp`)}
|
||||||
alt={consumable.name}
|
alt={unwrap(consumable.name)}
|
||||||
/>
|
/>
|
||||||
{consumable.name}
|
{unwrap(consumable.name)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
||||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const { strings } = useLocale();
|
const { strings, unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -244,7 +244,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={thisShell.name}
|
alt={unwrap(thisShell.name)}
|
||||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||||
style={{
|
style={{
|
||||||
width: '50%',
|
width: '50%',
|
||||||
@@ -362,7 +362,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
|||||||
<Button variant="ghost">
|
<Button variant="ghost">
|
||||||
<Flex gap="2" align="center">
|
<Flex gap="2" align="center">
|
||||||
<SmallTankIcon id={antagonistTank.id} size={16} />
|
<SmallTankIcon id={antagonistTank.id} size={16} />
|
||||||
{antagonistTank.name}
|
{unwrap(antagonistTank.name)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Button>
|
</Button>
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { NAVBAR_HEIGHT } from '../../../constants/navbar';
|
|||||||
import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions';
|
import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions';
|
||||||
import { Var } from '../../../core/radix/var';
|
import { Var } from '../../../core/radix/var';
|
||||||
import { useFullScreen } from '../../../hooks/useFullScreen';
|
import { useFullScreen } from '../../../hooks/useFullScreen';
|
||||||
|
import { useLocale } from '../../../hooks/useLocale';
|
||||||
import { Duel } from '../../../stores/duel';
|
import { Duel } from '../../../stores/duel';
|
||||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||||
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
||||||
@@ -18,6 +19,7 @@ import { TankSandboxLoader } from './components/TankSandboxLoader';
|
|||||||
const tankDefinitions = await awaitableTankDefinitions;
|
const tankDefinitions = await awaitableTankDefinitions;
|
||||||
|
|
||||||
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||||
|
const { unwrap } = useLocale();
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const canvas = useRef<HTMLCanvasElement>(null);
|
||||||
const isFullScreen = useFullScreen();
|
const isFullScreen = useFullScreen();
|
||||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||||
@@ -117,7 +119,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
wrap="nowrap"
|
wrap="nowrap"
|
||||||
color={treeColor}
|
color={treeColor}
|
||||||
>
|
>
|
||||||
{protagonist.name}
|
{unwrap(protagonist.name)}
|
||||||
</Heading>
|
</Heading>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -9,6 +9,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||||
import { App } from '../../../../../../stores/app';
|
import { App } from '../../../../../../stores/app';
|
||||||
import { Duel } from '../../../../../../stores/duel';
|
import { Duel } from '../../../../../../stores/duel';
|
||||||
import { Stars } from '../../Stars';
|
import { Stars } from '../../Stars';
|
||||||
@@ -24,6 +25,7 @@ export function VoteCaster() {
|
|||||||
const [postVote, setPostVote] = useState<boolean | null>(null);
|
const [postVote, setPostVote] = useState<boolean | null>(null);
|
||||||
// true = open, false = closed, null = casting
|
// true = open, false = closed, null = casting
|
||||||
const [showDialogue, setShowDialogue] = useState<boolean | null>(false);
|
const [showDialogue, setShowDialogue] = useState<boolean | null>(false);
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -35,7 +37,7 @@ export function VoteCaster() {
|
|||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
|
|
||||||
<Dialog.Content maxWidth="23rem">
|
<Dialog.Content maxWidth="23rem">
|
||||||
<Dialog.Title>Voting for {tank.name}</Dialog.Title>
|
<Dialog.Title>Voting for {unwrap(tank.name)}</Dialog.Title>
|
||||||
|
|
||||||
<Flex direction="column" mt="4">
|
<Flex direction="column" mt="4">
|
||||||
<Flex align="center" justify="between">
|
<Flex align="center" justify="between">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { formatCompact } from '@blitzkit/core';
|
import { formatCompact } from '@blitzkit/core';
|
||||||
import { Flex, Heading, Skeleton, Text } from '@radix-ui/themes';
|
import { Flex, Heading, Skeleton, Text } from '@radix-ui/themes';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
import { useLocale } from '../../../../../hooks/useLocale';
|
||||||
import { useTankVotes } from '../../../../../hooks/useTankVotes';
|
import { useTankVotes } from '../../../../../hooks/useTankVotes';
|
||||||
import { App } from '../../../../../stores/app';
|
import { App } from '../../../../../stores/app';
|
||||||
import { Duel } from '../../../../../stores/duel';
|
import { Duel } from '../../../../../stores/duel';
|
||||||
@@ -13,13 +14,14 @@ export const Votes = memo(
|
|||||||
const id = Duel.use((state) => state.protagonist.tank.id);
|
const id = Duel.use((state) => state.protagonist.tank.id);
|
||||||
const votes = useTankVotes(id);
|
const votes = useTankVotes(id);
|
||||||
const wargaming = App.use((state) => state.logins.wargaming);
|
const wargaming = App.use((state) => state.logins.wargaming);
|
||||||
|
const { locale } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="3">
|
<Flex direction="column" gap="3">
|
||||||
<Flex justify="between" align="center">
|
<Flex justify="between" align="center">
|
||||||
<Heading size="4">
|
<Heading size="4">
|
||||||
{!votes && <Skeleton height="1em" width="4rem" />}
|
{!votes && <Skeleton height="1em" width="4rem" />}
|
||||||
{votes && formatCompact(votes.votes)}{' '}
|
{votes && formatCompact(locale, votes.votes)}{' '}
|
||||||
{votes && (votes.votes > 1 ? 'votes' : 'vote')}
|
{votes && (votes.votes > 1 ? 'votes' : 'vote')}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function MetaSection() {
|
|||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||||
const [showSwapDialog, setShowSwapDialog] = useState(false);
|
const [showSwapDialog, setShowSwapDialog] = useState(false);
|
||||||
const { locale, strings } = useLocale();
|
const { locale, strings, unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex justify="center" align="center">
|
<Flex justify="center" align="center">
|
||||||
@@ -124,7 +124,7 @@ export function MetaSection() {
|
|||||||
<Flex direction="column" width="100%">
|
<Flex direction="column" width="100%">
|
||||||
{protagonist.name_full && (
|
{protagonist.name_full && (
|
||||||
<Listing label={strings.website.tools.tankopedia.meta.full_name}>
|
<Listing label={strings.website.tools.tankopedia.meta.full_name}>
|
||||||
{protagonist.name_full}
|
{unwrap(protagonist.name_full)}
|
||||||
</Listing>
|
</Listing>
|
||||||
)}
|
)}
|
||||||
<Listing label={strings.website.tools.tankopedia.meta.nation}>
|
<Listing label={strings.website.tools.tankopedia.meta.nation}>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const [tankDefinitions, averageDefinitions] = await Promise.all([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
||||||
const { locale, strings } = useLocale();
|
const { locale, strings, unwrap } = useLocale();
|
||||||
const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier);
|
const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier);
|
||||||
const tank = tankDefinitions.tanks[id];
|
const tank = tankDefinitions.tanks[id];
|
||||||
const nextTanks = nextIds?.map((id) => tankDefinitions.tanks[id]);
|
const nextTanks = nextIds?.map((id) => tankDefinitions.tanks[id]);
|
||||||
@@ -78,7 +78,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={tank.name}
|
alt={unwrap(tank.name)}
|
||||||
src={asset(`icons/tanks/big/${id}.webp`)}
|
src={asset(`icons/tanks/big/${id}.webp`)}
|
||||||
width={64}
|
width={64}
|
||||||
height={64}
|
height={64}
|
||||||
@@ -92,7 +92,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
|||||||
<Text color="gray" size="1">
|
<Text color="gray" size="1">
|
||||||
{TIER_ROMAN_NUMERALS[tank.tier]}
|
{TIER_ROMAN_NUMERALS[tank.tier]}
|
||||||
</Text>
|
</Text>
|
||||||
<Text wrap="nowrap">{tank.name}</Text>
|
<Text wrap="nowrap">{unwrap(tank.name)}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex gap="2" align="center">
|
<Flex gap="2" align="center">
|
||||||
<Text color="gray" size="1" wrap="nowrap">
|
<Text color="gray" size="1" wrap="nowrap">
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ import {
|
|||||||
type CompositeStats,
|
type CompositeStats,
|
||||||
type CompositeStatsKey,
|
type CompositeStatsKey,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import strings from '@blitzkit/core/lang/en.json';
|
import strings from '@blitzkit/i18n/strings/en.json';
|
||||||
import { amberDark, blueDark } from '@radix-ui/colors';
|
import { amberDark, blueDark } from '@radix-ui/colors';
|
||||||
import { Flex, Text, type FlexProps } from '@radix-ui/themes';
|
import { Flex, Text, type FlexProps } from '@radix-ui/themes';
|
||||||
import { times } from 'lodash-es';
|
import { times } from 'lodash-es';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { breakdownConfig } from '../constants/embeds';
|
import { breakdownConfig } from '../constants/embeds';
|
||||||
import { toRadiusVar } from '../core/radix/utils';
|
import { toRadiusVar } from '../core/radix/utils';
|
||||||
|
import { useLocale } from '../hooks/useLocale';
|
||||||
import { useEmbedStateCurry } from '../stores/embedState/utilities';
|
import { useEmbedStateCurry } from '../stores/embedState/utilities';
|
||||||
import { ClassHeavy } from './ClassIcon/components/ClassHeavy';
|
import { ClassHeavy } from './ClassIcon/components/ClassHeavy';
|
||||||
import { ClassLight } from './ClassIcon/components/ClassLight';
|
import { ClassLight } from './ClassIcon/components/ClassLight';
|
||||||
@@ -52,6 +53,7 @@ export function BreakdownEmbedCard({
|
|||||||
}: BreakdownEmbedCardProps) {
|
}: BreakdownEmbedCardProps) {
|
||||||
const { useEmbedState, useRichText } =
|
const { useEmbedState, useRichText } =
|
||||||
useEmbedStateCurry<typeof breakdownConfig>();
|
useEmbedStateCurry<typeof breakdownConfig>();
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
@@ -111,7 +113,7 @@ export function BreakdownEmbedCard({
|
|||||||
? 'Total'
|
? 'Total'
|
||||||
: tank === undefined
|
: tank === undefined
|
||||||
? `Unknown tank`
|
? `Unknown tank`
|
||||||
: tank.name}
|
: unwrap(tank.name)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { assertSecret, DEFAULT_LOCALE, localizedStrings } from '@blitzkit/core';
|
import { assertSecret } from '@blitzkit/core';
|
||||||
|
import { DEFAULT_LOCALE, localizedStrings } from '@blitzkit/i18n';
|
||||||
|
|
||||||
export function resolveBranchName(locale: string = DEFAULT_LOCALE) {
|
export function resolveBranchName(locale: string = DEFAULT_LOCALE) {
|
||||||
const strings = localizedStrings[locale];
|
const strings = localizedStrings[locale];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/core';
|
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||||
|
|
||||||
export function getBestLocale() {
|
export function getBestLocale() {
|
||||||
const desiredLocale =
|
const desiredLocale =
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE, localizedStrings } from '@blitzkit/core';
|
import { DEFAULT_LOCALE, localizedStrings } from '@blitzkit/i18n';
|
||||||
|
|
||||||
export function getStrings(locale: string = DEFAULT_LOCALE) {
|
export function getStrings(locale: string = DEFAULT_LOCALE) {
|
||||||
return localizedStrings[locale];
|
return localizedStrings[locale];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
|
||||||
import { createContext, useContext, type ReactNode } from 'react';
|
import { createContext, useContext, type ReactNode } from 'react';
|
||||||
import { getStrings } from '../core/i18n/getStrings';
|
import { getStrings } from '../core/i18n/getStrings';
|
||||||
|
|
||||||
@@ -29,8 +29,9 @@ export function useLocale() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const strings = getStrings(context.locale);
|
const strings = getStrings(context.locale);
|
||||||
|
const unwrap = unwrapper(context.locale);
|
||||||
|
|
||||||
return { locale: context.locale, strings };
|
return { locale: context.locale, strings, unwrap };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocaleAcceptorProps {
|
export interface LocaleAcceptorProps {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Footer from '../components/Footer.astro';
|
|||||||
import Navbar from '../components/Navbar.astro';
|
import Navbar from '../components/Navbar.astro';
|
||||||
import Party3 from '../components/Party3.astro';
|
import Party3 from '../components/Party3.astro';
|
||||||
import './base.css';
|
import './base.css';
|
||||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
noFooter?: boolean;
|
noFooter?: boolean;
|
||||||
@@ -40,7 +40,7 @@ const isRoot = pathname === '/';
|
|||||||
<Party3 />
|
<Party3 />
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { SUPPORTED_LOCALES } from '@blitzkit/core';
|
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||||
import { getBestLocale } from '../core/i18n/getBestLocale';
|
import { getBestLocale } from '../core/i18n/getBestLocale';
|
||||||
|
|
||||||
const isI18nPath = SUPPORTED_LOCALES.some((locale) =>
|
const isI18nPath = SUPPORTED_LOCALES.some((locale) =>
|
||||||
@@ -51,8 +51,6 @@ const isRoot = pathname === '/';
|
|||||||
window.location.pathname.startsWith(`/${locale}`),
|
window.location.pathname.startsWith(`/${locale}`),
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(bestLocale);
|
|
||||||
|
|
||||||
if (bestLocale !== currentPathLocale) {
|
if (bestLocale !== currentPathLocale) {
|
||||||
window.location.pathname = isI18nPath
|
window.location.pathname = isI18nPath
|
||||||
? window.location.pathname.replace(
|
? window.location.pathname.replace(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { App } from '../../../../stores/app';
|
|||||||
|
|
||||||
export const AUTH_PROVIDERS = ['wargaming'] as const;
|
export const AUTH_PROVIDERS = ['wargaming'] as const;
|
||||||
|
|
||||||
type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
export type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
||||||
|
|
||||||
export const AUTH_PROVIDER_NAMES: Record<AuthProvider, string> = {
|
export const AUTH_PROVIDER_NAMES: Record<AuthProvider, string> = {
|
||||||
wargaming: 'Wargaming',
|
wargaming: 'Wargaming',
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import type { GetStaticPaths } from 'astro';
|
|||||||
import { PageWrapper } from '../../../../components/PageWrapper';
|
import { PageWrapper } from '../../../../components/PageWrapper';
|
||||||
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
||||||
import { getStaticPaths as parentGetStaticPaths } from '../../index.astro';
|
import { getStaticPaths as parentGetStaticPaths } from '../../index.astro';
|
||||||
import { AUTH_PROVIDERS, AUTH_PROVIDER_NAMES, Authorize } from './_index';
|
import {
|
||||||
|
AUTH_PROVIDER_NAMES,
|
||||||
|
AUTH_PROVIDERS,
|
||||||
|
Authorize,
|
||||||
|
type AuthProvider,
|
||||||
|
} from './_index';
|
||||||
|
|
||||||
export const getStaticPaths = (() => {
|
export const getStaticPaths = (() => {
|
||||||
const parentPaths = parentGetStaticPaths();
|
const parentPaths = parentGetStaticPaths();
|
||||||
@@ -14,7 +19,7 @@ export const getStaticPaths = (() => {
|
|||||||
).flat();
|
).flat();
|
||||||
}) satisfies GetStaticPaths;
|
}) satisfies GetStaticPaths;
|
||||||
|
|
||||||
const { provider } = Astro.params;
|
const provider = Astro.params.provider as AuthProvider;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
---
|
---
|
||||||
import {
|
import { assertSecret } from '@blitzkit/core';
|
||||||
assertSecret,
|
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||||
DEFAULT_LOCALE,
|
|
||||||
SUPPORTED_LOCALES,
|
|
||||||
} from '@blitzkit/core';
|
|
||||||
import { Box, Flex, Heading } from '@radix-ui/themes';
|
import { Box, Flex, Heading } from '@radix-ui/themes';
|
||||||
import type { GetStaticPaths } from 'astro';
|
import type { GetStaticPaths } from 'astro';
|
||||||
import { AesonPlug } from '../../components/AesonPlug';
|
import { AesonPlug } from '../../components/AesonPlug';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||||
import { Flex, Switch, Text } from '@radix-ui/themes';
|
import { Flex, Switch, Text } from '@radix-ui/themes';
|
||||||
import { LocaleSwitcher } from '../../../components/LocaleSwitcher';
|
import { LocaleSwitcher } from '../../../components/LocaleSwitcher';
|
||||||
import { PageWrapper } from '../../../components/PageWrapper';
|
import { PageWrapper } from '../../../components/PageWrapper';
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
type IndividualAccountInfo,
|
type IndividualAccountInfo,
|
||||||
type IndividualTankStats,
|
type IndividualTankStats,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import strings from '@blitzkit/core/lang/en.json';
|
import strings from '@blitzkit/i18n/strings/en.json';
|
||||||
import {
|
import {
|
||||||
ArrowDownIcon,
|
ArrowDownIcon,
|
||||||
MagnifyingGlassIcon,
|
MagnifyingGlassIcon,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { CalloutsSection } from '../../../../../components/Tankopedia/CalloutsSe
|
|||||||
import { CharacteristicsSection } from '../../../../../components/Tankopedia/CharacteristicsSection';
|
import { CharacteristicsSection } from '../../../../../components/Tankopedia/CharacteristicsSection';
|
||||||
import { GameModeSection } from '../../../../../components/Tankopedia/GameModeSection';
|
import { GameModeSection } from '../../../../../components/Tankopedia/GameModeSection';
|
||||||
import { HeroSection } from '../../../../../components/Tankopedia/HeroSection';
|
import { HeroSection } from '../../../../../components/Tankopedia/HeroSection';
|
||||||
import { HistorySection } from '../../../../../components/Tankopedia/HistorySection';
|
|
||||||
import { MetaSection } from '../../../../../components/Tankopedia/MetaSection';
|
import { MetaSection } from '../../../../../components/Tankopedia/MetaSection';
|
||||||
import { TechTreeSection } from '../../../../../components/Tankopedia/TechTreeSection';
|
import { TechTreeSection } from '../../../../../components/Tankopedia/TechTreeSection';
|
||||||
import { VideoSection } from '../../../../../components/Tankopedia/VideoSection';
|
import { VideoSection } from '../../../../../components/Tankopedia/VideoSection';
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
asset,
|
asset,
|
||||||
createDefaultProvisions,
|
createDefaultProvisions,
|
||||||
createDefaultSkills,
|
createDefaultSkills,
|
||||||
DEFAULT_LOCALE,
|
|
||||||
fetchEquipmentDefinitions,
|
fetchEquipmentDefinitions,
|
||||||
fetchModelDefinitions,
|
fetchModelDefinitions,
|
||||||
fetchProvisionDefinitions,
|
fetchProvisionDefinitions,
|
||||||
@@ -11,6 +10,7 @@ import {
|
|||||||
fetchTankDefinitions,
|
fetchTankDefinitions,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
|
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
|
||||||
import type { GetStaticPaths } from 'astro';
|
import type { GetStaticPaths } from 'astro';
|
||||||
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||||
import { getStrings } from '../../../../../core/i18n/getStrings';
|
import { getStrings } from '../../../../../core/i18n/getStrings';
|
||||||
@@ -44,8 +44,9 @@ const tankModelDefinitions = await fetchModelDefinitions();
|
|||||||
const tank = tankDefinitions.tanks[id];
|
const tank = tankDefinitions.tanks[id];
|
||||||
const tankModelDefinition = tankModelDefinitions.models[tank.id];
|
const tankModelDefinition = tankModelDefinitions.models[tank.id];
|
||||||
const name = tank.name_full ?? tank.name;
|
const name = tank.name_full ?? tank.name;
|
||||||
|
const unwrap = unwrapper(locale);
|
||||||
const title = literals(strings.website.tools.tankopedia.tank_seo_title, [
|
const title = literals(strings.website.tools.tankopedia.tank_seo_title, [
|
||||||
name,
|
unwrap(name),
|
||||||
TIER_ROMAN_NUMERALS[tank.tier],
|
TIER_ROMAN_NUMERALS[tank.tier],
|
||||||
strings.common.nations_adjectives[
|
strings.common.nations_adjectives[
|
||||||
tank.nation as keyof typeof strings.common.nations_adjectives
|
tank.nation as keyof typeof strings.common.nations_adjectives
|
||||||
@@ -84,7 +85,7 @@ const characteristics = tankCharacteristics(
|
|||||||
const description = literals(
|
const description = literals(
|
||||||
strings.website.tools.tankopedia.tank_seo_description,
|
strings.website.tools.tankopedia.tank_seo_description,
|
||||||
[
|
[
|
||||||
name,
|
unwrap(name),
|
||||||
Math.round(characteristics.dpm).toLocaleString(locale),
|
Math.round(characteristics.dpm).toLocaleString(locale),
|
||||||
Math.round(characteristics.damage).toLocaleString(locale),
|
Math.round(characteristics.damage).toLocaleString(locale),
|
||||||
Math.round(characteristics.penetration).toLocaleString(locale),
|
Math.round(characteristics.penetration).toLocaleString(locale),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||||
import type { APIRoute, GetStaticPaths } from 'astro';
|
import type { APIRoute, GetStaticPaths } from 'astro';
|
||||||
import { fetchGlossary } from '../../../core/blitz/fetchGlossary';
|
import { fetchGlossary } from '../../../core/blitz/fetchGlossary';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import en from '@blitzkit/core/lang/en.json';
|
import en from '@blitzkit/i18n/strings/en.json';
|
||||||
import type { TankPerformanceSortType } from '.';
|
import type { TankPerformanceSortType } from '.';
|
||||||
|
|
||||||
export const TankPerformanceSortTypeNamesArray = Object.keys(
|
export const TankPerformanceSortTypeNamesArray = Object.keys(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type en from '@blitzkit/core/lang/en.json';
|
import type en from '@blitzkit/i18n/strings/en.json';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { createContextualStore } from '../../core/zustand/createContextualStore';
|
import { createContextualStore } from '../../core/zustand/createContextualStore';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type en from '@blitzkit/core/lang/en.json';
|
import type en from '@blitzkit/i18n/strings/en.json';
|
||||||
import * as lodash from 'lodash-es';
|
import * as lodash from 'lodash-es';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist, subscribeWithSelector } from 'zustand/middleware';
|
import { persist, subscribeWithSelector } from 'zustand/middleware';
|
||||||
|
|||||||
Reference in new issue
Block a user