diff --git a/app/features/build-analyzer/core/constants.ts b/app/features/build-analyzer/core/constants.ts index a9f248872..d8d1572e0 100644 --- a/app/features/build-analyzer/core/constants.ts +++ b/app/features/build-analyzer/core/constants.ts @@ -1,27 +1,9 @@ import type { DamageType } from "./types"; -import type objectDamages from "./object-dmg.json"; -import { type MainWeaponId, mainWeaponIds } from "~/modules/in-game-lists"; -import invariant from "tiny-invariant"; +import { type MainWeaponId } from "~/modules/in-game-lists"; export const MAX_LDE_INTENSITY = 21; export const MAX_AP = 57; -export const DAMAGE_RECEIVERS = [ - "Chariot", // Crab Tank - "NiceBall_Armor", // Booyah Bomb Armor - "ShockSonar", // Wave Breaker - "GreatBarrier_Barrier", // Big Bubbler Shield - "GreatBarrier_WeakPoint", // Big Bubbler Weak Point - "Gachihoko_Barrier", // Rainmaker Shield - "Wsb_Flag", // Squid Beakon - "Wsb_Shield", // Splash Wall - "Wsb_Sprinkler", // Sprinkler - "Bomb_TorpedoBullet", // Torpedo - "BulletUmbrellaCanopyCompact", // Undercover Brella Canopy - "BulletUmbrellaCanopyNormal", // Splat Brella Canopy - "BulletUmbrellaCanopyWide", // Tenta Brella Canopy -] as const; - export const DAMAGE_TYPE = [ "NORMAL_MIN", "NORMAL_MAX", @@ -65,155 +47,6 @@ export const damageTypeToWeaponType: Record< SPLATANA_HORIZONTAL_DIRECT: "MAIN", }; -export const objectDamageJsonKeyPriority: Record< - keyof typeof objectDamages, - Array | null -> = { - Blaster_BlasterMiddle: null, - Blaster_BlasterShort: null, - Blaster_KillOneShot: ["DIRECT"], - Blaster: null, - BlowerExhale_BombCore: null, - BlowerInhale: null, - BombFlower: null, - Bomb_CurlingBullet: null, - Bomb_DirectHit: ["BOMB_DIRECT"], - Bomb_Fizzy: null, - Bomb_Suction: null, - Bomb_TorpedoBullet: null, - Bomb_TorpedoSplashBurst: null, - Bomb_Trap: null, - Bomb: null, - BrushCore: null, - BrushSplash: null, - CannonMissile: null, - ChargerFull_Light: ["FULL_CHARGE"], - ChargerFull_Long: ["FULL_CHARGE"], - ChargerFull: ["FULL_CHARGE"], - Charger_Light: ["MAX_CHARGE", "TAP_SHOT"], - Charger_Long: ["MAX_CHARGE", "TAP_SHOT"], - Charger: ["MAX_CHARGE", "TAP_SHOT"], - Chariot_Body: null, - Chariot_Cannon: null, - Default: null, - EnemyFlyingHohei_BombCore: null, - GachihokoTimeUpBurst: null, - Gachihoko_BombCore: null, - Gachihoko_Bullet: null, - Geyser: null, - GoldenIkuraAttack: null, - InkStormRain: null, - InkStorm: null, - Jetpack_BombCore: null, - Jetpack_Bullet: null, - Jetpack_Coop: null, - Jetpack_Jet: null, - Maneuver_Short: null, - Maneuver: null, - MicroLaser: null, - MissionSalmonBuddy: null, - MovePainter_Burst: null, - MovePainter_Direct: null, - MultiMissile_BombCore: null, - MultiMissile_Bullet: null, - NiceBall: null, - ObjectEffect_Up: null, - RollerCore: null, - RollerSplash_Compact: null, - RollerSplash_Heavy: null, - RollerSplash_Hunter: null, - RollerSplash: null, - Saber_ChargeShot: ["SPLATANA_VERTICAL"], - Saber_ChargeSlash: ["SPLATANA_VERTICAL_DIRECT"], - Saber_Shot: ["SPLATANA_HORIZONTAL"], - Saber_Slash: ["SPLATANA_HORIZONTAL_DIRECT"], - Saber_ChargeSlash_Penetrate: null, - SakerocketBullet: null, - ShelterCanopy_Compact: null, - ShelterCanopy_Wide: null, - ShelterCanopy: null, - ShelterShot_Compact: null, - ShelterShot_Wide: null, - ShelterShot: null, - Shield: null, - ShockSonar_Wave: null, - Shooter_Blaze: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_Expert: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_First: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_FlashRepeat: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_Flash: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_Gravity: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_Heavy: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_Long: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_Precision: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_Short: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_TripleMiddle: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter_TripleQuick: ["NORMAL_MAX", "NORMAL_MIN"], - Shooter: null, - Skewer_Body: null, - Skewer: null, - Slosher_Bathtub: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN", "DISTANCE", "SPLASH"], - Slosher_Bear: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN", "DISTANCE", "SPLASH"], - Slosher_WashtubBombCore: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN"], - Slosher_Washtub: ["DISTANCE", "SPLASH"], - Slosher: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN", "DISTANCE", "SPLASH"], - Spinner: ["NORMAL_MAX", "NORMAL_MIN", "NORMAL_MAX_FULL_CHARGE", "SPLASH"], - Sprinkler: null, - Stringer_Short: ["NORMAL_MAX", "NORMAL_MIN", "DISTANCE"], - Stringer: ["NORMAL_MAX", "NORMAL_MIN", "DISTANCE"], - SuperHook: null, - SuperLanding: null, - TripleTornado: null, - UltraShot: null, - UltraStamp_Swing: null, - UltraStamp_Throw_BombCore: null, - UltraStamp_Throw: null, -}; - -export const damageTypesToCombine: Partial< - Record< - MainWeaponId, - Array<{ - when: DamageType; - combineWith: DamageType; - /** for this weapon "when" damage already includes "combineWith" damage, so calculating multiplier only */ - multiplierOnly?: boolean; - }> - > -> = { - // Explosher - 3040: [{ when: "DIRECT", combineWith: "DISTANCE" }], - // Tri-Stringer - 7010: [{ when: "NORMAL_MAX", combineWith: "DISTANCE" }], - // Splatana Stamper - 8000: [ - { when: "SPLATANA_VERTICAL_DIRECT", combineWith: "SPLATANA_VERTICAL" }, - { - when: "SPLATANA_HORIZONTAL_DIRECT", - combineWith: "SPLATANA_HORIZONTAL", - multiplierOnly: true, - }, - ], - // Splatana Wiper - 8010: [ - { when: "SPLATANA_VERTICAL_DIRECT", combineWith: "SPLATANA_VERTICAL" }, - { - when: "SPLATANA_HORIZONTAL_DIRECT", - combineWith: "SPLATANA_HORIZONTAL", - multiplierOnly: true, - }, - ], -}; -invariant( - mainWeaponIds.every((id) => { - // not Splatana - if (id < 8000 || id >= 9000) return true; - - return Boolean(damageTypesToCombine[id]); - }), - "Splatana weapon missing from damageTypesToCombine" -); - export const multiShot: Partial> = { // L-3 300: 3, diff --git a/app/features/build-analyzer/core/types.ts b/app/features/build-analyzer/core/types.ts index 5e0705613..d9d6a079d 100644 --- a/app/features/build-analyzer/core/types.ts +++ b/app/features/build-analyzer/core/types.ts @@ -7,7 +7,7 @@ import type { import type { SPECIAL_EFFECTS } from "./specialEffects"; import type weaponParams from "./weapon-params.json"; import type abilityValues from "./ability-values.json"; -import type { DAMAGE_RECEIVERS, DAMAGE_TYPE } from "./constants"; +import type { DAMAGE_TYPE } from "./constants"; import type { AbilityWithUnknown } from "~/modules/in-game-lists/types"; type Overwrites = Record< @@ -173,10 +173,6 @@ export interface FullInkTankOption { export type DamageType = typeof DAMAGE_TYPE[number]; -export type DamageReceiver = typeof DAMAGE_RECEIVERS[number]; - -export type HitPoints = Record; - export interface Damage { value: number; type: DamageType; diff --git a/app/features/build-analyzer/index.ts b/app/features/build-analyzer/index.ts index 97d042b62..b25a7c702 100644 --- a/app/features/build-analyzer/index.ts +++ b/app/features/build-analyzer/index.ts @@ -1,2 +1,20 @@ -export { possibleApValues } from "./core/utils"; -export type { DamageReceiver, DamageType } from "./core/types"; +export { + possibleApValues, + validatedWeaponIdFromSearchParams, + hpDivided, +} from "./core/utils"; +export type { + DamageType, + AbilityPoints, + AnalyzedBuild, + SpecialWeaponParams, + SubWeaponParams, +} from "./core/types"; +export { + buildStats, + specialDeviceHp, + specialFieldHp, + subStats, +} from "./core/stats"; +export { DAMAGE_TYPE, damageTypeToWeaponType } from "./core/constants"; +export { default as weaponParams } from "./core/weapon-params.json"; diff --git a/app/features/object-damage-calculator/calculator-constants.ts b/app/features/object-damage-calculator/calculator-constants.ts new file mode 100644 index 000000000..68366457e --- /dev/null +++ b/app/features/object-damage-calculator/calculator-constants.ts @@ -0,0 +1,169 @@ +import { type MainWeaponId, mainWeaponIds } from "~/modules/in-game-lists"; +import type { DamageType } from "~/features/build-analyzer"; +import type objectDamages from "./core/object-dmg.json"; +import invariant from "tiny-invariant"; + +export const DAMAGE_RECEIVERS = [ + "Chariot", // Crab Tank + "NiceBall_Armor", // Booyah Bomb Armor + "ShockSonar", // Wave Breaker + "GreatBarrier_Barrier", // Big Bubbler Shield + "GreatBarrier_WeakPoint", // Big Bubbler Weak Point + "Gachihoko_Barrier", // Rainmaker Shield + "Wsb_Flag", // Squid Beakon + "Wsb_Shield", // Splash Wall + "Wsb_Sprinkler", // Sprinkler + "Bomb_TorpedoBullet", // Torpedo + "BulletUmbrellaCanopyCompact", // Undercover Brella Canopy + "BulletUmbrellaCanopyNormal", // Splat Brella Canopy + "BulletUmbrellaCanopyWide", // Tenta Brella Canopy +] as const; + +export const objectDamageJsonKeyPriority: Record< + keyof typeof objectDamages, + Array | null +> = { + Blaster_BlasterMiddle: null, + Blaster_BlasterShort: null, + Blaster_KillOneShot: ["DIRECT"], + Blaster: null, + BlowerExhale_BombCore: null, + BlowerInhale: null, + BombFlower: null, + Bomb_CurlingBullet: null, + Bomb_DirectHit: ["BOMB_DIRECT"], + Bomb_Fizzy: null, + Bomb_Suction: null, + Bomb_TorpedoBullet: null, + Bomb_TorpedoSplashBurst: null, + Bomb_Trap: null, + Bomb: null, + BrushCore: null, + BrushSplash: null, + CannonMissile: null, + ChargerFull_Light: ["FULL_CHARGE"], + ChargerFull_Long: ["FULL_CHARGE"], + ChargerFull: ["FULL_CHARGE"], + Charger_Light: ["MAX_CHARGE", "TAP_SHOT"], + Charger_Long: ["MAX_CHARGE", "TAP_SHOT"], + Charger: ["MAX_CHARGE", "TAP_SHOT"], + Chariot_Body: null, + Chariot_Cannon: null, + Default: null, + EnemyFlyingHohei_BombCore: null, + GachihokoTimeUpBurst: null, + Gachihoko_BombCore: null, + Gachihoko_Bullet: null, + Geyser: null, + GoldenIkuraAttack: null, + InkStormRain: null, + InkStorm: null, + Jetpack_BombCore: null, + Jetpack_Bullet: null, + Jetpack_Coop: null, + Jetpack_Jet: null, + Maneuver_Short: null, + Maneuver: null, + MicroLaser: null, + MissionSalmonBuddy: null, + MovePainter_Burst: null, + MovePainter_Direct: null, + MultiMissile_BombCore: null, + MultiMissile_Bullet: null, + NiceBall: null, + ObjectEffect_Up: null, + RollerCore: null, + RollerSplash_Compact: null, + RollerSplash_Heavy: null, + RollerSplash_Hunter: null, + RollerSplash: null, + Saber_ChargeShot: ["SPLATANA_VERTICAL"], + Saber_ChargeSlash: ["SPLATANA_VERTICAL_DIRECT"], + Saber_Shot: ["SPLATANA_HORIZONTAL"], + Saber_Slash: ["SPLATANA_HORIZONTAL_DIRECT"], + Saber_ChargeSlash_Penetrate: null, + SakerocketBullet: null, + ShelterCanopy_Compact: null, + ShelterCanopy_Wide: null, + ShelterCanopy: null, + ShelterShot_Compact: null, + ShelterShot_Wide: null, + ShelterShot: null, + Shield: null, + ShockSonar_Wave: null, + Shooter_Blaze: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_Expert: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_First: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_FlashRepeat: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_Flash: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_Gravity: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_Heavy: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_Long: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_Precision: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_Short: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_TripleMiddle: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter_TripleQuick: ["NORMAL_MAX", "NORMAL_MIN"], + Shooter: null, + Skewer_Body: null, + Skewer: null, + Slosher_Bathtub: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN", "DISTANCE", "SPLASH"], + Slosher_Bear: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN", "DISTANCE", "SPLASH"], + Slosher_WashtubBombCore: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN"], + Slosher_Washtub: ["DISTANCE", "SPLASH"], + Slosher: ["DIRECT", "DIRECT_MAX", "DIRECT_MIN", "DISTANCE", "SPLASH"], + Spinner: ["NORMAL_MAX", "NORMAL_MIN", "NORMAL_MAX_FULL_CHARGE", "SPLASH"], + Sprinkler: null, + Stringer_Short: ["NORMAL_MAX", "NORMAL_MIN", "DISTANCE"], + Stringer: ["NORMAL_MAX", "NORMAL_MIN", "DISTANCE"], + SuperHook: null, + SuperLanding: null, + TripleTornado: null, + UltraShot: null, + UltraStamp_Swing: null, + UltraStamp_Throw_BombCore: null, + UltraStamp_Throw: null, +}; + +export const damageTypesToCombine: Partial< + Record< + MainWeaponId, + Array<{ + when: DamageType; + combineWith: DamageType; + /** for this weapon "when" damage already includes "combineWith" damage, so calculating multiplier only */ + multiplierOnly?: boolean; + }> + > +> = { + // Explosher + 3040: [{ when: "DIRECT", combineWith: "DISTANCE" }], + // Tri-Stringer + 7010: [{ when: "NORMAL_MAX", combineWith: "DISTANCE" }], + // Splatana Stamper + 8000: [ + { when: "SPLATANA_VERTICAL_DIRECT", combineWith: "SPLATANA_VERTICAL" }, + { + when: "SPLATANA_HORIZONTAL_DIRECT", + combineWith: "SPLATANA_HORIZONTAL", + multiplierOnly: true, + }, + ], + // Splatana Wiper + 8010: [ + { when: "SPLATANA_VERTICAL_DIRECT", combineWith: "SPLATANA_VERTICAL" }, + { + when: "SPLATANA_HORIZONTAL_DIRECT", + combineWith: "SPLATANA_HORIZONTAL", + multiplierOnly: true, + }, + ], +}; +invariant( + mainWeaponIds.every((id) => { + // not Splatana + if (id < 8000 || id >= 9000) return true; + + return Boolean(damageTypesToCombine[id]); + }), + "Splatana weapon missing from damageTypesToCombine" +); diff --git a/app/features/build-analyzer/core/useObjectDamage.ts b/app/features/object-damage-calculator/calculator-hooks.ts similarity index 92% rename from app/features/build-analyzer/core/useObjectDamage.ts rename to app/features/object-damage-calculator/calculator-hooks.ts index 2823bc397..32361aa89 100644 --- a/app/features/build-analyzer/core/useObjectDamage.ts +++ b/app/features/object-damage-calculator/calculator-hooks.ts @@ -1,11 +1,15 @@ import { useSearchParams } from "@remix-run/react"; import { assertType } from "~/utils/types"; import { type MainWeaponId } from "~/modules/in-game-lists"; -import type { DAMAGE_TYPE } from "./constants"; -import { calculateDamage } from "./objectDamage"; -import { buildStats } from "./stats"; -import type { AnalyzedBuild, DamageType } from "./types"; -import { possibleApValues, validatedWeaponIdFromSearchParams } from "./utils"; +import { + type DAMAGE_TYPE, + buildStats, + possibleApValues, + validatedWeaponIdFromSearchParams, + type AnalyzedBuild, + type DamageType, +} from "~/features/build-analyzer"; +import { calculateDamage } from "./core/objectDamage"; const ABILITY_POINTS_SP_KEY = "ap"; const DAMAGE_TYPE_SP_KEY = "dmg"; diff --git a/app/features/object-damage-calculator/calculator-types.ts b/app/features/object-damage-calculator/calculator-types.ts new file mode 100644 index 000000000..2bc840f7e --- /dev/null +++ b/app/features/object-damage-calculator/calculator-types.ts @@ -0,0 +1,5 @@ +import type { DAMAGE_RECEIVERS } from "./calculator-constants"; + +export type DamageReceiver = typeof DAMAGE_RECEIVERS[number]; + +export type HitPoints = Record; diff --git a/app/features/build-analyzer/core/object-dmg.json b/app/features/object-damage-calculator/core/object-dmg.json similarity index 100% rename from app/features/build-analyzer/core/object-dmg.json rename to app/features/object-damage-calculator/core/object-dmg.json diff --git a/app/features/build-analyzer/core/objectDamage.test.ts b/app/features/object-damage-calculator/core/objectDamage.test.ts similarity index 97% rename from app/features/build-analyzer/core/objectDamage.test.ts rename to app/features/object-damage-calculator/core/objectDamage.test.ts index 0d2cc878b..2f15b2d3e 100644 --- a/app/features/build-analyzer/core/objectDamage.test.ts +++ b/app/features/object-damage-calculator/core/objectDamage.test.ts @@ -2,8 +2,11 @@ import { suite } from "uvu"; import * as assert from "uvu/assert"; import type { MainWeaponId } from "~/modules/in-game-lists"; import { calculateDamage } from "./objectDamage"; -import { buildStats } from "./stats"; -import type { AbilityPoints, DamageType } from "./types"; +import { + buildStats, + type DamageType, + type AbilityPoints, +} from "~/features/build-analyzer"; function calculate({ mainWeaponId = 10, diff --git a/app/features/build-analyzer/core/objectDamage.ts b/app/features/object-damage-calculator/core/objectDamage.ts similarity index 96% rename from app/features/build-analyzer/core/objectDamage.ts rename to app/features/object-damage-calculator/core/objectDamage.ts index a39c6fc51..959a9e3f7 100644 --- a/app/features/build-analyzer/core/objectDamage.ts +++ b/app/features/object-damage-calculator/core/objectDamage.ts @@ -1,24 +1,24 @@ import type { AbilityPoints, AnalyzedBuild, - DamageReceiver, DamageType, -} from "./types"; +} from "~/features/build-analyzer"; +import { damageTypeToWeaponType } from "~/features/build-analyzer"; import objectDamages from "./object-dmg.json"; import type { MainWeaponId, SpecialWeaponId, SubWeaponId, } from "~/modules/in-game-lists"; +import { roundToNDecimalPlaces } from "~/utils/number"; +import invariant from "tiny-invariant"; +import { objectHitPoints } from "./objectHitPoints"; import { damageTypesToCombine, - damageTypeToWeaponType, DAMAGE_RECEIVERS, objectDamageJsonKeyPriority, -} from "./constants"; -import { roundToNDecimalPlaces } from "~/utils/number"; -import { objectHitPoints } from "./objectHitPoints"; -import invariant from "tiny-invariant"; +} from "../calculator-constants"; +import type { DamageReceiver } from "../calculator-types"; export function damageTypeToMultipliers({ type, diff --git a/app/features/build-analyzer/core/objectHitPoints.ts b/app/features/object-damage-calculator/core/objectHitPoints.ts similarity index 86% rename from app/features/build-analyzer/core/objectHitPoints.ts rename to app/features/object-damage-calculator/core/objectHitPoints.ts index 83827e621..98bbf31d3 100644 --- a/app/features/build-analyzer/core/objectHitPoints.ts +++ b/app/features/object-damage-calculator/core/objectHitPoints.ts @@ -4,15 +4,17 @@ import { CRAB_TANK_ID, SPLASH_WALL_ID, } from "~/modules/in-game-lists"; -import { specialDeviceHp, specialFieldHp, subStats } from "./stats"; -import type { - AbilityPoints, - HitPoints, - SpecialWeaponParams, - SubWeaponParams, -} from "./types"; -import { hpDivided } from "./utils"; -import weaponParams from "./weapon-params.json"; +import { + specialDeviceHp, + specialFieldHp, + subStats, + hpDivided, + weaponParams, + type AbilityPoints, + type SpecialWeaponParams, + type SubWeaponParams, +} from "~/features/build-analyzer"; +import type { HitPoints } from "../calculator-types"; const WAVE_BREAKER_HP = 400; const SPRINKER_HP = 100; diff --git a/app/routes/object-damage-calculator.tsx b/app/features/object-damage-calculator/routes/object-damage-calculator.tsx similarity index 98% rename from app/routes/object-damage-calculator.tsx rename to app/features/object-damage-calculator/routes/object-damage-calculator.tsx index 6eac62ee1..4b099baaa 100644 --- a/app/routes/object-damage-calculator.tsx +++ b/app/features/object-damage-calculator/routes/object-damage-calculator.tsx @@ -32,9 +32,9 @@ import { damageTypeTranslationString } from "~/utils/i18next"; import { useSetTitle } from "~/hooks/useSetTitle"; import type { ShouldReloadFunction } from "@remix-run/react"; import { Toggle } from "~/components/Toggle"; -import { useObjectDamage } from "~/features/build-analyzer/core/useObjectDamage"; -import type { DamageReceiver } from "~/features/build-analyzer"; +import { useObjectDamage } from "../calculator-hooks"; import { type DamageType, possibleApValues } from "~/features/build-analyzer"; +import type { DamageReceiver } from "../calculator-types"; export const CURRENT_PATCH = "2.0"; diff --git a/remix.config.js b/remix.config.js index 455edb2a3..9df116479 100644 --- a/remix.config.js +++ b/remix.config.js @@ -7,6 +7,10 @@ module.exports = { routes: async (defineRoutes) => { return defineRoutes((route) => { route("/analyzer", "features/build-analyzer/routes/analyzer.tsx"); + route( + "/object-damage-calculator", + "features/object-damage-calculator/routes/object-damage-calculator.tsx" + ); route("/to/:id", "features/tournament/routes/to.$id.tsx", () => { route("/to/:id", "features/tournament/routes/to.$id.index.tsx"); route(