feedback3 WB: 琳琅诗怀雅 S1 heals an injured ally beside her without needing an attack or an enemy, at most once per attack cycle (owner's decision 2026-10-04, community report #5)
This commit is contained in:
1 parent
d1a2696e44
commit
7d3d7318d5
2 files changed
+68
-6
No files matched your search
@@ -399,8 +399,9 @@ const KITS = {
|
|||||||
|
|
||||||
// ---- 3_04 琳琅诗怀雅 · 行商 — S2 “见面礼” (passive): each attack spends a coin to drop a champagne bomb in range;
|
// ---- 3_04 琳琅诗怀雅 · 行商 — S2 “见面礼” (passive): each attack spends a coin to drop a champagne bomb in range;
|
||||||
// 大买家: coin at skill start + coin & ATK stack per trait payment; 破财消灾: DP-paid revive (cost doubles)
|
// 大买家: coin at skill start + coin & ATK stack per trait payment; 破财消灾: DP-paid revive (cost doubles)
|
||||||
// S1 仗义疏财 (passive, 2 coins): an attack spends a coin to heal the most injured ally (< 70 % HP) of the 8
|
// S1 仗义疏财 (passive, 2 coins): a coin heals the most injured ally (< 70 % HP) of the 8 surrounding tiles for
|
||||||
// surrounding tiles for attack@heal_scale × ATK. S3 千金一掷 (持续时间无限): attacks hit twice, kills give a coin;
|
// attack@heal_scale × ATK — on her attack, or with no enemy to attack on her own attack timer (owner's decision
|
||||||
|
// 2026-10-04, against the official 「下一次攻击会为…」; installS1). S3 千金一掷 (持续时间无限): attacks hit twice, kills give a coin;
|
||||||
// closing it spends every coin on random ground enemies of range 2-4 in front and those she blocks (atk_scale phys +
|
// closing it spends every coin on random ground enemies of range 2-4 in front and those she blocks (atk_scale phys +
|
||||||
// a small push, radial despite the text's 向前 — PRTS 备注 "推开效果为径向推动"; client charpack char_1033_swire2:
|
// a small push, radial despite the text's 向前 — PRTS 备注 "推开效果为径向推动"; client charpack char_1033_swire2:
|
||||||
// the RandomGold ability (Skill_3_End) carries swire2_s_3[knockback] of template knockback[relative]; 地面敌方单位,
|
// the RandomGold ability (Skill_3_End) carries swire2_s_3[knockback] of template knockback[relative]; 地面敌方单位,
|
||||||
@@ -448,10 +449,19 @@ const KITS = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const healRatio = textNum(d.skill?.description, /血量不足(\d+)%/, 70) / 100;
|
const healRatio = textNum(d.skill?.description, /血量不足(\d+)%/, 70) / 100;
|
||||||
const installS1 = (battle, unit) => { // 仗义疏财
|
/**
|
||||||
|
* 仗义疏财 — official text 「消耗一枚金币,下一次攻击会为周围八格内血量不足70%的一名友方单位恢复相当于攻击力40%的生命」: the heal rides on an
|
||||||
|
* attack, so with no enemy around she never healed (community report #5 「琳琅诗怀雅1技能不会主动奶身边受伤的干员」). Owner's
|
||||||
|
* decision 2026-10-04 (a deliberate deviation, like §21.29's 重装 casts): she heals an injured ally beside her whether
|
||||||
|
* she attacks or not. Same target (the lowest HP ratio below 70 % of the 8 surrounding tiles, no 禁疗 / 孤立 unit, no
|
||||||
|
* device), coin and heal_scale × ATK. Cadence [ASSUMED]: at most one heal per attack cycle (her attack interval, ASPD
|
||||||
|
* included) — while she attacks, on the attack as before; when her last attack attempt found no target, on her own
|
||||||
|
* timer (the 'tick' hook runs after the attacks, so an attack due in the same tick takes it).
|
||||||
|
*/
|
||||||
|
const installS1 = (battle, unit) => {
|
||||||
const hs = num(bb['attack@heal_scale'], num(bb.heal_scale, 0));
|
const hs = num(bb['attack@heal_scale'], num(bb.heal_scale, 0));
|
||||||
battle.on('attack', (ctx) => {
|
let nextAt = -Infinity;
|
||||||
if (ctx.attacker !== unit || !alive(unit) || (unit.mem.coins ?? 0) < coinCost || !(hs > 0)) return;
|
const healTarget = () => {
|
||||||
let best = null;
|
let best = null;
|
||||||
for (const a of battle.allyUnits) {
|
for (const a of battle.allyUnits) {
|
||||||
if (a === unit || !alive(a) || a.hidden || a.kind === 'device' || a.hpRatio >= healRatio) continue;
|
if (a === unit || !alive(a) || a.hidden || a.kind === 'device' || a.hpRatio >= healRatio) continue;
|
||||||
@@ -459,11 +469,19 @@ const KITS = {
|
|||||||
if (Math.max(Math.abs(a.tileR - unit.tileR), Math.abs(a.tileC - unit.tileC)) !== 1) continue; // 周围八格
|
if (Math.max(Math.abs(a.tileR - unit.tileR), Math.abs(a.tileC - unit.tileC)) !== 1) continue; // 周围八格
|
||||||
if (!best || a.hpRatio < best.hpRatio || (a.hpRatio === best.hpRatio && a.deploySeq < best.deploySeq)) best = a;
|
if (!best || a.hpRatio < best.hpRatio || (a.hpRatio === best.hpRatio && a.deploySeq < best.deploySeq)) best = a;
|
||||||
}
|
}
|
||||||
|
return best;
|
||||||
|
};
|
||||||
|
const tryHeal = () => {
|
||||||
|
if (!alive(unit) || (unit.mem.coins ?? 0) < coinCost || !(hs > 0) || battle.time < nextAt - 1e-9) return;
|
||||||
|
const best = healTarget();
|
||||||
if (!best) return;
|
if (!best) return;
|
||||||
unit.mem.coins -= coinCost;
|
unit.mem.coins -= coinCost;
|
||||||
|
nextAt = battle.time + unit.s.interval;
|
||||||
battle.heal(unit, best, unit.s.atk * hs);
|
battle.heal(unit, best, unit.s.atk * hs);
|
||||||
fx(battle, 'coin', unit, { n: unit.mem.coins, heal: best.id, skill: 'swire2_1' });
|
fx(battle, 'coin', unit, { n: unit.mem.coins, heal: best.id, skill: 'swire2_1' });
|
||||||
}, { owner: unit, priority: -10 });
|
};
|
||||||
|
battle.on('attack', (ctx) => { if (ctx.attacker === unit) tryHeal(); }, { owner: unit, priority: -10 });
|
||||||
|
battle.on('tick', () => { if (unit.deployed && unit.canAct && !unit.trait?.hadTarget) tryHeal(); }, { owner: unit });
|
||||||
};
|
};
|
||||||
const installS3 = (battle, unit) => { // 千金一掷: "击倒敌人时获得一枚金币"
|
const installS3 = (battle, unit) => { // 千金一掷: "击倒敌人时获得一枚金币"
|
||||||
battle.on('kill', (ctx) => {
|
battle.on('kill', (ctx) => {
|
||||||
|
|||||||
@@ -226,6 +226,50 @@ test('3_04 琳琅诗怀雅 S1 仗义疏财: 2 coins; an attack spends one to hea
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Owner's decision 2026-10-04 (community report #5 「琳琅诗怀雅1技能不会主动奶身边受伤的干员」), a deliberate deviation from the official
|
||||||
|
// 「下一次攻击会为…」: S1 heals an injured ally beside her without an attack or an enemy — at most once per attack cycle
|
||||||
|
// (her attack interval, ASPD included) [ASSUMED cadence]; with an enemy the heal stays on her attack.
|
||||||
|
test('3_04 琳琅诗怀雅 S1 仗义疏财 (owner\'s decision): an injured ally beside her is healed with no enemy on the field, once per attack cycle; with an enemy, once per attack', () => {
|
||||||
|
const id = 'chess_char_3_04_a';
|
||||||
|
const b = SB(id, 'skchr_swire2_1');
|
||||||
|
const mk = (enemies = []) => makeBattle({ defs: { enemies: { enemy_d: dummy('enemy_d') } }, timeLimit: 60, autoFinish: false, hooks: ['heal', 'attack'], captureNoisy: true,
|
||||||
|
units: [U(id, 'skchr_swire2_1', 9, 5), { chessId: 'chess_char_3_16_a', row: 10, col: 5 }], enemies });
|
||||||
|
// no enemy: an ally at 80 % is left alone, one at 50 % is healed and a coin is spent
|
||||||
|
let h = mk();
|
||||||
|
let u = h.unit(id), ally = h.unit('chess_char_3_16_a');
|
||||||
|
h.step();
|
||||||
|
u.mem.coins = 2;
|
||||||
|
ally.hp = ally.s.maxHp * 0.8;
|
||||||
|
h.run(3);
|
||||||
|
assert.equal(h.hooksOf('heal').filter((c) => c.source === u).length, 0, '80 %: not below 70 %');
|
||||||
|
assert.equal(u.mem.coins, 2, 'the coin is kept');
|
||||||
|
ally.hp = ally.s.maxHp * 0.5;
|
||||||
|
h.step();
|
||||||
|
const heals = h.hooksOf('heal').filter((c) => c.source === u);
|
||||||
|
assert.equal(heals.length, 1, 'healed at once, no enemy needed');
|
||||||
|
assert.equal(heals[0].target, ally);
|
||||||
|
approx(heals[0].amount, u.s.atk * b['attack@heal_scale']);
|
||||||
|
assert.equal(u.mem.coins, 1, 'one coin spent');
|
||||||
|
assert.equal(h.hooksOf('attack').filter((c) => c.attacker === u).length, 0, 'without an attack');
|
||||||
|
// kept injured with coins to spare: one heal per attack interval, never more
|
||||||
|
const n0 = h.hooksOf('heal').length, t0 = h.b.time;
|
||||||
|
for (let i = 0; i < 300; i++) { ally.hp = ally.s.maxHp * 0.3; u.mem.coins = 2; h.step(); }
|
||||||
|
const per = h.hooksOf('heal').slice(n0).filter((c) => c.source === u).length;
|
||||||
|
assert.equal(per, Math.floor((h.b.time - t0) / u.s.interval + 1e-6), `one heal per ${u.s.interval} s`);
|
||||||
|
done(h);
|
||||||
|
// with an enemy in her range: the heal rides on each attack, never a second one in the cycle
|
||||||
|
h = mk([{ key: 'enemy_d', pos: [9, 6] }]);
|
||||||
|
u = h.unit(id); ally = h.unit('chess_char_3_16_a');
|
||||||
|
h.step();
|
||||||
|
const a0 = h.hooksOf('attack').length, h0 = h.hooksOf('heal').length; // (its first attack, at deployment, found the ally unhurt)
|
||||||
|
for (let i = 0; i < 300; i++) { ally.hp = ally.s.maxHp * 0.3; u.mem.coins = 2; h.step(); }
|
||||||
|
const atk = h.hooksOf('attack').slice(a0).filter((c) => c.attacker === u).length;
|
||||||
|
const hl = h.hooksOf('heal').slice(h0).filter((c) => c.source === u).length;
|
||||||
|
assert.ok(atk >= 5, `attacks (${atk})`);
|
||||||
|
assert.equal(hl, atk, 'one heal per attack');
|
||||||
|
done(h);
|
||||||
|
});
|
||||||
|
|
||||||
test('3_04 琳琅诗怀雅 S3 千金一掷: 二连击, kills pay a coin, a full purse is cashed out on the front range (hits + push)', () => {
|
test('3_04 琳琅诗怀雅 S3 千金一掷: 二连击, kills pay a coin, a full purse is cashed out on the front range (hits + push)', () => {
|
||||||
for (const id of BOTH('chess_char_3_04_a')) {
|
for (const id of BOTH('chess_char_3_04_a')) {
|
||||||
const b = SB(id, 'skchr_swire2_3'), t0 = TB(id, 0);
|
const b = SB(id, 'skchr_swire2_3'), t0 = TB(id, 0);
|
||||||
|
|||||||
Reference in new issue
Block a user