326 lines
22 KiB
JavaScript
326 lines
22 KiB
JavaScript
// server/sim/content/kits/ops/op-monstr.js — Mon3tr (char_4179_monstr) 自选 operator kit: 6★ 链愈师 (医疗), an owned-6★ pick of
|
||
// the tier-5 and tier-6 自选 slots; every skill, both talents, the trait and her module (XAH-X 记忆存档) at every form, and the
|
||
// kit of her summon 重构体 (token_10050_monstr_prosts). Kit contract and the 自选 rules: ../README.md ("How to add an operator
|
||
// (自选)").
|
||
//
|
||
// Forms (data/backups.json units.char_4179_monstr): normal = E2 Lv1, skills at rank 4, no module; elite = E2 Lv60, rank 7,
|
||
// XAH-X at stage 1 (tier 5) or 3 (tier 6) — the owner's decision of 2026-10-05. Full potential (the owner's decision of 2026-10-07).
|
||
// Sources: character_table / skill_table / battle_equip_table / token_table (zh_CN, as built into backups.json); PRTS Mon3tr
|
||
// (T1 备注 "治疗效果无视禁疗,但对象选取受禁疗制约,可以强行选择自身的重构体作为自身普通攻击的治疗目标", "攻击力加成效果同名效果取最高";
|
||
// T2 备注 "该效果重复触发刷新持续时间,不会因自身退场而消失"; S1 备注 "技能范围与技能期间治疗范围无关"; S2 备注 "可选择满生命值的重构体
|
||
// 进行治疗;重构体受到Mon3tr普通攻击治疗的0.6s后,Mon3tr再次以重构体为目标进行一次跳跃治疗", "仅技能期间治疗触发的第二天赋效果提升;
|
||
// …叠加时取最高"; S3 备注: the start (撤退重构体, 【移动】, the 路标形态 marker), 不死 ("期间受到致命伤害结算后立即结束技能;技能结束后
|
||
// 返回…以不高于1点的生命值在场;因返回而部署时获得1s不死"), "期间对自身进行治疗时,治疗跳跃不会衰减且不占用跳跃数", the return); PRTS
|
||
// 重构体 (备注 "持有禁疗(可以被Mon3tr无视)、不可阻挡", "嘲讽等级-1", "Mon3tr因移动之外的原因退场时强制撤退场上的重构体", 通常形态
|
||
// "阻挡数强制归0,每0.1s流失8生命值…退场时返还1个可部署的重构体"); PRTS 卫戍协议/帮助 (§战斗部署 placed summons, §作战阶段
|
||
// "若召唤物在战斗期间退场,将在满足条件后立即原地再部署1个"); the client's battle data: charpack char_4179_monstr (attacks
|
||
// `_ignoreHealFree`; talent "1" monstr_t_1[born_charge] / [die_to_kil_token]; talent "2" monstr_t_2[atk_spd], scaled in the S2
|
||
// mode by `_applyTalentScale` on attack_speed; modes S3_Reborn (Combat / Attack: PURE, targetMotion 1, `_maxNum` 3
|
||
// `_limitedMaxTargetNumToBlockedCnt`; ExtraHeal projectile_chr_monstr_s3 on every attack; talents monstr[free_jump],
|
||
// monstr_s_3[bleeding] (damage_per_second × 0.1 every 0.1 s), [reborned_passive], [sync_hp]); `_config` mapping S3's atk /
|
||
// damage_per_second / max_hp / block_cnt / base_attack_time and T1's attack@chain.extra_cnt); the skill prefabs (skchr_monstr_1
|
||
// next heal; skchr_monstr_2 its attack's activeBuff monstr_s_2[proj] lifeTime 0.6 → projectile_chr_monstr_s2_extra from the
|
||
// reconstruct; skchr_monstr_3 `_onlyAvailableWhenTokenValid`, the Respawn / RespawnEnd abilities, monstr_s_3[end_undeadable_
|
||
// restore] 1 s); the projectile prefabs (every heal projectile: `_atkScale` 0.75 = chain.atk_scale, the jump Selector
|
||
// `_rangeId` x-4 `_maxTarget` 3 lowest HP ratio `_useAdditionalTargetCount`, `_freeJumpValidator` monstr[free_jump]); the
|
||
// reconstruct's prefab (monstr[block_free] abnormal [3, 7] + taunt_level −1; prosts[atk_up] aura from the host's `atk`;
|
||
// prosts_t_1[born_charge] BLOCK_CNT ×0, interval 0.1, firstTriggerInterval 1, FixedValueDamage PURE `damage` on itself);
|
||
// buff_template_data (monstr_t_2[atk_spd] ON_OUTPUT_MODIFIER IsHeal; monstr_s_3[undead] ON_BEFORE_TRY_SET_HP_ZERO).
|
||
// - Trait (链愈师) "恢复友方单位生命,且会在3个友方单位间跳跃,每次跳跃治疗量降低25%" — the chain is this kit's (the profession's
|
||
// chain is replaced: heal mode single + `chainFrom`, its links the profession's — ai.js chainHealNext): after the heal on
|
||
// the main target the heal jumps to the lowest-HP-ratio ally (a full-HP one too — PRTS 分支特性信息 链愈师; ties: the latest
|
||
// deployed) of the 3×3 around the last one healed (x-4), each jump ×
|
||
// attack@chain.atk_scale (0.75; XAH-X 0.85), up to attack@chain.max_target units healed (3). A 重构体 (and she herself while S3
|
||
// runs: monstr[free_jump]) is a free link: healing it uses no target of the count and the next jump does not decay (XAH-X
|
||
// stage 3: the next attack@chain.extra_cnt jumps after that neither). Her heals pass 禁疗 ("治疗效果无视禁疗"); her selection
|
||
// takes her own 重构体 through its 禁疗 (`healThrough`), no other 禁疗 unit [ASSUMED: the jumps too].
|
||
// - Module XAH-X 记忆存档: the trait's 0.85 per jump (traitOverride); stage 3 changes T1 (below); ATK / DEF from the attributes.
|
||
// - T1 自我修复 (cnt 1): her 重构体 is a hand piece the player places (the talent's summon) on a ground tile of her attack
|
||
// range — "可以在攻击范围内的地面使用": the data's `ownerRange` from that talent text (tools/build-data.mjs summonRecord,
|
||
// 0.2.0 WE2) and its MELEE position, enforced by the match. Its kit: 禁疗 (only her heals reach it), blocks nothing, taunt
|
||
// −1, loses 8 HP every 0.1 s from 1 s after it lands (流失, Battle.loseHp — PRTS "每0.1s流失8生命值"; trait damage_per_second
|
||
// 80); every ally of its
|
||
// 3×3 but itself (no 孤立 one) ATK +atk (0.2; XAH-X stage 3 0.3; 同名效果取最高). Gone (destroyed or withdrawn) ⇒ back on its
|
||
// tile its redeploy time (15 s) after, paying its cost (3 DP), only while she stands — her deployment (not a 【移动】)
|
||
// readies a waiting one at once (monstr_t_1[born_charge]) [ASSUMED: the 卫戍 auto redeploy of a placed summon, as 凯尔希's
|
||
// Mon3tr]; withdrawn when she leaves the field (not on her S3 moves).
|
||
// - T2 战术协同: every heal of hers (chain jumps included) gives its target and herself ASPD +attack_speed for buff_duration s —
|
||
// ×talent_scale (S2) for a heal made while S2 runs; one effect per unit, the strongest (同名效果取最高 — a weaker one that
|
||
// outlasts it resumes afterwards: Battle.applyStrongest); a new one refreshes it.
|
||
// - S1 策略:超压链接 (AUTO, attack SP, data DEFAULT — a "next heal" waits for her heal, as 闪灵's S2): that heal is heal_scale ×
|
||
// ATK and heals up to chain.max_target (4) units.
|
||
// - S2 策略:超负荷 (MANUAL, data SKILL_RANGE on its y-2 — an enemy there; 30 s): her own 重构体 inside her range is her heal
|
||
// target, even at full HP (with nobody injured she heals it all the same: Battle.forceAttack); 0.6 s after each heal of her
|
||
// attack on it a second chain heal starts on it (her ATK × 1); T2 ×talent_scale.
|
||
// - S3 策略:熔毁 (MANUAL, data DEFAULT, 25 s; only while her 重构体 stands — the cast is refused otherwise): the 重构体 is withdrawn
|
||
// (its piece waits: back once its tile is free again) and she 【移动】s onto its tile; meanwhile range x-4, ATK +atk, base attack
|
||
// time base_attack_time (a flat −1.5 s on 2.85 s), block +block_cnt, max HP +max_hp (the HP ratio kept), 流失 of
|
||
// damage_per_second HP/s (8 every 0.1 s); "同时攻击阻挡的所有敌人" (her selector's _limitedMaxTargetNumToBlockedCnt): each attack
|
||
// takes up to her block count of ground enemies of her range, the blocked ones first (ai.js `hitAllBlocked`, the 强攻手 rule
|
||
// of PRTS 分支特性信息) for ATK true damage, and each attack starts a chain heal on herself at attack@heal_scale × ATK
|
||
// (she is a free link). 不死: a hit or 流失 that would knock her out leaves her at 1 HP, untargetable and blocking nothing, and
|
||
// the skill ends on the next tick. At the end she 【返回】s to the tile she left (her SP emptied; the max HP drop keeps the ratio,
|
||
// so after a fatal blow she stands at ≤ 1 HP) with 1 s of 不死. The 路标形态 marker on her tile is not modelled: should another
|
||
// unit stand there, she stays where she is [ASSUMED; officially she is knocked out].
|
||
|
||
import { num, talentBb, traitBb, skillRec, batMod, installAura, up } from '../shared/tier1.js';
|
||
import { COLS } from '../../../constants.js';
|
||
import { chainHealNext } from '../../../ai.js';
|
||
|
||
const S1 = 'skchr_monstr_1';
|
||
const S2 = 'skchr_monstr_2';
|
||
const S3 = 'skchr_monstr_3';
|
||
/** Her summon 重构体 (T1). */
|
||
export const PROSTS = 'token_10050_monstr_prosts';
|
||
/** S2: the second chain starts this long after a heal of hers on her 重构体 (monstr_s_2[proj] lifeTime). */
|
||
const S2_EXTRA_DELAY = 0.6;
|
||
/** The 重构体's 流失: every 0.1 s, from 1 s after it lands (prosts_t_1[born_charge] interval / firstTriggerInterval). */
|
||
const PROSTS_BLEED_IV = 0.1;
|
||
const PROSTS_BLEED_FIRST = 1;
|
||
/** The 重构体's ATK aura refresh [ASSUMED: the aura's interval is not in the data]. */
|
||
const PROSTS_AURA_IV = 0.1;
|
||
/** S3's 流失 period (monstr_s_3[bleeding] interval). */
|
||
const S3_BLEED_IV = 0.1;
|
||
/** S3: 不死 after the return (monstr_s_3[end_undeadable_restore] lifeTime). */
|
||
const RETURN_UNDEAD = 1;
|
||
/** How often a waiting 重构体 tries to come back once ready. */
|
||
const RETRY = 0.1;
|
||
|
||
const T2_KEY = 'talent:monstr:aspd';
|
||
const PROSTS_ATK_KEY = 'talent:monstr:prostsAtk';
|
||
const PROSTS_ABN_KEY = 'monstr:prosts:abnormal';
|
||
const S3_UNDEAD_KEY = 'skill:monstr:undead';
|
||
const TAG_PROSTS_BLEED = 'monstr:prostsBleed';
|
||
const TAG_S3_BLEED = 'monstr:meltdownBleed';
|
||
/** Her heals "无视禁疗" (every heal ability of hers: `_ignoreHealFree`). */
|
||
const HEAL_OPTS = Object.freeze({ ignoreHealFree: true });
|
||
|
||
const bbOf = (chess, id) => skillRec(chess, id)?.bb ?? {};
|
||
const skillOn = (unit, id) => !!(unit.skill && unit.skill.active && unit.skill.id === id);
|
||
const isProstsOf = (t, unit) => !!t && t.kind === 'token' && t.defId === PROSTS && t.ownerUnit === unit;
|
||
const prostsOf = (battle, unit) => battle.allyUnits.filter((t) => isProstsOf(t, unit));
|
||
/** Her 重构体 standing inside her current attack range. */
|
||
const prostsInRange = (battle, unit) => prostsOf(battle, unit).find((t) => up(t) && !!unit.rangeKeySet && unit.rangeKeySet.has(t.tileR * COLS + t.tileC)) ?? null;
|
||
/** `a` stands on the 3×3 around `b` (x-4: a heal jump's reach, the 重构体's aura). */
|
||
const nearX4 = (a, b) => Math.abs(a.tileR - b.tileR) <= 1 && Math.abs(a.tileC - b.tileC) <= 1;
|
||
|
||
/**
|
||
* A chain heal of hers from `main` (already healed by her attack when `mainDone`): up to `count` units healed, × `step` per
|
||
* jump; a free link (her 重构体; herself with `selfFree`) uses no target of the count and the next 1 + `extra` jumps keep the
|
||
* amount. Each link is the 链愈师 one (ai.js chainHealNext — the 3×3 around the last one healed, the lowest HP ratio a
|
||
* full-HP ally included, then the latest deployed; no 禁疗 unit but her 重构体, her `healThrough`).
|
||
*/
|
||
function chainFrom(battle, unit, main, base, { count, step, extra, mainDone = false, selfFree = false }) {
|
||
const free = (t) => isProstsOf(t, unit) || (selfFree && t === unit);
|
||
let amount = base;
|
||
if (!mainDone) battle.heal(unit, main, amount, HEAL_OPTS);
|
||
let counted = free(main) ? 0 : 1;
|
||
let calm = free(main) ? 1 + extra : 0;
|
||
const seen = new Set([main.id]);
|
||
let cur = main;
|
||
while (counted < count) {
|
||
const next = chainHealNext(battle, unit, cur, seen);
|
||
if (!next) break;
|
||
seen.add(next.id);
|
||
if (calm > 0) calm--; else amount *= step;
|
||
battle._ev(['atk', cur.id, next.id, 'chainHeal']);
|
||
battle.heal(unit, next, amount, HEAL_OPTS);
|
||
if (free(next)) calm = 1 + extra; else counted++;
|
||
cur = next;
|
||
}
|
||
}
|
||
|
||
/** The 重构体's kit (owner = Mon3tr): 禁疗, no block, taunt −1, its 流失, the ATK aura, its return. */
|
||
function prostsKit(owner, { atk }) {
|
||
return {
|
||
skill: null,
|
||
talents: [],
|
||
trait: { noAttack: true },
|
||
install(battle, t) {
|
||
const dps = num(t.def?.traitBb?.damage_per_second, 80);
|
||
battle.addBuff(t, { key: PROSTS_ABN_KEY, flags: { noHeal: true, noBlock: true }, mods: { taunt: -1 }, persist: true, allowDead: true });
|
||
if (atk > 0) installAura(battle, t, { key: PROSTS_ATK_KEY, select: (a) => a !== t && nearX4(a, t), mods: { atkPct: atk }, value: atk, interval: PROSTS_AURA_IV });
|
||
battle.on('deploy', (c) => {
|
||
if (c.unit !== t) return;
|
||
const seq = t.deploySeq;
|
||
battle.after(PROSTS_BLEED_FIRST, () => {
|
||
if (t.deploySeq !== seq || !up(t)) return;
|
||
battle.every(PROSTS_BLEED_IV, (b, sched) => {
|
||
if (t.deploySeq !== seq || !up(t)) { sched.cancel(); return; }
|
||
b.loseHp(t, dps * PROSTS_BLEED_IV, { source: t, tags: [TAG_PROSTS_BLEED] });
|
||
}, { owner: t, immediate: true });
|
||
}, { owner: t });
|
||
}, { owner: t });
|
||
// gone (destroyed or withdrawn): the piece stays and comes back on its tile once ready, while she stands
|
||
battle.on('death', (c) => {
|
||
if (c.unit !== t || battle.finished) return;
|
||
t.removed = false;
|
||
t.mem.readyAt = battle.time + Math.max(0, num(t.base.respawnTime));
|
||
if (t.mem.retry) return;
|
||
t.mem.retry = battle.every(RETRY, (b, sched) => {
|
||
const stop = () => { sched.cancel(); t.mem.retry = null; };
|
||
if (t.alive || t.removed || b.finished) { stop(); return; }
|
||
if (!up(owner) || b.time + 1e-9 < t.mem.readyAt) return;
|
||
if (b.redeploy(t, { free: false })) stop();
|
||
}, { owner: t });
|
||
}, { owner: t, priority: -10 });
|
||
},
|
||
};
|
||
}
|
||
|
||
/** S2: with nobody injured in her range she heals her 重构体 there all the same ("可选择满生命值的重构体进行治疗"). */
|
||
function s2CanAttack(battle, unit) {
|
||
const r = prostsInRange(battle, unit);
|
||
if (!r || battle.injuredAlliesInKeys(unit.rangeKeys, unit).length) return true;
|
||
if (battle.forceAttack(unit, [r])) unit.atkCd = Math.max(unit.atkCd, unit.s.interval);
|
||
return false;
|
||
}
|
||
|
||
export default {
|
||
char_4179_monstr: (bb, chess) => {
|
||
const tb = traitBb(chess); // attack@chain.max_target (3), attack@chain.atk_scale (0.75; XAH-X 0.85)
|
||
const t0 = talentBb(chess, 0); // 自我修复: cnt, atk (XAH-X stage 3: 0.3 + attack@chain.extra_cnt 1)
|
||
const t1 = talentBb(chess, 1); // 战术协同: buff_duration, attack_speed
|
||
const b1 = bbOf(chess, S1), b2 = bbOf(chess, S2), b3 = bbOf(chess, S3);
|
||
const s1 = skillRec(chess, S1), s3 = skillRec(chess, S3);
|
||
const chain = {
|
||
count: Math.max(1, Math.floor(num(tb['attack@chain.max_target'], 3))),
|
||
step: num(tb['attack@chain.atk_scale'], 0.75),
|
||
extra: Math.max(0, Math.floor(num(t0['attack@chain.extra_cnt']))),
|
||
};
|
||
const s1Count = Math.max(1, Math.floor(num(b1['chain.max_target'], chain.count)));
|
||
const picked = chess?.skill?.skillId ?? null;
|
||
return {
|
||
// the chain is chainFrom's (heal mode single: no profession chain); her 重构体 is selectable through its 禁疗
|
||
trait: { heal: { mode: 'single' }, healThrough: (healer, a) => isProstsOf(a, healer) },
|
||
skills: {
|
||
[S1]: { kind: num(s1?.maxChargeTime, 1) > 1 ? 'charges' : 'instant', attack: { healScale: num(b1.heal_scale, 1) } },
|
||
[S2]: { kind: 'duration', attack: { canAttack: (battle, unit) => s2CanAttack(battle, unit) } },
|
||
[S3]: {
|
||
kind: 'duration',
|
||
mods: { atkPct: num(b3.atk), batPct: batMod(b3.base_attack_time, chess), blockCnt: num(b3.block_cnt), hpFlat: num(b3.max_hp) },
|
||
targeting: s3?.rangeGrid ? { rangeGrid: s3.rangeGrid } : undefined,
|
||
attack: { dmgType: 'true', attack: 'melee', projectile: 'none', canHitFly: false, groundOnly: true, hitAllBlocked: true },
|
||
onStart({ battle, unit, skill }) {
|
||
const r = prostsOf(battle, unit).find(up);
|
||
if (!r) { skill.end('noToken'); skill.addCharge(1); return; } // (refused before: the activate guard)
|
||
const home = { r: unit.tileR, c: unit.tileC, dir: unit.dir };
|
||
const to = [r.tileR, r.tileC];
|
||
battle.retreat(r, { reason: 'retreat', permanent: true }); // 撤退场上的重构体(返回待部署区)
|
||
const fromX = unit.x, fromY = unit.y;
|
||
if (!battle.moveRedeploy(unit, to[0], to[1])) { skill.end('blocked'); skill.addCharge(1); return; } // 强制结束技能并返还技力
|
||
unit.mem.mHome = home;
|
||
battle.fx('teleport', { x: unit.x, y: unit.y, id: unit.id, fromX, fromY });
|
||
},
|
||
onTick({ unit, skill }) { if (unit.mem.mFatal) skill.end('fatal'); }, // 受到致命伤害结算后立即结束技能
|
||
onEnd({ battle, unit }) {
|
||
const h = unit.mem.mHome;
|
||
unit.mem.mHome = null;
|
||
unit.mem.mFatal = false;
|
||
battle.removeBuff(unit, S3_UNDEAD_KEY);
|
||
if (!h || !unit.alive || !unit.deployed) return;
|
||
// 【返回】 to the tile she left: SP emptied (S3_Restore ModifySp −100 %), 1 s 不死
|
||
const fromX = unit.x, fromY = unit.y, dir0 = unit.dir;
|
||
unit.dir = h.dir;
|
||
if (battle.moveRedeploy(unit, h.r, h.c, { clearSp: true })) battle.fx('teleport', { x: unit.x, y: unit.y, id: unit.id, fromX, fromY });
|
||
else unit.dir = dir0;
|
||
unit.mem.mUndeadUntil = battle.time + RETURN_UNDEAD;
|
||
},
|
||
},
|
||
},
|
||
talents: [
|
||
{ install(battle, unit) { // 自我修复: her pieces run the 重构体's kit; withdrawn when she leaves; readied by her deployment
|
||
for (const t of battle.allyUnits) {
|
||
if (!isProstsOf(t, unit) || t.alive || t.deployed) continue;
|
||
if (t.kit) battle.offOwner(t);
|
||
battle._setupUnit(t, prostsKit(unit, { atk: num(t0.atk) }));
|
||
}
|
||
battle.on('death', (c) => {
|
||
if (c.unit !== unit) return;
|
||
for (const t of prostsOf(battle, unit)) if (t.alive) battle.retreat(t, { reason: 'retreat', permanent: true });
|
||
}, { owner: unit });
|
||
battle.on('deploy', (c) => {
|
||
if (c.unit !== unit || c.move) return;
|
||
for (const t of prostsOf(battle, unit)) if (!t.alive && !t.removed) t.mem.readyAt = battle.time;
|
||
}, { owner: unit });
|
||
} },
|
||
{ install(battle, unit) { // 战术协同: every heal of hers ⇒ target and herself ASPD + (×talent_scale under S2)
|
||
const d = num(t1.buff_duration), v0 = num(t1.attack_speed);
|
||
if (!(d > 0) || !(v0 > 0)) return;
|
||
battle.on('heal', (c) => {
|
||
if (c.source !== unit || c.opts?.regen || !c.target || c.target.side !== 'ally' || !(c.amount > 0)) return;
|
||
const v = v0 * (skillOn(unit, S2) ? num(b2.talent_scale, 1) : 1);
|
||
const give = (x) => battle.applyStrongest(x, T2_KEY, { duration: d, value: v, mods: (a) => ({ aspd: a }), source: unit });
|
||
give(c.target);
|
||
if (c.target !== unit && unit.alive) give(unit);
|
||
}, { owner: unit });
|
||
} },
|
||
],
|
||
install(battle, unit) {
|
||
// the chain heals: after each heal attack of hers a chain from its target(s); under S3 every attack starts one on her
|
||
battle.on('attack', (c) => {
|
||
if (c.attacker !== unit || !unit.alive) return;
|
||
const prof = battle.effectiveProfile(unit);
|
||
if (prof.heal && prof.dmgType === 'heal') {
|
||
const sk = unit.skill;
|
||
const s1on = !!(sk && sk.id === S1 && sk.active && sk.pending);
|
||
const s2on = skillOn(unit, S2);
|
||
const base = unit.s.atk * (prof.atkScale ?? 1) * (prof.healScale ?? 1) * unit.s.atkScaleMul;
|
||
for (const t of c.targets || []) {
|
||
if (!t || t.side !== 'ally') continue;
|
||
chainFrom(battle, unit, t, base, { ...chain, count: s1on ? s1Count : chain.count, mainDone: true });
|
||
// S2: 0.6 s after her attack healed her 重构体, a second chain on it (her ATK × 1)
|
||
if (s2on && isProstsOf(t, unit)) {
|
||
battle.after(S2_EXTRA_DELAY, () => {
|
||
if (!unit.alive || !up(t)) return;
|
||
chainFrom(battle, unit, t, unit.s.atk * unit.s.atkScaleMul, chain);
|
||
}, { owner: unit });
|
||
}
|
||
}
|
||
} else if (skillOn(unit, S3) && unit.mem.mHome && !unit.mem.mFatal) {
|
||
chainFrom(battle, unit, unit, unit.s.atk * num(b3['attack@heal_scale'], 0.5) * unit.s.atkScaleMul, { ...chain, selfFree: true });
|
||
}
|
||
}, { owner: unit });
|
||
// S2: her 重构体 inside her range is the heal target
|
||
if (picked === S2) {
|
||
battle.on('beforeAttack', (c) => {
|
||
if (c.attacker !== unit || !skillOn(unit, S2) || !c.profile || c.profile.dmgType !== 'heal') return;
|
||
const r = prostsInRange(battle, unit);
|
||
if (r) c.targets = [r];
|
||
}, { owner: unit, priority: 10 });
|
||
}
|
||
if (picked !== S3) return;
|
||
// S3 `_onlyAvailableWhenTokenValid`: every activation path is refused while her 重构体 is not on the field
|
||
const sk = unit.skill;
|
||
if (sk && sk.id === S3 && !Object.prototype.hasOwnProperty.call(sk, 'activate')) {
|
||
const activate = sk.activate;
|
||
Object.defineProperty(sk, 'activate', {
|
||
configurable: true, writable: true, enumerable: false,
|
||
value(reason, opts) { return prostsOf(battle, unit).some(up) ? activate.call(this, reason, opts) : false; },
|
||
});
|
||
}
|
||
// S3's 流失
|
||
battle.every(S3_BLEED_IV, () => {
|
||
if (!skillOn(unit, S3) || !unit.mem.mHome || unit.mem.mFatal || !up(unit)) return;
|
||
battle.loseHp(unit, num(b3.damage_per_second) * S3_BLEED_IV, { source: unit, tags: [TAG_S3_BLEED] });
|
||
}, { owner: unit });
|
||
// S3 不死; the 1 s 不死 after the return
|
||
battle.on('fatal', (c) => {
|
||
if (c.unit !== unit) return;
|
||
if (skillOn(unit, S3) && unit.mem.mHome) {
|
||
c.prevented = true;
|
||
if (!unit.mem.mFatal) {
|
||
unit.mem.mFatal = true;
|
||
battle.addBuff(unit, { key: S3_UNDEAD_KEY, flags: { untargetable: true, noBlock: true }, tags: ['skill'] });
|
||
battle.releaseBlocked(unit);
|
||
battle.fx('undying', { x: unit.x, y: unit.y, id: unit.id });
|
||
}
|
||
return;
|
||
}
|
||
if (battle.time < num(unit.mem.mUndeadUntil, -Infinity) - 1e-9) c.prevented = true;
|
||
}, { owner: unit, priority: 10 });
|
||
},
|
||
};
|
||
},
|
||
};
|