diff --git a/app/modules/analyzer/ability-values.json b/app/modules/analyzer/ability-values.json
index 47316e03a..6e6973556 100644
--- a/app/modules/analyzer/ability-values.json
+++ b/app/modules/analyzer/ability-values.json
@@ -40,6 +40,7 @@
"WallJumpChargeFrm": [5.0, 18.0, 45.0],
"ReduceJumpSwerveRate": [1.0, 0.75, 0.0],
"SpawnSpeedZSpecUp": [0.0, 0.0, 0.0],
+ "SubSpecUpParam": [0.0, 0.0, 0.0],
"PeriodFirst": [0.0, 0.0, 0.0],
"PeriodSecond": [0.0, 0.0, 0.0],
"MarkingFrameSubSpec": [0.0, 0.0, 0.0],
diff --git a/app/modules/analyzer/stats.ts b/app/modules/analyzer/stats.ts
index a573ee2b9..c478a670d 100644
--- a/app/modules/analyzer/stats.ts
+++ b/app/modules/analyzer/stats.ts
@@ -107,6 +107,7 @@ export function buildStats({
subDefSprinklerDamagePercentage: subDefSprinklerDamagePercentage(input),
subDefBombDamageLightPercentage: subDefBombDamageLightPercentage(input),
subDefBombDamageHeavyPercentage: subDefBombDamageHeavyPercentage(input),
+ subQsjBoost: subQsjBoost(input),
...subStats(input),
specialDurationInSeconds: specialDurationInSeconds(input),
specialDamageDistance: specialDamageDistance(input),
@@ -963,6 +964,35 @@ function subDefBombDamageHeavyPercentage(
};
}
+function subQsjBoost(
+ args: StatFunctionInput
+): AnalyzedBuild["stats"]["subQsjBoost"] {
+ if (
+ !hasEffect({
+ key: "SubSpecUpParam",
+ weapon: args.subWeaponParams,
+ })
+ ) {
+ return;
+ }
+
+ const SUB_QSJ_BOOST_KEY = "BRU";
+ const { baseEffect, effect } = abilityPointsToEffects({
+ abilityPoints: apFromMap({
+ abilityPoints: args.abilityPoints,
+ ability: SUB_QSJ_BOOST_KEY,
+ }),
+ key: "SubSpecUpParam",
+ weapon: args.subWeaponParams,
+ });
+
+ return {
+ baseValue: Math.floor(baseEffect),
+ value: Math.floor(effect),
+ modifiedBy: SUB_QSJ_BOOST_KEY,
+ };
+}
+
function specialDurationInSeconds(
args: StatFunctionInput
): AnalyzedBuild["stats"]["specialDurationInSeconds"] {
diff --git a/app/modules/analyzer/types.ts b/app/modules/analyzer/types.ts
index e3ed9cfbf..965b0f2a8 100644
--- a/app/modules/analyzer/types.ts
+++ b/app/modules/analyzer/types.ts
@@ -226,6 +226,7 @@ export interface AnalyzedBuild {
subMarkingRadius?: Stat;
subExplosionRadius?: Stat;
subHp?: Stat;
+ subQsjBoost?: Stat;
specialDurationInSeconds?: Stat;
specialDamageDistance?: Stat;
diff --git a/app/modules/analyzer/weapon-params.json b/app/modules/analyzer/weapon-params.json
index 0cf22dd84..48712458a 100644
--- a/app/modules/analyzer/weapon-params.json
+++ b/app/modules/analyzer/weapon-params.json
@@ -918,7 +918,13 @@
]
},
"8": {
- "overwrites": {},
+ "overwrites": {
+ "SubSpecUpParam": {
+ "Low": 0,
+ "High": 57,
+ "Mid": 25
+ }
+ },
"SubInkSaveLv": 3,
"InkConsume": 0.75,
"InkRecoverStop": 0
diff --git a/app/routes/analyzer.tsx b/app/routes/analyzer.tsx
index 65d6298cd..ca831188f 100644
--- a/app/routes/analyzer.tsx
+++ b/app/routes/analyzer.tsx
@@ -226,6 +226,13 @@ export default function BuildAnalyzerPage() {
suffix={t("analyzer:suffix.hp")}
/>
)}
+ {analyzed.stats.subQsjBoost && (
+
+ )}
diff --git a/public/locales/en/analyzer.json b/public/locales/en/analyzer.json
index cb175ef68..1e383854a 100644
--- a/public/locales/en/analyzer.json
+++ b/public/locales/en/analyzer.json
@@ -52,6 +52,7 @@
"stat.sub.markingRadius": "Marking radius",
"stat.sub.explosionRadius": "Explosion radius",
"stat.sub.hp": "Durability",
+ "stat.sub.qsjBoost": "Quick Super Jump boost",
"stat.special.duration": "{{weapon}} duration",
"stat.special.duration.inkStormExplanation": "Increases distance painted but not the amount of ink.",
"stat.special.damageDistance": "{{weapon}} damage distance",
diff --git a/scripts/create-analyzer-json.ts b/scripts/create-analyzer-json.ts
index 58b2a0407..d539ddddc 100644
--- a/scripts/create-analyzer-json.ts
+++ b/scripts/create-analyzer-json.ts
@@ -2,13 +2,15 @@
// 1) WeaponInfoMain.json inside dicts
// 2) WeaponInfoSub.json inside dicts
// 3) WeaponInfoSpecial.json inside dicts
-// 4) params (weapon folder) inside dicts
+// 4) SplPlayer.game__GameParameterTable.json inside dicts
+// 5) params (weapon folder) inside dicts
-import type { SpecialWeaponId } from "~/modules/in-game-lists";
+import { type SpecialWeaponId, SQUID_BEAKON_ID } from "~/modules/in-game-lists";
import { type SubWeaponId, subWeaponIds } from "~/modules/in-game-lists";
import weapons from "./dicts/WeaponInfoMain.json";
import subWeapons from "./dicts/WeaponInfoSub.json";
import specialWeapons from "./dicts/WeaponInfoSpecial.json";
+import playersParams from "./dicts/SplPlayer.game__GameParameterTable.json";
import fs from "node:fs";
import path from "node:path";
import invariant from "tiny-invariant";
@@ -55,7 +57,7 @@ async function main() {
if (subWeaponShouldBeSkipped(subWeapon)) continue;
const rawParams = loadWeaponParamsObject(subWeapon);
- const params = parametersToSubWeaponResult(rawParams);
+ const params = parametersToSubWeaponResult(subWeapon, rawParams);
translationsToArray({
arr: translations,
@@ -217,7 +219,10 @@ function combineSwingsIfSame(params: MainWeaponParams): MainWeaponParams {
}
// const LEGAL_SUB_INK_SAVE_LV = [0, 1, 2, 3];
-function parametersToSubWeaponResult(params: any): SubWeaponParams {
+function parametersToSubWeaponResult(
+ subWeapon: SubWeapon,
+ params: any
+): SubWeaponParams {
const SubInkSaveLv = params["SubWeaponSetting"]?.["SubInkSaveLv"];
// xxx: enable when all sub weapons have SubInkSaveLv's
// invariant(
@@ -226,7 +231,7 @@ function parametersToSubWeaponResult(params: any): SubWeaponParams {
// );
return {
- overwrites: resolveSubWeaponOverwrites(params),
+ overwrites: resolveSubWeaponOverwrites(subWeapon, params),
SubInkSaveLv,
InkConsume: params["WeaponParam"]["InkConsume"],
InkRecoverStop: params["WeaponParam"]["InkRecoverStop"],
@@ -418,7 +423,7 @@ function resolveOverwritesWithArbitraryKeys(
}
}
-function resolveSubWeaponOverwrites(params: any) {
+function resolveSubWeaponOverwrites(subWeapon: SubWeapon, params: any) {
const result: SubWeaponParams["overwrites"] = {
SpawnSpeedZSpecUp: params["MoveParam"]?.["SpawnSpeedZSpecUp"],
PeriodFirst: params["MoveParam"]?.["PeriodFirst"],
@@ -430,6 +435,13 @@ function resolveSubWeaponOverwrites(params: any) {
SensorRadius: params["MoveParam"]?.["SensorRadius"],
ExplosionRadius: params["AreaParam"]?.["Distance"],
MaxHP: params["MoveParam"]?.["MaxHP"],
+ SubSpecUpParam: (subWeapon.Id === SQUID_BEAKON_ID
+ ? {
+ Low: 0.0,
+ ...playersParams.GameParameters.spl__PlayerBeaconSubSpecUpParam
+ .SubSpecUpParam,
+ }
+ : undefined) as any,
};
return Object.fromEntries(