Weapon params page (#3170)

This commit is contained in:
Kalle
2026-06-17 14:37:44 +03:00
committed by GitHub
parent 0460488dc3
commit abed7fa8bb
96 changed files with 38471 additions and 369 deletions

View File

@@ -1,9 +1,4 @@
// To run this script you need from https://github.com/Leanny/leanny.github.io
// 1) WeaponInfoMain.json inside dicts
// 2) WeaponInfoSub.json inside dicts
// 3) WeaponInfoSpecial.json inside dicts
// 4) SplPlayer.game__GameParameterTable.json inside dicts
// 5) params (weapon folder) inside dicts
// To run this script drop the https://github.com/Leanny/splat3 repo into scripts/dicts/splat3
import fs from "node:fs";
import path from "node:path";
@@ -28,19 +23,25 @@ import {
} from "~/modules/in-game-lists/weapon-ids";
import invariant from "~/utils/invariant";
import { logger } from "~/utils/logger";
import playersParams from "./dicts/SplPlayer.game__GameParameterTable.json";
import weapons from "./dicts/WeaponInfoMain.json";
import specialWeapons from "./dicts/WeaponInfoSpecial.json";
import subWeapons from "./dicts/WeaponInfoSub.json";
import {
LANG_JSONS_TO_CREATE,
loadLangDicts,
loadSplPlayerParams,
loadWeaponInfoMain,
loadWeaponInfoSpecial,
loadWeaponInfoSub,
translationJsonFolderName,
weaponParamsDir,
} from "./utils";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const playersParams = loadSplPlayerParams();
const weapons = loadWeaponInfoMain();
const subWeapons = loadWeaponInfoSub();
const specialWeapons = loadWeaponInfoSpecial();
const CURRENT_SEASON = 9;
type MainWeapon = (typeof weapons)[number];
@@ -1093,7 +1094,7 @@ function loadWeaponParamsObject(
) {
return JSON.parse(
fs.readFileSync(
path.join(__dirname, "dicts", "weapon", weaponRowIdToFileName(weapon)),
path.join(weaponParamsDir(), weaponRowIdToFileName(weapon)),
"utf8",
),
).GameParameters;

View File

@@ -3,11 +3,11 @@ import path from "node:path";
import { fileURLToPath } from "node:url";
import { brandIds } from "~/modules/in-game-lists/brand-ids";
import invariant from "~/utils/invariant";
import clothes from "./dicts/GearInfoClothes.json";
import head from "./dicts/GearInfoHead.json";
import shoes from "./dicts/GearInfoShoes.json";
import {
LANG_JSONS_TO_CREATE,
loadGearInfoClothes,
loadGearInfoHead,
loadGearInfoShoes,
loadLangDicts,
translationJsonFolderName,
} from "./utils";
@@ -15,6 +15,10 @@ import {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const clothes = loadGearInfoClothes();
const head = loadGearInfoHead();
const shoes = loadGearInfoShoes();
const CURRENT_SEASON = 9;
const OUTPUT_DIR_PATH = path.join(__dirname, "output");

View File

@@ -12,19 +12,35 @@ import {
specialWeaponIds,
subWeaponIds,
} from "~/modules/in-game-lists/weapon-ids";
// 1) WeaponInfoMain.json inside dicts
// 2) WeaponInfoSub.json inside dicts
// 3) WeaponInfoSpecial.json inside dicts
// 4) misc/spl__DamageRateInfoConfig.pp__CombinationDataTableData.json
import params from "./dicts/spl__DamageRateInfoConfig.pp__CombinationDataTableData.json";
import weapons from "./dicts/WeaponInfoMain.json";
import specialWeapons from "./dicts/WeaponInfoSpecial.json";
import subWeapons from "./dicts/WeaponInfoSub.json";
// To run this script drop the https://github.com/Leanny/splat3 repo into scripts/dicts/splat3
import {
loadDamageRateInfo,
loadWeaponInfoMain,
loadWeaponInfoSpecial,
loadWeaponInfoSub,
PARAMETER_DIR,
} from "./utils";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const params = loadDamageRateInfo();
const weapons = loadWeaponInfoMain();
const subWeapons = loadWeaponInfoSub();
const specialWeapons = loadWeaponInfoSpecial();
const OUTPUT_DIR_PATH = path.join(__dirname, "output");
const DAMAGE_RATE_CONFIG_FILE_NAME =
"spl__DamageRateInfoConfig.pp__CombinationDataTableData.json";
const HISTORY_OUTPUT_PATH = path.join(
__dirname,
"..",
"app",
"features",
"params",
"data",
"damage-rate-history.json",
);
type DamageReceiver = (typeof DAMAGE_RECEIVERS)[number];
@@ -41,6 +57,21 @@ type DamageRateCell = {
DamageRate?: number;
};
type DamageRateConfig = { CellList: Record<string, DamageRateCell> };
type TargetHistory = {
target: string;
current: number;
history: Array<{ version: string; value: number }>;
};
type DamageRateHistoryRow = {
mainWeaponIds: MainWeaponId[];
subWeaponIds: SubWeaponId[];
specialWeaponIds: SpecialWeaponId[];
targets: TargetHistory[];
};
const weaponParamsToWeaponIds = (
params: typeof weapons | typeof subWeapons | typeof specialWeapons,
key: string,
@@ -60,75 +91,194 @@ const weaponParamsToWeaponIds = (
const isDamageReceiver = (key: string): key is DamageReceiver =>
(DAMAGE_RECEIVERS as readonly string[]).includes(key);
const result: Record<string, ResultEntry | undefined> = {};
for (const cell of Object.values(params.CellList) as DamageRateCell[]) {
if (!isDamageReceiver(cell.ColumnKey)) continue;
if (!cell.DamageRate) continue;
const mainIdsForRow = (rowKey: string) =>
weaponParamsToWeaponIds(weapons, rowKey).filter((id): id is MainWeaponId =>
(mainWeaponIds as readonly number[]).includes(id),
);
const subIdsForRow = (rowKey: string) =>
weaponParamsToWeaponIds(subWeapons, rowKey).filter((id): id is SubWeaponId =>
(subWeaponIds as readonly number[]).includes(id),
);
const specialIdsForRow = (rowKey: string) =>
weaponParamsToWeaponIds(specialWeapons, rowKey).filter(
(id): id is SpecialWeaponId =>
(specialWeaponIds as readonly number[]).includes(id),
);
if (!result[cell.RowKey]) {
result[cell.RowKey] = {
mainWeaponIds: weaponParamsToWeaponIds(weapons, cell.RowKey).filter(
(id): id is MainWeaponId =>
(mainWeaponIds as readonly number[]).includes(id),
),
subWeaponIds: weaponParamsToWeaponIds(subWeapons, cell.RowKey).filter(
(id): id is SubWeaponId =>
(subWeaponIds as readonly number[]).includes(id),
),
specialWeaponIds: weaponParamsToWeaponIds(
specialWeapons,
cell.RowKey,
).filter((id): id is SpecialWeaponId =>
(specialWeaponIds as readonly number[]).includes(id),
),
rates: [],
};
/**
* Resolves the per-target damage rate of every damage rate info row in a single config dump.
* Only the PvP-relevant receivers are kept and the synthetic launched/Recycled Brella canopy
* targets are derived the same way the live object damage calculator expects them.
*/
const damageRatesByRow = (
config: DamageRateConfig,
): Map<string, Map<string, number>> => {
const result = new Map<string, Map<string, number>>();
for (const cell of Object.values(config.CellList)) {
if (!isDamageReceiver(cell.ColumnKey)) continue;
if (!cell.DamageRate) continue;
let row = result.get(cell.RowKey);
if (!row) {
row = new Map();
result.set(cell.RowKey, row);
}
row.set(cell.ColumnKey, cell.DamageRate);
// launched versions have double health but share the same rate
if (
cell.ColumnKey.includes("BulletUmbrellaCanopyNormal") ||
cell.ColumnKey.includes("BulletUmbrellaCanopyWide")
) {
row.set(`${cell.ColumnKey}_Launched`, cell.DamageRate);
}
// Recycled Brella reuses Splat Brella's special damage rates
if (cell.ColumnKey === "BulletUmbrellaCanopyNormal") {
row.set("BulletShelterCanopyFocus", cell.DamageRate);
row.set("BulletShelterCanopyFocus_Launched", cell.DamageRate);
}
}
const entry = result[cell.RowKey]!;
return result;
};
const result: Record<string, ResultEntry | undefined> = {};
for (const [rowKey, rates] of damageRatesByRow(params)) {
const mainWeaponIdsForRow = mainIdsForRow(rowKey);
const subWeaponIdsForRow = subIdsForRow(rowKey);
const specialWeaponIdsForRow = specialIdsForRow(rowKey);
// if it applies to no PvP weapons, we don't care about it
if (
entry.mainWeaponIds.length === 0 &&
entry.subWeaponIds.length === 0 &&
entry.specialWeaponIds.length === 0 &&
cell.RowKey !== "ObjectEffect_Up"
mainWeaponIdsForRow.length === 0 &&
subWeaponIdsForRow.length === 0 &&
specialWeaponIdsForRow.length === 0 &&
rowKey !== "ObjectEffect_Up"
) {
result[cell.RowKey] = undefined;
continue;
}
entry.rates.push({
target: cell.ColumnKey,
rate: cell.DamageRate,
});
// add a second rate for launched versions, since they have double health
if (
cell.ColumnKey.includes("BulletUmbrellaCanopyNormal") ||
cell.ColumnKey.includes("BulletUmbrellaCanopyWide")
) {
entry.rates.push({
target: `${cell.ColumnKey}_Launched`,
rate: cell.DamageRate,
});
}
// if it has special damage rates for Splat Brella, add the same value for Recycled Brella
if (cell.ColumnKey === "BulletUmbrellaCanopyNormal") {
entry.rates.push({
target: "BulletShelterCanopyFocus",
rate: cell.DamageRate,
});
entry.rates.push({
target: "BulletShelterCanopyFocus_Launched",
rate: cell.DamageRate,
});
}
result[rowKey] = {
mainWeaponIds: mainWeaponIdsForRow,
subWeaponIds: subWeaponIdsForRow,
specialWeaponIds: specialWeaponIdsForRow,
rates: [...rates].map(([target, rate]) => ({ target, rate })),
};
}
fs.writeFileSync(
path.join(OUTPUT_DIR_PATH, "object-dmg.json"),
JSON.stringify(result, null, 2),
);
writeDamageRateHistory();
function versionDirToDisplay(version: string): string {
const num = Number.parseInt(version, 10);
const major = Math.floor(num / 100);
const minor = Math.floor((num % 100) / 10);
const patch = num % 10;
return `${major}.${minor}.${patch}`;
}
/**
* Builds the per-row, per-target damage rate history across every versioned config dump and
* writes it for the params page to surface in its patch history. Only PvP-relevant rows and
* only targets whose rate actually changed at some point are kept, so the output stays small.
*/
function writeDamageRateHistory() {
const versionDirs = fs
.readdirSync(PARAMETER_DIR)
.filter((dir) => /^\d+$/.test(dir))
.sort((a, b) => Number.parseInt(a, 10) - Number.parseInt(b, 10));
const ratesByVersion = new Map<string, Map<string, Map<string, number>>>();
for (const version of versionDirs) {
const filePath = path.join(
PARAMETER_DIR,
version,
"misc",
DAMAGE_RATE_CONFIG_FILE_NAME,
);
if (!fs.existsSync(filePath)) continue;
const config = JSON.parse(
fs.readFileSync(filePath, "utf8"),
) as DamageRateConfig;
ratesByVersion.set(version, damageRatesByRow(config));
}
const presentVersions = versionDirs.filter((version) =>
ratesByVersion.has(version),
);
const latestVersion = presentVersions[presentVersions.length - 1];
const rows: Record<string, DamageRateHistoryRow> = {};
for (const [rowKey, latestRates] of ratesByVersion.get(latestVersion) ?? []) {
const mainWeaponIdsForRow = mainIdsForRow(rowKey);
const subWeaponIdsForRow = subIdsForRow(rowKey);
const specialWeaponIdsForRow = specialIdsForRow(rowKey);
if (
mainWeaponIdsForRow.length === 0 &&
subWeaponIdsForRow.length === 0 &&
specialWeaponIdsForRow.length === 0
) {
continue;
}
const targets: TargetHistory[] = [];
for (const [target, current] of latestRates) {
const presentForTarget = presentVersions.filter(
(version) =>
ratesByVersion.get(version)?.get(rowKey)?.get(target) !== undefined,
);
const history: Array<{ version: string; value: number }> = [];
for (let i = 0; i < presentForTarget.length - 1; i++) {
const value = ratesByVersion
.get(presentForTarget[i])!
.get(rowKey)!
.get(target)!;
const nextValue = ratesByVersion
.get(presentForTarget[i + 1])!
.get(rowKey)!
.get(target)!;
if (value !== nextValue) {
history.push({
version: versionDirToDisplay(presentForTarget[i]),
value,
});
}
}
if (history.length > 0) {
targets.push({ target, current, history });
}
}
if (targets.length > 0) {
rows[rowKey] = {
mainWeaponIds: mainWeaponIdsForRow,
subWeaponIds: subWeaponIdsForRow,
specialWeaponIds: specialWeaponIdsForRow,
targets,
};
}
}
fs.writeFileSync(
HISTORY_OUTPUT_PATH,
JSON.stringify(
{
metadata: { versions: presentVersions.map(versionDirToDisplay) },
rows,
},
null,
2,
),
);
}

View File

@@ -2,11 +2,13 @@ import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { logger } from "~/utils/logger";
import weapons from "./dicts/WeaponInfoMain.json";
import { loadWeaponInfoMain } from "./utils";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const weapons = loadWeaponInfoMain();
const DIR_PATH_1 = path.join(
__dirname,
"..",

View File

@@ -1,22 +1,41 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
mainWeaponIds,
specialWeaponIds,
subWeaponIds,
} from "~/modules/in-game-lists/weapon-ids";
import { logger } from "~/utils/logger";
import weapons from "./dicts/WeaponInfoMain.json";
import {
loadWeaponInfoMain,
loadWeaponInfoSpecial,
loadWeaponInfoSub,
MUSH_DIR,
PARAMETER_DIR,
} from "./utils";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PARAMETER_DIR = path.join(__dirname, "dicts", "parameter");
const weapons = loadWeaponInfoMain();
const subWeapons = loadWeaponInfoSub();
const specialWeapons = loadWeaponInfoSpecial();
const OUTPUT_DIR = path.join(
__dirname,
"..",
"app",
"features",
"weapons",
"params",
"data",
);
const OUTPUT_FILE = path.join(OUTPUT_DIR, "weapon-params.json");
const OUTPUT_FILE = path.join(OUTPUT_DIR, "all-version-weapon-params.json");
const SUB_OUTPUT_FILE = path.join(OUTPUT_DIR, "all-version-sub-params.json");
const SPECIAL_OUTPUT_FILE = path.join(
OUTPUT_DIR,
"all-version-special-params.json",
);
const WEAPON_TYPES_TO_IGNORE = [
"Mission",
@@ -72,6 +91,31 @@ function buildWeaponFileNameToIdMap(): Map<string, number> {
return map;
}
// Sub and special weapons share the per-version `weapon` GameParameterTable dump with main
// weapons, so only the canonical "Versus" entry of each id is kept (Hero/Mission/etc. variants
// of the same weapon are ignored).
function buildSubOrSpecialFileNameToIdMap(
entries: Array<{ Id: number; __RowId: string; Type: string }>,
allowedIds: ReadonlySet<number>,
): Map<string, number> {
const map = new Map<string, number>();
const seenFileNames = new Set<string>();
for (const entry of entries) {
if (entry.Type !== "Versus") continue;
if (!allowedIds.has(entry.Id)) continue;
const fileName = weaponRowIdToFileName(entry.__RowId);
if (!seenFileNames.has(fileName)) {
seenFileNames.add(fileName);
map.set(fileName, entry.Id);
}
}
return map;
}
function stripTypeFields(obj: unknown): unknown {
if (obj === null || typeof obj !== "object") {
return obj;
@@ -196,6 +240,133 @@ function mergeWithHistory(
return result;
}
// SpecialPoint lives in WeaponInfoMain (kit data), which is not part of the per-version weapon
// GameParameterTable dump, so it is read per version from the local mush dir.
function collectSpecialPointsByVersion(
sortedVersions: string[],
): Map<number, Map<string, number>> {
const result = new Map<number, Map<string, number>>();
const mainWeaponIdSet = new Set<number>(mainWeaponIds);
for (const version of sortedVersions) {
const filePath = path.join(MUSH_DIR, version, "WeaponInfoMain.json");
if (!fs.existsSync(filePath)) continue;
let entries: MainWeapon[];
try {
entries = JSON.parse(fs.readFileSync(filePath, "utf8"));
} catch {
logger.warn(`Failed to parse ${filePath}`);
continue;
}
for (const weapon of entries) {
if (mainWeaponShouldBeSkipped(weapon)) continue;
if (!mainWeaponIdSet.has(weapon.Id)) continue;
if (typeof weapon.SpecialPoint !== "number") continue;
if (!result.has(weapon.Id)) {
result.set(weapon.Id, new Map());
}
result.get(weapon.Id)!.set(version, weapon.SpecialPoint);
}
}
return result;
}
function buildSpecialPointsHistory(
specialPointsByVersion: Map<number, Map<string, number>>,
sortedVersions: string[],
): Record<
string,
{ current: number; history: Array<{ version: string; value: number }> }
> {
const result: Record<
string,
{ current: number; history: Array<{ version: string; value: number }> }
> = {};
for (const [weaponId, byVersion] of specialPointsByVersion) {
const presentVersions = sortedVersions.filter((v) => byVersion.has(v));
if (presentVersions.length === 0) continue;
const current = byVersion.get(presentVersions[presentVersions.length - 1])!;
const history: Array<{ version: string; value: number }> = [];
for (let i = 0; i < presentVersions.length - 1; i++) {
const value = byVersion.get(presentVersions[i])!;
const nextValue = byVersion.get(presentVersions[i + 1])!;
if (value !== nextValue) {
history.push({
version: parseVersionToDisplay(presentVersions[i]),
value,
});
}
}
result[String(weaponId)] = { current, history };
}
return result;
}
// Reads every per-version `weapon` GameParameterTable dump for the given files and folds the
// historical values of each weapon into its latest params using versioned (`Key@version`) keys.
function buildParamsWithHistory(
fileNameToId: Map<string, number>,
sortedVersions: string[],
): Record<string, Record<string, unknown>> {
const allVersions = new Map<number, Map<string, Record<string, unknown>>>();
for (const version of sortedVersions) {
const weaponDir = path.join(PARAMETER_DIR, version, "weapon");
if (!fs.existsSync(weaponDir)) continue;
for (const file of fs.readdirSync(weaponDir)) {
if (!fileNameToId.has(file)) continue;
const weaponId = fileNameToId.get(file)!;
const filePath = path.join(weaponDir, file);
try {
const content = JSON.parse(fs.readFileSync(filePath, "utf8"));
const params = stripTypeFields(content.GameParameters) as Record<
string,
unknown
>;
if (!allVersions.has(weaponId)) {
allVersions.set(weaponId, new Map());
}
allVersions.get(weaponId)!.set(version, params);
} catch {
logger.warn(`Failed to parse ${filePath}`);
}
}
}
const output: Record<string, Record<string, unknown>> = {};
const latestVersion = sortedVersions[sortedVersions.length - 1];
for (const [weaponId, versionParams] of allVersions) {
const latestParams = versionParams.get(latestVersion);
if (!latestParams) continue;
const versionsWithWeapon = sortedVersions.filter((v) =>
versionParams.has(v),
);
output[String(weaponId)] = mergeWithHistory(
latestParams,
versionParams,
versionsWithWeapon,
);
}
return output;
}
async function main() {
logger.info("Starting weapon params sync...");
@@ -208,78 +379,62 @@ async function main() {
`Found ${sortedVersions.length} versions: ${sortedVersions.map(parseVersionToDisplay).join(", ")}`,
);
const latestVersion = sortedVersions[sortedVersions.length - 1];
const metadata = {
generatedAt: new Date().toISOString(),
latestVersion: parseVersionToDisplay(latestVersion),
versions: sortedVersions.map(parseVersionToDisplay),
};
const weaponFileNameToId = buildWeaponFileNameToIdMap();
logger.info(`Processing ${weaponFileNameToId.size} unique weapons`);
const outputWeapons = buildParamsWithHistory(
weaponFileNameToId,
sortedVersions,
);
const weaponParamsAllVersions = new Map<
number,
Map<string, Record<string, unknown>>
>();
const specialPoints = buildSpecialPointsHistory(
collectSpecialPointsByVersion(sortedVersions),
sortedVersions,
);
for (const version of sortedVersions) {
const weaponDir = path.join(PARAMETER_DIR, version, "weapon");
if (!fs.existsSync(weaponDir)) continue;
const outputSubWeapons = buildParamsWithHistory(
buildSubOrSpecialFileNameToIdMap(subWeapons, new Set(subWeaponIds)),
sortedVersions,
);
const files = fs.readdirSync(weaponDir);
for (const file of files) {
if (!weaponFileNameToId.has(file)) continue;
const weaponId = weaponFileNameToId.get(file)!;
const filePath = path.join(weaponDir, file);
try {
const content = JSON.parse(fs.readFileSync(filePath, "utf8"));
const params = stripTypeFields(content.GameParameters) as Record<
string,
unknown
>;
if (!weaponParamsAllVersions.has(weaponId)) {
weaponParamsAllVersions.set(weaponId, new Map());
}
weaponParamsAllVersions.get(weaponId)!.set(version, params);
} catch {
logger.warn(`Failed to parse ${filePath}`);
}
}
}
const outputWeapons: Record<string, Record<string, unknown>> = {};
const latestVersion = sortedVersions[sortedVersions.length - 1];
for (const [weaponId, versionParams] of weaponParamsAllVersions) {
const latestParams = versionParams.get(latestVersion);
if (!latestParams) continue;
const versionsWithWeapon = sortedVersions.filter((v) =>
versionParams.has(v),
);
const paramsWithHistory = mergeWithHistory(
latestParams,
versionParams,
versionsWithWeapon,
);
outputWeapons[String(weaponId)] = paramsWithHistory;
}
const output = {
metadata: {
generatedAt: new Date().toISOString(),
latestVersion: parseVersionToDisplay(latestVersion),
versions: sortedVersions.map(parseVersionToDisplay),
},
weapons: outputWeapons,
};
const outputSpecialWeapons = buildParamsWithHistory(
buildSubOrSpecialFileNameToIdMap(specialWeapons, new Set(specialWeaponIds)),
sortedVersions,
);
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(output, null, 2));
fs.writeFileSync(
OUTPUT_FILE,
JSON.stringify(
{ metadata, weapons: outputWeapons, specialPoints },
null,
2,
),
);
fs.writeFileSync(
SUB_OUTPUT_FILE,
JSON.stringify({ metadata, weapons: outputSubWeapons }, null, 2),
);
fs.writeFileSync(
SPECIAL_OUTPUT_FILE,
JSON.stringify({ metadata, weapons: outputSpecialWeapons }, null, 2),
);
logger.info(`Written to ${OUTPUT_FILE}`);
logger.info(`Total weapons: ${Object.keys(outputWeapons).length}`);
logger.info(`Total main weapons: ${Object.keys(outputWeapons).length}`);
logger.info(`Total sub weapons: ${Object.keys(outputSubWeapons).length}`);
logger.info(
`Total special weapons: ${Object.keys(outputSpecialWeapons).length}`,
);
}
main().catch((err) => logger.error(err));

View File

@@ -1,12 +1,29 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import type euEn from "./dicts/langs/EUen.json";
import type euEn from "./dicts/splat3/data/language/EUen.json";
// The splat3 dump exposes a `latest` symlink to the newest version folder, so these loaders never
// need to hard-code a version.
import type gearInfoClothes from "./dicts/splat3/data/mush/latest/GearInfoClothes.json";
import type gearInfoHead from "./dicts/splat3/data/mush/latest/GearInfoHead.json";
import type gearInfoShoes from "./dicts/splat3/data/mush/latest/GearInfoShoes.json";
import type weaponInfoMain from "./dicts/splat3/data/mush/latest/WeaponInfoMain.json";
import type weaponInfoSpecial from "./dicts/splat3/data/mush/latest/WeaponInfoSpecial.json";
import type weaponInfoSub from "./dicts/splat3/data/mush/latest/WeaponInfoSub.json";
import type splPlayer from "./dicts/splat3/data/parameter/latest/misc/SplPlayer.game__GameParameterTable.json";
import type damageRateInfo from "./dicts/splat3/data/parameter/latest/misc/spl__DamageRateInfoConfig.pp__CombinationDataTableData.json";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const LANG_DICTS_PATH = path.join(__dirname, "dicts", "langs");
const SPLAT3_DATA_PATH = path.join(__dirname, "dicts", "splat3", "data");
/** Per-version weapon/sub/special `GameParameterTable` dumps, keyed by patch version folder. */
export const PARAMETER_DIR = path.join(SPLAT3_DATA_PATH, "parameter");
/** Per-version `WeaponInfo`/`GearInfo` dumps, keyed by patch version folder. */
export const MUSH_DIR = path.join(SPLAT3_DATA_PATH, "mush");
const LANG_DICTS_PATH = path.join(SPLAT3_DATA_PATH, "language");
export const LANG_JSONS_TO_CREATE = [
"EUen",
@@ -47,3 +64,44 @@ export function translationJsonFolderName(langCode: string) {
if (langCode === "USfr") return "fr-CA";
return langCode.slice(2);
}
/** Latest-version directory holding the per-weapon `GameParameterTable` dumps. */
export function weaponParamsDir() {
return path.join(PARAMETER_DIR, "latest", "weapon");
}
export const loadWeaponInfoMain = () =>
loadLatestMushJson<typeof weaponInfoMain>("WeaponInfoMain");
export const loadWeaponInfoSub = () =>
loadLatestMushJson<typeof weaponInfoSub>("WeaponInfoSub");
export const loadWeaponInfoSpecial = () =>
loadLatestMushJson<typeof weaponInfoSpecial>("WeaponInfoSpecial");
export const loadGearInfoClothes = () =>
loadLatestMushJson<typeof gearInfoClothes>("GearInfoClothes");
export const loadGearInfoHead = () =>
loadLatestMushJson<typeof gearInfoHead>("GearInfoHead");
export const loadGearInfoShoes = () =>
loadLatestMushJson<typeof gearInfoShoes>("GearInfoShoes");
export const loadSplPlayerParams = () =>
loadLatestParameterMiscJson<typeof splPlayer>(
"SplPlayer.game__GameParameterTable",
);
export const loadDamageRateInfo = () =>
loadLatestParameterMiscJson<typeof damageRateInfo>(
"spl__DamageRateInfoConfig.pp__CombinationDataTableData",
);
function loadLatestMushJson<T>(fileName: string): T {
return JSON.parse(
fs.readFileSync(path.join(MUSH_DIR, "latest", `${fileName}.json`), "utf8"),
);
}
function loadLatestParameterMiscJson<T>(fileName: string): T {
return JSON.parse(
fs.readFileSync(
path.join(PARAMETER_DIR, "latest", "misc", `${fileName}.json`),
"utf8",
),
);
}