Official: S2 「该角色受到攻击时刺玫对目标造成攻击力20%的法术伤害并仅对该角色触发刺玫特性」 (en "...and activates her Trait on the ally"); the client buff vendla_tr heals through the S2TraitHealRange selector (the vendla_s_2 holder) only for a damage whose modifier carries vendla_s_2 (the counter, vendla_s_2[extra_damage]), else through TraitHealRange (_excludeOwner 0). Cause: kits/ops/chess_char_1_06-vendla.js sent EVERY trait heal to the protégé while the skill ran (usually at full HP), so a 源石溶剂 carrier healed nobody for 15 s. Golden: roster-001 / roster-002 (刺玫 S2 in band 1), bond-deputShip-low / -high, bond-suntShip-high, bond-victoriaShip-low / -high (刺玫 on those teams), matches solo-FUNNY-2, coop2-NORMAL-3, coop3-ABYSS-5, coop4-FUNNY-6, coop4-ABYSS-7 (bots field 刺玫: her heals land on injured allies during S2, fights change). Tests: test/content/feedback5-vendla-heal.test.js (2, fail on the parent); kits_t1t2's 刺玫 assertion message reworded.
59 lines
4.0 KiB
JavaScript
59 lines
4.0 KiB
JavaScript
// test/content/feedback5-vendla-heal.test.js — 刺玫 S2 荆藤庇荫 and her 咒愈师 trait (community report of 2026-10-06
|
|
// 「干员刺玫开技能(装备源石药剂)之后不能治疗」): the protégé takes only the trait heal of the S2 counter ("该角色受到攻击时刺玫
|
|
// 对目标造成攻击力20%的法术伤害并仅对该角色触发刺玫特性"; en "…and activates her Trait on the ally"; client buff vendla_tr: the
|
|
// S2TraitHealRange selector only for a damage whose modifier carries vendla_s_2). Her own attacks keep healing the lowest-HP
|
|
// ally in range — herself too (TraitHealRange: _excludeOwner 0). Until 0.2.0 every damage she dealt during the skill healed
|
|
// the protégé, so with 源石溶剂 draining her she healed nobody while it ran.
|
|
// Run: node --test test/content/feedback5-vendla-heal.test.js
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { makeBattle, enemyRec, chessRec, checkInvariants } from '../helpers/battleHarness.js';
|
|
|
|
const VENDLA = 'chess_char_1_06_a', SOLVENT = 'chess_item_1_05_e_a';
|
|
const tank = chessRec({ id: 't_tank', stats: { maxHp: 9000, atk: 0, def: 0 }, skill: null });
|
|
const still = (r, c) => ({ motion: 'WALK', start: [r, c], end: [r, c], checkpoints: [] });
|
|
|
|
/** 刺玫 (+ 源石溶剂) on (10, 4), a high-max-HP ally on (11, 4) — her protégé —, one dummy enemy on (10, 6) hitting `hits`. */
|
|
function field({ enemyAtk = 0, hits = null } = {}) {
|
|
const h = makeBattle({
|
|
defs: { chess: { t_tank: tank }, enemies: { e_dummy: enemyRec({ key: 'e_dummy', hp: 1e8, atk: enemyAtk, speed: 0, range: enemyAtk ? 5 : 0, bat: 1 }) } },
|
|
units: [{ chessId: VENDLA, row: 10, col: 4, items: [SOLVENT] }, { chessId: 't_tank', row: 11, col: 4 }],
|
|
enemies: [{ key: 'e_dummy', time: 0, pos: [10, 6], route: still(10, 6) }],
|
|
autoFinish: false, timeLimit: 60, seed: 3, captureNoisy: true, hooks: ['heal', 'damaged', 'skillStart'],
|
|
setup: hits ? (b) => b.on('beforeAttack', (ctx) => { if (ctx.attacker.side === 'enemy') ctx.targets = [b.allyUnits.find((u) => u.defId === hits)]; }, { priority: 50 }) : null,
|
|
});
|
|
const v = h.unit(VENDLA), tk = h.unit('t_tank');
|
|
h.run(8); // the 源石溶剂 drain has her below full HP
|
|
const sk = v.skill;
|
|
sk.sp = sk.spCost; sk.charges = Math.max(1, sk.charges);
|
|
assert.ok(sk.activate('manual'), 'S2 starts');
|
|
assert.equal(v.mem.protege, tk, 'the protégé is the highest-max-HP ally in range');
|
|
return { h, v, tk, t0: h.b.time };
|
|
}
|
|
|
|
test('during 荆藤庇荫 her attacks heal the most injured ally in range (herself, drained by 源石溶剂), not the full-HP protégé', () => {
|
|
const { h, v, tk, t0 } = field();
|
|
h.run(10);
|
|
assert.ok(v.skill.active, 'still running');
|
|
const heals = h.hooksOf('heal').filter((c) => c.source === v && c.t >= t0 && c.amount > 0);
|
|
assert.ok(heals.length >= 4, `she keeps healing: ${heals.length} heals in 10 s`);
|
|
assert.ok(heals.every((c) => c.target === v), 'every attack heal goes to her — the only injured ally — never to the protégé');
|
|
assert.equal(tk.hp, tk.s.maxHp);
|
|
checkInvariants(h.b);
|
|
assert.equal(h.b.errors.length, 0);
|
|
});
|
|
|
|
test('the S2 counter (a hit on the protégé) heals the protégé; her attacks still heal the lowest-HP ally', () => {
|
|
const { h, v, tk, t0 } = field({ enemyAtk: 300, hits: 't_tank' });
|
|
h.run(8);
|
|
const counters = h.hooksOf('damaged').filter((c) => c.source === v && c.t >= t0 && (c.dmg?.tags || []).includes('counter'));
|
|
assert.ok(counters.length >= 3, 'the enemy hitting the protégé draws counters');
|
|
const heals = h.hooksOf('heal').filter((c) => c.source === v && c.t >= t0);
|
|
const toTank = heals.filter((c) => c.target === tk), toHer = heals.filter((c) => c.target === v);
|
|
assert.ok(toTank.length >= counters.length, `each counter heals the protégé (${toTank.length} heals for ${counters.length} counters)`);
|
|
assert.ok(toHer.length > 0, 'her attacks heal the most injured ally — herself when she is lower than the protégé');
|
|
checkInvariants(h.b);
|
|
assert.equal(h.b.errors.length, 0);
|
|
});
|