Files
ark-stronghold-protocol/test/feedback5-operator-sfx.test.js
sganggs fe2a0cbc29 feedback5 CB3: 银灰's normal attack no longer plays his S3 swing (and S2 / S3 impact); 雷蛇, 初雪 and 圣约送葬人 lose a skill mode's impact the same way (community report of 2026-10-06, item 54)
Official (excel/audio_data.json, local client charpack char_172_svrash): his normal attack is the Default mode's Combat —
ON_ABILITY_HIT.char_172_svrash.combat p_imp_spear_n, no swing bank; .attack.2 (p_atk_silver_n swing, p_imp_sword_n impact) is
his S3 mode, .attack.1 his S2 mode. Cause: tools/assets/audio.mjs pickUnitSfx took the numbered variants because their files
end in _n (its skill-mode test is by file name). Now an operator whose default mode is the unsuffixed ability (a plain
attack / combat bank of any event) never takes a numbered variant; operators that number their default mode (attack.0 …)
keep the earlier order. Over the 209 operators of the manifest it changes 4: 银灰 (charpack modes Default / S2 / S3), 雷蛇
(attack.1 = S2, lightning impact), 初雪 (attack.1 = her S1-2 mode) and 圣约送葬人 (attack.4 = S3_End).

data/assets.json regenerated offline (fetch-assets --offline --add-only --allow-shrink; JSON compare: exactly the 4 operators'
entries, hash and stats): 雷蛇 / 初雪 / 圣约送葬人 drop their wrong `hit`; 银灰's entry goes too, because his official impact
p_imp_spear_n.mp3 was never downloaded on this machine — one online `node tools/fetch-assets.mjs --add-only` adds it (needs
the owner's OK for that download). Golden: unchanged (the manifest is client-side). Tests: test/feedback5-operator-sfx.test.js
(3; 2 fail on the parent). Docs: docs/ASSETS.md.
2026-10-07 02:38:07 +08:00

45 lines
3.5 KiB
JavaScript

// test/feedback5-operator-sfx.test.js — community report of 2026-10-06 (item 54) 「干员银灰的普通攻击的音效错误的使用了3技能期间
// 的攻击音效」. The official banks of 银灰 (excel/audio_data.json): ON_ABILITY_HIT.char_172_svrash.combat (p_imp_spear_n — his
// Default mode's Combat, the normal attack, which has no swing bank), ON_ABILITY_START / HIT .attack.2 (p_atk_silver_n /
// p_imp_sword_n — his S3 mode; the charpack's modes are Default, S2, S3) and HIT .attack.1 (p_imp_sword_n — S2). The picker
// took the numbered variants because their files end in _n. Now an operator whose default mode is the unsuffixed ability (a
// plain attack / combat bank of any event) never takes a numbered variant (tools/assets/audio.mjs pickUnitSfx). The same
// rule drops 雷蛇's S2 lightning impact, 初雪's S1-2 mode impact and 圣约送葬人's S3_End impact from their normal attacks.
// Run: node --test test/feedback5-operator-sfx.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { pickUnitSfx } from '../tools/assets/audio.mjs';
const P = (f) => [`player/${f.startsWith('p_imp') ? 'p_imp' : 'p_atk'}/${f}.mp3`];
const op = (entries, projectile = {}) => pickUnitSfx(new Map(entries.map(([k, f]) => [k, P(f)])), { operator: true, projectile });
test('银灰: the normal attack is the Default mode\'s Combat — spear impact, no swing; the S3 swing and the S2 / S3 impact never', () => {
const sfx = op([
['ON_ABILITY_HIT.combat', 'p_imp_spear_n'],
['ON_ABILITY_START.attack.2', 'p_atk_silver_n'],
['ON_ABILITY_HIT.attack.2', 'p_imp_sword_n'],
['ON_ABILITY_HIT.attack.1', 'p_imp_sword_n'],
]);
assert.deepEqual(sfx, { hit: P('p_imp_spear_n') });
});
test('雷蛇 / 初雪 / 圣约送葬人: a plain swing, and no skill mode\'s impact on the normal attack', () => {
assert.deepEqual(op([['ON_ABILITY_ON.attack', 'p_atk_pistol_n'], ['ON_ABILITY_HIT.attack.1', 'p_imp_lightning_n'], ['ON_ABILITY_START.attack.1', 'p_atk_lightning_n']]),
{ attack: P('p_atk_pistol_n') }, '雷蛇 (attack.1 = S2)');
assert.deepEqual(op([['ON_ABILITY_ON.attack', 'p_atk_shakebell_n'], ['ON_ABILITY_HIT.attack.1', 'p_imp_shakebell_n']]),
{ attack: P('p_atk_shakebell_n') }, '初雪 (attack.1 = the S1-2 mode)');
assert.deepEqual(op([['ON_ABILITY_ON.attack', 'p_atk_shotgunx_n'], ['ON_ABILITY_ON.attack.1', 'p_atk_shotgunx_d'], ['ON_ABILITY_START.attack.4', 'p_atk_bngel'], ['ON_ABILITY_HIT.attack.4', 'p_imp_bngel']]),
{ attack: P('p_atk_shotgunx_n') }, '圣约送葬人 (attack.4 = S3_End)');
// the operator's own projectile bank still serves when the plain ability has no impact bank
assert.deepEqual(op([['ON_ABILITY_ON.attack', 'p_atk_x_n'], ['ON_ABILITY_HIT.attack.1', 'p_imp_x_s']], { hit: P('p_imp_x_n') }), { attack: P('p_atk_x_n'), hit: P('p_imp_x_n') });
});
test('operators that number their default mode (attack.0 …) keep the earlier rule; a plain bank that is a skill-mode file does not count', () => {
assert.deepEqual(op([['ON_ABILITY_START.attack.0', 'p_atk_y_n'], ['ON_ABILITY_HIT.attack.0', 'p_imp_y_n'], ['ON_ABILITY_START.attack.1', 'p_atk_y_n2']]),
{ attack: P('p_atk_y_n'), hit: P('p_imp_y_n') });
// 荒芜拉普兰德: the unsuffixed START bank is her _d file (a skill mode), her default is attack.0
assert.deepEqual(op([['ON_ABILITY_START.attack.0', 'p_atk_whtolfdrksl_n'], ['ON_ABILITY_START.attack', 'p_atk_whtolfdrksl_d'], ['ON_ABILITY_ON.attack.2', 'p_atk_whtolfdrksl_h']]),
{ attack: P('p_atk_whtolfdrksl_n') });
});