feedback7 R22-LOADOUT: 圣约送葬人's REA-Y shows the trait's heal as 50 per enemy hit, as the battle heals (GitHub #400); golden: unchanged

#400 by @PCL1234: 干员调配 (and every card that shows the elite with REA-Y — 局内数值, the shop / detail card) read
「每攻击到一个敌人回复自身12生命」; the battle heals 50. Cause: official battle_equip_table uniequip_003_excu2 has a
DISPLAY part (display only) whose blackboard `value` 12 fills its own added line 「攻击范围内存在2名及以上敌人时攻击速度
+{value}」; tools/build-data.mjs applyModuleTraitParts merged that blackboard into the trait's before writing the class
trait 「…回复自身{value}生命…」, so the trait line took 12 (not potential, not the module level: the text template).
The sim already healed 50 (the kit reads traitBase for REA-Y). Now the trait line is written with the blackboards of
the parts that carry trait data (TRAIT / TRAIT_DATA_ONLY / OVERWRITE_BATTLE_DATA) or rewrite the line; a DISPLAY part
that only adds a line fills that line. `trait.bb` (what the kits read) is unchanged, as are tokens' traits.

- Data: chess.json — exactly chess_char_5_01_b REA-Y traitOverride desc / descRaw (12 → 50); backups / tokens and
  every other file identical. data/i18n rebuilt offline (en / ja / ko / zh-TW): only that text and the record hash.
- Docs: DATA.md `trait` row.
- Tests: test/data.test.js (REA-Y's line = the class trait's, its added line +12, REA-X 60; no module line anywhere
  differs from its class trait only in its own added line's numbers — fails on the old data); test/content/
  kits_alt_t5.test.js (the reaper heal 50 with REA-Y / none, 60 with REA-X); test/ui/loadout-rows.test.js (the
  干员调配 module card: 特性 50, 特性追加 +12). GOLDEN_FULL: unchanged.
This commit is contained in:
sganggs committed 2026-10-08 22:26:00 +08:00
1 parent 4d63e9d518
commit 6629710632
10 files changed
+66 -11

No files matched your search

+13
View File
@@ -178,6 +178,19 @@ test('圣约送葬人 module REA-Y (已知悉): ASPD +12 with ≥ 2 enemies in r
}
});
test('圣约送葬人 heals 50 per enemy hit with REA-Y or no module, 60 with REA-X (the REA-Y line\'s 12 is its ASPD, GitHub #400)', () => {
for (const [moduleId, want] of [['uniequip_003_excu2', 50], ['uniequip_002_excu2', 60], ['none', 50]]) {
const h = run({
defs: { enemies: { enemy_dummy: dummy('enemy_dummy') } },
units: [{ chessId: 'chess_char_5_01_b', row: 10, col: 4, moduleId }],
enemies: [{ key: 'enemy_dummy', pos: [10, 5] }],
});
const u = h.unit('chess_char_5_01_b');
assert.equal(u.profile.selfHeal, want, moduleId);
done(h);
}
});
// =================================================================================================================
// 缇缇
+26
View File
@@ -729,6 +729,32 @@ test('official spot checks (hard-coded values from the zh_CN client data)', () =
assert.equal(config.modes.mode_multi_abyss.enemyScale['6'].hp, 2.239488); // 1.2^4 × 1.08
});
test('a module trait part that only adds a display line writes that line only (GitHub #400): 圣约送葬人 REA-Y heals 50 per enemy hit, its line ASPD +12', () => {
// official battle_equip_table uniequip_003_excu2: a DISPLAY part whose blackboard `value` 12 belongs to its own
// 「攻击速度+{value}」; the class trait's 「回复自身{value}生命」 stays the base 50 (REA-X's TRAIT_DATA_ONLY part rewrites it: 60)
const g = chess.chess_char_5_01_b;
const y = g.modules.find((m) => m.uniEquipId === 'uniequip_003_excu2').traitOverride;
assert.equal(y.desc, g.traitBase.desc, 'the class trait as without a module');
assert.match(y.desc, /每攻击到一个敌人回复自身50生命/);
assert.match(y.descRaw, /回复自身<@ba\.kw>50<\/>生命/);
assert.equal(y.moduleDesc, '攻击范围内存在2名及以上敌人时攻击速度+12');
assert.match(g.modules.find((m) => m.uniEquipId === 'uniequip_002_excu2').traitOverride.desc, /回复自身60生命/, 'REA-X');
// no other module record carries a trait line that differs from its owner's class trait only in a number its own
// display line uses (the class of this bug)
const forms = [...Object.values(chess), ...Object.values(D.backups.units || {}).flatMap((u) => Object.values(u.forms || {}))];
for (const f of forms) {
for (const m of f.modules || []) {
const o = m.traitOverride;
if (!o?.moduleDesc || !f.traitBase?.desc || o.desc === f.traitBase.desc) continue;
const nums = (t) => String(t).match(/\d+(?:\.\d+)?/g) || [];
const sameWords = o.desc.replace(/\d+(?:\.\d+)?/g, '#') === f.traitBase.desc.replace(/\d+(?:\.\d+)?/g, '#');
if (!sameWords) continue;
const moved = nums(o.desc).filter((n, i) => n !== nums(f.traitBase.desc)[i]);
assert.ok(!moved.every((n) => nums(o.moduleDesc).includes(n)), `${f.chessId || f.charId} ${m.uniEquipId}: ${o.desc} / ${o.moduleDesc}`);
}
}
});
/** Read an official cache file (only called when HAS_CACHE). */
const raw = (rel) => JSON.parse(readFileSync(join(CACHE, rel), 'utf8'));
+6
View File
@@ -157,6 +157,12 @@ test('the module card reads its talent changes at the operator\'s 潜能, as 局
assert.ok(p1.some((x) => x.includes('生命上限1.5%的生命')), '潜能1');
assert.ok(!p1.some((x) => x.includes('(+0.5%)')));
assert.deepEqual(texts(ModuleInfo({ m: null, golden: g, opt: { id: 'none', rec: null }, potential: 1 })), [g.traitBase.descRaw], '不装备: the base 特性');
// 圣约送葬人 REA-Y (GitHub #400): 特性 heals 50 per enemy hit as in battle; 特性追加 is the module's ASPD line
const ex = get('chess_char_5_01_b');
const rea = ex.modules.find((x) => x.uniEquipId === 'uniequip_003_excu2');
const lines = texts(ModuleInfo({ m: null, golden: ex, opt: { id: rea.uniEquipId, rec: rea }, potential: 6 }));
assert.ok(lines.some((x) => x.includes('每攻击到一个敌人回复自身<@ba.kw>50</>生命')), lines.join(' | '));
assert.ok(lines.some((x) => x === '攻击范围内存在2名及以上敌人时攻击速度<@ba.kw>+12</>'));
// the detail hands the operator's 潜能 to it
const src = readFileSync(path.join(ROOT, 'public/js/screens/loadout.js'), 'utf8');
assert.match(src, /<\$\{ModuleInfo\} m=\$\{m\} golden=\$\{golden\} opt=\$\{modOpt\} potential=\$\{opsOf\(ops, chess\.charId\)\.potential\} \/>/);