feedback7 R22-OSIM: 引星棘刺's S1 / S2 alchemy units restore HP as 生命回复速度, so 不屈者 and 禁疗 operators recover too; golden: 12 scenarios move (each fields 引星棘刺)
Community report relayed by the owner: 炼金师分支干员的炼金单元无法给不屈者等禁疗的干员提供生命恢复. The
alchemist branch is 锡人 and 引星棘刺. 锡人 S2's recovery was already an hpRegen buff (PRTS 锡人 备注:
"生命恢复的提供方式为增加目标的“生命回复速度”属性,不受治疗加成和禁疗影响"). 引星棘刺 S1 度算浪波 ("每秒回复相当于
攻击力…的生命值") and S2 解构涌潮 ("友方单位每秒恢复相当于攻击力…的生命") carry the same wording and blackboard key
(hp_recovery_per_sec_ratio[_chr]) but called battle.heal, which 不屈者 (no heal from others) and 禁疗 refuse.
Both are now an hpRegen buff per alchemy unit, set at each 1 s pulse until the next (锡人's pattern): the
same amount per second, not scaled by heal bonuses, credited to the target's own regen; S2 skips 孤立
units like every ally effect. Her PRTS page has no 备注 on this; the classification rests on 锡人 and the report.
Golden: roster-014, roster-015, roster-033, roster-034, bond-maniShip-high, bond-soloShip-high,
boss-boss_2-pair, boss-boss_4-pair, boss-boss_6-pair, hidden-boss_8-pair, hidden-boss_10-pair (引星棘刺 on
the board, S2 by default) and coop2-NORMAL-14-standins (h_0 fields her from round 11): her allies' HP
now rises over each second instead of in one lump, and the heal stats move from her to them.
Tests: test/content/feedback7-thorn2-regen.test.js (2; both fail on the parent); the S1 case of
kits_alt_t5 asserts the regen buff instead of a heal of hers.
This commit is contained in:
1 parent
6d6fbe9277
commit
42caca3be8
10 files changed
+301
-235
No files matched your search
@@ -0,0 +1,55 @@
|
||||
// test/content/feedback7-thorn2-regen.test.js — 引星棘刺's alchemy units restore HP as 生命回复速度 (community report:
|
||||
// 炼金师分支干员的炼金单元无法给不屈者等禁疗的干员提供生命恢复). S1 度算浪波 "每秒回复相当于攻击力…的生命值" and S2 解构涌潮
|
||||
// "友方单位每秒恢复相当于攻击力…的生命" carry 锡人 S2's wording and blackboard key (hp_recovery_per_sec_ratio), and PRTS 锡人
|
||||
// 备注 says that recovery "为增加目标的“生命回复速度”属性,不受治疗加成和禁疗影响" — an hpRegen buff, which 不屈者 (no heal
|
||||
// from others) and 禁疗 units take too. Up to 0.2.1 both were heals (battle.heal), which those units refuse.
|
||||
// Run: node --test test/content/feedback7-thorn2-regen.test.js
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
|
||||
/** 引星棘刺 (skill `skillIndex`) beside 角峰 (10,5), 折桠 — 不屈者 — (10,6) and a 禁疗 角峰 (10,7); her skill cast at once. */
|
||||
function field(skillIndex) {
|
||||
const h = makeBattle({
|
||||
defs: { enemies: { e_dummy: enemyRec({ key: 'e_dummy', hp: 1e9, speed: 0, atk: 0 }) } },
|
||||
timeLimit: 20, autoFinish: false, seed: 2, flags: { dpInit: 99, dpPerSec: 0, dpMax: 999 }, hooks: ['heal'], captureNoisy: true,
|
||||
units: [
|
||||
{ uid: 1, chessId: 'chess_char_5_15_a', row: 10, col: 4, dir: 'RIGHT', skillIndex },
|
||||
{ uid: 2, chessId: 'chess_char_1_02_a', row: 10, col: 5 },
|
||||
{ uid: 3, chessId: 'chess_char_2_17_a', row: 10, col: 6 },
|
||||
{ uid: 4, chessId: 'chess_char_1_02_a', row: 10, col: 7 },
|
||||
],
|
||||
});
|
||||
h.step();
|
||||
const [thorn, plain, unyield, banned] = [1, 2, 3, 4].map((uid) => h.b.allyUnits.find((u) => u.uid === uid));
|
||||
h.b.addBuff(banned, { key: 'test:healFree', status: 'healFree', flags: { noHeal: true, healFree: true } }); // 禁疗
|
||||
plain.hp = plain.s.maxHp * 0.3;
|
||||
unyield.hp = unyield.s.maxHp * 0.2; // S1 picks the lowest HP ratio: its 3×3 covers all three
|
||||
banned.hp = banned.s.maxHp * 0.3;
|
||||
const hp0 = new Map([plain, unyield, banned].map((a) => [a, a.hp]));
|
||||
h.spawn('e_dummy', { pos: [10, 6] });
|
||||
thorn.skill.gainSp(999, 'test');
|
||||
assert.ok(thorn.skill.activate('manual'), thorn.skill.id);
|
||||
h.run(3.2);
|
||||
return { h, thorn, plain, unyield, banned, gained: (a) => a.hp - hp0.get(a) };
|
||||
}
|
||||
|
||||
for (const [skillIndex, id, key] of [[0, 'skchr_thorn2_1', 'hp_recovery_per_sec_ratio'], [1, 'skchr_thorn2_2', 'hp_recovery_per_sec_ratio_chr']]) {
|
||||
test(`引星棘刺 ${id}: the alchemy unit's 生命回复速度 reaches 不屈者 (折桠) and 禁疗 operators as much as the others`, () => {
|
||||
const { h, thorn, plain, unyield, banned, gained } = field(skillIndex);
|
||||
assert.equal(thorn.skill.id, id);
|
||||
assert.equal(unyield.profile.noHeal, true, '不屈者: no heal from others');
|
||||
const perSec = thorn.s.atk * thorn.def.skill.bb[key];
|
||||
for (const a of [plain, unyield, banned]) {
|
||||
const regen = a.buffs.find((b) => b.source === thorn && b.mods?.hpRegen > 0);
|
||||
assert.ok(regen, `${a.defId}: an hpRegen buff`);
|
||||
assert.ok(Math.abs(regen.mods.hpRegen - perSec) < 1e-6, `${regen.mods.hpRegen} ≈ ${perSec}`);
|
||||
assert.ok(gained(a) > perSec, `${a.defId} recovered ${gained(a).toFixed(1)}`);
|
||||
}
|
||||
assert.ok(Math.abs(gained(unyield) - gained(plain)) < 1, 'the same for 不屈者');
|
||||
assert.ok(Math.abs(gained(banned) - gained(plain)) < 1, 'the same under 禁疗');
|
||||
assert.equal(h.hooksOf('heal').filter((c) => c.source === thorn).length, 0, 'no heal of hers (治疗加成 does not apply)');
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
}
|
||||
@@ -1000,7 +1000,7 @@ test('凛御银灰 S3 变革已至: skill range, line attacks bird_atk_scale ×
|
||||
// =================================================================================================================
|
||||
// 引星棘刺
|
||||
|
||||
test('引星棘刺 S1 度算浪波: an alchemy unit on the lowest-HP ally: DEF +def and hp ratio × ATK/s on the 3×3 around it (+3 s 心相)', () => {
|
||||
test('引星棘刺 S1 度算浪波: an alchemy unit on the lowest-HP ally: DEF +def and 生命回复速度 hp ratio × ATK/s on the 3×3 around it (+3 s 心相)', () => {
|
||||
for (const id of pair('15')) {
|
||||
const h = run({
|
||||
defs: { chess: { t_low: ally('t_low', { stats: { def: 100 } }), t_near: ally('t_near', { stats: { def: 100 } }), t_far: ally('t_far', { stats: { def: 100 } }) }, enemies: { enemy_dummy: dummy('enemy_dummy') } },
|
||||
@@ -1019,7 +1019,10 @@ test('引星棘刺 S1 度算浪波: an alchemy unit on the lowest-HP ally: DEF +
|
||||
h.run(1.1);
|
||||
for (const a of [low, near]) assert.equal(a.s.def, 100 + bb.def, a.defId);
|
||||
assert.equal(far.s.def, 100);
|
||||
approx(heals(h, u, (c) => c.target === near)[0].amount, u.s.atk * bb.hp_recovery_per_sec_ratio);
|
||||
// 生命回复速度 (an hpRegen buff per unit, like 锡人 S2), not a heal of hers
|
||||
for (const a of [low, near]) approx(a.buffs.find((b) => b.key === z.key)?.mods.hpRegen, u.s.atk * bb.hp_recovery_per_sec_ratio, a.defId);
|
||||
assert.equal(far.buffs.some((b) => b.key === z.key), false);
|
||||
assert.equal(heals(h, u, () => true).length, 0);
|
||||
mute(h, u);
|
||||
h.run(z.dur);
|
||||
assert.ok(!u.mem.zones.includes(z), 'expired');
|
||||
|
||||
@@ -711,7 +711,7 @@ test('凛御银灰 S2 auto-cast: SKILL_RANGE — an enemy inside the skill range
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------
|
||||
test('引星棘刺 S2: alchemy unit (+3 s with an op in range) — 120 % ATK arts/s, healing ×0.5 on ground enemies, heals allies; T1 ATK +10 %; T2 ASPD ±10 on long roads', () => {
|
||||
test('引星棘刺 S2: alchemy unit (+3 s with an op in range) — 120 % ATK arts/s, healing ×0.5 on ground enemies, allies recover (生命回复速度); T1 ATK +10 %; T2 ASPD ±10 on long roads', () => {
|
||||
const h = makeBattle({
|
||||
defs: { enemies: { enemy_dummy: dummy() }, chess: { t_near: ally('t_near') } },
|
||||
units: [{ chessId: 'chess_char_5_15_a', row: 10, col: 3 }, { chessId: 't_near', row: 11, col: 5 }],
|
||||
@@ -734,7 +734,7 @@ test('引星棘刺 S2: alchemy unit (+3 s with an op in range) — 120 % ATK art
|
||||
const ticks = tagged(h, 'alchemy', e);
|
||||
assert.ok(ticks.length >= 1);
|
||||
approx(ticks[0].amount, u.s.atk * bb.atk_scale, 1e-6);
|
||||
assert.ok(near.hp > 5000, 'allies inside heal');
|
||||
assert.ok(near.hp > 5000, 'allies inside recover');
|
||||
h.run(bb.projectile_delay_time + t0.projectile_extend);
|
||||
assert.equal(u.mem.zones.length, 0, 'expired');
|
||||
clean(h);
|
||||
|
||||
Reference in new issue
Block a user