From ca5831c928fe1983d37da82e3b1c3cba8add8666 Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Tue, 20 Oct 2026 00:40:04 +0700 Subject: [PATCH] fix: show memosprite contribution and Other damage category --- js/app.js | 22 ++++++++++++++++++---- tests/damage-order.test.cjs | 11 +++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/js/app.js b/js/app.js index 1169cb6..6dece6e 100644 --- a/js/app.js +++ b/js/app.js @@ -8391,6 +8391,18 @@ function orderBattleReportAvatars(avatars) { return avatars; } +function damageBreakdownForAvatar(avatar) { + const damage = avatar.attack_type_damage || {}; + const normal = Number(damage.Normal) || 0; + const skill = Number(damage.BPSkill ?? damage.Skill) || 0; + const ultimate = Number(damage.Ultra ?? damage.Ultimate) || 0; + const breakDamage = Number(avatar.total_break_damage || damage.BreakDamage) || 0; + // Aggregate totals can include damage without an attack-type entry. Keep it + // visible without counting named subcategories a second time. + const other = Math.max(0, (Number(avatar.total_damage) || 0) - normal - skill - ultimate - breakDamage); + return { normal, skill, ultimate, breakDamage, other }; +} + function renderDamageAnalyst() { const report = analystState.currentReport; if (!report) { @@ -8506,10 +8518,7 @@ function renderDamageAnalyst() { } } - const dNormal = a.attack_type_damage?.Normal || 0; - const dSkill = a.attack_type_damage?.BPSkill || a.attack_type_damage?.Skill || 0; - const dUlt = a.attack_type_damage?.Ultra || a.attack_type_damage?.Ultimate || 0; - const dBreak = a.total_break_damage || a.attack_type_damage?.BreakDamage || 0; + const { normal: dNormal, skill: dSkill, ultimate: dUlt, breakDamage: dBreak, other: dOther } = damageBreakdownForAvatar(a); const dFollowup = (a.attack_type_damage?.Insert || 0) + (a.attack_type_damage?.Pursued || 0); const dDot = a.attack_type_damage?.DOT || 0; @@ -8550,6 +8559,7 @@ function renderDamageAnalyst() {
${t('analystTotalDamage')}
${hasAvatarDamage ? formatNumber(a.total_damage) : '—'}
+ ${Number(a.memosprite_damage) > 0 ? `
Memosprite: ${formatNumber(a.memosprite_damage)} (included)
` : ''}
${t('analystMaxHit')}
@@ -8581,6 +8591,10 @@ function renderDamageAnalyst() { ${t('analystDmgBreak')} ${hasDamageBreakdown ? formatNumber(dBreak) : '—'}
+
+ Other + ${hasAvatarDamage ? formatNumber(dOther) : '—'} +
${dFollowup > 0 ? `
${t('analystDmgFollowup')} diff --git a/tests/damage-order.test.cjs b/tests/damage-order.test.cjs index 338865b..b5e5691 100644 --- a/tests/damage-order.test.cjs +++ b/tests/damage-order.test.cjs @@ -21,3 +21,14 @@ test('historical reports keep recorded order when battle slots are absent', () = const result = context.orderBattleReportAvatars([{ avatar_id: 10 }, { avatar_id: 30 }, { avatar_id: 20 }]); assert.deepEqual(Array.from(result, a => a.avatar_id), [10, 30, 20]); }); + +test('Other includes unclassified and missing category damage exactly once', () => { + const result = context.damageBreakdownForAvatar({ total_damage: 1000, total_break_damage: 100, + attack_type_damage: { Normal: 100, BPSkill: 200, Ultra: 100, Insert: 50, TrueDamage: 150 } }); + assert.equal(result.other, 500); + assert.equal(result.normal + result.skill + result.ultimate + result.breakDamage + result.other, 1000); +}); + +test('Other never becomes negative when the native breakdown overlaps', () => { + assert.equal(context.damageBreakdownForAvatar({total_damage: 10, attack_type_damage: {Normal: 20}}).other, 0); +});