mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
Deduplicate weapon-params.ts (#2695)
This commit is contained in:
parent
ee4f3ef4b9
commit
38d80d670f
|
|
@ -14,12 +14,9 @@ type Overwrites = Record<
|
|||
Partial<Record<"High" | "Mid" | "Low", number>>
|
||||
>;
|
||||
|
||||
export interface MainWeaponParams {
|
||||
subWeaponId: SubWeaponId;
|
||||
specialWeaponId: SpecialWeaponId;
|
||||
export interface BaseWeaponStats {
|
||||
/** Replacing default values of the ability json for this specific weapon */
|
||||
overwrites?: Overwrites;
|
||||
SpecialPoint: number;
|
||||
/** Weapon's weight class. "Light/Heavy weapon" */
|
||||
WeaponSpeedType?: "Slow" | "Fast";
|
||||
/** Total frames it takes the weapon to shoot out three times */
|
||||
|
|
@ -105,6 +102,14 @@ export interface MainWeaponParams {
|
|||
// SpeedInkConsumeMin_WeaponRollParam?: number;
|
||||
}
|
||||
|
||||
export interface WeaponKit {
|
||||
subWeaponId: SubWeaponId;
|
||||
specialWeaponId: SpecialWeaponId;
|
||||
SpecialPoint: number;
|
||||
}
|
||||
|
||||
export type MainWeaponParams = BaseWeaponStats & WeaponKit;
|
||||
|
||||
export interface DistanceDamage {
|
||||
Damage: number;
|
||||
Distance: number;
|
||||
|
|
@ -148,7 +153,8 @@ export type SpecialWeaponParams = SpecialWeaponParamsObject[SpecialWeaponId] & {
|
|||
};
|
||||
|
||||
export type ParamsJson = {
|
||||
mainWeapons: Record<MainWeaponId, MainWeaponParams>;
|
||||
baseWeaponStats: Record<MainWeaponId, BaseWeaponStats>;
|
||||
weaponKits: Record<MainWeaponId, WeaponKit>;
|
||||
subWeapons: Record<SubWeaponId, SubWeaponParams>;
|
||||
specialWeapons: SpecialWeaponParamsObject;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import type { MainWeaponId } from "~/modules/in-game-lists/types";
|
|||
import { SendouButton } from "../../../components/elements/Button";
|
||||
import { SendouPopover } from "../../../components/elements/Popover";
|
||||
import { MAX_AP } from "../analyzer-constants";
|
||||
import type { FullInkTankOption } from "../analyzer-types";
|
||||
import type { FullInkTankOption, SpecialWeaponParams } from "../analyzer-types";
|
||||
import { fullInkTankOptions } from "../core/stats";
|
||||
import { weaponParams } from "../core/utils";
|
||||
import { mainWeaponParams, weaponParams } from "../core/utils";
|
||||
|
||||
interface PerInkTankGridProps {
|
||||
weaponSplId: MainWeaponId;
|
||||
|
|
@ -243,13 +243,13 @@ function inkTankOptionsWhenNSubsUsed({
|
|||
subsUsed: number;
|
||||
weaponSplId: MainWeaponId;
|
||||
}) {
|
||||
const mainWeaponParams = weaponParams().mainWeapons[weaponSplId];
|
||||
const mainParams = mainWeaponParams(weaponSplId);
|
||||
|
||||
const subWeaponParams =
|
||||
weaponParams().subWeapons[mainWeaponParams.subWeaponId];
|
||||
const subWeaponParams = weaponParams().subWeapons[mainParams.subWeaponId];
|
||||
|
||||
const specialWeaponParams =
|
||||
weaponParams().specialWeapons[mainWeaponParams.specialWeaponId];
|
||||
const specialWeaponParams = weaponParams().specialWeapons[
|
||||
mainParams.specialWeaponId
|
||||
] as SpecialWeaponParams;
|
||||
|
||||
const options = fullInkTankOptions({
|
||||
abilityPoints: new Map([
|
||||
|
|
@ -259,7 +259,7 @@ function inkTankOptionsWhenNSubsUsed({
|
|||
weaponSplId,
|
||||
hasTacticooler: false,
|
||||
mainOnlyAbilities: [],
|
||||
mainWeaponParams,
|
||||
mainWeaponParams: mainParams,
|
||||
specialWeaponParams,
|
||||
subWeaponParams,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import {
|
|||
abilityPointsToEffects,
|
||||
abilityValues,
|
||||
apFromMap,
|
||||
mainWeaponParams as getMainWeaponParams,
|
||||
hasEffect,
|
||||
hpDivided,
|
||||
weaponIdToMultiShotCount,
|
||||
|
|
@ -62,8 +63,7 @@ export function buildStats({
|
|||
mainOnlyAbilities?: Array<Ability>;
|
||||
hasTacticooler: boolean;
|
||||
}): AnalyzedBuild {
|
||||
const mainWeaponParams = weaponParams().mainWeapons[weaponSplId];
|
||||
invariant(mainWeaponParams, `Weapon with splId ${weaponSplId} not found`);
|
||||
const mainWeaponParams = getMainWeaponParams(weaponSplId);
|
||||
|
||||
const subWeaponParams =
|
||||
weaponParams().subWeapons[mainWeaponParams.subWeaponId];
|
||||
|
|
@ -72,8 +72,9 @@ export function buildStats({
|
|||
`Sub weapon with splId ${mainWeaponParams.subWeaponId} not found`,
|
||||
);
|
||||
|
||||
const specialWeaponParams =
|
||||
weaponParams().specialWeapons[mainWeaponParams.specialWeaponId];
|
||||
const specialWeaponParams = weaponParams().specialWeapons[
|
||||
mainWeaponParams.specialWeaponId
|
||||
] as SpecialWeaponParams;
|
||||
invariant(
|
||||
specialWeaponParams,
|
||||
`Special weapon with splId ${mainWeaponParams.specialWeaponId} not found`,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,16 @@ import { abilityValues as abilityValuesJson } from "./ability-values";
|
|||
import { weaponParams as rawWeaponParams } from "./weapon-params";
|
||||
|
||||
export function weaponParams(): ParamsJson {
|
||||
return rawWeaponParams as ParamsJson;
|
||||
return rawWeaponParams as unknown as ParamsJson;
|
||||
}
|
||||
|
||||
export function mainWeaponParams(weaponId: MainWeaponId): MainWeaponParams {
|
||||
const params = rawWeaponParams as unknown as ParamsJson;
|
||||
const baseId = weaponIdToBaseWeaponId(weaponId);
|
||||
const baseStats = params.baseWeaponStats[baseId];
|
||||
const kit = params.weaponKits[weaponId];
|
||||
|
||||
return { ...baseStats, ...kit } as MainWeaponParams;
|
||||
}
|
||||
|
||||
export function buildToAbilityPoints(build: BuildAbilitiesTupleWithUnknown) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -8,8 +8,11 @@ import {
|
|||
specialFieldHp,
|
||||
subStats,
|
||||
} from "~/features/build-analyzer/core/stats";
|
||||
import { hpDivided } from "~/features/build-analyzer/core/utils";
|
||||
import { weaponParams } from "~/features/build-analyzer/core/weapon-params";
|
||||
import {
|
||||
hpDivided,
|
||||
mainWeaponParams,
|
||||
weaponParams,
|
||||
} from "~/features/build-analyzer/core/utils";
|
||||
import {
|
||||
BIG_BUBBLER_ID,
|
||||
CRAB_TANK_ID,
|
||||
|
|
@ -30,19 +33,21 @@ const SUPER_CHUMP_HP = 60;
|
|||
const TRIPLE_SPLASHDOWN_HP = 100;
|
||||
|
||||
export const objectHitPoints = (abilityPoints: AbilityPoints): HitPoints => {
|
||||
const params = weaponParams();
|
||||
|
||||
const Wsb_Shield = subStats({
|
||||
abilityPoints,
|
||||
subWeaponParams: weaponParams.subWeapons[SPLASH_WALL_ID] as SubWeaponParams,
|
||||
subWeaponParams: params.subWeapons[SPLASH_WALL_ID] as SubWeaponParams,
|
||||
}).subHp?.value;
|
||||
const GreatBarrier_Barrier = specialFieldHp({
|
||||
abilityPoints,
|
||||
specialWeaponParams: weaponParams.specialWeapons[
|
||||
specialWeaponParams: params.specialWeapons[
|
||||
BIG_BUBBLER_ID
|
||||
] as SpecialWeaponParams,
|
||||
})?.value;
|
||||
const GreatBarrier_WeakPoint = specialDeviceHp({
|
||||
abilityPoints,
|
||||
specialWeaponParams: weaponParams.specialWeapons[
|
||||
specialWeaponParams: params.specialWeapons[
|
||||
BIG_BUBBLER_ID
|
||||
] as SpecialWeaponParams,
|
||||
})?.value;
|
||||
|
|
@ -53,25 +58,19 @@ export const objectHitPoints = (abilityPoints: AbilityPoints): HitPoints => {
|
|||
|
||||
return {
|
||||
BulletUmbrellaCanopyNormal: SPLAT_BRELLA_SHIELD_HP,
|
||||
BulletUmbrellaCanopyWide: hpDivided(
|
||||
weaponParams.mainWeapons[6010].CanopyHP,
|
||||
),
|
||||
BulletUmbrellaCanopyCompact: hpDivided(
|
||||
weaponParams.mainWeapons[6020].CanopyHP,
|
||||
),
|
||||
BulletShelterCanopyFocus: hpDivided(
|
||||
weaponParams.mainWeapons[6030].CanopyHP,
|
||||
),
|
||||
BulletUmbrellaCanopyWide: hpDivided(mainWeaponParams(6010).CanopyHP!),
|
||||
BulletUmbrellaCanopyCompact: hpDivided(mainWeaponParams(6020).CanopyHP!),
|
||||
BulletShelterCanopyFocus: hpDivided(mainWeaponParams(6030).CanopyHP!),
|
||||
BulletUmbrellaCanopyNormal_Launched: SPLAT_BRELLA_SHIELD_HP * 2,
|
||||
BulletUmbrellaCanopyWide_Launched: hpDivided(
|
||||
weaponParams.mainWeapons[6010].CanopyHP * (10 / 6),
|
||||
mainWeaponParams(6010).CanopyHP! * (10 / 6),
|
||||
),
|
||||
BulletShelterCanopyFocus_Launched: hpDivided(
|
||||
weaponParams.mainWeapons[6030].CanopyHP * (10 / 6),
|
||||
mainWeaponParams(6030).CanopyHP! * (10 / 6),
|
||||
),
|
||||
Wsb_Shield,
|
||||
Bomb_TorpedoBullet: TORPEDO_HP,
|
||||
Chariot: hpDivided(weaponParams.specialWeapons[CRAB_TANK_ID].ArmorHP),
|
||||
Chariot: hpDivided(params.specialWeapons[CRAB_TANK_ID].ArmorHP),
|
||||
Gachihoko_Barrier: RAINMAKER_HP,
|
||||
GreatBarrier_Barrier,
|
||||
GreatBarrier_WeakPoint,
|
||||
|
|
|
|||
|
|
@ -12,15 +12,18 @@ import path from "node:path";
|
|||
import { fileURLToPath } from "node:url";
|
||||
import { z } from "zod";
|
||||
import type {
|
||||
BaseWeaponStats,
|
||||
MainWeaponParams,
|
||||
ParamsJson,
|
||||
SubWeaponParams,
|
||||
WeaponKit,
|
||||
} from "~/features/build-analyzer/analyzer-types";
|
||||
import {
|
||||
type SpecialWeaponId,
|
||||
SQUID_BEAKON_ID,
|
||||
type SubWeaponId,
|
||||
subWeaponIds,
|
||||
weaponIdToBaseWeaponId,
|
||||
} from "~/modules/in-game-lists/weapon-ids";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
|
|
@ -44,13 +47,20 @@ type SubWeapon = (typeof subWeapons)[number];
|
|||
type SpecialWeapon = (typeof specialWeapons)[number];
|
||||
type TranslationArray = Array<{ language: string; key: string; value: string }>;
|
||||
|
||||
const KIT_PROPERTIES = [
|
||||
"SpecialPoint",
|
||||
"subWeaponId",
|
||||
"specialWeaponId",
|
||||
] as const;
|
||||
|
||||
async function main() {
|
||||
const mainWeaponsResult: Record<number, MainWeaponParams> = {};
|
||||
const allMainWeaponParams: Record<number, MainWeaponParams> = {};
|
||||
const subWeaponsResult: Record<number, SubWeaponParams> = {};
|
||||
const specialWeaponsResult: any = {};
|
||||
const translations: TranslationArray = [];
|
||||
|
||||
const langDicts = await loadLangDicts();
|
||||
const hasLangDicts = langDicts.length > 0;
|
||||
|
||||
for (const weapon of weapons) {
|
||||
if (mainWeaponShouldBeSkipped(weapon)) continue;
|
||||
|
|
@ -60,30 +70,37 @@ async function main() {
|
|||
parametersToMainWeaponResult(weapon, rawParams),
|
||||
);
|
||||
|
||||
translationsToArray({
|
||||
arr: translations,
|
||||
internalName: weapon.__RowId,
|
||||
weaponId: weapon.Id,
|
||||
type: "Main",
|
||||
translations: langDicts,
|
||||
});
|
||||
if (hasLangDicts) {
|
||||
translationsToArray({
|
||||
arr: translations,
|
||||
internalName: weapon.__RowId,
|
||||
weaponId: weapon.Id,
|
||||
type: "Main",
|
||||
translations: langDicts,
|
||||
});
|
||||
}
|
||||
|
||||
mainWeaponsResult[weapon.Id] = params;
|
||||
allMainWeaponParams[weapon.Id] = params;
|
||||
}
|
||||
|
||||
const { baseWeaponStats, weaponKits } =
|
||||
splitIntoBaseStatsAndKits(allMainWeaponParams);
|
||||
|
||||
for (const subWeapon of subWeapons) {
|
||||
if (subWeaponShouldBeSkipped(subWeapon)) continue;
|
||||
|
||||
const rawParams = loadWeaponParamsObject(subWeapon);
|
||||
const params = parametersToSubWeaponResult(subWeapon, rawParams);
|
||||
|
||||
translationsToArray({
|
||||
arr: translations,
|
||||
internalName: subWeapon.__RowId,
|
||||
weaponId: subWeapon.Id,
|
||||
type: "Sub",
|
||||
translations: langDicts,
|
||||
});
|
||||
if (hasLangDicts) {
|
||||
translationsToArray({
|
||||
arr: translations,
|
||||
internalName: subWeapon.__RowId,
|
||||
weaponId: subWeapon.Id,
|
||||
type: "Sub",
|
||||
translations: langDicts,
|
||||
});
|
||||
}
|
||||
|
||||
subWeaponsResult[subWeapon.Id] = params;
|
||||
}
|
||||
|
|
@ -94,30 +111,107 @@ async function main() {
|
|||
const rawParams = loadWeaponParamsObject(specialWeapon);
|
||||
const params = parametersToSpecialWeaponResult(rawParams);
|
||||
|
||||
translationsToArray({
|
||||
arr: translations,
|
||||
internalName: specialWeapon.__RowId,
|
||||
weaponId: specialWeapon.Id,
|
||||
type: "Special",
|
||||
translations: langDicts,
|
||||
});
|
||||
if (hasLangDicts) {
|
||||
translationsToArray({
|
||||
arr: translations,
|
||||
internalName: specialWeapon.__RowId,
|
||||
weaponId: specialWeapon.Id,
|
||||
type: "Special",
|
||||
translations: langDicts,
|
||||
});
|
||||
}
|
||||
|
||||
specialWeaponsResult[specialWeapon.Id] = params;
|
||||
}
|
||||
|
||||
const toFile: ParamsJson = {
|
||||
mainWeapons: mainWeaponsResult,
|
||||
baseWeaponStats,
|
||||
weaponKits,
|
||||
subWeapons: subWeaponsResult,
|
||||
specialWeapons: specialWeaponsResult,
|
||||
};
|
||||
|
||||
const weaponParamsTs = `export const weaponParams = ${JSON.stringify(toFile, null, "\t")} as const;\n`;
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, "output", "params.json"),
|
||||
`${JSON.stringify(toFile, null, 2)}\n`,
|
||||
path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"app",
|
||||
"features",
|
||||
"build-analyzer",
|
||||
"core",
|
||||
"weapon-params.ts",
|
||||
),
|
||||
weaponParamsTs,
|
||||
);
|
||||
|
||||
writeTranslationsJsons(translations);
|
||||
logWeaponIds(mainWeaponsResult);
|
||||
if (hasLangDicts) {
|
||||
writeTranslationsJsons(translations);
|
||||
}
|
||||
logWeaponIds(weaponKits);
|
||||
}
|
||||
|
||||
function splitIntoBaseStatsAndKits(
|
||||
allParams: Record<number, MainWeaponParams>,
|
||||
): {
|
||||
baseWeaponStats: Record<number, BaseWeaponStats>;
|
||||
weaponKits: Record<number, WeaponKit>;
|
||||
} {
|
||||
const weaponGroups: Record<
|
||||
number,
|
||||
Array<{ id: number; params: MainWeaponParams }>
|
||||
> = {};
|
||||
|
||||
for (const [idStr, params] of Object.entries(allParams)) {
|
||||
const id = Number(idStr);
|
||||
const baseId = weaponIdToBaseWeaponId(id);
|
||||
if (!weaponGroups[baseId]) weaponGroups[baseId] = [];
|
||||
weaponGroups[baseId].push({ id, params });
|
||||
}
|
||||
|
||||
const baseWeaponStats: Record<number, BaseWeaponStats> = {};
|
||||
const weaponKits: Record<number, WeaponKit> = {};
|
||||
|
||||
for (const [baseIdStr, variants] of Object.entries(weaponGroups)) {
|
||||
const baseId = Number(baseIdStr);
|
||||
const firstVariant = variants[0].params;
|
||||
|
||||
const nonKitProps = Object.keys(firstVariant).filter(
|
||||
(k) => !KIT_PROPERTIES.includes(k as (typeof KIT_PROPERTIES)[number]),
|
||||
) as Array<keyof MainWeaponParams>;
|
||||
|
||||
const sharedProps: Partial<MainWeaponParams> = {};
|
||||
for (const prop of nonKitProps) {
|
||||
const firstVal = JSON.stringify(firstVariant[prop]);
|
||||
const allSame = variants.every(
|
||||
(v) => JSON.stringify(v.params[prop]) === firstVal,
|
||||
);
|
||||
if (allSame && firstVariant[prop] !== undefined) {
|
||||
(sharedProps as any)[prop] = firstVariant[prop];
|
||||
}
|
||||
}
|
||||
|
||||
baseWeaponStats[baseId] = sharedProps as BaseWeaponStats;
|
||||
|
||||
for (const variant of variants) {
|
||||
const kit: WeaponKit = {
|
||||
SpecialPoint: variant.params.SpecialPoint,
|
||||
subWeaponId: variant.params.subWeaponId,
|
||||
specialWeaponId: variant.params.specialWeaponId,
|
||||
};
|
||||
|
||||
for (const prop of nonKitProps) {
|
||||
if (!(prop in sharedProps) && variant.params[prop] !== undefined) {
|
||||
(kit as any)[prop] = variant.params[prop];
|
||||
}
|
||||
}
|
||||
|
||||
weaponKits[variant.id] = kit;
|
||||
}
|
||||
}
|
||||
|
||||
return { baseWeaponStats, weaponKits };
|
||||
}
|
||||
|
||||
function parametersToMainWeaponResult(
|
||||
|
|
@ -167,7 +261,8 @@ function parametersToMainWeaponResult(
|
|||
const BlastParam_DistanceDamage = () => {
|
||||
// REEF-LUX has distance damage listed in params
|
||||
// but actually doesn't deal it in game
|
||||
if (weapon.Id === 7020) return undefined;
|
||||
if (weapon.Id === 7020 || weapon.Id === 7021 || weapon.Id === 7022)
|
||||
return undefined;
|
||||
|
||||
return (
|
||||
params.BlastParam?.DistanceDamage ??
|
||||
|
|
@ -199,7 +294,8 @@ function parametersToMainWeaponResult(
|
|||
};
|
||||
|
||||
const slosherDirectDamageSecondary = () => {
|
||||
const isDreadWringer = weapon.Id === 3050 || weapon.Id === 3051;
|
||||
const isDreadWringer =
|
||||
weapon.Id === 3050 || weapon.Id === 3051 || weapon.Id === 3052;
|
||||
if (!isDreadWringer) return;
|
||||
|
||||
const DamageParam_Secondary_ValueDirectMax =
|
||||
|
|
@ -217,7 +313,7 @@ function parametersToMainWeaponResult(
|
|||
params.WeaponKeepChargeParam?.KeepChargeFullFrame ??
|
||||
params.spl__WeaponStringerParam?.ChargeKeepParam?.KeepChargeFullFrame;
|
||||
|
||||
const isSloshingMachine = weapon.Id === 3020;
|
||||
const isSloshingMachine = weapon.Id === 3020 || weapon.Id === 3021;
|
||||
|
||||
const DamageParam_SplatanaHorizontalDirect =
|
||||
params.BulletSaberHorizontalParam?.DamageParam?.HitDamage +
|
||||
|
|
@ -330,7 +426,7 @@ function parametersToMainWeaponResult(
|
|||
),
|
||||
CanopyHP:
|
||||
params.spl__BulletShelterCanopyParam?.CanopyHP ??
|
||||
(weapon.Id === 6000 || weaponId === 6001 || weaponId === 6005
|
||||
(weapon.Id === 6000 || weapon.Id === 6001 || weapon.Id === 6005
|
||||
? 5000
|
||||
: undefined),
|
||||
ChargeFrameFullCharge:
|
||||
|
|
@ -890,7 +986,7 @@ function writeTranslationsJsons(arr: TranslationArray) {
|
|||
}
|
||||
}
|
||||
|
||||
function logWeaponIds(weapons: Record<number, MainWeaponParams>) {
|
||||
function logWeaponIds(weapons: Record<number, WeaponKit>) {
|
||||
logger.info(JSON.stringify(Object.keys(weapons).map(Number)));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user