mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: slugs
This commit is contained in:
1 parent
da506083c9
commit
f030fd53b1
2 files changed
+1628
-957
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -37,6 +37,7 @@ import {
|
|||||||
SUPPORTED_LOCALES,
|
SUPPORTED_LOCALES,
|
||||||
} from '@blitzkit/i18n';
|
} from '@blitzkit/i18n';
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from 'fs/promises';
|
||||||
|
import { deburr } from 'lodash-es';
|
||||||
import { parse as parsePath } from 'path';
|
import { parse as parsePath } from 'path';
|
||||||
import { Vector3Tuple } from 'three';
|
import { Vector3Tuple } from 'three';
|
||||||
import { parse as parseYaml } from 'yaml';
|
import { parse as parseYaml } from 'yaml';
|
||||||
@@ -48,6 +49,23 @@ import { DATA } from './constants';
|
|||||||
import { Avatar } from './skillIcons';
|
import { Avatar } from './skillIcons';
|
||||||
import { TankParameters } from './tankIcons';
|
import { TankParameters } from './tankIcons';
|
||||||
|
|
||||||
|
const nonAlphanumericRegex = /[^a-z0-9]/g;
|
||||||
|
const multipleDashesRegex = /--+/g;
|
||||||
|
const trailingDashRegex = /-$/g;
|
||||||
|
const leadingDashRegex = /^-/g;
|
||||||
|
|
||||||
|
const nationSlugDiscriminators = {
|
||||||
|
china: 'cn',
|
||||||
|
european: 'eu',
|
||||||
|
france: 'fr',
|
||||||
|
germany: 'de',
|
||||||
|
japan: 'jp',
|
||||||
|
other: 'ot',
|
||||||
|
uk: 'gb',
|
||||||
|
usa: 'us',
|
||||||
|
ussr: 'ru',
|
||||||
|
};
|
||||||
|
|
||||||
function parseResearchCost(raw: number | string) {
|
function parseResearchCost(raw: number | string) {
|
||||||
if (typeof raw === 'number') {
|
if (typeof raw === 'number') {
|
||||||
return {
|
return {
|
||||||
@@ -502,7 +520,7 @@ const blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = {
|
|||||||
HIGH_EXPLOSIVE: ShellType.HE,
|
HIGH_EXPLOSIVE: ShellType.HE,
|
||||||
HOLLOW_CHARGE: ShellType.HEAT,
|
HOLLOW_CHARGE: ShellType.HEAT,
|
||||||
};
|
};
|
||||||
export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU(R?)))$/;
|
export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU))$/;
|
||||||
const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = {
|
const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = {
|
||||||
chassis: ModuleType.TRACKS,
|
chassis: ModuleType.TRACKS,
|
||||||
engine: ModuleType.ENGINE,
|
engine: ModuleType.ENGINE,
|
||||||
@@ -703,6 +721,93 @@ export async function definitions() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const slugRequesters = new Map<string, { id: number; key: string }[]>();
|
||||||
|
const idToNation: Record<number, string> = {};
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
nations.map(async (nation) => {
|
||||||
|
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const tankKey in tankList.root) {
|
||||||
|
if (botPattern.test(tankKey)) continue;
|
||||||
|
|
||||||
|
const tank = tankList.root[tankKey];
|
||||||
|
const tankId = toUniqueId(nation, tank.id);
|
||||||
|
const name = (
|
||||||
|
(tank.shortUserString
|
||||||
|
? getString(tank.shortUserString)
|
||||||
|
: undefined) ?? getString(tank.userString)
|
||||||
|
).locales.en;
|
||||||
|
|
||||||
|
let slug = deburr(name).toLowerCase();
|
||||||
|
slug = slug.replaceAll(nonAlphanumericRegex, '-');
|
||||||
|
slug = slug.replaceAll(multipleDashesRegex, '-');
|
||||||
|
slug = slug.replaceAll(trailingDashRegex, '');
|
||||||
|
slug = slug.replaceAll(leadingDashRegex, '');
|
||||||
|
|
||||||
|
idToNation[tankId] = nation;
|
||||||
|
|
||||||
|
if (slugRequesters.has(slug)) {
|
||||||
|
slugRequesters.get(slug)!.push({ id: tankId, key: tankKey });
|
||||||
|
} else {
|
||||||
|
slugRequesters.set(slug, [{ id: tankId, key: tankKey }]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const slugs = new Map<number, string>();
|
||||||
|
|
||||||
|
slugRequesters.forEach((requesters, slug) => {
|
||||||
|
if (requesters.length === 1) {
|
||||||
|
slugs.set(requesters[0].id, slug);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(
|
||||||
|
`Multiple tanks share slug ${slug}: ${requesters.map(({ key }) => key).join(', ')}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (requesters.length !== 2) {
|
||||||
|
throw new Error('Unresolvable number of duplicates :(');
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonCanonical = requesters.find(({ key }) => key.endsWith('TUR'));
|
||||||
|
|
||||||
|
if (nonCanonical === undefined) {
|
||||||
|
console.log('Using nations to discriminate');
|
||||||
|
// both are non-tutorial tanks, will have to discriminate using nation
|
||||||
|
|
||||||
|
requesters.forEach(({ id, key }) => {
|
||||||
|
const nation = idToNation[id];
|
||||||
|
const discriminator =
|
||||||
|
nationSlugDiscriminators[
|
||||||
|
nation as keyof typeof nationSlugDiscriminators
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log(`Solution: ${key} -> ${slug}-${discriminator}`);
|
||||||
|
slugs.set(id, `${slug}-${discriminator}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('Using tutorial bot suffix to discriminate');
|
||||||
|
|
||||||
|
const canonical = requesters.find(({ key }) => !key.endsWith('TUR'));
|
||||||
|
|
||||||
|
if (canonical === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
'Two tutorial bots share the same slug? The world is truly broken.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Solution: ${canonical.key} -> ${slug}`);
|
||||||
|
slugs.set(canonical.id, slug);
|
||||||
|
console.log(`Solution: ${nonCanonical.key} -> ${slug}-tur`);
|
||||||
|
slugs.set(nonCanonical.id, `${slug}-tur`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
nations.map(async (nation) => {
|
nations.map(async (nation) => {
|
||||||
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
@@ -787,6 +892,7 @@ export async function definitions() {
|
|||||||
const hullArmor: Armor = { spaced: [], thickness: {} };
|
const hullArmor: Armor = { spaced: [], thickness: {} };
|
||||||
const equipment = tankDefinition.root.optDevicePreset;
|
const equipment = tankDefinition.root.optDevicePreset;
|
||||||
tankStringIdMap[`${nation}:${tankKey}`] = tankId;
|
tankStringIdMap[`${nation}:${tankKey}`] = tankId;
|
||||||
|
const slug = slugs.get(tank.id)!;
|
||||||
|
|
||||||
if (tank.sellPrice) {
|
if (tank.sellPrice) {
|
||||||
tankPrice = {
|
tankPrice = {
|
||||||
@@ -903,7 +1009,7 @@ export async function definitions() {
|
|||||||
(tank.shortUserString
|
(tank.shortUserString
|
||||||
? getString(tank.shortUserString)
|
? getString(tank.shortUserString)
|
||||||
: undefined) ?? getString(tank.userString),
|
: undefined) ?? getString(tank.userString),
|
||||||
name_full: getString(tank.userString),
|
slug,
|
||||||
nation,
|
nation,
|
||||||
type: tankTags.includes('collectible')
|
type: tankTags.includes('collectible')
|
||||||
? TankType.COLLECTOR
|
? TankType.COLLECTOR
|
||||||
@@ -939,13 +1045,6 @@ export async function definitions() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
tankDefinitions.tanks[tankId].name ===
|
|
||||||
tankDefinitions.tanks[tankId].name_full
|
|
||||||
) {
|
|
||||||
delete tankDefinitions.tanks[tankId].name_full;
|
|
||||||
}
|
|
||||||
|
|
||||||
modelDefinitions.models[tankId] = {
|
modelDefinitions.models[tankId] = {
|
||||||
armor: hullArmor,
|
armor: hullArmor,
|
||||||
turret_origin: vector3TupleToBlitzkit(turretOrigin),
|
turret_origin: vector3TupleToBlitzkit(turretOrigin),
|
||||||
|
|||||||
Reference in new issue
Block a user