mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d6bae847b | ||
|
|
cc70b3ccc3 | ||
|
|
e3bf619808 | ||
|
|
f6cb4f2518 | ||
|
|
f396d6c4a1 | ||
|
|
768c420ae4 | ||
|
|
7fba197bef | ||
|
|
b074481e1a | ||
|
|
987e35ef8f | ||
|
|
83c77b5945 | ||
|
|
e4f0120d67 | ||
|
|
2055c00154 | ||
|
|
c814230dcb | ||
|
|
2f31aadb29 | ||
|
|
3113bf3a47 | ||
|
|
70bd54e03b | ||
|
|
8863ac5fde | ||
|
|
b62009ce2b | ||
|
|
0f3d230f42 | ||
|
|
9db6b997c3 | ||
|
|
d9fcbd028b | ||
|
|
db74058075 | ||
|
|
4180736d37 | ||
|
|
382f04229e | ||
|
|
3c9ba9ec53 | ||
|
|
4848234264 | ||
|
|
2ad0fcfdf9 | ||
|
|
8f0894364b | ||
|
|
3695b8f7f4 | ||
|
|
4f8c1d416f | ||
|
|
453286677a | ||
|
|
53a49f199a | ||
|
|
fdfde46342 | ||
|
|
1d836b2053 | ||
|
|
6d448dc5c4 | ||
|
|
75d4d60b30 | ||
|
|
e7f47fd2fd | ||
|
|
2458d2c80c | ||
|
|
37a72e3bb5 | ||
|
|
415861f6e8 | ||
|
|
3124afa253 | ||
|
|
8523b9f44f | ||
|
|
71e0404890 | ||
|
|
c01fd1f4ff |
No files matched your search
+1
-1
@@ -8,7 +8,7 @@ package-lock.json
|
|||||||
dist/
|
dist/
|
||||||
dist.*
|
dist.*
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
/test.*
|
test.*
|
||||||
/docs/.vitepress/dist
|
/docs/.vitepress/dist
|
||||||
/docs/.vitepress/cache
|
/docs/.vitepress/cache
|
||||||
servers.csv
|
servers.csv
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# BlitzKit 1.26.5
|
||||||
|
|
||||||
|
¡Ahora en 6 idiomas! This version of BlitzKit adds support for 4 new languages totalling up to 6! [Please help translate on Crowdin](https://crowdin.com/project/blitzkrieg).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added Croatian, Russian, Ukrainian, and Chinese
|
||||||
|
- Pre-existing languages: English, Spanish
|
||||||
|
- Added better lighting making tanks a little less yellow and a bit more realistic
|
||||||
|
- Added custom tank icons (some may be missing when the update drops; just give them an hour or so to update)
|
||||||
|
- Increased the maximum recently viewed tanks list size to 14
|
||||||
|
- Tweaked the tank name in the hero section of the Tankopedia to allow the tank to be in the center of the page
|
||||||
|
- The headers in the Performance tool are now sticky just like the Compare tool
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Removed unnecessary third party script that did absolutely nothing
|
||||||
|
- Localizations are now stricter in what they allow, reducing errors in the Discord bot
|
||||||
|
- Added 50 gallons of tomato soup to the bot in an attempt to keep it alive
|
||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkit",
|
"name": "blitzkit",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.26.4",
|
"version": "1.26.5",
|
||||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "./types.d.ts",
|
"types": "./types.d.ts",
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"sharp": "^0.33.5",
|
||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type Result =
|
|||||||
available_from?: Date;
|
available_from?: Date;
|
||||||
available_before?: Date;
|
available_before?: Date;
|
||||||
price?: Price;
|
price?: Price;
|
||||||
next_price?: Price | null;
|
next_price: Price | null;
|
||||||
available: true;
|
available: true;
|
||||||
display?: boolean;
|
display?: boolean;
|
||||||
next_price_datetime: Date;
|
next_price_datetime: Date;
|
||||||
@@ -109,16 +109,18 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const name = `[${tank.name}](<https://blitzkit.app/tools/tankopedia/${
|
const name = `[${tank.name}](<https://blitzkit.app/tools/tankopedia/${
|
||||||
tank.id
|
tank.id
|
||||||
}>)`;
|
}>)`;
|
||||||
const next = translate('bot.commands.auction.body.next', [
|
const next =
|
||||||
`<:gold:1317173197082333244> ${data.next_price!.value.toLocaleString(
|
data.next_price === null
|
||||||
|
? ''
|
||||||
|
: `\n-# ${translate('bot.commands.auction.body.next', [
|
||||||
|
`<:gold:1317173197082333244> ${data.next_price.value.toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale,
|
||||||
)}`,
|
)}`,
|
||||||
]);
|
])}`;
|
||||||
const available = translate('bot.commands.auction.body.available', [
|
const available = translate('bot.commands.auction.body.available', [
|
||||||
data.current_count.toLocaleString(interaction.locale),
|
data.current_count.toLocaleString(interaction.locale),
|
||||||
data.initial_count.toLocaleString(interaction.locale),
|
data.initial_count.toLocaleString(interaction.locale),
|
||||||
]);
|
]);
|
||||||
const nextLine = `\n-# ${next}`;
|
|
||||||
const isOut = data.current_count === 0;
|
const isOut = data.current_count === 0;
|
||||||
const outString = isOut ? '~~' : '';
|
const outString = isOut ? '~~' : '';
|
||||||
const isLow = data.current_count <= WARNING_COUNT;
|
const isLow = data.current_count <= WARNING_COUNT;
|
||||||
@@ -127,7 +129,7 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
outString
|
outString
|
||||||
}${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString(
|
}${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale,
|
||||||
)}${outString}\n-#${isLow ? ' ‼️ ' : ' '}${available}${isOut ? '' : nextLine}`;
|
)}${outString}\n-#${isLow ? ' ‼️ ' : ' '}${available}${isOut ? '' : next}`;
|
||||||
});
|
});
|
||||||
const header = `${title}\n${subtitle}`;
|
const header = `${title}\n${subtitle}`;
|
||||||
const lines = [header, ...body];
|
const lines = [header, ...body];
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
return option
|
return option
|
||||||
.setName('threshold')
|
.setName('threshold')
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.commands.inactive.options.threshold'),
|
localizationObject(
|
||||||
|
'bot.commands.inactive.options.threshold',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
translate('bot.commands.inactive.options.threshold.description'),
|
translate('bot.commands.inactive.options.threshold.description'),
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
'bot.commands.rating_leaderboard.options.limit',
|
'bot.commands.rating_leaderboard.options.limit',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
@@ -87,6 +89,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
'bot.commands.rating_leaderboard.options.season',
|
'bot.commands.rating_leaderboard.options.season',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
@@ -148,6 +152,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
'bot.commands.rating_leaderboard.options.league',
|
'bot.commands.rating_leaderboard.options.league',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
option
|
option
|
||||||
.setName(t`bot.commands.research.options.target_tank`)
|
.setName(t`bot.commands.research.options.target_tank`)
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.commands.research.options.target_tank'),
|
localizationObject(
|
||||||
|
'bot.commands.research.options.target_tank',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
t`bot.commands.research.options.target_tank.description`,
|
t`bot.commands.research.options.target_tank.description`,
|
||||||
@@ -53,7 +57,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
option
|
option
|
||||||
.setName(t`bot.commands.research.options.starting_tank`)
|
.setName(t`bot.commands.research.options.starting_tank`)
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.commands.research.options.starting_tank'),
|
localizationObject(
|
||||||
|
'bot.commands.research.options.starting_tank',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
t`bot.commands.research.options.starting_tank.description`,
|
t`bot.commands.research.options.starting_tank.description`,
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
return option
|
return option
|
||||||
.setName(t`bot.commands.search_clans.options.limit`)
|
.setName(t`bot.commands.search_clans.options.limit`)
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.commands.search_clans.options.limit'),
|
localizationObject(
|
||||||
|
'bot.commands.search_clans.options.limit',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
translate('bot.commands.search_clans.options.limit.description', [
|
translate('bot.commands.search_clans.options.limit.description', [
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
option
|
option
|
||||||
.setName(t`bot.commands.search_players.options.limit`)
|
.setName(t`bot.commands.search_players.options.limit`)
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.commands.search_players.options.limit'),
|
localizationObject(
|
||||||
|
'bot.commands.search_players.options.limit',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
translate('bot.commands.search_players.options.limit.description', [
|
translate('bot.commands.search_players.options.limit.description', [
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ export function addClanChoices(option: SlashCommandStringOption) {
|
|||||||
|
|
||||||
return option
|
return option
|
||||||
.setName('clan')
|
.setName('clan')
|
||||||
.setNameLocalizations(localizationObject('bot.common.options.clan'))
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.common.options.clan', undefined, true),
|
||||||
|
)
|
||||||
.setDescription(translate('bot.common.options.clan.description'))
|
.setDescription(translate('bot.common.options.clan.description'))
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject('bot.common.options.clan.description'),
|
localizationObject('bot.common.options.clan.description'),
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ export function addFilterOptions<
|
|||||||
option
|
option
|
||||||
.setName(translate('bot.common.options.filters.nation'))
|
.setName(translate('bot.common.options.filters.nation'))
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.common.options.filters.nation'),
|
localizationObject(
|
||||||
|
'bot.common.options.filters.nation',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
translate('bot.common.options.filters.nation.description'),
|
translate('bot.common.options.filters.nation.description'),
|
||||||
@@ -52,7 +56,11 @@ export function addFilterOptions<
|
|||||||
option
|
option
|
||||||
.setName(translate('bot.common.options.filters.class'))
|
.setName(translate('bot.common.options.filters.class'))
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.common.options.filters.class'),
|
localizationObject(
|
||||||
|
'bot.common.options.filters.class',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
translate('bot.common.options.filters.class.description'),
|
translate('bot.common.options.filters.class.description'),
|
||||||
@@ -78,7 +86,11 @@ export function addFilterOptions<
|
|||||||
option
|
option
|
||||||
.setName(translate('bot.common.options.filters.type'))
|
.setName(translate('bot.common.options.filters.type'))
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject('bot.common.options.filters.type'),
|
localizationObject(
|
||||||
|
'bot.common.options.filters.type',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
translate('bot.common.options.filters.type.description'),
|
translate('bot.common.options.filters.type.description'),
|
||||||
|
|||||||
@@ -18,12 +18,20 @@ export function addPeriodSubCommands(
|
|||||||
) {
|
) {
|
||||||
const { translate } = translator(Locale.EnglishUS);
|
const { translate } = translator(Locale.EnglishUS);
|
||||||
const subcommandNameLocalizations = {
|
const subcommandNameLocalizations = {
|
||||||
today: localizationObject('bot.common.subcommands.today'),
|
today: localizationObject('bot.common.subcommands.today', undefined, true),
|
||||||
30: localizationObject('bot.common.subcommands.30'),
|
30: localizationObject('bot.common.subcommands.30', undefined, true),
|
||||||
60: localizationObject('bot.common.subcommands.60'),
|
60: localizationObject('bot.common.subcommands.60', undefined, true),
|
||||||
90: localizationObject('bot.common.subcommands.90'),
|
90: localizationObject('bot.common.subcommands.90', undefined, true),
|
||||||
career: localizationObject('bot.common.subcommands.career'),
|
career: localizationObject(
|
||||||
custom: localizationObject('bot.common.subcommands.custom'),
|
'bot.common.subcommands.career',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
custom: localizationObject(
|
||||||
|
'bot.common.subcommands.custom',
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
const subcommandDescriptionLocalizations = {
|
const subcommandDescriptionLocalizations = {
|
||||||
today: localizationObject('bot.common.subcommands.today.description'),
|
today: localizationObject('bot.common.subcommands.today.description'),
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ export function addRegionChoices(option: SlashCommandStringOption) {
|
|||||||
|
|
||||||
return option
|
return option
|
||||||
.setName(t`bot.common.options.region`)
|
.setName(t`bot.common.options.region`)
|
||||||
.setNameLocalizations(localizationObject('bot.common.options.region'))
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.common.options.region', undefined, true),
|
||||||
|
)
|
||||||
.setDescription(t`bot.common.options.region.description`)
|
.setDescription(t`bot.common.options.region.description`)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject('bot.common.options.region.description'),
|
localizationObject('bot.common.options.region.description'),
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ export function addTankChoices(option: SlashCommandStringOption) {
|
|||||||
|
|
||||||
return option
|
return option
|
||||||
.setName(translate('bot.common.options.tank'))
|
.setName(translate('bot.common.options.tank'))
|
||||||
.setNameLocalizations(localizationObject('bot.common.options.tank'))
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.common.options.tank', undefined, true),
|
||||||
|
)
|
||||||
.setDescription(translate('bot.common.options.tank.description'))
|
.setDescription(translate('bot.common.options.tank.description'))
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject('bot.common.options.tank.description'),
|
localizationObject('bot.common.options.tank.description'),
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ export function addTierChoices(option: SlashCommandStringOption) {
|
|||||||
|
|
||||||
return option
|
return option
|
||||||
.setName(t`bot.common.options.tier`)
|
.setName(t`bot.common.options.tier`)
|
||||||
.setNameLocalizations(localizationObject('bot.common.options.tier'))
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.common.options.tier', undefined, true),
|
||||||
|
)
|
||||||
.setDescription(t`bot.common.options.tier.description`)
|
.setDescription(t`bot.common.options.tier.description`)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject('bot.common.options.tier.description'),
|
localizationObject('bot.common.options.tier.description'),
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ export function addUsernameChoices(option: SlashCommandStringOption) {
|
|||||||
|
|
||||||
return option
|
return option
|
||||||
.setName(t`bot.common.options.username`)
|
.setName(t`bot.common.options.username`)
|
||||||
.setNameLocalizations(localizationObject('bot.common.options.username'))
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.common.options.username', undefined, true),
|
||||||
|
)
|
||||||
.setDescription(t`bot.common.options.username.description`)
|
.setDescription(t`bot.common.options.username.description`)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject('bot.common.options.username.description'),
|
localizationObject('bot.common.options.username.description'),
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ export function createLocalizedCommand(
|
|||||||
const { translate } = translator(Locale.EnglishUS);
|
const { translate } = translator(Locale.EnglishUS);
|
||||||
const slashCommand = new SlashCommandBuilder()
|
const slashCommand = new SlashCommandBuilder()
|
||||||
.setName(command)
|
.setName(command)
|
||||||
.setNameLocalizations(localizationObject(`bot.commands.${commandPathItem}`))
|
.setNameLocalizations(
|
||||||
|
localizationObject(`bot.commands.${commandPathItem}`, undefined, true),
|
||||||
|
)
|
||||||
.setDescription(translate(`bot.commands.${commandPathItem}.description`))
|
.setDescription(translate(`bot.commands.${commandPathItem}.description`))
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(`bot.commands.${commandPathItem}.description`),
|
localizationObject(`bot.commands.${commandPathItem}.description`),
|
||||||
@@ -38,6 +40,8 @@ export function createLocalizedCommand(
|
|||||||
if ('subcommand' in subItem) {
|
if ('subcommand' in subItem) {
|
||||||
const subcommandNameLocalizations = localizationObject(
|
const subcommandNameLocalizations = localizationObject(
|
||||||
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`,
|
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
const subcommandDescriptionLocalizations = localizationObject(
|
const subcommandDescriptionLocalizations = localizationObject(
|
||||||
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
|
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
|
||||||
@@ -59,6 +63,8 @@ export function createLocalizedCommand(
|
|||||||
} else {
|
} else {
|
||||||
const subcommandNameLocalizations = localizationObject(
|
const subcommandNameLocalizations = localizationObject(
|
||||||
`bot.commands.${commandPathItem}.groups.${subItem.group}`,
|
`bot.commands.${commandPathItem}.groups.${subItem.group}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
const subcommandDescriptionLocalizations = localizationObject(
|
const subcommandDescriptionLocalizations = localizationObject(
|
||||||
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
|
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
|
||||||
|
|||||||
@@ -2,13 +2,29 @@ import { Locale } from 'discord.js';
|
|||||||
import { SUPPORTED_LOCALES } from '../localization/strings/constants';
|
import { SUPPORTED_LOCALES } from '../localization/strings/constants';
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from '../localization/translator';
|
||||||
|
|
||||||
export function localizationObject(path: string, literals?: string[]) {
|
const validNameRegex = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
|
||||||
|
|
||||||
|
export function localizationObject(
|
||||||
|
path: string,
|
||||||
|
literals?: string[],
|
||||||
|
strict = false,
|
||||||
|
) {
|
||||||
return SUPPORTED_LOCALES.reduce<Partial<Record<Locale, string>>>(
|
return SUPPORTED_LOCALES.reduce<Partial<Record<Locale, string>>>(
|
||||||
(localizations, locale) => {
|
(localizations, locale) => {
|
||||||
const { translate } = translator(locale);
|
const { translate } = translator(locale);
|
||||||
|
const translation = translate(path, literals);
|
||||||
|
const isValid = !strict || validNameRegex.test(translation);
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
console.warn(
|
||||||
|
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`,
|
||||||
|
);
|
||||||
|
return localizations;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...localizations,
|
...localizations,
|
||||||
[locale]: translate(path, literals),
|
[locale]: translation,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
import { Locale } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
|
|
||||||
export const SUPPORTED_LOCALES: Locale[] = [Locale.EnglishUS, Locale.SpanishES];
|
export const SUPPORTED_LOCALES: Locale[] = [
|
||||||
|
Locale.EnglishUS,
|
||||||
|
Locale.SpanishES,
|
||||||
|
Locale.Croatian,
|
||||||
|
Locale.Russian,
|
||||||
|
Locale.Ukrainian,
|
||||||
|
Locale.ChineseCN,
|
||||||
|
];
|
||||||
@@ -126,9 +126,7 @@ commands.then((awaitedCommands) => {
|
|||||||
|
|
||||||
rest.put(
|
rest.put(
|
||||||
Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)),
|
Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)),
|
||||||
{
|
{ body },
|
||||||
body,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+55
-27
@@ -5,51 +5,73 @@ import {
|
|||||||
} from './core/blitzkit/nonBlockingPromises';
|
} from './core/blitzkit/nonBlockingPromises';
|
||||||
import { manager } from './core/discord/manager';
|
import { manager } from './core/discord/manager';
|
||||||
|
|
||||||
console.log('pre manager event listeners and spawn');
|
async function setupShardManager() {
|
||||||
|
console.log('Pre-manager setup starting...');
|
||||||
|
|
||||||
let isFirst = true;
|
const readyShards = new Set();
|
||||||
const interval = setInterval(() => {
|
const totalShards = manager.totalShards;
|
||||||
if (!isFirst) {
|
|
||||||
console.warn('respawning all shards because manager died');
|
|
||||||
manager.respawnAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
isFirst = false;
|
const allShardsReady = new Promise((resolve, reject) => {
|
||||||
}, 5000);
|
const timeout = setTimeout(() => {
|
||||||
|
reject(new Error('Shard initialization timed out after 60 seconds'));
|
||||||
|
}, 60000);
|
||||||
|
|
||||||
const shards = await manager
|
manager.on('shardCreate', (shard) => {
|
||||||
.on('shardCreate', (shard) => {
|
|
||||||
clearInterval(interval);
|
|
||||||
console.log(`🟡 Launching shard ${shard.id}`);
|
console.log(`🟡 Launching shard ${shard.id}`);
|
||||||
shard.on('ready', () => console.log(`🟢 Launched shard ${shard.id}`));
|
|
||||||
})
|
|
||||||
.spawn();
|
|
||||||
|
|
||||||
console.log('post manager event listeners and spawn');
|
shard.on('ready', () => {
|
||||||
|
console.log(`🟢 Launched shard ${shard.id}`);
|
||||||
|
readyShards.add(shard.id);
|
||||||
|
|
||||||
|
if (readyShards.size === totalShards) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
shard.on('error', (error) => {
|
||||||
|
console.error(`❌ Shard ${shard.id} encountered an error:`, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
shard.on('disconnect', () => {
|
||||||
|
console.warn(`⚠️ Shard ${shard.id} disconnected`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Spawning shards...');
|
||||||
|
const shards = await manager.spawn();
|
||||||
|
console.log('Shards spawned, waiting for ready state...');
|
||||||
|
|
||||||
|
await allShardsReady;
|
||||||
|
console.log('All shards ready, setting up presence...');
|
||||||
|
|
||||||
try {
|
|
||||||
let servers = 0;
|
let servers = 0;
|
||||||
let channels = 0;
|
let channels = 0;
|
||||||
let users = 0;
|
let users = 0;
|
||||||
|
|
||||||
for (const [, shard] of shards) {
|
for (const [, shard] of shards) {
|
||||||
// wtf is this typing bro???
|
try {
|
||||||
const guilds = (await shard.fetchClientValue('guilds.cache')) as any;
|
const guilds = (await shard.fetchClientValue('guilds.cache')) as any;
|
||||||
|
|
||||||
servers += guilds.length;
|
servers += guilds.length;
|
||||||
|
|
||||||
for (const guild of guilds) {
|
for (const guild of guilds) {
|
||||||
channels += guild.channels.length;
|
channels += guild.channels.length;
|
||||||
users += guild.memberCount;
|
users += guild.memberCount;
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch statistics from shard:`, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const [awaitedTankDefinitions, awaitedDiscoveredIdsDefinitions] =
|
||||||
|
await Promise.all([tankDefinitions, discoveredIdsDefinitions]);
|
||||||
|
|
||||||
const tanks = Object.values(awaitedTankDefinitions.tanks).length;
|
const tanks = Object.values(awaitedTankDefinitions.tanks).length;
|
||||||
const awaitedDiscoveredIdsDefinitions = await discoveredIdsDefinitions;
|
|
||||||
const usernames = awaitedDiscoveredIdsDefinitions.count;
|
const usernames = awaitedDiscoveredIdsDefinitions.count;
|
||||||
|
|
||||||
manager.broadcastEval(
|
await manager.broadcastEval(
|
||||||
(client, { servers, users, channels, tanks, usernames }) => {
|
(client, { servers, users, channels, tanks, usernames }) => {
|
||||||
const bios = [
|
const bios = [
|
||||||
`Living in ${servers.toLocaleString()} servers`,
|
`Living in ${servers.toLocaleString()} servers`,
|
||||||
@@ -70,9 +92,15 @@ try {
|
|||||||
},
|
},
|
||||||
{ context: { servers, channels, users, tanks, usernames } },
|
{ context: { servers, channels, users, tanks, usernames } },
|
||||||
);
|
);
|
||||||
} catch (error) {
|
|
||||||
console.warn(
|
console.log('Bot startup complete!');
|
||||||
'An error occurred when setting up presence, ignoring to keep the process alive',
|
} catch (error) {
|
||||||
error,
|
console.error('Critical error during startup:', error);
|
||||||
);
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupShardManager().catch((error) => {
|
||||||
|
console.error('Failed to start the bot:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -142,7 +142,9 @@
|
|||||||
"no_tank_stats": "# No hay estadísticas de tanque disponibles\nEste jugador no tiene estadísticas disponibles. Este podría no ser el jugador que estás buscando.",
|
"no_tank_stats": "# No hay estadísticas de tanque disponibles\nEste jugador no tiene estadísticas disponibles. Este podría no ser el jugador que estás buscando.",
|
||||||
"get_help": "Servidor de ayuda de Discord",
|
"get_help": "Servidor de ayuda de Discord",
|
||||||
"uncaught_error": "# ¡Blitzkrieg sufrió un error!\nLo sentimos mucho. Siéntete libre de [unirte al servidor oficial de Discord](https://discord.gg/nDt7AjGJQH) para obtener ayuda.",
|
"uncaught_error": "# ¡Blitzkrieg sufrió un error!\nLo sentimos mucho. Siéntete libre de [unirte al servidor oficial de Discord](https://discord.gg/nDt7AjGJQH) para obtener ayuda.",
|
||||||
"preview": "# Este comando es una vista previa\n\nSolo puedes acceder a este comando en [el servidor oficial de Discord](https://discord.gg/nDt7AjGJQH). Generalmente este comando estará disponible pronto."
|
"preview": "# Este comando es una vista previa\n\nSolo puedes acceder a este comando en [el servidor oficial de Discord](https://discord.gg/nDt7AjGJQH). Generalmente este comando estará disponible pronto.",
|
||||||
|
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
|
||||||
|
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
|
||||||
},
|
},
|
||||||
"wn8": {
|
"wn8": {
|
||||||
"$": "WN8",
|
"$": "WN8",
|
||||||
@@ -211,13 +213,13 @@
|
|||||||
"$": "nación",
|
"$": "nación",
|
||||||
"description": "Nación"
|
"description": "Nación"
|
||||||
},
|
},
|
||||||
"tank_class": {
|
"class": {
|
||||||
"$": "clase-de-tanque",
|
"$": "class",
|
||||||
"description": "Clase de tanque"
|
"description": "Class"
|
||||||
},
|
},
|
||||||
"tree_type": {
|
"type": {
|
||||||
"$": "tipo-de-árbol",
|
"$": "type",
|
||||||
"description": "Tipo de árbol"
|
"description": "Type"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"region": {
|
"region": {
|
||||||
|
|||||||
+74
-72
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"wn8_percentile": {
|
"wn8_percentile": {
|
||||||
"0": "Jako loše",
|
"0": "Very bad",
|
||||||
"1": "Loše",
|
"1": "Bad",
|
||||||
"2": "Ispod prosečnog",
|
"2": "Below average",
|
||||||
"3": "Prosečno",
|
"3": "Average",
|
||||||
"4": "Nad prosečno",
|
"4": "Above average",
|
||||||
"5": "Dobro",
|
"5": "Good",
|
||||||
"6": "Jako dobro",
|
"6": "Very good",
|
||||||
"7": "Super",
|
"7": "Great",
|
||||||
"8": "Legendarno",
|
"8": "Unicum",
|
||||||
"9": "Super Legendarno"
|
"9": "Super unicum"
|
||||||
},
|
},
|
||||||
"bkni_percentile": {
|
"bkni_percentile": {
|
||||||
"0": "Super malum",
|
"0": "Super malum",
|
||||||
@@ -26,15 +26,15 @@
|
|||||||
"10": "Super unicum"
|
"10": "Super unicum"
|
||||||
},
|
},
|
||||||
"nations": {
|
"nations": {
|
||||||
"ussr": "S.S.S.R.",
|
"ussr": "U.S.S.R.",
|
||||||
"germany": "Nemačka",
|
"germany": "Germany",
|
||||||
"usa": "S.A.D.",
|
"usa": "U.S.A.",
|
||||||
"china": "Kina",
|
"china": "China",
|
||||||
"uk": "Vel. Brit.",
|
"uk": "U.K.",
|
||||||
"france": "Francuska",
|
"france": "France",
|
||||||
"japan": "Japan",
|
"japan": "Japan",
|
||||||
"european": "Evropska Unija",
|
"european": "European Nation",
|
||||||
"other": "Hibridna nacija"
|
"other": "Hybrid Nation"
|
||||||
},
|
},
|
||||||
"nations_adjectives": {
|
"nations_adjectives": {
|
||||||
"ussr": "U.S.S.R.",
|
"ussr": "U.S.S.R.",
|
||||||
@@ -75,9 +75,9 @@
|
|||||||
},
|
},
|
||||||
"regions": {
|
"regions": {
|
||||||
"normal": {
|
"normal": {
|
||||||
"com": "Severna Amerika",
|
"com": "North America",
|
||||||
"eu": "Evropa",
|
"eu": "Europe",
|
||||||
"asia": "Azija-Pasifik"
|
"asia": "Asia-Pacific"
|
||||||
},
|
},
|
||||||
"short": {
|
"short": {
|
||||||
"com": "NA",
|
"com": "NA",
|
||||||
@@ -86,11 +86,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"leagues": {
|
"leagues": {
|
||||||
"diamond": "Dijamant",
|
"diamond": "Diamond",
|
||||||
"platinum": "Platina",
|
"platinum": "Platinum",
|
||||||
"gold": "Zlato",
|
"gold": "Gold",
|
||||||
"silver": "Srebro",
|
"silver": "Silver",
|
||||||
"bronze": "Bronza"
|
"bronze": "Bronze"
|
||||||
},
|
},
|
||||||
"composite_stats": {
|
"composite_stats": {
|
||||||
"normalized_damage_dealt": "Damage",
|
"normalized_damage_dealt": "Damage",
|
||||||
@@ -136,58 +136,60 @@
|
|||||||
"errors": {
|
"errors": {
|
||||||
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
|
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
|
||||||
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
||||||
"player_not_linked": "# I don't know who you are\nUse the `/link` command to get started.",
|
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
|
||||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
|
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
|
||||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
|
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
|
||||||
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
||||||
"get_help": "Help Discord server",
|
"get_help": "Help Discord server",
|
||||||
"uncaught_error": "# Blitzkrieg ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
||||||
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
|
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
|
||||||
|
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
|
||||||
|
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
|
||||||
},
|
},
|
||||||
"wn8": {
|
"wn8": {
|
||||||
"$": "WN8",
|
"$": "WN8",
|
||||||
"very_bad": "Jako loše",
|
"very_bad": "Very bad",
|
||||||
"bad": "Loše",
|
"bad": "Bad",
|
||||||
"below_average": "Ispod prosečnog",
|
"below_average": "Below average",
|
||||||
"average": "Prosečno",
|
"average": "Average",
|
||||||
"above_average": "Nad prosečno",
|
"above_average": "Above average",
|
||||||
"good": "Dobro",
|
"good": "Good",
|
||||||
"very_good": "Jako dobro",
|
"very_good": "Very good",
|
||||||
"great": "Super",
|
"great": "Great",
|
||||||
"unicum": "Legendarno",
|
"unicum": "Unicum",
|
||||||
"super_unicum": "Super Legendarno"
|
"super_unicum": "Super unicum"
|
||||||
},
|
},
|
||||||
"subcommands": {
|
"subcommands": {
|
||||||
"today": {
|
"today": {
|
||||||
"$": "danas",
|
"$": "today",
|
||||||
"description": "Danas"
|
"description": "Today"
|
||||||
},
|
},
|
||||||
"30": {
|
"30": {
|
||||||
"$": "30",
|
"$": "30",
|
||||||
"description": "30 dana"
|
"description": "30 days"
|
||||||
},
|
},
|
||||||
"60": {
|
"60": {
|
||||||
"$": "60",
|
"$": "60",
|
||||||
"description": "60 dana"
|
"description": "60 days"
|
||||||
},
|
},
|
||||||
"90": {
|
"90": {
|
||||||
"$": "90",
|
"$": "90",
|
||||||
"description": "90 dana"
|
"description": "90 days"
|
||||||
},
|
},
|
||||||
"career": {
|
"career": {
|
||||||
"$": "karijera",
|
"$": "career",
|
||||||
"description": "Karijera"
|
"description": "Career"
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"$": "proizvoljno",
|
"$": "custom",
|
||||||
"description": "Proizvoljna daljina",
|
"description": "Custom range",
|
||||||
"options": {
|
"options": {
|
||||||
"start": {
|
"start": {
|
||||||
"$": "kreni",
|
"$": "start",
|
||||||
"description": "Start in number of days ago from today"
|
"description": "Start in number of days ago from today"
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"$": "kraj",
|
"$": "end",
|
||||||
"description": "End in number of days ago from today"
|
"description": "End in number of days ago from today"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +197,7 @@
|
|||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"clan": {
|
"clan": {
|
||||||
"$": "klan",
|
"$": "clan",
|
||||||
"description": "The clan name or tag"
|
"description": "The clan name or tag"
|
||||||
},
|
},
|
||||||
"tank": {
|
"tank": {
|
||||||
@@ -211,13 +213,13 @@
|
|||||||
"$": "nation",
|
"$": "nation",
|
||||||
"description": "Nation"
|
"description": "Nation"
|
||||||
},
|
},
|
||||||
"tank_class": {
|
"class": {
|
||||||
"$": "tank-class",
|
"$": "class",
|
||||||
"description": "Tank class"
|
"description": "Class"
|
||||||
},
|
},
|
||||||
"tree_type": {
|
"type": {
|
||||||
"$": "tree-type",
|
"$": "type",
|
||||||
"description": "Tree type"
|
"description": "Type"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"region": {
|
"region": {
|
||||||
@@ -321,23 +323,23 @@
|
|||||||
"commands": {
|
"commands": {
|
||||||
"$": "commands",
|
"$": "commands",
|
||||||
"description": "List of all commands and their descriptions",
|
"description": "List of all commands and their descriptions",
|
||||||
"body": "# Commands\n\nBlitzkrieg offers the following commands:\n\n"
|
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
|
||||||
},
|
},
|
||||||
"embeds": {
|
"embeds": {
|
||||||
"$": "embeds",
|
"$": "embeds",
|
||||||
"description": "Kako embed radi"
|
"description": "How embeds work"
|
||||||
},
|
},
|
||||||
"introduction": {
|
"introduction": {
|
||||||
"$": "uvod",
|
"$": "introduction",
|
||||||
"description": "O bot-u"
|
"description": "About the bot"
|
||||||
},
|
},
|
||||||
"invite": {
|
"invite": {
|
||||||
"$": "pozovi",
|
"$": "invite",
|
||||||
"description": "Kako pozvati robota"
|
"description": "How to invite the bot"
|
||||||
},
|
},
|
||||||
"timezones": {
|
"timezones": {
|
||||||
"$": "vremenske zone",
|
"$": "timezones",
|
||||||
"description": "Kako \"Blitzkrieg\" koristi podatke o tvojoj vremenskoj zoni"
|
"description": "How BlitzKit infers and uses your timezone"
|
||||||
},
|
},
|
||||||
"credits": {
|
"credits": {
|
||||||
"$": "credits",
|
"$": "credits",
|
||||||
@@ -346,19 +348,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"breakdown": {
|
"breakdown": {
|
||||||
"$": "analiza",
|
"$": "breakdown",
|
||||||
"description": "A period's breakdown by tanks played",
|
"description": "A period's breakdown by tanks played",
|
||||||
"body": {
|
"body": {
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"battles": "Borbe",
|
"battles": "Battles",
|
||||||
"wn8": "WN8",
|
"wn8": "WN8",
|
||||||
"winrate": "Procenat pobede",
|
"winrate": "Winrate",
|
||||||
"damage": "Šteta",
|
"damage": "Damage",
|
||||||
"unknown_tank": "Ne poznat tenk %s"
|
"unknown_tank": "Unknown tank %s"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"$": "odklanjanje-grešaka",
|
"$": "debug",
|
||||||
"description": "Debug information about the bot",
|
"description": "Debug information about the bot",
|
||||||
"body": {
|
"body": {
|
||||||
"title": "# Debug\n\n",
|
"title": "# Debug\n\n",
|
||||||
@@ -572,7 +574,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rating": {
|
"rating": {
|
||||||
"$": "rating-session",
|
"$": "rating",
|
||||||
"description": "Rating player's statistics",
|
"description": "Rating player's statistics",
|
||||||
"subcommands": {
|
"subcommands": {
|
||||||
"today": {
|
"today": {
|
||||||
|
|||||||
@@ -142,7 +142,9 @@
|
|||||||
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
||||||
"get_help": "Help Discord server",
|
"get_help": "Help Discord server",
|
||||||
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
||||||
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
|
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
|
||||||
|
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
|
||||||
|
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
|
||||||
},
|
},
|
||||||
"wn8": {
|
"wn8": {
|
||||||
"$": "WN8",
|
"$": "WN8",
|
||||||
@@ -211,13 +213,13 @@
|
|||||||
"$": "nation",
|
"$": "nation",
|
||||||
"description": "Nation"
|
"description": "Nation"
|
||||||
},
|
},
|
||||||
"tank_class": {
|
"class": {
|
||||||
"$": "tank-class",
|
"$": "class",
|
||||||
"description": "Tank class"
|
"description": "Class"
|
||||||
},
|
},
|
||||||
"tree_type": {
|
"type": {
|
||||||
"$": "tree-type",
|
"$": "type",
|
||||||
"description": "Tree type"
|
"description": "Type"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"region": {
|
"region": {
|
||||||
@@ -142,7 +142,9 @@
|
|||||||
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
||||||
"get_help": "Help Discord server",
|
"get_help": "Help Discord server",
|
||||||
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
||||||
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
|
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
|
||||||
|
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
|
||||||
|
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
|
||||||
},
|
},
|
||||||
"wn8": {
|
"wn8": {
|
||||||
"$": "WN8",
|
"$": "WN8",
|
||||||
@@ -211,13 +213,13 @@
|
|||||||
"$": "nation",
|
"$": "nation",
|
||||||
"description": "Nation"
|
"description": "Nation"
|
||||||
},
|
},
|
||||||
"tank_class": {
|
"class": {
|
||||||
"$": "tank-class",
|
"$": "class",
|
||||||
"description": "Tank class"
|
"description": "Class"
|
||||||
},
|
},
|
||||||
"tree_type": {
|
"type": {
|
||||||
"$": "tree-type",
|
"$": "type",
|
||||||
"description": "Tree type"
|
"description": "Type"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"region": {
|
"region": {
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"wn8_percentile": {
|
"wn8_percentile": {
|
||||||
"0": "Very bad",
|
"0": "非常差",
|
||||||
"1": "Bad",
|
"1": "差",
|
||||||
"2": "Below average",
|
"2": "较差",
|
||||||
"3": "Average",
|
"3": "平均",
|
||||||
"4": "Above average",
|
"4": "高于平均",
|
||||||
"5": "Good",
|
"5": "好",
|
||||||
"6": "Very good",
|
"6": "非常好",
|
||||||
"7": "Great",
|
"7": "很好",
|
||||||
"8": "Unicum",
|
"8": "Unicum",
|
||||||
"9": "Super unicum"
|
"9": "Super unicum"
|
||||||
},
|
},
|
||||||
@@ -16,53 +16,53 @@
|
|||||||
"0": "Super malum",
|
"0": "Super malum",
|
||||||
"1": "Malum",
|
"1": "Malum",
|
||||||
"2": "Very bad",
|
"2": "Very bad",
|
||||||
"3": "Bad",
|
"3": "差",
|
||||||
"4": "Below average",
|
"4": "较差",
|
||||||
"5": "Average",
|
"5": "平均",
|
||||||
"6": "Above average",
|
"6": "高于平均",
|
||||||
"7": "Good",
|
"7": "好",
|
||||||
"8": "Very good",
|
"8": "非常好",
|
||||||
"9": "Unicum",
|
"9": "Unicum",
|
||||||
"10": "Super unicum"
|
"10": "Super unicum"
|
||||||
},
|
},
|
||||||
"nations": {
|
"nations": {
|
||||||
"ussr": "U.S.S.R.",
|
"ussr": "苏联 (S系)",
|
||||||
"germany": "Germany",
|
"germany": "德国 (D系)",
|
||||||
"usa": "U.S.A.",
|
"usa": "美国 (M系)",
|
||||||
"china": "China",
|
"china": "中国 (C系)",
|
||||||
"uk": "U.K.",
|
"uk": "英国 (Y系)",
|
||||||
"france": "France",
|
"france": "法国 (F系)",
|
||||||
"japan": "Japan",
|
"japan": "日本 (R系)",
|
||||||
"european": "European Nation",
|
"european": "欧盟 (E系)",
|
||||||
"other": "Hybrid Nation"
|
"other": "X系"
|
||||||
},
|
},
|
||||||
"nations_adjectives": {
|
"nations_adjectives": {
|
||||||
"ussr": "U.S.S.R.",
|
"ussr": "苏联 (S系)",
|
||||||
"germany": "German",
|
"germany": "德国 (D系)",
|
||||||
"usa": "U.S.A.",
|
"usa": "美国 (M系)",
|
||||||
"china": "Chinese",
|
"china": "中国 (C系)",
|
||||||
"uk": "U.K.",
|
"uk": "英国 (Y系)",
|
||||||
"france": "French",
|
"france": "法国 (F系)",
|
||||||
"japan": "Japanese",
|
"japan": "日本 (R系)",
|
||||||
"european": "European",
|
"european": "欧盟 (E系)",
|
||||||
"other": "Hybrid"
|
"other": "X系"
|
||||||
},
|
},
|
||||||
"tank_class_short": {
|
"tank_class_short": {
|
||||||
"-1": "unrecognized",
|
"-1": "无法识别",
|
||||||
"0": "Light",
|
"0": "轻型坦克",
|
||||||
"1": "Medium",
|
"1": "中型坦克",
|
||||||
"2": "Heavy",
|
"2": "重型坦克",
|
||||||
"3": "TD"
|
"3": "坦克歼击车"
|
||||||
},
|
},
|
||||||
"tank_class_short_plural_lowercase": {
|
"tank_class_short_plural_lowercase": {
|
||||||
"0": "lights",
|
"0": "轻型坦克",
|
||||||
"1": "mediums",
|
"1": "中型坦克",
|
||||||
"2": "heavies",
|
"2": "重型坦克",
|
||||||
"3": "TDs"
|
"3": "坦克歼击车"
|
||||||
},
|
},
|
||||||
"tank_class_medium": {
|
"tank_class_medium": {
|
||||||
"-1": "unrecognized",
|
"-1": "无法识别",
|
||||||
"0": "Light",
|
"0": "轻型坦克",
|
||||||
"1": "Medium",
|
"1": "Medium",
|
||||||
"2": "Heavy",
|
"2": "Heavy",
|
||||||
"3": "Tank destroyer"
|
"3": "Tank destroyer"
|
||||||
@@ -118,84 +118,86 @@
|
|||||||
"cumulative_frags": "Total kills",
|
"cumulative_frags": "Total kills",
|
||||||
"cumulative_hits": "Total hits",
|
"cumulative_hits": "Total hits",
|
||||||
"cumulative_inaccuracy": "Inaccuracy",
|
"cumulative_inaccuracy": "Inaccuracy",
|
||||||
"cumulative_kills_to_death_ratio": "Kills to death ratio",
|
"cumulative_kills_to_death_ratio": "战损比",
|
||||||
"cumulative_losses": "Losses",
|
"cumulative_losses": "失败",
|
||||||
"cumulative_misses": "Total misses",
|
"cumulative_misses": "总射击失误数",
|
||||||
"cumulative_shots": "Total shots",
|
"cumulative_shots": "总射击数",
|
||||||
"cumulative_spotted": "Total spots",
|
"cumulative_spotted": "场均发现敌人",
|
||||||
"cumulative_survived_battles": "Survived battles",
|
"cumulative_survived_battles": "幸存场次",
|
||||||
"cumulative_win_and_survived": "Won and survived battles",
|
"cumulative_win_and_survived": "胜利和幸存场次",
|
||||||
"cumulative_wins": "Wins",
|
"cumulative_wins": "胜利",
|
||||||
"cumulative_xp": "Total XP",
|
"cumulative_xp": "总经验",
|
||||||
"cumulative_wn8": "WN8",
|
"cumulative_wn8": "WN8",
|
||||||
"cumulative_battle_life_time": "Total battle time"
|
"cumulative_battle_life_time": "总战斗时间"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bot": {
|
"bot": {
|
||||||
"common": {
|
"common": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
|
"clan_not_found": "# 找不到军团\n找不到\"%s\"。请尝试从搜索结果中选择一个军团。",
|
||||||
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
|
"player_not_found": "# 找不到用户\n无法找到用户 \"%s\"。 尝试从搜索结果中挑选用户,输入一个有效的用户名或使用 \"/link\" 命令。",
|
||||||
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
|
"player_not_linked": "# 使用 `/link` 来开始\n我不知道你是谁,但你今天可以告诉我!",
|
||||||
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
|
"tank_name_not_found": "# 坦克找不到\n无法找到名为 \"%s \"的坦克。",
|
||||||
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
|
"tank_id_not_found": "# 坦克找不到\n无法找到ID \"%s\"的坦克。 试着输入坦克的名称而不是数字。",
|
||||||
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
|
"no_tank_stats": "# 没有这个坦克的数据\n这个玩家没有数据。你可能找错人了。",
|
||||||
"get_help": "Help Discord server",
|
"get_help": "Help Discord server",
|
||||||
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
"uncaught_error": "# BlitzKit 崩溃了!\n非常抱歉。请加入[官方Discord](https://discord.gg/nDt7AjGJQH),以方便帮助你的问题。",
|
||||||
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
|
"preview": "# 此命令处于预览\n\n您只能在[官方Discord服务器](https://discord.gg/nDt7AjGJQH)访问此命令。该命令将很快可用。",
|
||||||
|
"query_too_small": "# 您的搜索太短了\n您的搜索必须至少 3 个字符长。",
|
||||||
|
"query_too_long": "# 您的搜索太长了\n您的搜索必须最长为 100 个字符。"
|
||||||
},
|
},
|
||||||
"wn8": {
|
"wn8": {
|
||||||
"$": "WN8",
|
"$": "WN8",
|
||||||
"very_bad": "Very bad",
|
"very_bad": "非常差",
|
||||||
"bad": "Bad",
|
"bad": "差",
|
||||||
"below_average": "Below average",
|
"below_average": "较差",
|
||||||
"average": "Average",
|
"average": "平均",
|
||||||
"above_average": "Above average",
|
"above_average": "高于平均",
|
||||||
"good": "Good",
|
"good": "好",
|
||||||
"very_good": "Very good",
|
"very_good": "非常好",
|
||||||
"great": "Great",
|
"great": "很好",
|
||||||
"unicum": "Unicum",
|
"unicum": "Unicum",
|
||||||
"super_unicum": "Super unicum"
|
"super_unicum": "Super unicum"
|
||||||
},
|
},
|
||||||
"subcommands": {
|
"subcommands": {
|
||||||
"today": {
|
"today": {
|
||||||
"$": "today",
|
"$": "今日:",
|
||||||
"description": "Today"
|
"description": "今日:"
|
||||||
},
|
},
|
||||||
"30": {
|
"30": {
|
||||||
"$": "30",
|
"$": "30",
|
||||||
"description": "30 days"
|
"description": "30天"
|
||||||
},
|
},
|
||||||
"60": {
|
"60": {
|
||||||
"$": "60",
|
"$": "60",
|
||||||
"description": "60 days"
|
"description": "60天"
|
||||||
},
|
},
|
||||||
"90": {
|
"90": {
|
||||||
"$": "90",
|
"$": "90",
|
||||||
"description": "90 days"
|
"description": "90天"
|
||||||
},
|
},
|
||||||
"career": {
|
"career": {
|
||||||
"$": "career",
|
"$": "玩家资料",
|
||||||
"description": "Career"
|
"description": "玩家资料"
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"$": "custom",
|
"$": "自定义",
|
||||||
"description": "Custom range",
|
"description": "自定义范围",
|
||||||
"options": {
|
"options": {
|
||||||
"start": {
|
"start": {
|
||||||
"$": "start",
|
"$": "开始时间",
|
||||||
"description": "Start in number of days ago from today"
|
"description": "从今天开始于数天前"
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"$": "end",
|
"$": "结束时间",
|
||||||
"description": "End in number of days ago from today"
|
"description": "从今天开始于数天前"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"clan": {
|
"clan": {
|
||||||
"$": "clan",
|
"$": "军团",
|
||||||
"description": "The clan name or tag"
|
"description": "The clan name or tag"
|
||||||
},
|
},
|
||||||
"tank": {
|
"tank": {
|
||||||
@@ -211,13 +213,13 @@
|
|||||||
"$": "nation",
|
"$": "nation",
|
||||||
"description": "Nation"
|
"description": "Nation"
|
||||||
},
|
},
|
||||||
"tank_class": {
|
"class": {
|
||||||
"$": "tank-class",
|
"$": "class",
|
||||||
"description": "Tank class"
|
"description": "Class"
|
||||||
},
|
},
|
||||||
"tree_type": {
|
"type": {
|
||||||
"$": "tree-type",
|
"$": "type",
|
||||||
"description": "Tree type"
|
"description": "Type"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"region": {
|
"region": {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"pickleparser": "^0.2.1",
|
"pickleparser": "^0.2.1",
|
||||||
"protobufjs": "^7.4.0",
|
"protobufjs": "^7.4.0",
|
||||||
"sharp": "^0.33.5",
|
|
||||||
"ts-proto": "^2.2.2",
|
"ts-proto": "^2.2.2",
|
||||||
"yaml": "^2.5.0"
|
"yaml": "^2.5.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { asset } from '@blitzkit/core';
|
import { asset } from '@blitzkit/core';
|
||||||
|
|
||||||
export function tankIcon(id: number, size: 'big' | 'small' = 'big') {
|
export const BLITZKIT_TANK_ICON_SIZE = { width: 40 * 16, height: 30 * 16 };
|
||||||
|
|
||||||
|
export function tankIcon(
|
||||||
|
id: number,
|
||||||
|
size: 'big' | 'small' | 'blitzkit' = 'blitzkit',
|
||||||
|
) {
|
||||||
return asset(`icons/tanks/${size}/${id}.webp`);
|
return asset(`icons/tanks/${size}/${id}.webp`);
|
||||||
}
|
}
|
||||||
@@ -13,15 +13,16 @@
|
|||||||
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts"
|
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.19.12",
|
|
||||||
"@blitzkit/core": "workspace:*",
|
"@blitzkit/core": "workspace:*",
|
||||||
"@gltf-transform/core": "^4.0.10",
|
"@gltf-transform/core": "^4.0.10",
|
||||||
"@gltf-transform/extensions": "^4.0.10",
|
"@gltf-transform/extensions": "^4.0.10",
|
||||||
"@gltf-transform/functions": "^4.0.10",
|
"@gltf-transform/functions": "^4.0.10",
|
||||||
"csv-parse": "^5.5.6",
|
"csv-parse": "^5.5.6",
|
||||||
"decompress": "^4.2.1",
|
"decompress": "^4.2.1",
|
||||||
|
"esbuild": "^0.19.12",
|
||||||
"fast-xml-parser": "^4.4.1",
|
"fast-xml-parser": "^4.4.1",
|
||||||
"progress": "^2.0.3"
|
"progress": "^2.0.3",
|
||||||
|
"puppeteer": "^23.10.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/decompress": "^4.2.7",
|
"@types/decompress": "^4.2.7",
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { BLITZKIT_TANK_ICON_SIZE, fetchTankDefinitions } from '@blitzkit/core';
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
import { launch } from 'puppeteer';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import { commitAssets } from '../core/github/commitAssets';
|
||||||
|
import { FileChange } from '../core/github/commitMultipleFiles';
|
||||||
|
|
||||||
|
export async function blitzkitTankIcons() {
|
||||||
|
console.log('Building blitzkit tank icons...');
|
||||||
|
|
||||||
|
console.log('Starting website dev server...');
|
||||||
|
|
||||||
|
const child = exec('cd ../.. && bun dev:website');
|
||||||
|
const browser = await launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
const tanks = Object.values((await fetchTankDefinitions()).tanks);
|
||||||
|
const files: FileChange[] = [];
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
for (const { id, name } of tanks) {
|
||||||
|
await page.setViewport({
|
||||||
|
width: BLITZKIT_TANK_ICON_SIZE.width - 1,
|
||||||
|
height: BLITZKIT_TANK_ICON_SIZE.height,
|
||||||
|
});
|
||||||
|
await page.goto(`http://localhost:4321/api/tankopedia/tank-icon/${id}/`, {
|
||||||
|
waitUntil: 'networkidle0',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index === 0) {
|
||||||
|
console.log('Dev server started!');
|
||||||
|
}
|
||||||
|
|
||||||
|
const screenshot = await page.screenshot({
|
||||||
|
type: 'webp',
|
||||||
|
omitBackground: true,
|
||||||
|
});
|
||||||
|
const content = await sharp(screenshot).trim({ threshold: 100 }).toBuffer();
|
||||||
|
|
||||||
|
index++;
|
||||||
|
console.log(
|
||||||
|
`Rendered ${id} (${name}) ${((index / tanks.length) * 100).toFixed(2)}%`,
|
||||||
|
);
|
||||||
|
|
||||||
|
files.push({ path: `icons/tanks/blitzkit/${id}.webp`, content });
|
||||||
|
}
|
||||||
|
|
||||||
|
child.kill('SIGHUP');
|
||||||
|
await browser.close();
|
||||||
|
|
||||||
|
await commitAssets('tank icons', files);
|
||||||
|
}
|
||||||
@@ -39,9 +39,9 @@ import { readStringDVPL } from '../core/blitz/readStringDVPL';
|
|||||||
import { readXMLDVPL } from '../core/blitz/readXMLDVPL';
|
import { readXMLDVPL } from '../core/blitz/readXMLDVPL';
|
||||||
import { readYAMLDVPL } from '../core/blitz/readYAMLDVPL';
|
import { readYAMLDVPL } from '../core/blitz/readYAMLDVPL';
|
||||||
import { commitAssets } from '../core/github/commitAssets';
|
import { commitAssets } from '../core/github/commitAssets';
|
||||||
|
import { TankParameters } from './blitzkitTankIcons';
|
||||||
import { DATA } from './constants';
|
import { DATA } from './constants';
|
||||||
import { Avatar } from './skillIcons';
|
import { Avatar } from './skillIcons';
|
||||||
import { TankParameters } from './tankIcons';
|
|
||||||
|
|
||||||
function parseResearchCost(raw: number | string) {
|
function parseResearchCost(raw: number | string) {
|
||||||
if (typeof raw === 'number') {
|
if (typeof raw === 'number') {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const targets = argv
|
|||||||
?.split('=')[1]
|
?.split('=')[1]
|
||||||
.split(',');
|
.split(',');
|
||||||
|
|
||||||
|
import { blitzkitTankIcons } from './blitzkitTankIcons';
|
||||||
import { boosterIcons } from './boosterIcons';
|
import { boosterIcons } from './boosterIcons';
|
||||||
import { camouflageIcons } from './camouflageIcons';
|
import { camouflageIcons } from './camouflageIcons';
|
||||||
import { consumableProvisionIcons } from './consumableProvisionIcons';
|
import { consumableProvisionIcons } from './consumableProvisionIcons';
|
||||||
@@ -28,7 +29,6 @@ if (!targets && !allTargets) throw new Error('No target(s) specified');
|
|||||||
|
|
||||||
const methods = [
|
const methods = [
|
||||||
definitions,
|
definitions,
|
||||||
tankIcons,
|
|
||||||
tankModels,
|
tankModels,
|
||||||
tankArmors,
|
tankArmors,
|
||||||
gameModeBanners,
|
gameModeBanners,
|
||||||
@@ -41,6 +41,8 @@ const methods = [
|
|||||||
consumableProvisionIcons,
|
consumableProvisionIcons,
|
||||||
camouflageIcons,
|
camouflageIcons,
|
||||||
boosterIcons,
|
boosterIcons,
|
||||||
|
tankIcons,
|
||||||
|
blitzkitTankIcons,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const method of methods) {
|
for (const method of methods) {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { readXMLDVPL } from '../core/blitz/readXMLDVPL';
|
|||||||
import { readYAMLDVPL } from '../core/blitz/readYAMLDVPL';
|
import { readYAMLDVPL } from '../core/blitz/readYAMLDVPL';
|
||||||
import { commitAssets } from '../core/github/commitAssets';
|
import { commitAssets } from '../core/github/commitAssets';
|
||||||
import { FileChange } from '../core/github/commitMultipleFiles';
|
import { FileChange } from '../core/github/commitMultipleFiles';
|
||||||
|
import { TankParameters } from './blitzkitTankIcons';
|
||||||
import { DATA } from './constants';
|
import { DATA } from './constants';
|
||||||
import { VehicleDefinitionList } from './definitions';
|
import { VehicleDefinitionList } from './definitions';
|
||||||
import { TankParameters } from './tankIcons';
|
|
||||||
|
|
||||||
export async function tankModels() {
|
export async function tankModels() {
|
||||||
console.log('Building tank models...');
|
console.log('Building tank models...');
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
---
|
---
|
||||||
import { assertSecret } from '@blitzkit/core';
|
import { assertSecret } from '@blitzkit/core';
|
||||||
|
|
||||||
|
const PUBLIC_GOOGLE_TAG_MANAGER_ID = assertSecret(
|
||||||
|
import.meta.env.PUBLIC_GOOGLE_TAG_MANAGER_ID,
|
||||||
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
{/* google analytics */}
|
{/* google analytics */}
|
||||||
<script
|
<script
|
||||||
is:inline
|
is:inline
|
||||||
async
|
async
|
||||||
src={`https://www.googletagmanager.com/gtag/js?id=${assertSecret(
|
src={`https://www.googletagmanager.com/gtag/js?id=${PUBLIC_GOOGLE_TAG_MANAGER_ID}`}
|
||||||
import.meta.env.PUBLIC_GOOGLE_TAG_MANAGER_ID,
|
></script>
|
||||||
)}`}></script>
|
<script is:inline define:vars={{ PUBLIC_GOOGLE_TAG_MANAGER_ID }}>
|
||||||
<script is:inline>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
|
||||||
function gtag() {
|
function gtag() {
|
||||||
dataLayer.push(arguments);
|
dataLayer.push(arguments);
|
||||||
}
|
}
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
gtag('config', assertSecret(import.meta.env.PUBLIC_GOOGLE_TAG_MANAGER_ID));
|
gtag('config', PUBLIC_GOOGLE_TAG_MANAGER_ID);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{/* nitro ads */}
|
{/* nitro ads */}
|
||||||
|
{
|
||||||
|
/*
|
||||||
<script is:inline data-cfasync="false">
|
<script is:inline data-cfasync="false">
|
||||||
window.nitroAds = window.nitroAds || {
|
window.nitroAds = window.nitroAds || {
|
||||||
createAd: function () {
|
createAd: function () {
|
||||||
@@ -33,3 +38,5 @@ import { assertSecret } from '@blitzkit/core';
|
|||||||
queue: [],
|
queue: [],
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -6,11 +6,16 @@ import { Tanks } from './Tanks';
|
|||||||
|
|
||||||
export function TankTable({ skeleton }: MaybeSkeletonComponentProps) {
|
export function TankTable({ skeleton }: MaybeSkeletonComponentProps) {
|
||||||
return (
|
return (
|
||||||
<Flex justify="center">
|
<Flex justify="center" maxWidth="100%" flexGrow="1" position="relative">
|
||||||
<StickyTableRoot
|
<StickyTableRoot
|
||||||
size={{ initial: '1', sm: '2' }}
|
size={{ initial: '1', sm: '2' }}
|
||||||
variant="surface"
|
variant="surface"
|
||||||
style={{ maxWidth: '100%' }}
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '100%',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Header />
|
<Header />
|
||||||
<Tanks skeleton={skeleton} />
|
<Tanks skeleton={skeleton} />
|
||||||
|
|||||||
@@ -23,9 +23,10 @@
|
|||||||
|
|
||||||
.tank-search-card .image {
|
.tank-search-card .image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 4rem;
|
height: 6rem;
|
||||||
object-position: center right;
|
object-position: center right;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
filter: brightness(1.25) drop-shadow(-1px -1px 4px black);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tank-search-card .class-icon {
|
.tank-search-card .class-icon {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { asset, tankIcon, TankType, type TankDefinition } from '@blitzkit/core';
|
import { asset, TankType, type TankDefinition } from '@blitzkit/core';
|
||||||
import { Flex, Link, Text, type TextProps } from '@radix-ui/themes';
|
import { Flex, Link, Text, type TextProps } from '@radix-ui/themes';
|
||||||
import { uniq } from 'lodash-es';
|
import { uniq } from 'lodash-es';
|
||||||
import { forwardRef, type ReactNode } from 'react';
|
import { forwardRef, type ReactNode } from 'react';
|
||||||
@@ -34,7 +34,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
|||||||
<Text
|
<Text
|
||||||
ref={ref}
|
ref={ref}
|
||||||
tabIndex={onSelect ? 0 : undefined}
|
tabIndex={onSelect ? 0 : undefined}
|
||||||
size="1"
|
size="2"
|
||||||
color={
|
color={
|
||||||
tank.type === TankType.COLLECTOR
|
tank.type === TankType.COLLECTOR
|
||||||
? 'blue'
|
? 'blue'
|
||||||
@@ -70,7 +70,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={tank.name}
|
alt={tank.name}
|
||||||
src={tankIcon(tank.id)}
|
src={asset(`icons/tanks/blitzkit/${tank.id}.webp`)}
|
||||||
className="image"
|
className="image"
|
||||||
draggable={false}
|
draggable={false}
|
||||||
/>
|
/>
|
||||||
@@ -81,6 +81,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
|||||||
align="center"
|
align="center"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
mt="1"
|
||||||
>
|
>
|
||||||
<Icon className="class-icon" />
|
<Icon className="class-icon" />
|
||||||
<Text align="center" className="name">
|
<Text align="center" className="name">
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const SkeletonTankCard = memo(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="2" ref={card}>
|
<Flex direction="column" gap="2" ref={card}>
|
||||||
<Skeleton height="64px" />
|
<Skeleton height="5rem" />
|
||||||
<Skeleton height="1em" />
|
<Skeleton height="1em" />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ export function TankCardWrapper(props: GridProps) {
|
|||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
py="4"
|
py="4"
|
||||||
columns="repeat(auto-fill, minmax(100px, 1fr))"
|
columns={{
|
||||||
|
initial: 'repeat(auto-fill, minmax(6rem, 1fr))',
|
||||||
|
sm: 'repeat(auto-fill, minmax(8rem, 1fr))',
|
||||||
|
}}
|
||||||
flow="dense"
|
flow="dense"
|
||||||
gap="2"
|
gap="2"
|
||||||
gapY="6"
|
gapY="6"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export const MAX_RECENTLY_VIEWED = 9;
|
export const MAX_RECENTLY_VIEWED = 14;
|
||||||
@@ -39,8 +39,8 @@ type TankSearchProps = Omit<FlexProps, 'onSelect'> & {
|
|||||||
onSelectAll?: (tanks: TankDefinition[]) => void;
|
onSelectAll?: (tanks: TankDefinition[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PREVIEW_COUNT = 25;
|
const PREVIEW_COUNT = 20;
|
||||||
const DEFAULT_LOADED_CARDS = 75;
|
const DEFAULT_LOADED_CARDS = 64;
|
||||||
|
|
||||||
const [gameDefinitions, modelDefinitions, tankDefinitions, tankNames] =
|
const [gameDefinitions, modelDefinitions, tankDefinitions, tankNames] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
|||||||
+7
-3
@@ -88,7 +88,11 @@ export function Controls({ naked }: ControlsProps) {
|
|||||||
protagonistHullOrigin.y +
|
protagonistHullOrigin.y +
|
||||||
protagonistTurretOrigin.y +
|
protagonistTurretOrigin.y +
|
||||||
protagonistGunOrigin.y;
|
protagonistGunOrigin.y;
|
||||||
const inspectModeInitialPosition = [-8, gunHeight + 4, -13] as const;
|
const inspectModeInitialPosition = [
|
||||||
|
-8,
|
||||||
|
gunHeight + (naked ? 10 : 4),
|
||||||
|
-13,
|
||||||
|
] as const;
|
||||||
const protagonistGunOriginOnlyY = new Vector3(
|
const protagonistGunOriginOnlyY = new Vector3(
|
||||||
0,
|
0,
|
||||||
protagonistTurretModelDefinition.gun_origin.y,
|
protagonistTurretModelDefinition.gun_origin.y,
|
||||||
@@ -294,10 +298,10 @@ export function Controls({ naked }: ControlsProps) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
camera.position.set(...inspectModeInitialPosition);
|
camera.position.set(...inspectModeInitialPosition);
|
||||||
orbitControls.current.target.set(0, gunHeight / 2, 0);
|
orbitControls.current.target.set(0, gunHeight / (naked ? 4 : 2), 0);
|
||||||
orbitControls.current.enablePan = true;
|
orbitControls.current.enablePan = true;
|
||||||
orbitControls.current.enableZoom = true;
|
orbitControls.current.enableZoom = true;
|
||||||
camera.fov = 25;
|
camera.fov = naked ? 20 : 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
|
|||||||
+5
-8
@@ -1,9 +1,10 @@
|
|||||||
import { Environment } from '@react-three/drei';
|
import { Environment } from '@react-three/drei';
|
||||||
import { times } from 'lodash-es';
|
import { times } from 'lodash-es';
|
||||||
|
import { Euler } from 'three';
|
||||||
|
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||||
import { useModel } from '../../../../../../hooks/useModel';
|
import { useModel } from '../../../../../../hooks/useModel';
|
||||||
import { Duel } from '../../../../../../stores/duel';
|
import { Duel } from '../../../../../../stores/duel';
|
||||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||||
import { TankopediaPersistent } from '../../../../../../stores/tankopediaPersistent';
|
|
||||||
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
||||||
|
|
||||||
const LIGHTS_COUNT = 5;
|
const LIGHTS_COUNT = 5;
|
||||||
@@ -14,17 +15,13 @@ export function Lighting() {
|
|||||||
const { hasPbr } = useModel(protagonist.tank.id);
|
const { hasPbr } = useModel(protagonist.tank.id);
|
||||||
const isBrighterLighting =
|
const isBrighterLighting =
|
||||||
!hasPbr && display !== TankopediaDisplay.StaticArmor;
|
!hasPbr && display !== TankopediaDisplay.StaticArmor;
|
||||||
const showEnvironment = TankopediaPersistent.use(
|
|
||||||
(state) => state.showEnvironment,
|
|
||||||
);
|
|
||||||
const environment = TankopediaPersistent.use((state) => state.environment);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Environment
|
<Environment
|
||||||
preset={environment}
|
files="https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/1k/winter_river_1k.hdr"
|
||||||
background={showEnvironment}
|
environmentIntensity={isBrighterLighting ? 1.5 : 1}
|
||||||
environmentIntensity={isBrighterLighting ? 3 : 1}
|
environmentRotation={new Euler(0, degToRad(180), 0)}
|
||||||
/>
|
/>
|
||||||
{display === TankopediaDisplay.ShootingRange && (
|
{display === TankopediaDisplay.ShootingRange && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
+3
-2
@@ -1,3 +1,4 @@
|
|||||||
|
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
|
||||||
import { Canvas } from '@react-three/fiber';
|
import { Canvas } from '@react-three/fiber';
|
||||||
import {
|
import {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
@@ -182,8 +183,8 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
width: naked ? 640 : '100%',
|
width: naked ? BLITZKIT_TANK_ICON_SIZE.width : '100%',
|
||||||
height: naked ? 480 : '100%',
|
height: naked ? BLITZKIT_TANK_ICON_SIZE.height : '100%',
|
||||||
outline: naked ? '1rem red solid' : undefined,
|
outline: naked ? '1rem red solid' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
+6
-3
@@ -1,4 +1,4 @@
|
|||||||
import { tankIcon } from '@blitzkit/core';
|
import { asset } from '@blitzkit/core';
|
||||||
import { Flex, type FlexProps, Progress } from '@radix-ui/themes';
|
import { Flex, type FlexProps, Progress } from '@radix-ui/themes';
|
||||||
import { useProgress } from '@react-three/drei';
|
import { useProgress } from '@react-three/drei';
|
||||||
|
|
||||||
@@ -11,11 +11,14 @@ export function TankSandboxLoader({ id, ...props }: TankSandboxLoaderProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex width="100%" height="100%" align="center" justify="center" {...props}>
|
<Flex width="100%" height="100%" align="center" justify="center" {...props}>
|
||||||
<img src={tankIcon(id)} style={{ height: '60%', filter: 'blur(4px)' }} />
|
<img
|
||||||
|
src={asset(`icons/tanks/blitzkit/${id}.webp`)}
|
||||||
|
style={{ height: '75%', filter: 'blur(1rem)' }}
|
||||||
|
/>
|
||||||
<Progress
|
<Progress
|
||||||
size="3"
|
size="3"
|
||||||
value={data.progress}
|
value={data.progress}
|
||||||
style={{ position: 'absolute', width: 128 }}
|
style={{ position: 'absolute', width: '16rem', maxWidth: '50vw' }}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
height={
|
height={
|
||||||
isFullScreen ? '100vh' : `calc(100vh - ${NAVBAR_HEIGHT}px - 8rem)`
|
isFullScreen ? '100vh' : `calc(100vh - ${NAVBAR_HEIGHT}px - 8rem)`
|
||||||
}
|
}
|
||||||
maxHeight={isFullScreen ? undefined : '40rem'}
|
maxHeight={isFullScreen ? undefined : '60rem'}
|
||||||
maxWidth={isFullScreen ? undefined : '120rem'}
|
maxWidth={isFullScreen ? undefined : '120rem'}
|
||||||
flexGrow="1"
|
flexGrow="1"
|
||||||
width={isFullScreen ? '100vw' : undefined}
|
width={isFullScreen ? '100vw' : undefined}
|
||||||
@@ -92,20 +92,18 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
top={isFullScreen ? '0' : undefined}
|
top={isFullScreen ? '0' : undefined}
|
||||||
left={isFullScreen ? '0' : undefined}
|
left={isFullScreen ? '0' : undefined}
|
||||||
>
|
>
|
||||||
<Flex justify="center">
|
|
||||||
<Flex
|
<Flex
|
||||||
display={isFullScreen ? 'none' : 'flex'}
|
|
||||||
style={{ userSelect: 'none' }}
|
|
||||||
direction="column"
|
|
||||||
align={{ initial: 'center', md: 'start' }}
|
|
||||||
justify="center"
|
justify="center"
|
||||||
pl={{ initial: '0', md: '9' }}
|
position={{ initial: 'relative', md: 'absolute' }}
|
||||||
pt={{ initial: '5', md: '0' }}
|
left={{ initial: '0', md: '5', lg: '9' }}
|
||||||
overflow="hidden"
|
py="4"
|
||||||
flexGrow="1"
|
top={{ initial: '8', md: '50%' }}
|
||||||
maxWidth="40rem"
|
style={{ transform: 'translateY(-50%)' }}
|
||||||
|
direction="column"
|
||||||
|
gap={{ initial: '0', lg: '2' }}
|
||||||
|
align={{ initial: 'center', md: 'start' }}
|
||||||
>
|
>
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="3">
|
||||||
<Heading
|
<Heading
|
||||||
color={treeColor}
|
color={treeColor}
|
||||||
trim="end"
|
trim="end"
|
||||||
@@ -116,7 +114,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
|
|
||||||
<Heading
|
<Heading
|
||||||
weight="bold"
|
weight="bold"
|
||||||
size={{ initial: '8', lg: '9' }}
|
size={{ initial: '7', lg: '9' }}
|
||||||
wrap="nowrap"
|
wrap="nowrap"
|
||||||
color={treeColor}
|
color={treeColor}
|
||||||
>
|
>
|
||||||
@@ -126,14 +124,13 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
|
|
||||||
<Text
|
<Text
|
||||||
color="gray"
|
color="gray"
|
||||||
size="3"
|
size={{ initial: '3', lg: '4' }}
|
||||||
weight="light"
|
weight="light"
|
||||||
ml={{ initial: '0', md: '7', lg: '8' }}
|
ml={{ initial: '0', md: '7', lg: '8' }}
|
||||||
>
|
>
|
||||||
BlitzKit Tankopedia
|
BlitzKit Tankopedia
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
className="tank-sandbox-container"
|
className="tank-sandbox-container"
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { PageWrapper } from '../../../components/PageWrapper';
|
import { PageWrapper } from '../../../components/PageWrapper';
|
||||||
import { Charts } from '../../../components/Performance/Charts';
|
import { Charts } from '../../../components/Performance/Charts';
|
||||||
import { ModeSwitcher } from '../../../components/Performance/ModeSwitcher';
|
|
||||||
import { PerformanceInfo } from '../../../components/Performance/PerformanceInfo';
|
|
||||||
import { PlayerCountControl } from '../../../components/Performance/PlayerCountControl';
|
import { PlayerCountControl } from '../../../components/Performance/PlayerCountControl';
|
||||||
import { TankTable } from '../../../components/Performance/Table';
|
import { TankTable } from '../../../components/Performance/Table';
|
||||||
import { FilterControl } from '../../../components/TankSearch/components/FilterControl';
|
import { FilterControl } from '../../../components/TankSearch/components/FilterControl';
|
||||||
@@ -26,10 +24,10 @@ function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
const mode = TankPerformanceEphemeral.use((state) => state.mode);
|
const mode = TankPerformanceEphemeral.use((state) => state.mode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="jade" maxWidth="unset">
|
<PageWrapper color="jade" maxWidth="100%">
|
||||||
<ModeSwitcher />
|
{/* <ModeSwitcher /> */}
|
||||||
|
|
||||||
<PerformanceInfo skeleton={skeleton} />
|
{/* <PerformanceInfo skeleton={skeleton} /> */}
|
||||||
<FilterControl />
|
<FilterControl />
|
||||||
<PlayerCountControl />
|
<PlayerCountControl />
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
---
|
---
|
||||||
|
import ToolOpenGraph from '../../../components/ToolOpenGraph.astro';
|
||||||
import { performanceTool } from '../../../constants/tools';
|
import { performanceTool } from '../../../constants/tools';
|
||||||
import ToolLayout from '../../../layouts/ToolLayout.astro';
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
||||||
import { Page } from './_index';
|
import { Page } from './_index';
|
||||||
---
|
---
|
||||||
|
|
||||||
<ToolLayout tool={performanceTool}>
|
<ToolOpenGraph tool={performanceTool} />
|
||||||
|
|
||||||
|
<BaseLayout noFooter>
|
||||||
<Page client:only="react">
|
<Page client:only="react">
|
||||||
<Page skeleton slot="fallback" />
|
<Page skeleton slot="fallback" />
|
||||||
</Page>
|
</Page>
|
||||||
</ToolLayout>
|
</BaseLayout>
|
||||||
@@ -5,7 +5,7 @@ import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
|
|||||||
|
|
||||||
export function Page() {
|
export function Page() {
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="purple" maxWidth="64rem">
|
<PageWrapper color="purple" maxWidth="80rem">
|
||||||
<TankopediaPersistent.Provider>
|
<TankopediaPersistent.Provider>
|
||||||
<App.Provider>
|
<App.Provider>
|
||||||
<TankSearch />
|
<TankSearch />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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';
|
||||||
import { ENVIRONMENTS } from '../../constants/lightingEnvironments';
|
|
||||||
import { createContextualStore } from '../../core/zustand/createContextualStore';
|
import { createContextualStore } from '../../core/zustand/createContextualStore';
|
||||||
import { SORT_NAMES } from './constants';
|
import { SORT_NAMES } from './constants';
|
||||||
|
|
||||||
@@ -13,10 +12,8 @@ export interface TankopediaPersistentStore {
|
|||||||
wireframe: boolean;
|
wireframe: boolean;
|
||||||
opaque: boolean;
|
opaque: boolean;
|
||||||
advancedHighlighting: boolean;
|
advancedHighlighting: boolean;
|
||||||
environment: (typeof ENVIRONMENTS)[number];
|
|
||||||
showGrid: boolean;
|
showGrid: boolean;
|
||||||
greenPenetration: boolean;
|
greenPenetration: boolean;
|
||||||
showEnvironment: boolean;
|
|
||||||
showSpacedArmor: boolean;
|
showSpacedArmor: boolean;
|
||||||
showExternalModules: boolean;
|
showExternalModules: boolean;
|
||||||
showPrimaryArmor: boolean;
|
showPrimaryArmor: boolean;
|
||||||
@@ -35,10 +32,8 @@ export const TankopediaPersistent = createContextualStore(() =>
|
|||||||
wireframe: false,
|
wireframe: false,
|
||||||
opaque: false,
|
opaque: false,
|
||||||
advancedHighlighting: false,
|
advancedHighlighting: false,
|
||||||
environment: ENVIRONMENTS[0],
|
|
||||||
showGrid: true,
|
showGrid: true,
|
||||||
greenPenetration: false,
|
greenPenetration: false,
|
||||||
showEnvironment: false,
|
|
||||||
showSpacedArmor: true,
|
showSpacedArmor: true,
|
||||||
showExternalModules: true,
|
showExternalModules: true,
|
||||||
showPrimaryArmor: true,
|
showPrimaryArmor: true,
|
||||||
|
|||||||
Reference in new issue
Block a user