Add Explosher 90dmg to comp analyzer

This commit is contained in:
Kalle 2026-01-20 20:57:26 +02:00
parent 6009832864
commit b599e78a61
22 changed files with 91 additions and 0 deletions

View File

@ -45,6 +45,7 @@ export const DAMAGE_TYPE = [
"SPECIAL_TICK",
"SECONDARY_MODE_MAX",
"SECONDARY_MODE_MIN",
"COMBO",
] as const;
export const damageTypeToWeaponType: Record<
@ -93,6 +94,7 @@ export const damageTypeToWeaponType: Record<
SPECIAL_BUMP: "SPECIAL",
SPECIAL_JUMP: "SPECIAL",
SPECIAL_TICK: "SPECIAL",
COMBO: "MAIN",
};
export const RAINMAKER_SPEED_PENALTY_MODIFIER = 0.8;

View File

@ -446,6 +446,8 @@ const damageTypeToParamsKey: Record<
SPECIAL_BUMP: "BumpDamage",
SPECIAL_JUMP: "JumpDamage",
SPECIAL_TICK: "TickDamage",
// Virtual damage type for comp-analyzer, calculated from other damages
COMBO: [],
};
function damages(args: StatFunctionInput): AnalyzedBuild["stats"]["damages"] {

View File

@ -1,4 +1,6 @@
import type { DamageType } from "~/features/build-analyzer/analyzer-types";
import type {
MainWeaponId,
SpecialWeaponId,
SubWeaponId,
} from "~/modules/in-game-lists/types";
@ -76,3 +78,21 @@ export const SPECIAL_CATEGORY_ORDER = [
export type SubWeaponCategory = (typeof SUB_CATEGORY_ORDER)[number];
export type SpecialWeaponCategory = (typeof SPECIAL_CATEGORY_ORDER)[number];
interface VirtualDamageCombo {
damageTypes: DamageType[];
virtualType: DamageType;
}
const EXPLOSHER_ID = 3040 as MainWeaponId;
export const VIRTUAL_DAMAGE_COMBOS: Partial<
Record<MainWeaponId, VirtualDamageCombo[]>
> = {
[EXPLOSHER_ID]: [
{
damageTypes: ["DIRECT", "DISTANCE"],
virtualType: "COMBO",
},
],
};

View File

@ -16,6 +16,7 @@ const SPLAT_ROLLER_ID = 1010;
const SPLAT_CHARGER_ID = 2010;
const AEROSPRAY_MG_ID = 30;
const SPLATTERSHOT_JR_ID = 10;
const EXPLOSHER_ID = 3040;
describe("extractDamageSources", () => {
test("extracts main weapon damages", () => {
@ -309,3 +310,33 @@ describe("calculateInkTimeToKill", () => {
expect(result).toBeGreaterThan(0);
});
});
describe("virtual damage combos", () => {
test("Explosher has COMBO damage type combining DIRECT and DISTANCE", () => {
const sources = extractDamageSources([EXPLOSHER_ID]);
const damages = sources[0].damages;
const comboDamage = damages.find((d) => d.type === "COMBO");
expect(comboDamage).toBeDefined();
expect(comboDamage?.weaponType).toBe("MAIN");
const directDamage = damages.find((d) => d.type === "DIRECT");
const distanceDamage = damages.find((d) => d.type === "DISTANCE");
expect(directDamage).toBeDefined();
expect(distanceDamage).toBeDefined();
const expectedComboValue =
(directDamage?.value ?? 0) + (distanceDamage?.value ?? 0);
expect(comboDamage?.value).toBeCloseTo(expectedComboValue, 1);
});
test("COMBO damage appears in damage combos", () => {
const combos = calculateDamageCombos([EXPLOSHER_ID, SPLATTERSHOT_ID]);
const comboWithComboType = combos.find((c) =>
c.segments.some((s) => s.damageType === "COMBO"),
);
expect(comboWithComboType).toBeDefined();
});
});

View File

@ -14,6 +14,7 @@ import {
MAX_COMBOS_DISPLAYED,
MAX_DAMAGE_TYPES_PER_COMBO,
MAX_REPEATS_PER_DAMAGE_TYPE,
VIRTUAL_DAMAGE_COMBOS,
} from "../comp-analyzer-constants";
import type {
DamageCombo,
@ -76,6 +77,24 @@ export function extractDamageSources(
});
}
const virtualCombos = VIRTUAL_DAMAGE_COMBOS[weaponId];
if (virtualCombos) {
for (const combo of virtualCombos) {
const combinedValue = combo.damageTypes.reduce((sum, type) => {
const damage = damages.find((d) => d.type === type);
return sum + (damage?.value ?? 0);
}, 0);
if (combinedValue > 0) {
damages.push({
type: combo.virtualType,
value: combinedValue,
weaponType: "MAIN",
});
}
}
}
return {
weaponSlot: slot,
weaponId,

View File

@ -144,6 +144,7 @@ const damageTypePriorityList = [
"SPECIAL_TICK",
"SECONDARY_MODE_MAX",
"SECONDARY_MODE_MIN",
"COMBO",
] as const;
assertType<
(typeof damageTypePriorityList)[number],

View File

@ -136,6 +136,7 @@
"damage.SPECIAL_TICK": "Løbende",
"damage.TURRET_MIN": "Stationær skydning (Minimum)",
"damage.TURRET_MAX": "Stationær skydning (Maximum)",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "HP",
"suffix.specialPointsShort": "p",

View File

@ -136,6 +136,7 @@
"damage.SPECIAL_TICK": "",
"damage.TURRET_MIN": "",
"damage.TURRET_MAX": "",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",

View File

@ -136,6 +136,7 @@
"damage.SPECIAL_TICK": "Tick",
"damage.TURRET_MIN": "Turret (Minimum)",
"damage.TURRET_MAX": "Turret (Maximum)",
"damage.COMBO": "Combo",
"suffix.seconds": "s",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",

View File

@ -137,6 +137,7 @@
"damage.SPECIAL_TICK": "Por tiempo",
"damage.TURRET_MIN": "Tras rodar (mínimo)",
"damage.TURRET_MAX": "Tras rodar (máximo)",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",

View File

@ -137,6 +137,7 @@
"damage.SPECIAL_TICK": "Por tiempo",
"damage.TURRET_MIN": "Tras rodar (mínimo)",
"damage.TURRET_MAX": "Tras rodar (máximo)",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",

View File

@ -137,6 +137,7 @@
"damage.SPECIAL_TICK": "Dégâts sur la durée",
"damage.TURRET_MIN": "Combiné (Minimum)",
"damage.TURRET_MAX": "Combiné (Maximum)",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "pv",
"suffix.specialPointsShort": "p",

View File

@ -137,6 +137,7 @@
"damage.SPECIAL_TICK": "Dégâts sur la durée",
"damage.TURRET_MIN": "Combiné (Minimum)",
"damage.TURRET_MAX": "Combiné (Maximum)",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "pv",
"suffix.specialPointsShort": "p",

View File

@ -137,6 +137,7 @@
"damage.SPECIAL_TICK": "Tick",
"damage.TURRET_MIN": "נייח (מינימום)",
"damage.TURRET_MAX": "נייח (מקסימום)",
"damage.COMBO": "",
"suffix.seconds": "ש",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",

View File

@ -137,6 +137,7 @@
"damage.SPECIAL_TICK": "Tick",
"damage.TURRET_MIN": "Torretta (Minimo)",
"damage.TURRET_MAX": "Torretta (Massimo)",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "ps",
"suffix.specialPointsShort": "p",

View File

@ -134,6 +134,7 @@
"damage.SPECIAL_TICK": "ティックあたり",
"damage.TURRET_MIN": "スライド (最小)",
"damage.TURRET_MAX": "スライド (最大)",
"damage.COMBO": "",
"suffix.seconds": "秒",
"suffix.hp": "ダメージ",
"suffix.specialPointsShort": "ポイント",

View File

@ -134,6 +134,7 @@
"damage.SPECIAL_TICK": "틱",
"damage.TURRET_MIN": "구르기 (최소)",
"damage.TURRET_MAX": "구르기 (최대)",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",

View File

@ -136,6 +136,7 @@
"damage.SPECIAL_TICK": "",
"damage.TURRET_MIN": "",
"damage.TURRET_MAX": "",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",

View File

@ -138,6 +138,7 @@
"damage.SPECIAL_TICK": "",
"damage.TURRET_MIN": "",
"damage.TURRET_MAX": "",
"damage.COMBO": "",
"suffix.seconds": "s",
"suffix.hp": "hp",
"suffix.specialPointsShort": "pkt",

View File

@ -137,6 +137,7 @@
"damage.SPECIAL_TICK": "Tick",
"damage.TURRET_MIN": "Tiros contínuos (Mínimo)",
"damage.TURRET_MAX": "Tiros contínuos (Máximo)",
"damage.COMBO": "",
"suffix.seconds": " seg",
"suffix.hp": "HP",
"suffix.specialPointsShort": "p",

View File

@ -138,6 +138,7 @@
"damage.SPECIAL_TICK": "Тик",
"damage.TURRET_MIN": "Турель (Минимум)",
"damage.TURRET_MAX": "Турель (Максимум)",
"damage.COMBO": "",
"suffix.seconds": "сек",
"suffix.hp": "hp",
"suffix.specialPointsShort": "очк.",

View File

@ -134,6 +134,7 @@
"damage.SPECIAL_TICK": "雨滴",
"damage.TURRET_MIN": "双枪合一 (最小值)",
"damage.TURRET_MAX": "双枪合一 (最大值)",
"damage.COMBO": "",
"suffix.seconds": "秒",
"suffix.hp": "hp",
"suffix.specialPointsShort": "p",