Add damage multiplires for brellas+blasters, ensure each weapon only has one multiplier Closes #1044

This commit is contained in:
Kalle
2023-06-23 13:55:12 +03:00
parent 4adb396d56
commit 73a9d2b784
6 changed files with 246 additions and 252 deletions

View File

@@ -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",

View File

@@ -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<DamageType> | 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<AnyWeapon["id"]>,
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<MainWeaponId, Array<CombineWith>>

View File

@@ -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(

View File

@@ -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<keyof typeof objectDamages> = [];
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<keyof typeof objectDamages> = [];
function resolveRelevantKey({
keys,
type,
weapon,
}: {
keys: Array<keyof typeof objectDamages>;
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);

View File

@@ -90,18 +90,20 @@ export default function ObjectDamagePage() {
fullWidth
/>
</div>
<div
className={clsx({
invisible: !damagesToReceivers || allDamageTypes.length === 1,
})}
>
<Label htmlFor="damage">{t("analyzer:labels.damageType")}</Label>
<DamageTypesSelect
handleChange={handleChange}
damageType={damageType}
allDamageTypes={allDamageTypes}
/>
</div>
{allDamageTypes.length > 0 ? (
<div
className={clsx({
invisible: !damagesToReceivers || allDamageTypes.length === 1,
})}
>
<Label htmlFor="damage">{t("analyzer:labels.damageType")}</Label>
<DamageTypesSelect
handleChange={handleChange}
damageType={damageType}
allDamageTypes={allDamageTypes}
/>
</div>
) : null}
</div>
{multiShotCount ? (
<div className="stack sm horizontal items-center label-no-spacing">

View File

@@ -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();
});
});