feedback5 WL: 生命回复速度 is not 治疗 — the 吟游者 trait, 调香师, 瑕光 S2, 铃兰 S3 and 锡人 give an hpRegen buff, so 无法被友方治疗 / 禁疗 units get it and 治疗加成 does not scale it (GitHub #96 / #137)
PRTS: 分支特性信息 吟游者 「特性为基于自身攻击力来增加受益者的“生命回复速度”属性」 (魔王, 浊心斯卡蒂); the 备注 of 调香师 熏衣草, 瑕光 S2, 铃兰 S3 and 锡人 S2 「生命恢复的提供方式为增加目标的“生命回复速度”属性…不受治疗加成和禁疗影响」 (铃兰: none in the first second, refreshed every second); 异常效果 禁疗 「增减生命回复速度…的效果不会被识别为治疗类能力」. These were heals from the provider, which damage.js refused on 收割者 / 不屈者 / 武者 (noHeal) and under 禁疗 — 隐德来希 gained nothing under 魔王. This is the separation proposed in PR #135 by @SpiritedAwayCN, written anew (the PR's patch is not used): - professions.js bardRegen: the trait is an hpRegen buff per bard refreshed every 0.25 s; 魔王's 微尘 ×1.5 goes through its `bardRegen` hook (both 魔王 kits); 浊心斯卡蒂's pulse and the 海嗣 token use it too. - 调香师 (every ally), 瑕光 S2 (the skill range, removed at its end), 铃兰 S3 (from the first full second, removed at its end), 锡人 (per 炼金单元, its cached ATK): hpRegen buffs instead of battle.heal. - damage.js: a `regen` tick takes no healing multiplier and a heal hook cannot change its amount (no 治疗加成 on any 生命回复速度); 流明's 凡人之愿 and 华法琳's S1 bonus skip a regen tick of their own. Direct heals are unchanged (still refused / scaled); 魔王 S3's redistribution and 纯烬's HoT are untouched. Other per-second recoveries without such a note stay heals (引星棘刺, 塞雷娅 S3, 流明 / 纯烬 HoTs; 余 闲云隐市 and 天马之盔 left open). Golden moved (every one fields 魔王 / 浊心斯卡蒂 / 调香师 / 瑕光 S2 / 铃兰 / 锡人): roster-004, roster-005, roster-006, roster-011, roster-013, roster-015, roster-016, roster-018, roster-023, roster-025, roster-030, roster-032, roster-035, roster-037, roster-039, roster-041, roster-042, roster-045, roster-046; bond-deputShip-high, bond-emptyShip-low, bond-emptyShip-high, bond-indomShip-high, bond-kazimierzShip-low, bond-kazimierzShip-high, bond-sargonShip-low, bond-sargonShip-high, bond-skillfulShip-high; boss-boss_1-pair, boss-boss_1-solo, boss-boss_2-solo, boss-boss_3-pair, boss-boss_3-solo, boss-boss_4-solo, boss-boss_5-pair, boss-boss_5-solo, boss-boss_6-solo, boss-boss_7-pair, boss-boss_7-solo, hidden-boss_8-solo, hidden-boss_9-pair, hidden-boss_9-solo, hidden-boss_10-solo, unite-1; solo-NORMAL-1, solo-HARD-1, solo-ABYSS-1, coop4-ABYSS-7, coop2-NORMAL-8-serverrun, coop2-NORMAL-14-standins. Docs: SIM.md (heal pipeline, bard row), PLAYING.md, kits/README.md (checklist item 11).
This commit is contained in:
1 parent
696085ac8e
commit
5c6524e1ad
27 files changed
+943
-721
No files matched your search
@@ -236,6 +236,11 @@ Each item is a mistake this project already made once. Tick every one for every
|
||||
`enemies_bosses.test.js` "沉默: exactly the abilities whose handbook line is SILENCE-flagged can be silenced".
|
||||
- [ ] **10. Tests** — at least one per skill and per talent, plus the trigger rule, the range toggle, anti-air and
|
||||
every module; the golden files updated in the same pull request (an intended change).
|
||||
- [ ] **11. 治疗 or 生命回复速度** — an effect PRTS describes as raising the target's 「生命回复速度」 (a 备注 「增加目标的
|
||||
“生命回复速度”属性,不受治疗加成和禁疗影响」; 分支特性信息 吟游者) is an hpRegen / hpRegenRatio buff on the target, never
|
||||
`battle.heal`: 禁疗 and 无法被友方治疗 (`noHeal`) do not stop it and no 治疗加成 scales it (damage.js heal `regen`).
|
||||
Examples: 安洁莉娜 兼职工作, the 吟游者 trait (professions.js `bardRegen`), 调香师, 瑕光 S2, 铃兰 S3, 锡人;
|
||||
`test/content/feedback5-regen.test.js` (GitHub #96 / #137). A per-second 「恢复…生命」 without such a note stays a heal.
|
||||
|
||||
## Old references
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ import { num, talentBb, moduleBb, enemiesInGrid, once, spTimeBonus, makeZone, in
|
||||
* for projectile_delay_time s, ground enemies within projectile_range tiles take atk_scale × ATK arts per second,
|
||||
* allies there recover hp_recovery_per_sec_ratio × ATK per second. Elite: 凋敝魂灵 (skill@damage_scale: damage-over-
|
||||
* time taken by ground enemies inside a unit ×1.2) and the module's +sp_recovery_per_sec SP while a unit exists.
|
||||
* PRTS 锡人 技能2 备注: the unit caches her ATK at the cast, several can exist and their effects add up, and "生命恢复的
|
||||
* 提供方式为增加目标的“生命回复速度”属性,不受治疗加成和禁疗影响" — an hpRegen buff per unit on the allies inside it
|
||||
* (no heal: 无法被友方治疗 / 禁疗 units get it too), set at each pulse until the next.
|
||||
*/
|
||||
export function tinmanKit(bb, chess, def) {
|
||||
const dur = num(bb.projectile_delay_time, 10);
|
||||
@@ -28,6 +31,7 @@ export function tinmanKit(bb, chess, def) {
|
||||
const x = tgt ? tgt.x : unit.x + unit.fwd[1], y = tgt ? tgt.y : unit.y + unit.fwd[0];
|
||||
const atk = unit.s.atk;
|
||||
unit.mem.tinZones = (unit.mem.tinZones ?? 0) + 1;
|
||||
const regenKey = `tinman:zone:${unit.id}:${unit.mem.tinZoneSeq = (unit.mem.tinZoneSeq ?? 0) + 1}`;
|
||||
makeZone(battle, unit, {
|
||||
x, y, radius, duration: dur, skill: 'tinman', onPulse(b) {
|
||||
for (const e of b.foesInRadius(x, y, radius)) {
|
||||
@@ -35,7 +39,12 @@ export function tinmanKit(bb, chess, def) {
|
||||
if (wither > 1) b.addBuff(e, { key: witherKey, duration: 1.05, data: { mul: wither }, source: unit });
|
||||
b.dealDamage(unit, e, { amount: atk * dmgScale, type: 'arts', isSkill: true, canDodge: false, tags: ['dot', 'zone'] });
|
||||
}
|
||||
if (healRatio > 0) for (const a of b.alliesInRadius(x, y, radius, null)) if (a.hp < a.s.maxHp) b.heal(unit, a, atk * healRatio, { tags: ['zone'] });
|
||||
// pulses every second: the buff bridges to the next pulse (two ticks over), the last one ends with the unit
|
||||
if (healRatio > 0) {
|
||||
for (const a of b.alliesInRadius(x, y, radius, null)) {
|
||||
if (b.allySelectable(a, unit)) b.addBuff(a, { key: regenKey, duration: 1 + 2 * b.dt, source: unit, mods: { hpRegen: atk * healRatio } });
|
||||
}
|
||||
}
|
||||
},
|
||||
onEnd() { unit.mem.tinZones = Math.max(0, (unit.mem.tinZones ?? 1) - 1); },
|
||||
});
|
||||
|
||||
@@ -6,7 +6,9 @@ import { num, talentBb, moduleBb, up } from '../shared/tier1.js';
|
||||
export default {
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// 2_14 调香师 精调: ASPD +attack_speed (−50), ATK +atk. 熏衣草: every ally on the field recovers
|
||||
// atk_to_hp_recovery_ratio × ATK HP per second. Elite module (RIN-Y, hidden attack@max_target): heals 4 allies.
|
||||
// atk_to_hp_recovery_ratio × ATK HP per second — PRTS 天赋备注 "生命恢复的提供方式为增加目标的“生命回复速度”属性(直接加算),
|
||||
// 不受治疗加成和禁疗影响": an hpRegen buff, no heal, so 无法被友方治疗 (折桠, 收割者) and 禁疗 units get it too (GitHub #137).
|
||||
// Elite module (RIN-Y, hidden attack@max_target): heals 4 allies.
|
||||
// S1 治疗强化·β型 (alt): ATK +atk for its duration.
|
||||
chess_char_2_14_a: (bb, chess) => {
|
||||
const t = talentBb(chess, 0);
|
||||
@@ -18,10 +20,12 @@ export default {
|
||||
talents: [{ install(battle, unit) {
|
||||
const r = num(t.atk_to_hp_recovery_ratio);
|
||||
if (!(r > 0)) return;
|
||||
battle.every(1, () => {
|
||||
// one buff per 调香师 (直接加算: two add up), refreshed while she is on the field; it lapses 0.5 s after she leaves
|
||||
const key = `flower:lavender:${unit.id}`;
|
||||
battle.every(0.25, () => {
|
||||
if (!up(unit)) return;
|
||||
const amt = unit.s.atk * r;
|
||||
for (const a of battle.alliesFor(unit)) if (a.hp < a.s.maxHp) battle.heal(unit, a, amt, { aura: true, tags: ['talent'] });
|
||||
const v = unit.s.atk * r;
|
||||
for (const a of battle.alliesFor(unit)) battle.addBuff(a, { key, duration: 0.5, source: unit, mods: { hpRegen: v }, tags: ['talent'] });
|
||||
}, { owner: unit });
|
||||
} }],
|
||||
};
|
||||
|
||||
@@ -7,14 +7,18 @@ import {
|
||||
giveSp,
|
||||
} from '../shared/tier3.js';
|
||||
|
||||
/** Refresh period (s) of 瑕光 S2's 生命回复速度 buff (it lasts two periods, so it never lapses while the skill runs). */
|
||||
const REGEN_IV = 0.25;
|
||||
|
||||
export default {
|
||||
// ---- 3_12 瑕光 · 守护者 — S3 先贤化身: ATK/DEF +, bonus arts per hit, heals another nearby ally per attack;
|
||||
// 剑盾骑士: hurt-SP skills of the team also gain SP on attack; 仁慈: attacks sleeping enemies (first, ×atk_scale);
|
||||
// 精锐 module GUA-Y: damage taken −15 %
|
||||
// S1 光芒涌动 (自动触发 ⇒ DEFAULT, charges): next attack ×atk_scale phys + heals the most injured ally of the 3×3 (herself
|
||||
// included) for heal_scale × ATK; S2 慑敌辉光: ATK +, puts every ground enemy on her own tile and every enemy she
|
||||
// blocks to sleep (PRTS 备注; for the skill's duration: no own value in the data) and heals every ally of the
|
||||
// skill range by ATK × ratio each second;
|
||||
// blocks to sleep (PRTS 备注; for the skill's duration: no own value in the data) and gives every ally of the
|
||||
// skill range 生命回复速度 +ATK × ratio while it runs (PRTS 技能2 备注 "生命恢复的提供方式为基于自己的攻击力,增加目标的
|
||||
// “生命回复速度”属性,不受治疗加成和禁疗影响": an hpRegen buff, no heal — GitHub #137);
|
||||
// 精锐 module GUA-X: her heals on allies under hp_ratio HP × heal_scale
|
||||
chess_char_3_12_a: (bb, chess, def) => {
|
||||
const d = defOf(chess, def);
|
||||
@@ -62,6 +66,19 @@ export default {
|
||||
skchr_blemsh_2: (s) => {
|
||||
const grid = copyGrid(s.rangeGrid) ?? NINE;
|
||||
const ratio = num(s.bb['attack@atk_to_hp_recovery_ratio'], num(s.bb.atk_to_hp_recovery_ratio, 0));
|
||||
// "周围的所有友方单位每秒恢复相当于攻击力N%的生命值": an hpRegen buff on every ally on the skill range's tiles (no
|
||||
// device, no 孤立 unit), refreshed every REGEN_IV while the skill runs and removed at its end [ASSUMED: from the
|
||||
// start of the skill — unlike 铃兰's, the note names no delay]
|
||||
const regenKey = (unit) => `blemsh:regen:${unit.id}`;
|
||||
const regen = (battle, unit) => {
|
||||
if (!(ratio > 0)) return;
|
||||
const keys = new Set(gridKeys(grid, unit));
|
||||
const v = unit.s.atk * ratio;
|
||||
for (const a of battle.allyUnits) {
|
||||
if (!a.alive || !a.deployed || a.hidden || a.kind === 'device' || !onTiles(a, keys) || !battle.allySelectable(a, unit)) continue;
|
||||
battle.addBuff(a, { key: regenKey(unit), duration: REGEN_IV * 2, source: unit, mods: { hpRegen: v } });
|
||||
}
|
||||
};
|
||||
return {
|
||||
kind: 'duration',
|
||||
heal: false,
|
||||
@@ -78,14 +95,16 @@ export default {
|
||||
}
|
||||
fx(battle, 'aoe', unit, { radius: 0.5, skill: 'blemsh_2', status: 'sleep', n });
|
||||
unit.mem.blemshRegen = 0;
|
||||
regen(battle, unit);
|
||||
},
|
||||
onTick({ battle, unit, dt }) { // "周围的所有友方单位每秒恢复相当于攻击力N%的生命值"
|
||||
if (!(ratio > 0)) return;
|
||||
onTick({ battle, unit, dt }) {
|
||||
unit.mem.blemshRegen = (unit.mem.blemshRegen ?? 0) + dt;
|
||||
while (unit.mem.blemshRegen >= 1 - 1e-9) {
|
||||
unit.mem.blemshRegen -= 1;
|
||||
for (const a of battle.injuredAlliesInKeys(new Set(gridKeys(grid, unit)), unit)) battle.heal(unit, a, unit.s.atk * ratio);
|
||||
}
|
||||
if (unit.mem.blemshRegen < REGEN_IV - 1e-9) return;
|
||||
unit.mem.blemshRegen = 0;
|
||||
regen(battle, unit);
|
||||
},
|
||||
onEnd({ battle, unit }) {
|
||||
for (const a of battle.allyUnits) if (a.findBuff(regenKey(unit))) battle.removeBuff(a, regenKey(unit));
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -92,7 +92,7 @@ export default withDefaults({
|
||||
onEnd({ unit }) { if (unit.mem.bardBase != null) unit.profile.auraRatio = unit.mem.bardBase; },
|
||||
},
|
||||
talents: [
|
||||
{ install(battle, unit) { // 过往尘埃: 3 motes; touching an operator ⇒ her trait heal ×1.5 on it for 6 s; mote back after 6 s
|
||||
{ install(battle, unit) { // 过往尘埃: 3 motes; touching an operator ⇒ her trait regen ×1.5 on it for 6 s; mote back after 6 s
|
||||
unit.mem.motes = new Array(baseCnt).fill(-Infinity);
|
||||
unit.mem.noInspire = true; // trait: 自身不受鼓舞影响 (another bard's 鼓舞 skips her)
|
||||
const cooldown = () => (S1 && skillActive(unit) ? num(bb.talent_cool_down, 3) : num(t0.cooldown, 6)); // S1: 重生速度加快
|
||||
@@ -126,8 +126,9 @@ export default withDefaults({
|
||||
battle.fx('mote', { x: a.x, y: a.y, id: a.id });
|
||||
}
|
||||
});
|
||||
battle.on('heal', (c) => {
|
||||
if (c.source === unit && c.opts?.aura && c.target.findBuff(`cetsyr:mote:${unit.id}`)) c.amount *= num(t0['attack@trait_mul'], 1.5);
|
||||
// "受到魔王特性效果提升至1.5倍": her trait's 生命回复速度 on that operator (professions.js bardRegen hook)
|
||||
battle.on('bardRegen', (c) => {
|
||||
if (c.unit === unit && c.target.findBuff(`cetsyr:mote:${unit.id}`)) c.value *= num(t0['attack@trait_mul'], 1.5);
|
||||
}, { owner: unit });
|
||||
} },
|
||||
{ install(battle, unit) { // 魔王残响: every ally takes −10 % damage from 萨卡兹 enemies (while she is in the squad)
|
||||
|
||||
@@ -46,7 +46,7 @@ export default {
|
||||
c.amount = 0;
|
||||
}, { owner: unit, priority: 100 });
|
||||
battle.on('heal', (c) => { // merged into the same heal
|
||||
if (c.source !== unit || !unit.mem.bldskBonus || c.target !== unit.mem.bldskBonus) return;
|
||||
if (c.source !== unit || c.opts?.regen || !unit.mem.bldskBonus || c.target !== unit.mem.bldskBonus) return; // (not her own 生命回复速度 tick)
|
||||
unit.mem.bldskBonus = null;
|
||||
// the bonus is part of the same heal: the healer's and the target's healing multipliers apply to it too
|
||||
c.amount += c.target.s.maxHp * ratio * num(unit.s.healingDealtMul, 1) * num(c.target.s.healingTakenMul, 1);
|
||||
|
||||
@@ -10,9 +10,10 @@ const MOTE_HIT_RADIUS = 0.4;
|
||||
|
||||
export default {
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// 魔王 — bard (aura heal 10 % ATK/s). S3 编织重构现世 (30 s): skill range, aura 65 %, motes never vanish, other allies in
|
||||
// range get 鼓舞 = +65 % of her max HP, HP of everyone in range is equalised every 2 s.
|
||||
// T1 过往尘埃: 3 motes orbit her; one colliding with an operator vanishes and gives it ×1.5 aura healing for 6 s, then
|
||||
// 魔王 — bard (trait: 生命回复速度 +10 % ATK on the allies in range, professions.js bardRegen). S3 编织重构现世 (30 s):
|
||||
// skill range, trait 65 %, motes never vanish, other allies in range get 鼓舞 = +65 % of her max HP, HP of everyone in
|
||||
// range is equalised every 2 s.
|
||||
// T1 过往尘埃: 3 motes orbit her; one colliding with an operator vanishes and gives it ×1.5 of her trait for 6 s, then
|
||||
// respawns after 6 s.
|
||||
// T2 魔王残响: allies take −10 % damage from Sarkaz enemies. Module (elite): ≥2 other ops in range → ATK +8 %.
|
||||
chess_char_5_09_a: (bb, chess, def) => {
|
||||
@@ -94,10 +95,11 @@ export default {
|
||||
battle.fx('mote', { x: hit.x, y: hit.y, id: hit.id });
|
||||
}
|
||||
});
|
||||
battle.on('heal', (c) => {
|
||||
if (c.source !== unit || !c.opts?.aura) return;
|
||||
// "受到魔王特性效果提升至1.5倍": his trait's 生命回复速度 on that operator (professions.js bardRegen hook)
|
||||
battle.on('bardRegen', (c) => {
|
||||
if (c.unit !== unit) return;
|
||||
const b = c.target.findBuff('cetsyr:mote');
|
||||
if (b) c.amount *= num(b.data.mul, 1);
|
||||
if (b) c.value *= num(b.data.mul, 1);
|
||||
}, { owner: unit });
|
||||
} },
|
||||
{ install(battle, unit) { // 魔王残响
|
||||
|
||||
@@ -5,8 +5,11 @@ import { AURA_IV, AURA_DUR, num, talent, skillGrid, mods, isOp, lazySkills, whil
|
||||
|
||||
export default {
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// 铃兰 — S3 狐火渺然 (27/29 s): no attacks, skill range, every enemy in range is sluggish, allies in range heal 9/11 %
|
||||
// ATK per second, T2 ×scale_delta_to_one. T1 技力光环·辅助: Supporters +0.4 SP/s (highest wins).
|
||||
// 铃兰 — S3 狐火渺然 (27/29 s): no attacks, skill range, every enemy in range is sluggish, allies in range recover 9/11 %
|
||||
// ATK per second, T2 ×scale_delta_to_one. PRTS 技能3 备注: "生命恢复的提供方式为基于铃兰的攻击力增加目标的“生命回复速度”
|
||||
// 属性,不受治疗加成和禁疗影响" and "技能开启第一秒内提供生命回复为0,生命回复速度数额每秒刷新一次" — an hpRegen buff (no heal:
|
||||
// 无法被友方治疗 / 禁疗 units get it too), set at each full second of the skill on the allies then in range, none in the
|
||||
// first second, removed when the skill ends. T1 技力光环·辅助: Supporters +0.4 SP/s (highest wins).
|
||||
// T2 画地为牢: sluggish enemies in range also take +20 % damage for the same time. Module (elite): +0.2 SP/s with an
|
||||
// enemy in range.
|
||||
chess_char_5_10_a: (bb, chess, def) => {
|
||||
@@ -14,6 +17,7 @@ export default {
|
||||
const boost = num(bb.scale_delta_to_one, 1);
|
||||
const healRatio = num(bb['attack@atk_to_hp_recovery_ratio']);
|
||||
const fragile = num(t1.damage_scale, 1) - 1;
|
||||
const foxKey = (unit) => `lisa:fox:${unit.id}`;
|
||||
return {
|
||||
// S1 全力以赴 (duration): ATK +, ASPD +. S2 儿时的舞乐 (toggle, 持续时间无限): ATK +, 2 targets.
|
||||
skills: lazySkills({
|
||||
@@ -35,9 +39,14 @@ export default {
|
||||
if (unit.mem.foxHeal >= 1) {
|
||||
unit.mem.foxHeal -= 1;
|
||||
if (!(healRatio > 0)) return;
|
||||
for (const a of battle.alliesInGrid(unit)) if (a.hp < a.s.maxHp) battle.heal(unit, a, unit.s.atk * healRatio, { aura: true });
|
||||
// until the next refresh (a little longer, so it never lapses in between; onEnd takes it off)
|
||||
const v = unit.s.atk * healRatio;
|
||||
for (const a of battle.alliesInGrid(unit)) battle.addBuff(a, { key: foxKey(unit), duration: 1.25, source: unit, mods: { hpRegen: v } });
|
||||
}
|
||||
},
|
||||
onEnd({ battle, unit }) {
|
||||
for (const a of battle.allyUnits) if (a.findBuff(foxKey(unit))) battle.removeBuff(a, foxKey(unit));
|
||||
},
|
||||
},
|
||||
talents: [
|
||||
{ install(battle, unit) { spAura(battle, unit, num(t0.sp_recovery_per_sec), (a) => isOp(a) && a.def?.profession === 'SUPPORT'); } },
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import { aggregateMods } from '../../../buffs.js';
|
||||
import { COLS } from '../../../constants.js';
|
||||
import { bardRegen } from '../../../professions.js';
|
||||
import {
|
||||
num, bv, tbb, tdesc, moduleBb, parseN, live, enemiesIn, isTok, onDefaultSkill, selectedSkill, aura,
|
||||
} from '../shared/tier6.js';
|
||||
@@ -37,7 +38,8 @@ function skadi2(bb, chess, def) {
|
||||
const isDef = onDefaultSkill(chess), sid = selectedSkill(chess, def);
|
||||
const tokId = def?.talents?.[0]?.tokenKey || (chess?.tokens || [])[0] || 'token_10017_skadi2_dedant';
|
||||
const auraRatio = num(tb['attack@atk_to_hp_recovery_ratio'], 0.1);
|
||||
// S1 / S2 raise the trait heal ("特性效果提高至N%") while they run; S3 (default) turns it into the tide
|
||||
// S1 / S2 raise the trait ("特性效果提高至N%") while they run; S3 (default) turns it into the tide. The trait is a
|
||||
// 生命回复速度 buff on the allies, not a heal (PRTS 分支特性信息 吟游者; professions.js bardRegen)
|
||||
const skillRatio = num(bb['attack@atk_to_hp_recovery_ratio'], auraRatio);
|
||||
const seaborns = (battle, unit) => battle.allyUnits.filter((t) => isTok(t, tokId, unit) && live(t));
|
||||
const covered = (battle, unit, toks) => { // allies inside her range ∪ the seaborns' ranges
|
||||
@@ -52,7 +54,7 @@ function skadi2(bb, chess, def) {
|
||||
return seaborns(battle, unit).some((t) => t.rangeKeySet?.has(k));
|
||||
};
|
||||
const skills = {
|
||||
// S1 同归殊途之吟 (SP_FULL): full self heal, max HP +max_hp, trait heal attack@atk_to_hp_recovery_ratio, and
|
||||
// S1 同归殊途之吟 (SP_FULL): full self heal, max HP +max_hp, trait attack@atk_to_hp_recovery_ratio, and
|
||||
// damage_resistance of the damage taken by every ally of her (+ 海嗣) range is transferred to her (install)
|
||||
skchr_skadi2_1: {
|
||||
kind: 'duration',
|
||||
@@ -60,7 +62,7 @@ function skadi2(bb, chess, def) {
|
||||
onStart({ battle, unit }) { unit.hp = unit.s.maxHp; battle.fx('heal', { x: unit.x, y: unit.y, id: unit.id }); },
|
||||
},
|
||||
// S2 同葬无光之愿 (toggle): 鼓舞 ATK / DEF = atk / def × her ATK / DEF on every other ally of her (+ 海嗣) range,
|
||||
// trait heal attack@atk_to_hp_recovery_ratio (trait pulse). 自动触发 with effects on her allies only (nothing to target):
|
||||
// trait attack@atk_to_hp_recovery_ratio (trait pulse). 自动触发 with effects on her allies only (nothing to target):
|
||||
// on as soon as it is ready (SP_FULL, like 魔王 S1 往昔萦绕身旁); until 0.2.0 the data's DEFAULT left it off until an
|
||||
// enemy came into her (or a 海嗣's) range.
|
||||
skchr_skadi2_2: { kind: 'toggle', trigger: 'SP_FULL' },
|
||||
@@ -72,7 +74,7 @@ function skadi2(bb, chess, def) {
|
||||
onStart({ battle, unit }) { battle.fx('tide', { x: unit.x, y: unit.y, id: unit.id }); },
|
||||
},
|
||||
trait: {
|
||||
install(battle, unit) { // replaces the bard aura: heal normally, tide (true damage + 鼓舞 + self drain) during S3
|
||||
install(battle, unit) { // replaces the bard aura: 生命回复速度 normally, tide (true damage + 鼓舞 + self drain) during S3
|
||||
unit.mem.noInspire = true; // 自身不受鼓舞影响
|
||||
const isInspire = (b) => b.key === 'inspire' || b.status === 'inspire' || b.key.startsWith('inspire:') || b.key.endsWith(':inspire');
|
||||
// "海嗣的攻击范围视为自身攻击范围的延伸": an enemy in a 海嗣's range also satisfies her DEFAULT trigger
|
||||
@@ -103,10 +105,9 @@ function skadi2(bb, chess, def) {
|
||||
const va = unit.s.atk * num(bb.atk), vd = unit.s.def * num(bb.def);
|
||||
for (const a of allies) if (a !== unit) { inspire(battle, a, va, unit); inspire(battle, a, vd, unit, 'def'); }
|
||||
}
|
||||
if (n % 2 === 0) {
|
||||
const amount = unit.s.atk * (on ? skillRatio : num(unit.profile?.auraRatio, auraRatio));
|
||||
for (const a of allies) if (a.hp < a.s.maxHp) battle.heal(unit, a, amount, { aura: true });
|
||||
}
|
||||
// the trait over her range ∪ the 海嗣' ranges, refreshed every pulse (it lapses 0.75 s after the last one)
|
||||
const v = unit.s.atk * (on ? skillRatio : num(unit.profile?.auraRatio, auraRatio));
|
||||
for (const a of allies) bardRegen(battle, unit, a, v, 0.75);
|
||||
}, { owner: unit });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -93,7 +93,7 @@ function lumen(bb, chess, def) {
|
||||
const res = -num(t0.one_minus_status_resistance, -0.5), thr = num(t0.hp_ratio, 0.75);
|
||||
battle.on('heal', (ctx) => {
|
||||
const t = ctx.target;
|
||||
if (ctx.source !== unit || !t || !(res > 0) || !(base > 0)) return;
|
||||
if (ctx.source !== unit || ctx.opts?.regen || !t || !(res > 0) || !(base > 0)) return; // "治疗的目标": not her own 生命回复速度 tick
|
||||
const after = Math.min(t.s.maxHp, t.hp + ctx.amount);
|
||||
battle.applyStatus(t, 'resist', { duration: after / t.s.maxHp > thr ? special : base, value: Math.min(1, res), source: unit });
|
||||
}, { owner: unit, priority: -10 });
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
// 沙之碑 on appear: owner ATK × atk_scale arts + stun (skill range 3×3), blocks 3 (no attack), talent duration 20 s
|
||||
// 战术装备 on appear: stun around (bb.stun), blocked enemies DEF + talent def (−160), talent duration 25 s
|
||||
// “小自在” arts melee blocker, talent duration 25 s; kills emit `summonKill` (夕's 化境 is the 夕 kit's job)
|
||||
// 斯卡蒂的海嗣 untargetable range extension of 浊心斯卡蒂: heals allies in its range (owner ATK × trait ratio /s);
|
||||
// 斯卡蒂的海嗣 untargetable range extension of 浊心斯卡蒂: her trait's 生命回复速度 on the allies in its range (owner
|
||||
// ATK × trait ratio /s);
|
||||
// while the owner's skill runs: owner ATK × atk_scale true dmg/s to enemies + 鼓舞 owner ATK × bb.atk;
|
||||
// talent duration 25/30 s, then redeploys after respawnTime (30/25 s, DP cost from data)
|
||||
// “耀阳” on appear: owner ATK × atk_scale true + stun in the skill grid (+1 hit if the previously deployed
|
||||
@@ -85,6 +86,7 @@ import { absoluteRangeKeys, sortEnemyTargets, canTargetEnemy } from '../targetin
|
||||
import { bodyInKeys, bodyOnTile } from '../body.js';
|
||||
import { hasHp } from '../damage.js';
|
||||
import { genericKit } from './generic.js';
|
||||
import { bardRegen } from '../professions.js';
|
||||
import { normDir, localOrder } from '../dir.js';
|
||||
import { SKILL_SUMMON_START_DEPLOY } from '../../../shared/constants.js';
|
||||
|
||||
@@ -535,7 +537,11 @@ function duskDragon(bb, raw, def) {
|
||||
};
|
||||
}
|
||||
|
||||
/** 斯卡蒂的海嗣 (浊心斯卡蒂 talent 远古血亲): range extension of its owner (heal aura / S3 damage + 鼓舞). */
|
||||
/**
|
||||
* 斯卡蒂的海嗣 (浊心斯卡蒂 talent 远古血亲): range extension of its owner (her trait's 生命回复速度 — professions.js bardRegen,
|
||||
* keyed by the owner: one trait effect per ally — / S3 damage + 鼓舞). Only while the owner fights with the generic kit:
|
||||
* her own kit (kits/ops/chess_char_6_04-skadi2.js) covers the 海嗣' ranges itself.
|
||||
*/
|
||||
function seaborn(bb, raw, def) {
|
||||
const life = num(talentBb(def, 'duration').duration, 0);
|
||||
const healRatio = num(def?.traitBb?.['attack@atk_to_hp_recovery_ratio'], 0);
|
||||
@@ -567,9 +573,9 @@ function seaborn(bb, raw, def) {
|
||||
} else if (healRatio > 0) {
|
||||
const ownerKeys = o && o.alive && o.deployed ? (o.rangeKeySet || new Set(o.rangeKeys || [])) : null;
|
||||
for (const a of battle.alliesInGrid(unit)) {
|
||||
if (a === unit || a.hp >= a.s.maxHp - 1e-6) continue;
|
||||
if (ownerKeys && ownerKeys.has(a.tileR * COLS + a.tileC)) continue; // the owner's own aura already heals it
|
||||
battle.heal(unit, a, atk * healRatio);
|
||||
if (a === unit) continue;
|
||||
if (ownerKeys && ownerKeys.has(a.tileR * COLS + a.tileC)) continue; // the owner's own trait already covers it
|
||||
bardRegen(battle, o ?? unit, a, atk * healRatio, 1.25); // refreshed every second
|
||||
}
|
||||
}
|
||||
}, { owner: unit });
|
||||
|
||||
@@ -509,18 +509,23 @@ export function reduceElement(target, amount, el = null) {
|
||||
* PRTS 异常效果 HEAL_FREE "受到的治疗量变为0") stops the unit's own too — except an HP-regeneration attribute (`regen`:
|
||||
* "增减生命回复速度或生命回复速度(百分比)属性的效果不会被识别为治疗类能力") and a heal that ignores it (`ignoreHealFree`:
|
||||
* 史尔特尔 S3's start heal, PRTS "无视禁疗").
|
||||
* A `regen` tick (Battle status: the unit's own 生命回复速度, `s.hpRegen` — 吟游者 / 调香师 / 瑕光 S2 / 铃兰 S3 / 锡人 add to it
|
||||
* with an hpRegen buff, PRTS 备注 "不受治疗加成和禁疗影响") is no 治疗: no 治疗加成 scales it — neither the healing
|
||||
* multipliers nor a `heal` handler (handlers still see it, with `opts.regen`; a change they make to its amount is
|
||||
* ignored).
|
||||
*/
|
||||
export function heal(battle, source, target, amount, opts = {}) {
|
||||
if (!target || !target.alive || target.removed || !target.deployed || target.bossPool) return 0;
|
||||
const self = source === target || !!opts.self;
|
||||
if (!self && (target.s.flags.noHeal || (target.profile && target.profile.noHeal))) return 0;
|
||||
if (target.s.flags.healFree && !opts.regen && !opts.ignoreHealFree) return 0;
|
||||
let amt = amount * (source && source.s ? source.s.healingDealtMul : 1) * target.s.healingTakenMul;
|
||||
const regen = !!opts.regen;
|
||||
if (target.s.flags.healFree && !regen && !opts.ignoreHealFree) return 0;
|
||||
let amt = regen ? amount : amount * (source && source.s ? source.s.healingDealtMul : 1) * target.s.healingTakenMul;
|
||||
if (!(amt > 0) || !Number.isFinite(amt)) return 0;
|
||||
if (battle._hooks.heal) {
|
||||
const ctx = { source, target, amount: amt, opts };
|
||||
battle.emit('heal', ctx);
|
||||
amt = Number.isFinite(ctx.amount) ? Math.max(0, ctx.amount) : 0;
|
||||
if (!regen) amt = Number.isFinite(ctx.amount) ? Math.max(0, ctx.amount) : 0;
|
||||
// a handler may have killed / retreated the target: healing a dead unit would leave it "dead with hp > 0"
|
||||
if (!target.alive || !target.deployed) return 0;
|
||||
}
|
||||
|
||||
@@ -371,13 +371,31 @@ const installMystic = (battle, unit) => {
|
||||
}, { owner: unit });
|
||||
};
|
||||
|
||||
/** Refresh period / lifetime (s) of a bard trait's 生命回复速度 buff: it lapses within BARD_REGEN_DUR once the bard stops. */
|
||||
export const BARD_REGEN_IV = 0.25;
|
||||
export const BARD_REGEN_DUR = 0.5;
|
||||
|
||||
/**
|
||||
* 吟游者 trait "不攻击,持续恢复范围内所有友军生命(每秒相当于自身攻击力10%的生命)": PRTS 分支特性信息 吟游者 "特性为基于自身攻击力
|
||||
* 来增加受益者的“生命回复速度”属性" — an hpRegen buff of `value` (the bard's ATK × ratio) on `ally`, one per bard (two bards
|
||||
* add up), which the caller refreshes before it lapses (`duration`, default BARD_REGEN_DUR). No heal: 禁疗 and 无法被友方
|
||||
* 治疗 (收割者 / 不屈者 / 武者 noHeal) do not stop it — PRTS 异常效果 禁疗 "增减生命回复速度…的效果不会被识别为治疗类能力"; the
|
||||
* regeneration tick is the ally's own (damage.js heal `regen`, no 治疗加成). The `bardRegen` hook { unit (the bard),
|
||||
* target, value } may scale one ally's share (魔王's 微尘: "使该干员受到魔王特性效果提升至1.5倍"). Also the 海嗣 range of
|
||||
* 浊心斯卡蒂 (content/tokens.js, kits/ops/chess_char_6_04-skadi2.js).
|
||||
*/
|
||||
export function bardRegen(battle, bard, ally, value, duration = BARD_REGEN_DUR) {
|
||||
if (!bard || !ally || !ally.alive || !(value > 0)) return;
|
||||
let v = value;
|
||||
if (battle.hasHook('bardRegen')) v = num(battle.emit('bardRegen', { unit: bard, target: ally, value: v }).value, 0);
|
||||
if (v > 0) battle.addBuff(ally, { key: `trait:bard:${bard.id}`, duration, source: bard, mods: { hpRegen: v } });
|
||||
}
|
||||
|
||||
const installBard = (battle, unit) => {
|
||||
battle.every(1, () => {
|
||||
battle.every(BARD_REGEN_IV, () => {
|
||||
if (!unit.canAct) return;
|
||||
const amount = unit.s.atk * (unit.profile.auraRatio ?? 0.1);
|
||||
for (const ally of battle.alliesInGrid(unit)) {
|
||||
if (ally.hp < ally.s.maxHp) battle.heal(unit, ally, amount, { aura: true });
|
||||
}
|
||||
const v = unit.s.atk * (unit.profile.auraRatio ?? 0.1);
|
||||
for (const ally of battle.alliesInGrid(unit)) bardRegen(battle, unit, ally, v);
|
||||
}, { owner: unit });
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user