Compare commits

...
57 Commits
Author SHA1 Message Date
tresabhi 8ef355c574 feat: we don't talk about it 2025-04-02 16:11:09 -05:00
tresabhi 71e1ab1362 aaaaaaaaaaaaaaaa 2025-04-01 19:38:58 -05:00
tresabhi 53dc108356 Merge branch 'main' of https://github.com/tresabhi/blitzkit 2025-04-01 19:38:01 -05:00
tresabhi 57b3218c5a undo cue4 2025-04-01 19:38:00 -05:00
tresabhi 7472e39faf fix: gifs 2025-04-01 19:34:03 -05:00
tresabhi 034417d19f fix: remove cue4 2025-03-31 17:49:23 -05:00
tresabhi 0368e9ceda feat: pink bot 2025-03-31 17:44:08 -05:00
tresabhi 28f365f762 fix: no class list 2025-03-31 17:39:42 -05:00
tresabhi 40a81f6b1b fix: class list 2025-03-31 17:39:01 -05:00
tresabhi 9475d22472 feat: uh hahaha you'll see ... again! 2025-03-31 17:36:40 -05:00
tresabhi 9538a03afb feat: uh hahaha you'll see 2025-03-31 16:54:32 -05:00
tresabhi d5d171bea6 4 2025-03-30 14:37:40 -05:00
tresabhi a9c9a90ac1 fix: disable /auction 2025-03-30 12:53:14 -05:00
tresabhi f7c562f8b6 fix: detect invalid 0-length translations 2025-03-30 12:51:34 -05:00
TrèsAbhi 836cf69f01 New Crowdin updates (#149)
* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Spanish)

* New translations en.json (Chinese Simplified)
2025-03-26 14:37:29 -05:00
tresabhi ba17aae706 feat: bump int size 2025-03-25 15:29:41 -05:00
TrèsAbhi 30ca5c053a Update tankNames.ts 2025-03-14 12:14:55 -05:00
tresabhi c36419f5b6 fix: don't include cue4 2025-03-11 10:40:02 -05:00
tresabhi 1879895db2 feat: add 124 support 2025-03-11 10:13:02 -05:00
tresabhi c964e3c8ec feat: google crawler 2025-03-03 17:49:45 -06:00
tresabhi 2ef6d18f82 lol why cue4??? 2025-03-03 16:19:43 -06:00
tresabhi 10b672572b feat: try appending dvpl? 2025-03-03 16:15:58 -06:00
TrèsAbhi 2a4efa8cd2 fix: check for changes for real 2025-03-03 12:29:25 -06:00
TrèsAbhi 5a8435da29 Update Footer.astro 2025-03-03 12:15:05 -06:00
TrèsAbhi 3461349a96 Update Footer.astro 2025-03-03 12:13:06 -06:00
TrèsAbhi 3791e9df12 New Crowdin updates (#147)
* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Japanese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Portuguese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)
2025-03-03 11:10:15 -06:00
TrèsAbhi b3c36aac93 New Crowdin updates (#146)
* New translations en.json (Polish)

* New translations en.json (Polish)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Spanish)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Polish)

* New translations en.json (Polish)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Ukrainian)

* New translations en.json (Polish)

* New translations en.json (Chinese Simplified)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (French)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Chinese Simplified)
2025-02-03 11:07:41 -06:00
TrèsAbhi c0895409bc Update index.ts 2025-01-24 11:27:45 -06:00
TrèsAbhi 438b565cbb New Crowdin updates (#145) 2025-01-19 13:00:15 -06:00
tresabhi cf1a82e89b fix: remove default 2025-01-19 12:51:26 -06:00
tresabhi 0506e361b2 feat: more prune 2025-01-19 12:46:57 -06:00
tresabhi d6a1357a82 Merge branch 'main' into dev 2025-01-19 12:41:06 -06:00
tresabhi a5a1dbd51b fix: [Oo] for /debug 2025-01-18 14:16:49 -06:00
tresabhi 5201350e44 fix: [Oo] for /stats 2025-01-18 14:16:26 -06:00
tresabhi 6a84b9d5ae fix: stricter pattern 2025-01-18 14:07:47 -06:00
tresabhi cf04fe21b3 fix: logs 2025-01-18 13:59:14 -06:00
tresabhi 84eb3c2da3 fix: version 2025-01-18 13:49:37 -06:00
tresabhi 22bf2dc1d8 feat: logs 2025-01-18 13:49:18 -06:00
tresabhi b9c339daa7 fix: search keys 2025-01-18 13:47:31 -06:00
tresabhi ef3006cac1 fix: double full stops 2025-01-18 13:42:52 -06:00
TrèsAbhi e482fd40c1 Update applyPatches.ts 2025-01-18 11:41:45 -06:00
TrèsAbhi cc4569966e Update applyPatches.ts 2025-01-18 09:19:38 -06:00
tresabhi 3708a3ced7 fix: handle deleted tanks 2025-01-17 20:29:33 -06:00
tresabhi 0ae59c2fd7 fix: check dir exists 2025-01-17 20:15:09 -06:00
tresabhi 7027f63236 feat: prune 2025-01-17 19:56:46 -06:00
TrèsAbhi 6725e79021 New Crowdin updates (#140) 2025-01-17 19:36:39 -06:00
tresabhi 5ca3cee9da feat: verbose 2025-01-17 19:22:02 -06:00
tresabhi 602a5a1e99 fix: move env 2025-01-17 19:19:45 -06:00
tresabhi bfbf3bdc39 fix: no double dvpl 2025-01-17 19:17:39 -06:00
tresabhi 644686582e fix: icon api 2025-01-17 19:06:07 -06:00
TrèsAbhi 1c00bc81f8 New Crowdin updates (#139)
* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)
2025-01-17 12:46:05 -06:00
TrèsAbhi ac3b69bec5 Update assets.yml 2025-01-17 12:45:12 -06:00
TrèsAbhi 3e14c943b6 Update assets.yml 2025-01-16 23:05:13 -06:00
TrèsAbhi d33633be2c Update assets.yml 2025-01-16 22:15:24 -06:00
TrèsAbhi 4bf70d02d0 Update Crowdin configuration file 2025-01-16 20:48:29 -06:00
tresabhi 377bcf0e38 fix: env 2025-01-16 20:44:55 -06:00
tresabhi 884b6d4318 fix: run tests even on dev 2025-01-16 20:36:49 -06:00
56 changed files with 7470 additions and 1926 deletions

No files matched your search

+15 -1
View File
@@ -39,6 +39,17 @@ env:
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: ${{ github.event.inputs.cdn_branch || secrets.PUBLIC_ASSET_BRANCH }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: true
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
PUBLIC_PATREON_CLIENT_ID: ${{ secrets.PUBLIC_PATREON_CLIENT_ID }}
PUBLIC_PATREON_REDIRECT_URI: ${{ secrets.PUBLIC_PATREON_REDIRECT_URI }}
PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }}
PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }}
PUBLIC_DEBUG_MISSING_I18N: false
jobs:
download_client:
@@ -106,6 +117,9 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- name: Setup bun
uses: oven-sh/setup-bun@v1
@@ -127,7 +141,7 @@ jobs:
- name: Start server and build assets
run: |
bun dev:website &
bun dev:website --verbosepa &
SERVER_PID=$!
bun run build:assets --all-targets --depot --production
+3 -1
View File
@@ -2,7 +2,6 @@ name: Website
on:
push:
branches: main
workflow_dispatch:
schedule:
@@ -27,6 +26,8 @@ env:
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
jobs:
build:
@@ -53,6 +54,7 @@ jobs:
path: dist/website
deploy-github-pages:
if: ${{ github.ref == 'refs/heads/main' }}
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
needs: build
+1
View File
@@ -27,3 +27,4 @@ target
/.vercel
/.astro
.vercel
/src
+1 -1
View File
@@ -1,3 +1,3 @@
files:
- source: /packages/core/lang/en.json
- source: /packages/i18n/strings/en.json
translation: /%original_path%/%locale%.json
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.1
## Fixes
- Fixed a crash that occurs if tanks are deleted in Blitz and were once available in BlitzKit
- All tank icons are now back
- The tank search once again returns results
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.2
This update only affects the website.
## Fixes
- The new Improved Vertical Stabilizer equipment which only affects the gun depression found on the Type 63 now works
+9
View File
@@ -0,0 +1,9 @@
# BlitzKit 1.27.3
This update only affects the bot.
## Fixes
- Added support for 64-bit IDs allowing users with new accounts to link with BlitzKit
- This comes at the added cost of resetting the links of all other accounts
- Just re-link and you're good to go
+3
View File
@@ -0,0 +1,3 @@
# FreakKit 1.27.4
It has come to our attention that tanks are very harsh objects. To even it out, we made the website a pretty pretty princess <3
+11
View File
@@ -0,0 +1,11 @@
# BlitzKit 1.27.5
This update only affects the bot.
## Changes
- Removed `/auction`
## Fixes
- Fixed a bug that caused `/rating` to disappear
+3 -2
View File
@@ -1,7 +1,7 @@
{
"name": "blitzkit",
"private": true,
"version": "1.27.0",
"version": "1.27.5",
"description": "🎉 Tools for everything World of Tanks Blitz",
"type": "module",
"types": "./types.d.ts",
@@ -35,7 +35,8 @@
"build:protos": "bun packages/scripts/src/protos.ts",
"preview:website": "cd packages/website && bun run preview",
"build:reviews": "cd packages/scripts && bun build:reviews",
"build:ids": "cd packages/scripts && bun build:ids"
"build:ids": "cd packages/scripts && bun build:ids",
"prunestrings": "cd packages/scripts && bun prunestrings"
},
"license": "MIT",
"dependencies": {
+1 -1
View File
@@ -17,7 +17,7 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
const list = [
[strings.bot.commands.debug.body.version, packageJSON.version],
[
strings.bot.commands.debug.body.shard,
strings.bot.commands.debug.body.shard.label,
client.shard?.ids[0] ?? strings.bot.commands.debug.body.shard.default,
],
[
@@ -16,8 +16,8 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
gap: 16,
width: '100%',
color: theme.colors.textHighContrast,
backgroundColor: theme.colors.appBackground1,
position: 'relative',
background: theme.colors.appBackground1,
}}
>
{children}
+2 -4
View File
@@ -1,3 +1,4 @@
import { SEARCH_KEYS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { go } from 'fuzzysort';
@@ -19,10 +20,7 @@ export async function resolveTankId(
const searchResult = go(
`${tank}`,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 1,
},
{ keys: SEARCH_KEYS, limit: 1 },
);
if (searchResult.length === 0) {
@@ -8,12 +8,12 @@ export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale,
) {
const { strings } = translator(locale);
const { strings, bkLocale } = translator(locale);
const awaitedTankDefinitions = await fetchTankDefinitions();
const info: string[] = [];
// TODO: somehow localize tank names?
if (tank) return awaitedTankDefinitions.tanks[tank].name;
if (tank) return awaitedTankDefinitions.tanks[tank].name.locales[bkLocale];
if (nation)
info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) {
@@ -8,6 +8,8 @@ export async function getBlitzFromDiscord(discordId: bigint) {
if (unique === null) return null;
await flagUserActivity(unique.blitz_id);
return { id: unique.blitz_id, region: idToRegion(unique.blitz_id) };
const id = Number(unique.blitz_id);
await flagUserActivity(id);
return { id, region: idToRegion(id) };
}
@@ -1,5 +1,4 @@
import { fetchTankNames, TankType } from '@blitzkit/core';
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core';
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
@@ -32,16 +31,7 @@ export async function autocompleteTanks(
go(
focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: [
...SUPPORTED_LOCALES.map((locale) => [
`searchableName.{locale}`,
`searchableNameDeburr.{locale}`,
]).flat(),
'camouflages',
],
limit: 10,
},
{ keys: SEARCH_KEYS, limit: 10 },
).map(async (item) => {
let name = unwrap(item.obj.searchableName);
@@ -3,7 +3,8 @@ import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
import { translator } from '../localization/translator';
const validNameRegex = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
const validNameRegex =
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
export function localizationObject(
path: (strings: BlitzKitStrings) => string,
@@ -14,7 +15,8 @@ export function localizationObject(
(localizations, locale) => {
const { strings } = translator(locale);
const translation = literalsFunc(path(strings), literals);
const isValid = !strict || validNameRegex.test(translation);
const isValid =
(!strict || validNameRegex.test(translation)) && translation.length > 0;
if (!isValid) {
console.warn(
@@ -12,8 +12,9 @@ export function translator(localeRaw: Locale) {
SUPPORTED_LOCALES_DISCORD.includes(localeRaw)
? localeRaw
: DEFAULT_LOCALE_DISCORD;
const strings = STRINGS[SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]];
const unwrap = unwrapper(SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]);
const bkLocale = SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale];
const strings = STRINGS[bkLocale];
const unwrap = unwrapper(bkLocale);
return { locale, strings, unwrap };
return { locale, strings, unwrap, bkLocale };
}
@@ -19,11 +19,11 @@ import { Writeable } from '../../../types/writable';
export async function handleChatInputCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
await interaction.deferReply();
const awaitedCommands = await commands;
const registry = awaitedCommands[interaction.commandName];
await interaction.deferReply();
try {
const returnable = await registry.handler(interaction);
@@ -15,7 +15,6 @@ import {
SlashCommandSubcommandsOnlyBuilder,
} from 'discord.js';
import { aboutCommand } from '../../commands/about';
import { auctionCommand } from '../../commands/auction';
import { breakdownCommand } from '../../commands/breakdown';
import { debugCommand } from '../../commands/debug';
import { evolutionCommand } from '../../commands/evolution';
@@ -80,7 +79,7 @@ interface CommandRegistryExplicit extends CommandRegistryBase {
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
auctionCommand,
// auctionCommand,
permissionsCommand,
debugCommand,
aboutCommand,
+8 -1
View File
@@ -45,7 +45,7 @@ const interval = setInterval(async () => {
const usernames = awaitedDiscoveredIdsDefinitions.count;
manager.broadcastEval(
(client, { servers, users, channels, tanks, usernames }) => {
async (client, { servers, users, channels, tanks, usernames }) => {
const bios = [
`Living in ${servers.toLocaleString()} servers`,
`Serving ${users.toLocaleString()} users`,
@@ -62,6 +62,13 @@ const interval = setInterval(async () => {
},
],
});
for (const guild of client.guilds.cache.values()) {
try {
const botMember = await guild.members.fetch(client.user.id);
await botMember.setNickname('BlitzKit');
} catch {}
}
},
{ context: { servers, channels, users, tanks, usernames } },
);
+16 -1
View File
@@ -18,12 +18,27 @@ import {
} from '@radix-ui/colors';
import { createBorderStyles, createColors, createSpaces } from 'bepaint';
const funnyColors = {
pink1: crimsonDark.crimson5,
pink2: crimsonDark.crimson5,
pink3: crimsonDark.crimson5,
pink4: crimsonDark.crimson6,
pink5: crimsonDark.crimson6,
pink6: crimsonDark.crimson7,
pink7: crimsonDark.crimson7,
pink8: crimsonDark.crimson8,
pink9: crimsonDark.crimson9,
pink10: crimsonDark.crimson10,
pink11: crimsonDark.crimson11,
pink12: crimsonDark.crimson12,
};
// this isn't really a stitches config, but the object is compliant
export const theme = {
spaces: createSpaces(),
colors: {
...createColors(slateDark),
...createColors(funnyColors),
...createColors(slateDarkA, 'alpha'),
...createColors(slate, 'light'),
+12 -1
View File
@@ -1,4 +1,5 @@
import { deburr } from 'lodash-es';
import { SUPPORTED_LOCALES } from '../../../i18n/src/strings';
import { I18nString } from '../protos';
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
import { fetchTankDefinitions } from './tankDefinitions';
@@ -25,11 +26,21 @@ export async function fetchTankNames() {
searchableName: tank.name_full ?? tank.name,
searchableNameDeburr,
camouflages: tank.camouflages
?.map((id) => camouflageDefinitions.camouflages[id]?.name)
?.map((id) => SUPPORTED_LOCALES.map((locale) => camouflageDefinitions.camouflages[id]?.name.locales[locale]))
.flat()
.filter(Boolean)
.map(deburr)
.join(' '),
treeType: tank.type,
};
}),
);
}
export const SEARCH_KEYS = [
...SUPPORTED_LOCALES.map((locale) => [
`searchableName.locales.${locale}`,
`searchableNameDeburr.locales.${locale}`,
]).flat(),
'camouflages',
];
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -10,7 +10,8 @@
"build:reviews": "bun src/reviews.ts",
"build:ids": "bun src/discoverIds.ts",
"build:bot": "bun src/buildBot.ts",
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts"
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts",
"prunestrings": "bun src/pruneStrings.ts"
},
"dependencies": {
"@blitzkit/core": "workspace:*",
+18 -10
View File
@@ -45,19 +45,27 @@ while (true) {
files.length,
);
await Promise.all(
files.map(async ({ path, data }) => {
const { dir } = parsePath(path);
for (const { path, data } of files) {
console.log(`Patching "${path}"...`);
const { dir } = parsePath(path);
try {
await mkdir(`${DATA}/${dir}`, { recursive: true });
await writeFile(
`${DATA}/${path}`,
new Uint8Array(writeDVPL(Buffer.from(data))),
);
} catch (error) {
console.warn(`Failed to make directory "${dir}"`);
}
bar.tick();
}),
);
const isDvpl = path.endsWith('.dvpl');
const buffer = Buffer.from(data);
await writeFile(
`${DATA}/${path}${isDvpl ? '' : '.dvpl'}`,
new Uint8Array(isDvpl ? buffer : writeDVPL(buffer)),
);
bar.tick();
}
if ('dynamicContentLocalizationsDir' in data) {
console.log('Found dynamic content localizations; patching...');
@@ -3,7 +3,7 @@ import { DATA_COMPRESSED } from '../../buildAssets/constants';
import { readDVPL } from './readDVPL';
export async function readDVPLFile(file: string) {
if (DATA_COMPRESSED) {
if (DATA_COMPRESSED && !file.endsWith('.dvpl')) {
return readDVPL(await readFile(`${file}.dvpl`));
}
@@ -6,7 +6,7 @@ import { TextureMutation } from './constants';
export async function readTexture(path: string, mutation?: TextureMutation) {
const ddsTexturePath = path.replace('.tex', '.dx11.dds');
const isDds = existsSync(ddsTexturePath);
const isDds = existsSync(`${ddsTexturePath}.dvpl`);
const resolvedTexturePath = isDds
? ddsTexturePath
: ddsTexturePath.replace('.dds', '.pvr');
@@ -30,10 +30,7 @@ export async function commitMultipleFiles(
} else if (response.status === 200) {
const buffer = Buffer.from(await response.arrayBuffer());
// we discard blob if they're the same size; it's unlikely their
// contents will be different; I love playing russian roulette!
const equal = buffer.length === change.content.length || buffer.equals(change.content);
if (!equal) {
if (!buffer.equals(change.content)) {
const diff = change.content.length - buffer.length;
console.log(
+53
View File
@@ -0,0 +1,53 @@
import {
DEFAULT_LOCALE,
SUPPORTED_LOCALES,
TranslationTree,
} from '@blitzkit/i18n';
import { writeFile } from 'fs/promises';
const ROOT = '@blitzkit/i18n/strings';
const WORKING_ROOT = '../../packages/i18n/strings';
const defaultStrings = require(`${ROOT}/${DEFAULT_LOCALE}.json`).default;
const nonDefaultLocales = SUPPORTED_LOCALES.filter(
(locale) => locale !== DEFAULT_LOCALE,
);
function prune(defaultStrings: TranslationTree, strings: TranslationTree) {
for (const key in strings) {
if (key in defaultStrings) {
if (typeof defaultStrings[key] === typeof strings[key]) {
if (typeof strings[key] === 'string') {
if (
// are equivalent regardless of %s flavor
(defaultStrings[key] as string).replaceAll(/%s\d/g, '%s') ===
(strings[key] as string).replaceAll(/%s\d/g, '%s') ||
// string is empty
strings[key] === '' ||
// string has old flavor of %s
strings[key].match(/%s([^\d]|$)/g) !== null
) {
delete strings[key];
}
} else {
prune(defaultStrings[key] as TranslationTree, strings[key]);
}
} else {
delete strings[key];
}
} else {
delete strings[key];
}
}
return strings;
}
for (const locale of nonDefaultLocales) {
console.log(`Pruning ${locale}...`);
const strings = require(`${ROOT}/${locale}.json`).default;
const pruned = prune(defaultStrings, strings);
writeFile(`${WORKING_ROOT}/${locale}.json`, JSON.stringify(pruned, null, 2));
}
@@ -180,6 +180,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = await hasEquipment(
124,
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position);
@@ -191,6 +196,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
}
@@ -275,6 +281,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = await hasEquipment(
124,
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY);
@@ -285,6 +296,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
}
@@ -357,6 +369,11 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = await hasEquipment(
124,
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY);
@@ -367,6 +384,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
}
@@ -1,4 +1,9 @@
import { asset, TankType, type TankDefinition } from '@blitzkit/core';
import {
asset,
fetchTankDefinitions,
TankType,
type TankDefinition,
} from '@blitzkit/core';
import { Flex, Text, type TextProps } from '@radix-ui/themes';
import { uniq } from 'lodash-es';
import { forwardRef, type ReactNode } from 'react';
@@ -16,6 +21,8 @@ type TankCardProps = TextProps & {
noLink?: boolean;
};
const tankDefinitions = await fetchTankDefinitions();
export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
(
{
@@ -49,10 +56,9 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
onClick={() => {
onSelect?.(tank);
mutateTankopediaPersistent((draft) => {
draft.recentlyViewed = uniq([
tank.id,
...draft.recentlyViewed,
]).slice(0, MAX_RECENTLY_VIEWED);
draft.recentlyViewed = uniq([tank.id, ...draft.recentlyViewed])
.filter((id) => id in tankDefinitions.tanks)
.slice(0, MAX_RECENTLY_VIEWED);
});
}}
className="tank-search-card"
@@ -12,7 +12,7 @@ export function NoResults({ type = 'filters' }: NoResultsProps) {
return (
<Flex flexGrow="1" align="center" justify="center">
<Text color="gray">
{strings.website.common.tank_search.no_tanks_found.body}.{' '}
{strings.website.common.tank_search.no_tanks_found.body}{' '}
<Link
href="#"
underline="always"
@@ -25,7 +25,6 @@ export function NoResults({ type = 'filters' }: NoResultsProps) {
? strings.website.common.tank_search.no_tanks_found.clear_filters
: strings.website.common.tank_search.no_tanks_found.search_again}
</Link>
.
</Text>
</Flex>
);
@@ -16,7 +16,9 @@ export function RecentlyViewed() {
const tankopediaPersistentStore = TankopediaPersistent.useStore();
const filters = useStore($tankFilters);
// non-reactive because it is a little weird that it updates instantly even before the page loads
const recentlyViewed = tankopediaPersistentStore.getState().recentlyViewed;
const recentlyViewed = tankopediaPersistentStore
.getState()
.recentlyViewed.filter((id) => id in tankDefinitions.tanks);
const sort = useStore($tankopediaSort);
const hasFilters = useMemo(
() =>
@@ -2,6 +2,7 @@ import {
metaSortTank,
normalizeBoundingBox,
resolveDpm,
SEARCH_KEYS,
unionBoundingBox,
type TankDefinition,
} from '@blitzkit/core';
@@ -410,7 +411,7 @@ export const TankSearch = memo<TankSearchProps>(
: sorted.reverse();
} else {
const searchedRaw = fuzzysort.go(tankFilters.search, tankNames, {
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
keys: SEARCH_KEYS,
});
const searchedTanks = searchedRaw.map(
(result) => tankDefinitions.tanks[result.obj.id],
@@ -456,7 +457,9 @@ export const TankSearch = memo<TankSearchProps>(
draft.recentlyViewed = uniq([
...tanksFiltered.map(({ id }) => id),
...draft.recentlyViewed,
]).slice(0, MAX_RECENTLY_VIEWED);
])
.filter((id) => id in tankDefinitions.tanks)
.slice(0, MAX_RECENTLY_VIEWED);
});
}}
>
@@ -1,4 +1,4 @@
import { assertSecret } from '@blitzkit/core';
import { assertSecret, fetchTankDefinitions } from '@blitzkit/core';
import { MinusCircledIcon } from '@radix-ui/react-icons';
import { Callout, Flex } from '@radix-ui/themes';
import { uniq } from 'lodash-es';
@@ -9,6 +9,8 @@ import { AesonPlug } from '../AesonPlug';
import { ExperimentIcon } from '../ExperimentIcon';
import { MAX_RECENTLY_VIEWED } from '../TankSearch/constants';
const tankDefinitions = await fetchTankDefinitions();
export function CalloutsSection() {
const tank = Duel.use((state) => state.protagonist.tank);
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
@@ -17,10 +19,9 @@ export function CalloutsSection() {
useEffect(() => {
mutateTankopediaPersistent((draft) => {
draft.recentlyViewed = uniq([tank.id, ...draft.recentlyViewed]).slice(
0,
MAX_RECENTLY_VIEWED,
);
draft.recentlyViewed = uniq([tank.id, ...draft.recentlyViewed])
.filter((id) => id in tankDefinitions.tanks)
.slice(0, MAX_RECENTLY_VIEWED);
});
}, [tank]);
@@ -107,6 +107,7 @@ export function Characteristics() {
const hasSupercharger = useEquipment(107);
const hasCalibratedShells = useEquipment(103);
const hasImprovedVerticalStabilizer = useEquipment(122);
const hasDownImprovedVerticalStabilizer = useEquipment(124);
const penetrationCoefficient = coefficient([
hasCalibratedShells,
resolvePenetrationCoefficient(true, shell.type) - 1,
@@ -129,9 +130,10 @@ export function Characteristics() {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
});
}, [hasImprovedVerticalStabilizer]);
}, [hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer]);
return (
<Flex
@@ -25,6 +25,7 @@ export function QuickInputs() {
const initialGunPitch =
tankModelDefinition.initial_turret_rotation?.pitch ?? 0;
const hasImprovedVerticalStabilizer = useEquipment(122);
const hasDownImprovedVerticalStabilizer = useEquipment(124);
useEffect(() => {
if (!yawInput.current) return;
@@ -92,6 +93,7 @@ export function QuickInputs() {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
mutateDuel((state) => {
@@ -133,6 +135,7 @@ export function QuickInputs() {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
mutateDuel((state) => {
@@ -118,6 +118,11 @@ export function Controls({ naked }: ControlsProps) {
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = await hasEquipment(
124,
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
switch (event) {
case Pose.HullDown: {
@@ -127,6 +132,7 @@ export function Controls({ naked }: ControlsProps) {
protagonistGunModelDefinition.pitch,
protagonistTurretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
camera.position
@@ -159,6 +165,7 @@ export function Controls({ naked }: ControlsProps) {
protagonistGunModelDefinition.pitch,
protagonistTurretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
camera.position
@@ -185,6 +185,11 @@ export function TankModel() {
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = await hasEquipment(
124,
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position);
@@ -196,6 +201,7 @@ export function TankModel() {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
}
@@ -287,6 +293,11 @@ export function TankModel() {
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = await hasEquipment(
124,
duel.protagonist.tank.equipment_preset,
duel.protagonist.equipmentMatrix,
);
const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY);
@@ -297,6 +308,7 @@ export function TankModel() {
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
}
@@ -41,6 +41,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
const canvas = useRef<HTMLCanvasElement>(null);
const hasImprovedVerticalStabilizer = useEquipment(122);
const hasDownImprovedVerticalStabilizer = useEquipment(124);
const protagonistGun = Duel.use((state) => state.protagonist.gun);
const protagonistTurret = Duel.use((state) => state.protagonist.turret);
const mutateDuel = Duel.useMutation();
@@ -82,6 +83,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
gunModelDefinition.pitch,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
@@ -12,6 +12,7 @@ export function applyPitchYawLimits(
pitchLimits: PitchLimits, // deg
yawLimits?: YawLimits, // deg
verticalStabilizer = false,
downVerticalStabilizer = false,
) {
let newPitch = pitch;
let newYaw = yaw;
@@ -22,7 +23,8 @@ export function applyPitchYawLimits(
);
}
const depressionBuff = verticalStabilizer ? degToRad(3) : 0;
const depressionBuff =
verticalStabilizer || downVerticalStabilizer ? degToRad(3) : 0;
const elevationBuff = verticalStabilizer ? degToRad(4) : 0;
let lowerPitch = -degToRad(pitchLimits.max) - depressionBuff;
let upperPitch = -degToRad(pitchLimits.min) + elevationBuff;
@@ -109,6 +109,7 @@ export function tankCharacteristics(
const hasEngineAccelerator = equipment(117);
const hasCamouflageNet = equipment(115);
const hasImprovedVerticalStabilizer = equipment(122);
const hasDownImprovedVerticalStabilizer = equipment(124);
const hasImprovedSuspension = equipment(123);
const hasTungsten = consumable(45);
@@ -425,7 +426,8 @@ export function tankCharacteristics(
const gunDepression =
gunModelDefinition.pitch.max +
gunDefaultPitch +
(hasImprovedVerticalStabilizer ? 3 : 0);
(hasImprovedVerticalStabilizer ? 3 : 0) +
(hasDownImprovedVerticalStabilizer ? 3 : 0);
const gunElevation =
-gunModelDefinition.pitch.min -
gunDefaultPitch +
@@ -29,7 +29,7 @@ const locale = Astro.params.locale ?? DEFAULT_LOCALE;
<script>
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
import { getBestLocale } from '../core/i18n/getBestLocale';
const isAPI = window.location.pathname.startsWith('/api');
const isI18nPath = SUPPORTED_LOCALES.some((locale) =>
window.location.pathname.startsWith(`/${locale}`),
);
@@ -38,7 +38,11 @@ const locale = Astro.params.locale ?? DEFAULT_LOCALE;
window.location.pathname.startsWith(`/${locale}`),
);
if (bestLocale !== currentPathLocale) {
if (
!isAPI && // not an api, duh!
localStorage.getItem('preferred-locale') !== null && // no preference, so probably google's crawler
bestLocale !== currentPathLocale // we need to redirect
) {
window.location.pathname = isI18nPath
? window.location.pathname.replace(
`/${currentPathLocale}`,
@@ -1,6 +1,7 @@
---
import { assertSecret } from '@blitzkit/core';
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/i18n';
import { literals } from '@blitzkit/i18n/src/literals';
import { Box, Flex, Heading, Link } from '@radix-ui/themes';
import type { GetStaticPaths } from 'astro';
import { AesonPlug } from '../../components/AesonPlug';
@@ -21,7 +22,6 @@ import {
tierListTool,
} from '../../constants/tools';
import { getStrings } from '../../core/i18n/getStrings';
import { literals } from '@blitzkit/i18n/src/literals';
import BaseLayout from '../../layouts/BaseLayout.astro';
const PUBLIC_ASSET_BRANCH = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
@@ -2,6 +2,7 @@ import { TankSandbox } from '../../../../../components/Tankopedia/HeroSection/co
import { awaitableModelDefinitions } from '../../../../../core/awaitables/modelDefinitions';
import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions';
import { awaitableTankDefinitions } from '../../../../../core/awaitables/tankDefinitions';
import { LocaleProvider } from '../../../../../hooks/useLocale';
import { App } from '../../../../../stores/app';
import { Duel } from '../../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
@@ -23,15 +24,17 @@ export function Page({ id }: PageProps) {
const model = modelDefinitions.models[id];
return (
<App.Provider>
<TankopediaPersistent.Provider>
<Duel.Provider data={{ provisionDefinitions, tank }}>
<TankopediaEphemeral.Provider data={model}>
<Content />
</TankopediaEphemeral.Provider>
</Duel.Provider>
</TankopediaPersistent.Provider>
</App.Provider>
<LocaleProvider locale="en">
<App.Provider>
<TankopediaPersistent.Provider>
<Duel.Provider data={{ provisionDefinitions, tank }}>
<TankopediaEphemeral.Provider data={model}>
<Content />
</TankopediaEphemeral.Provider>
</Duel.Provider>
</TankopediaPersistent.Provider>
</App.Provider>
</LocaleProvider>
);
}
+3 -16
View File
@@ -9,28 +9,15 @@ datasource db {
}
model User {
blitz_id Int @id @map("blitz") // map: "blitz" was too vague
last_used DateTime? @default(now())
tank_votes TankVote[]
blitz_id BigInt @id @map("blitz") // map: "blitz" was too vague
last_used DateTime? @default(now())
@@map("Users") // map: "Users" was mistakenly plural
}
model TankVote {
id Int @id
last_updated DateTime @default(now())
easiness Int?
firepower Int?
maneuverability Int?
survivability Int?
user User @relation(fields: [user_blitz_id], references: [blitz_id])
user_blitz_id Int
}
model DiscordUser {
discord_id BigInt @id @map("discord") // map: "discord" was too vague
blitz_id Int @map("blitz") // map: "blitz" was too vague
blitz_id BigInt @map("blitz") // map: "blitz" was too vague
@@map("DiscordUsers") // map: "DiscordUsers" was mistakenly plural
}