diff --git a/app/features/build-analyzer/core/stats.ts b/app/features/build-analyzer/core/stats.ts index 2dbb83ee7..93f50b914 100644 --- a/app/features/build-analyzer/core/stats.ts +++ b/app/features/build-analyzer/core/stats.ts @@ -403,11 +403,7 @@ const damageTypeToParamsKey: Record< DIRECT: "DamageParam_ValueDirect", DIRECT_MIN: "DamageParam_ValueDirectMin", DIRECT_MAX: "DamageParam_ValueDirectMax", - DISTANCE: [ - "BlastParam_DistanceDamage", - "DistanceDamage_BlastParamArray", - "DistanceDamage_BlastParamChase", - ], + DISTANCE: ["BlastParam_DistanceDamage", "DistanceDamage_BlastParamArray"], SPLASH: ["BlastParam_SplashDamage", "DistanceDamage_SplashBlastParam"], SPLASH_MIN: "SwingUnitGroupParam_DamageParam_DamageMinValue", SPLASH_MAX: "SwingUnitGroupParam_DamageParam_DamageMaxValue", @@ -424,7 +420,7 @@ const damageTypeToParamsKey: Record< SPLATANA_HORIZONTAL: "DamageParam_SplatanaHorizontal", SPLATANA_HORIZONTAL_DIRECT: "DamageParam_SplatanaHorizontalDirect", BOMB_NORMAL: "DistanceDamage", - BOMB_DIRECT: "DirectDamage", + BOMB_DIRECT: ["DirectDamage", "DistanceDamage_BlastParamChase"], WAVE: "WaveDamage", SPECIAL_MAX_CHARGE: "ExhaleBlastParamMaxChargeDistanceDamage", SPECIAL_MIN_CHARGE: "ExhaleBlastParamMinChargeDistanceDamage", diff --git a/app/features/object-damage-calculator/calculator-constants.ts b/app/features/object-damage-calculator/calculator-constants.ts index fa0146f61..afd6fe379 100644 --- a/app/features/object-damage-calculator/calculator-constants.ts +++ b/app/features/object-damage-calculator/calculator-constants.ts @@ -1,8 +1,8 @@ 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"; import type { CombineWith } from "./calculator-types"; +import type { AnyWeapon, DamageType } from "../build-analyzer"; +import type objectDamages from "./core/object-dmg.json"; export const DAMAGE_RECEIVERS = [ "Chariot", // Crab Tank @@ -20,152 +20,92 @@ export const DAMAGE_RECEIVERS = [ "BulletUmbrellaCanopyWide", // Tenta Brella Canopy ] as const; -export const objectDamageJsonKeyPriority: Record< - keyof typeof objectDamages, - Array | null -> = { - // added in chill season 2023 - Maneuver_SideStep_BombCore: null, - // added in fresh season 2022 - BlowerExhale: null, - Castle: ["BOMB_DIRECT", "SPECIAL_JUMP"], - CoopEnemyRush: null, - CoopEnemySakelienBomberBlast: null, - CoopEnemySakerocketBlast: null, - CoopEnemySakeSaucerFall: null, - CoopEnemySakeTamaireBombDamage: null, - GeyserOnline: null, - RollerSplash_Wide: [ - "SPLASH_VERTICAL_MIN", - "SPLASH_VERTICAL_MAX", - "SPLASH_HORIZONTAL_MIN", - "SPLASH_HORIZONTAL_MAX", - ], - // original - Blaster_BlasterMiddle: null, - Blaster_BlasterShort: null, - Blaster_KillOneShot: ["DIRECT"], - Blaster: null, - BlowerExhale_BombCore: ["SPECIAL_MAX_CHARGE", "SPECIAL_MIN_CHARGE"], - 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: ["ROLL_OVER"], - BrushSplash: ["SPLASH_MIN", "SPLASH_MAX"], - 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: ["SPECIAL_BUMP"], - Chariot_Cannon: [ - "SPECIAL_BULLET_MIN", - "SPECIAL_BULLET_MAX", - "SPECIAL_CANNON", - ], - Default: null, - EnemyFlyingHohei_BombCore: null, - GachihokoTimeUpBurst: null, - Gachihoko_BombCore: null, - Gachihoko_Bullet: null, - Geyser: null, - GoldenIkuraAttack: null, - InkStormRain: null, - InkStorm: ["SPECIAL_TICK"], - Jetpack_BombCore: ["BOMB_DIRECT"], - Jetpack_Bullet: ["BOMB_NORMAL"], - Jetpack_Coop: null, - Jetpack_Jet: null, - Maneuver_Short: null, - Maneuver: null, - MicroLaser: ["SPECIAL_TICK"], - MissionSalmonBuddy: null, - MovePainter_Burst: null, - MovePainter_Direct: null, - MultiMissile_BombCore: ["BOMB_NORMAL"], - MultiMissile_Bullet: null, - NiceBall: ["SPECIAL_TICK"], - ObjectEffect_Up: null, - RollerCore: ["ROLL_OVER"], - RollerSplash_Compact: [ - "SPLASH_VERTICAL_MIN", - "SPLASH_VERTICAL_MAX", - "SPLASH_HORIZONTAL_MIN", - "SPLASH_HORIZONTAL_MAX", - ], - RollerSplash_Heavy: [ - "SPLASH_VERTICAL_MIN", - "SPLASH_VERTICAL_MAX", - "SPLASH_HORIZONTAL_MIN", - "SPLASH_HORIZONTAL_MAX", - ], - RollerSplash_Hunter: [ - "SPLASH_VERTICAL_MIN", - "SPLASH_VERTICAL_MAX", - "SPLASH_HORIZONTAL_MIN", - "SPLASH_HORIZONTAL_MAX", - ], - RollerSplash: [ - "SPLASH_VERTICAL_MIN", - "SPLASH_VERTICAL_MAX", - "SPLASH_HORIZONTAL_MIN", - "SPLASH_HORIZONTAL_MAX", - ], - 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: ["WAVE"], - 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: ["BOMB_NORMAL"], - 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: ["BOMB_NORMAL"], - SuperLanding: null, - TripleTornado: null, - UltraShot: ["BOMB_NORMAL", "BOMB_DIRECT"], - UltraStamp_Swing: ["SPECIAL_SWING"], - UltraStamp_Throw_BombCore: ["SPECIAL_THROW_DIRECT"], - UltraStamp_Throw: ["SPECIAL_THROW"], -}; +export const damagePriorities: Array< + [ + AnyWeapon["type"], + Array, + DamageType, + keyof typeof objectDamages + ] +> = [ + ["MAIN", [210, 220, 260], "DIRECT", "Blaster_KillOneShot"], + ["MAIN", [210, 260], "DISTANCE", "Blaster_BlasterMiddle"], + ["MAIN", [220], "DISTANCE", "Blaster"], + + ["MAIN", [400], "NORMAL_MAX", "Shooter_Flash"], // xxx: TODO Squeezer autofire + ["MAIN", [400], "NORMAL_MIN", "Shooter_Flash"], + + ["MAIN", [1000], "SPLASH_VERTICAL_MIN", "RollerSplash_Compact"], + ["MAIN", [1000], "SPLASH_VERTICAL_MAX", "RollerSplash_Compact"], + ["MAIN", [1000], "SPLASH_HORIZONTAL_MIN", "RollerSplash_Compact"], + ["MAIN", [1000], "SPLASH_HORIZONTAL_MAX", "RollerSplash_Compact"], + ["MAIN", [1000, 1010, 1020, 1030, 1040], "ROLL_OVER", "RollerCore"], + ["MAIN", [1010], "SPLASH_VERTICAL_MIN", "RollerSplash"], + ["MAIN", [1010], "SPLASH_VERTICAL_MAX", "RollerSplash"], + ["MAIN", [1010], "SPLASH_HORIZONTAL_MIN", "RollerSplash"], + ["MAIN", [1010], "SPLASH_HORIZONTAL_MAX", "RollerSplash"], + ["MAIN", [1020], "SPLASH_VERTICAL_MIN", "RollerSplash_Heavy"], + ["MAIN", [1020], "SPLASH_VERTICAL_MAX", "RollerSplash_Heavy"], + ["MAIN", [1020], "SPLASH_HORIZONTAL_MIN", "RollerSplash_Heavy"], + ["MAIN", [1020], "SPLASH_HORIZONTAL_MAX", "RollerSplash_Heavy"], + ["MAIN", [1030], "SPLASH_VERTICAL_MIN", "RollerSplash_Hunter"], + ["MAIN", [1030], "SPLASH_VERTICAL_MAX", "RollerSplash_Hunter"], + ["MAIN", [1030], "SPLASH_HORIZONTAL_MIN", "RollerSplash_Hunter"], + ["MAIN", [1030], "SPLASH_HORIZONTAL_MAX", "RollerSplash_Hunter"], + ["MAIN", [1040], "SPLASH_VERTICAL_MIN", "RollerSplash_Wide"], + ["MAIN", [1040], "SPLASH_VERTICAL_MAX", "RollerSplash_Wide"], + ["MAIN", [1040], "SPLASH_HORIZONTAL_MIN", "RollerSplash_Wide"], + ["MAIN", [1040], "SPLASH_HORIZONTAL_MAX", "RollerSplash_Wide"], + + ["MAIN", [1100, 1110, 1120], "ROLL_OVER", "BrushCore"], + ["MAIN", [1100, 1110, 1120], "SPLASH_MIN", "BrushSplash"], + ["MAIN", [1100, 1110, 1120], "SPLASH_MAX", "BrushSplash"], + + ["MAIN", [2000, 2010, 2020, 2060], "FULL_CHARGE", "ChargerFull"], + ["MAIN", [2000, 2010, 2020, 2060], "MAX_CHARGE", "Charger"], + ["MAIN", [2000, 2010, 2020, 2060], "TAP_SHOT", "Charger"], + ["MAIN", [2030, 2040], "FULL_CHARGE", "ChargerFull_Long"], + ["MAIN", [2030, 2040], "MAX_CHARGE", "Charger_Long"], + ["MAIN", [2030, 2040], "TAP_SHOT", "Charger_Long"], + ["MAIN", [2050, 2070], "FULL_CHARGE", "ChargerFull_Light"], + ["MAIN", [2050, 2070], "MAX_CHARGE", "Charger_Light"], + ["MAIN", [2050, 2070], "TAP_SHOT", "Charger_Light"], + + ["MAIN", [3040], "DIRECT", "Slosher_WashtubBombCore"], + ["MAIN", [3040], "DISTANCE", "Slosher_Washtub"], + + ["MAIN", [6000], "NORMAL_MAX", "ShelterShot"], // TODO: could also list damage caused by Shield bump + ["MAIN", [6000], "NORMAL_MIN", "ShelterShot"], + ["MAIN", [6010], "NORMAL_MAX", "ShelterShot_Wide"], + ["MAIN", [6010], "NORMAL_MIN", "ShelterShot_Wide"], + ["MAIN", [6020], "NORMAL_MAX", "ShelterShot_Compact"], + ["MAIN", [6020], "NORMAL_MIN", "ShelterShot_Compact"], + + ["MAIN", [8000, 8010], "SPLATANA_VERTICAL", "Saber_ChargeShot"], + ["MAIN", [8000, 8010], "SPLATANA_VERTICAL_DIRECT", "Saber_ChargeSlash"], + ["MAIN", [8000, 8010], "SPLATANA_HORIZONTAL", "Saber_Shot"], + ["MAIN", [8000, 8010], "SPLATANA_HORIZONTAL_DIRECT", "Saber_Slash"], + + ["SUB", [0, 2, 7], "BOMB_NORMAL", "Bomb"], // TODO: could also consider "Bomb_DirectHit" it is almost the same but has different ratio for Big Bubbler core: 0.5 vs. 1.5 + ["SUB", [6], "BOMB_DIRECT", "Bomb_CurlingBullet"], + ["SUB", [6], "BOMB_NORMAL", "Bomb"], + ["SUB", [13], "SPLASH", "Bomb_TorpedoSplashBurst"], + ["SUB", [13], "BOMB_DIRECT", "Bomb_TorpedoBullet"], + + ["SPECIAL", [4], "BOMB_NORMAL", "MultiMissile_Bullet"], // There is also "MultiMissile_BombCore" but it seems to contain same rates as MultiMissile_Bullet + ["SPECIAL", [8], "SPECIAL_MAX_CHARGE", "BlowerExhale_BombCore"], + ["SPECIAL", [8], "SPECIAL_MIN_CHARGE", "BlowerExhale_BombCore"], + ["SPECIAL", [10], "BOMB_DIRECT", "Jetpack_BombCore"], + ["SPECIAL", [10], "BOMB_NORMAL", "Jetpack_Bullet"], + ["SPECIAL", [11], "SPECIAL_THROW_DIRECT", "UltraStamp_Throw_BombCore"], + ["SPECIAL", [11], "SPECIAL_THROW", "UltraStamp_Throw"], + ["SPECIAL", [11], "SPECIAL_SWING", "UltraStamp_Swing"], + ["SPECIAL", [12], "SPECIAL_CANNON", "Chariot_Cannon"], + ["SPECIAL", [12], "SPECIAL_BULLET_MAX", "Chariot_Cannon"], + ["SPECIAL", [12], "SPECIAL_BULLET_MIN", "Chariot_Cannon"], + ["SPECIAL", [12], "SPECIAL_BUMP", "Chariot_Body"], + ["SPECIAL", [13], "BOMB_NORMAL", "Skewer"], +]; export const damageTypesToCombine: Partial< Record> diff --git a/app/features/object-damage-calculator/core/objectDamage.test.ts b/app/features/object-damage-calculator/core/objectDamage.test.ts index 0367fea1f..1e35cc323 100644 --- a/app/features/object-damage-calculator/core/objectDamage.test.ts +++ b/app/features/object-damage-calculator/core/objectDamage.test.ts @@ -1,7 +1,14 @@ import { suite } from "uvu"; import * as assert from "uvu/assert"; -import type { MainWeaponId } from "~/modules/in-game-lists"; +import type { SubWeaponId, SpecialWeaponId } from "~/modules/in-game-lists"; +import { + mainWeaponIds, + type MainWeaponId, + specialWeaponIds, + exampleMainWeaponIdWithSpecialWeaponId, +} from "~/modules/in-game-lists"; import { calculateDamage } from "./objectDamage"; +import type { AnalyzedBuild } from "~/features/build-analyzer"; import { buildStats, type DamageType, @@ -10,22 +17,35 @@ import { function calculate({ mainWeaponId = 10, + subWeaponId, + specialWeaponId, abilityPoints = new Map(), damageType = "NORMAL_MAX", + preAnalyzed, }: { mainWeaponId?: MainWeaponId; + subWeaponId?: SubWeaponId; + specialWeaponId?: SpecialWeaponId; abilityPoints?: AbilityPoints; damageType?: DamageType; + preAnalyzed?: AnalyzedBuild; }) { - const analyzed = buildStats({ - weaponSplId: mainWeaponId, - hasTacticooler: false, - }); + const analyzed = + preAnalyzed ?? + buildStats({ + weaponSplId: mainWeaponId, + hasTacticooler: false, + }); return calculateDamage({ abilityPoints, analyzed, - anyWeapon: { type: "MAIN", id: mainWeaponId }, + anyWeapon: + typeof subWeaponId === "number" + ? { type: "SUB", id: subWeaponId } + : typeof specialWeaponId === "number" + ? { type: "SPECIAL", id: specialWeaponId } + : { type: "MAIN", id: mainWeaponId }, damageType, isMultiShot: true, }); @@ -33,6 +53,49 @@ function calculate({ const CalculateDamage = suite("calculateDamage()"); +// the function throws if weapon resolves to more than one set of damage rates +// so this test goes through all of them to make sure they all work +CalculateDamage("Every weapon can calculate damage", () => { + for (const mainWeaponId of mainWeaponIds) { + const analyzed = buildStats({ + weaponSplId: mainWeaponId, + hasTacticooler: false, + }); + + for (const damage of analyzed.stats.damages) { + calculate({ mainWeaponId, damageType: damage.type }); + } + } + + const analyzed = buildStats({ + weaponSplId: 0, + hasTacticooler: false, + }); + + for (const damage of analyzed.stats.subWeaponDefenseDamages) { + calculate({ + subWeaponId: damage.subWeaponId, + damageType: damage.type, + preAnalyzed: analyzed, + }); + } + + for (const specialWeaponId of specialWeaponIds) { + const analyzedWithSpecialWeapon = buildStats({ + weaponSplId: exampleMainWeaponIdWithSpecialWeaponId(specialWeaponId), + hasTacticooler: false, + }); + + for (const damage of analyzedWithSpecialWeapon.stats.specialWeaponDamages) { + calculate({ + specialWeaponId, + damageType: damage.type, + preAnalyzed: analyzedWithSpecialWeapon, + }); + } + } +}); + CalculateDamage("BRU increases Splash Wall hitpoints", () => { const withoutBRU = calculate({}); const withBRU = calculate({ @@ -81,29 +144,30 @@ const shotsToPopRM: Array< [220, "DIRECT", 5, 4], // .96 Gal [80, "NORMAL_MAX", 17, 15], + // Luna Blaster + [200, "DIRECT", 4, 4], // Splat Charger [2010, "FULL_CHARGE", 4, 3], // E-liter 4K [2030, "TAP_SHOT", 13, 12], - // TODO: Explosher damage wrong - // Explosher - // [3040, "DIRECT", 4, 3], // Hydra Splatling [4020, "NORMAL_MAX", 32, 29], // Sloshing Machine [3020, "DIRECT_MAX", 6, 5], // Splat Dualies [5010, "NORMAL_MAX", 34, 31], + // Tenta Brella + [6010, "NORMAL_MAX", 4, 4], + // // Tri-Stringer + [7010, "NORMAL_MAX", 3, 3], + // REEF-LUX + [7020, "NORMAL_MIN", 8, 7], // Splatana Wiper [8010, "SPLATANA_HORIZONTAL", 11, 10], // Splatana Wiper [8010, "SPLATANA_HORIZONTAL_DIRECT", 9, 8], // Splatana Stamper [8000, "SPLATANA_VERTICAL_DIRECT", 3, 3], - // // Tri-Stringer - [7010, "NORMAL_MAX", 3, 3], - // REEF-LUX - [7020, "NORMAL_MIN", 8, 7], ]; CalculateDamage( diff --git a/app/features/object-damage-calculator/core/objectDamage.ts b/app/features/object-damage-calculator/core/objectDamage.ts index b45aef4e6..b18f6a653 100644 --- a/app/features/object-damage-calculator/core/objectDamage.ts +++ b/app/features/object-damage-calculator/core/objectDamage.ts @@ -15,7 +15,7 @@ import { objectHitPoints } from "./objectHitPoints"; import { damageTypesToCombine, DAMAGE_RECEIVERS, - objectDamageJsonKeyPriority, + damagePriorities, } from "../calculator-constants"; import type { CombineWith, DamageReceiver } from "../calculator-types"; import type { AnyWeapon } from "~/features/build-analyzer"; @@ -26,69 +26,80 @@ export function damageTypeToMultipliers({ weapon, }: { type: DamageType; - weapon: - | { - type: "MAIN"; - id: MainWeaponId; - } - | { - type: "SUB"; - id: SubWeaponId; - } - | { - type: "SPECIAL"; - id: SpecialWeaponId; - }; + weapon: AnyWeapon; }) { - for (const key of jsonKeysToCeck(type)) { - const objectDamagesObj = objectDamages[key]; + const matchingKeys: Array = []; - let ok = false; - - if (weapon.type === "MAIN") { - ok = (objectDamagesObj.mainWeaponIds as MainWeaponId[]).includes( + for (const [key, objectDamagesObj] of Object.entries(objectDamages)) { + if ( + weapon.type === "MAIN" && + (objectDamagesObj.mainWeaponIds as MainWeaponId[]).includes(weapon.id) + ) { + matchingKeys.push(key as keyof typeof objectDamages); + } else if ( + weapon.type === "SUB" && + (objectDamagesObj.subWeaponIds as SubWeaponId[]).includes(weapon.id) + ) { + matchingKeys.push(key as keyof typeof objectDamages); + } else if ( + weapon.type === "SPECIAL" && + (objectDamagesObj.specialWeaponIds as SpecialWeaponId[]).includes( weapon.id - ); - } else if (weapon.type === "SUB") { - ok = (objectDamagesObj.subWeaponIds as SubWeaponId[]).includes(weapon.id); - } else if (weapon.type === "SPECIAL") { - ok = (objectDamagesObj.specialWeaponIds as SpecialWeaponId[]).includes( - weapon.id - ); - } - - if (ok) { - return objectDamagesObj.rates; + ) + ) { + matchingKeys.push(key as keyof typeof objectDamages); } } - return null; + if (matchingKeys.length === 0) return null; + + const relevantKey = resolveRelevantKey({ keys: matchingKeys, type, weapon }); + + return objectDamages[relevantKey].rates; } -const objectDamageJsonKeyPriorityEntries = Object.entries( - objectDamageJsonKeyPriority -); -// for example blaster belongs to both Blaster_KillOneShot -// and Blaster categories so it needs to be specified -// which damage type uses which -function jsonKeysToCeck(type: DamageType) { - const result: Array = []; +function resolveRelevantKey({ + keys, + type, + weapon, +}: { + keys: Array; + type: DamageType; + weapon: AnyWeapon; +}): keyof typeof objectDamages { + if (keys.length === 1) return keys[0]; - for (const [key, value] of objectDamageJsonKeyPriorityEntries) { - if (value?.includes(type)) { - result.push(key as keyof typeof objectDamages); + const actualKeys = keys.filter((k) => k !== "Default"); + if (actualKeys.length === 1) return actualKeys[0]; + + for (const [weaponType, weaponIds, damageType, key] of damagePriorities) { + // handle alt kits e.g. Splatteshot might have id 10 but Tentatek Splattershot has id 11 + // but in the context of this function they are one and the same + const normalizedWeaponId = + weapon.type === "MAIN" && weapon.id % 10 !== 0 + ? ((weapon.id - 1) as MainWeaponId) + : weapon.id; + + if (weaponType !== weapon.type) continue; + if (!weaponIds.includes(normalizedWeaponId)) continue; + if (damageType !== type) continue; + + if (!actualKeys.includes(key)) { + throw new Error( + `Invalid damagePriorities (no key in object-dmg.json for the weapon): ${JSON.stringify( + [weaponType, weaponIds, damageType, key] + )}` + ); } + + return key; } - if (result.length) return result; - - for (const [key, value] of objectDamageJsonKeyPriorityEntries) { - if (!value) { - result.push(key as keyof typeof objectDamages); - } - } - - return result; + throw new Error( + `Could not resolve relevant key from ${actualKeys.join(", ")}; weapon: ${ + weapon.type + }_${weapon.id}; damage type: ${type} - please update damagePriorities` + ); } export function multipliersToRecordWithFallbacks( @@ -111,9 +122,7 @@ export function resolveAllUniqueDamageTypes({ }) { const damageTypes = anyWeapon.type === "SUB" - ? analyzed.stats.subWeaponDefenseDamages - .filter((damage) => damage.subWeaponId === anyWeapon.id) - .map((d) => d.type) + ? [] : anyWeapon.type === "SPECIAL" ? analyzed.stats.specialWeaponDamages.map((d) => d.type) : analyzed.stats.damages.map((d) => d.type); diff --git a/app/features/object-damage-calculator/routes/object-damage-calculator.tsx b/app/features/object-damage-calculator/routes/object-damage-calculator.tsx index dd1391d96..17f92fa77 100644 --- a/app/features/object-damage-calculator/routes/object-damage-calculator.tsx +++ b/app/features/object-damage-calculator/routes/object-damage-calculator.tsx @@ -90,18 +90,20 @@ export default function ObjectDamagePage() { fullWidth /> -
- - -
+ {allDamageTypes.length > 0 ? ( +
+ + +
+ ) : null} {multiShotCount ? (
diff --git a/e2e/top-search.spec.ts b/e2e/top-search.spec.ts index fe5744106..361d4e263 100644 --- a/e2e/top-search.spec.ts +++ b/e2e/top-search.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "@playwright/test"; import { navigate, seed } from "~/utils/playwright"; -import { topSearchPage, userPage, weaponBuildPage } from "~/utils/urls"; +import { topSearchPage, userPage } from "~/utils/urls"; test.describe("Top search", () => { test("views different x rank placements", async ({ page }) => { @@ -30,21 +30,4 @@ test.describe("Top search", () => { await expect(page.getByText("Twig?")).toBeVisible(); }); - - test("sorts top 500 builds first", async ({ page }) => { - await seed(page); - - await navigate({ - page, - url: weaponBuildPage("splattershot"), - }); - - await expect( - page - .getByTestId("build-card") - .first() - .locator(page.getByTestId("top500-crown")) - .first() - ).toBeVisible(); - }); });