- op-ifrit.js: 轰击术师 line; 精神融解 RES ×0.6 aura, BLA-Δ Lv3 +50 % ATK 元素伤害 on burn-bursting targets (attacks and 灼地 ticks); 莱茵回路 +2 SP / 6 s, BLA-X Lv3 30 % +5; S1 ATK / ASPD; S2 charges, DEF cut + 灼伤 (current ATK, lasts after she leaves); S3 维持技能状态 (no attack, ground-only ticks from the Begin clip's event then every 1 s, RES cut, 2 %/s 流失, ended by stun); BLA-X distance scaling on all her damage while she stands, BLA-Δ 8 % 灼燃损伤 rider. - op-aphris.js: her range is the extension path (5 tiles, +2 and turned by her 中继器; S2 unbounded and turned by allied operators); the 中继器's lifetime / marks / redeploy (leaves and returns with her, cleared by S3), ATK +25 / 35 % while it stands, 取样优化 法术穿透 on allied operators of her path; S1 SP_FULL toggle; S2 beam every 0.5 s (no attack); S3 ammo 20, 3 s 强制缴械, extra hit + longer 停顿 past a 中继器; BLA-Y cost −8, Lv3 35 s / 20 s / +35 %. - op-lin.js: 琉璃璧 shield (absorbs ≤ 200, breaks above: stun + burst on her current range, regrows 8 / 6 s), 韬光 per hit taken; S1 状态切换 toggle (停顿), S2 琉璃璧 copies for allies in range, S3 x-2 + threshold ×2 / ×2.5 + kill refresh; PLX-X keeps DEF +100 % / RES +10 in skills, PLX-Y +3 % per enemy in range (≤ 5). - Sources: data/backups.json, PRTS (备注), the client's battle data (charpacks, battle/prefabs skills / equips, buff templates) and the battle skeletons' clip events; [ASSUMED] items in the kit headers. - kits/index.js OPERATOR_KIT_FILES += op-ifrit.js, op-aphris.js, op-lin.js; tests test/content/op_ifrit / op_aphris / op_lin (10 each). - Golden: diy only — diy-003 … diy-006 now pack the new kits (they held the prototypes), diy-007 … diy-012 are new (the rest of 林, then the prototypes repacked into diy-008 … diy-012); diy-001 / 002 (推进之王) and every other family unchanged.
295 lines
19 KiB
JavaScript
295 lines
19 KiB
JavaScript
// server/sim/content/kits/ops/op-aphris.js — 谬因 (char_4229_aphris) 自选 operator kit: 6★ 轰击术师 (术师), an owned-6★ pick
|
||
// of the tier-5 and tier-6 自选 slots; every skill, both talents, the trait, her module and her summon “中继器” at every form.
|
||
// Kit contract and the 自选 rules: ../README.md ("How to add an operator (自选)").
|
||
//
|
||
// Forms (data/backups.json units.char_4229_aphris, the DIY slot statuses): normal = E2 Lv1, skills at rank 4, no module;
|
||
// elite = E2 Lv60, rank 7, the picked module at stage 1 (tier 5) or 3 (tier 6). Potential 0 [ASSUMED: no account].
|
||
// Summon: tokens.token_10070_aphris_pc “中继器” (talent 链路协议; a hand piece the player places with a facing — data
|
||
// `placeable`, a talent summon: it deploys with the board, docs/SIM.md §1.1 token pieces).
|
||
// Sources: character_table / skill_table / battle_equip_table (zh_CN, as built into backups.json); PRTS 谬因 (链路协议 备注:
|
||
// her range "以自己所在格为起点向部署方向延伸,直至达到延伸距离上限" — 5 tiles from E1 —, "若延伸到了自身的中继器所在的地块,会令延伸
|
||
// 距离上限增加(并非增加谬因的攻击距离属性)并改变接下来的延伸方向", "尝试延伸到已延伸到的地块或地图边界外时延伸会提前终止", "索敌与攻击
|
||
// 始终采用格子判定", "攻击力增加效果不可叠加"; 取样优化 备注 "无视法抗效果为增加受益者的法术穿透属性(直接加算)"; 连续映射 备注
|
||
// "中继器持续时间结束时不再进行自动撤退"; 临界瞬爆 备注 "技能持续期间,自身的攻击范围可无限延伸直至无法继续延伸", "持有孤立的我方干员
|
||
// 也可令谬因攻击范围延伸效果转向"; 混沌的本质 备注 "技能开启后3秒内持有强制缴械", "“停顿时间延长”指的是增加本次攻击对该敌人施加的
|
||
// 停顿的时间", "技能开始/结束时强制撤回所有中继器", 修正 "清空再部署"); PRTS 中继器 (备注: 无敌, 禁疗, 孤立, 阻挡数 0, 再部署策略
|
||
// 队列); PRTS 卫戍协议/帮助 §作战阶段 ("若战场区初始部署有召唤物,若召唤物在战斗期间退场,将在满足条件后立即原地再部署1个"); the client's
|
||
// battle data (charpack char_4229_aphris — the range selectors, the talent abilities; battle/prefabs [uc]skills skchr_aphris_1/2/3;
|
||
// buff_template_data aphris_token_add_atk_tohost, aphris_pc_t[with-draw], aphris_sk1(_token), aphris_sk2_switch_mode4,
|
||
// aphris_sk3 / [token-withdraw] / [on_finish], die_to_kill_token, charge_token[born]).
|
||
// - Range (链路协议; her selectors `_extendable`, `_extendValidator` = the 中继器, `_extendLengthEveryValidateChar` 2): a path,
|
||
// not her data grid — from her tile along her facing, one tile at a time, up to her 5-1 length; reaching her own 中继器
|
||
// adds 2 to the length still allowed and turns the path to that piece's facing; it stops at a tile it already holds or at
|
||
// the map's edge. The path is her range (`unit.rangeGrid`, rebuilt when a piece or an operator comes or goes, at her
|
||
// skill's start / end, and checked every tick): her attacks, the DEFAULT trigger, 取样优化 and the detail card all read it.
|
||
// [ASSUMED] a huge enemy counts on every tile it covers (the engine's selection; PRTS: 格子判定 by its position), her hits
|
||
// land at once (the client spaces them `_hitInterval` 0.05 s along the beam) and a 攻击距离 bonus (none exists for her in
|
||
// this mode) would extend it as the engine extends any range.
|
||
// - Trait (轰击术师) "攻击造成超远距离的群体法术伤害": the profession's `rangeAoe` — every selectable enemy of her path at once,
|
||
// air units too. Module BLA-Y “第三相态”: 部署费用 −8 (the attribute; stats 34 → 26), HP / ATK in the stats, no trait change;
|
||
// stage 3 changes T1 (35 s piece, 再部署时间变短 — the piece's byModule respawnTime 30 → 20 —, ATK +35 %).
|
||
// - T1 链路协议: one 中继器 lasting the token talent's duration (25 s; BLA-Y stage 3: its hidden module talent, 35 s) from each
|
||
// deployment (aphris_pc_t[with-draw]: it withdraws when that time is up unless it holds aphris_sk1_token — given to every
|
||
// piece of hers when 连续映射 starts, and to a piece taking the field while 连续映射 or 混沌的本质 runs). Back on its tile its
|
||
// respawnTime (30 / 20 s) after it left, paying its 3 DP — the 卫戍 auto redeploy of a placed summon, as 斯卡蒂的海嗣 —, and
|
||
// only while 谬因 stands: her leaving the field withdraws it (die_to_kill_token), her (re)deployment makes it ready at once
|
||
// (charge_token[born]). ATK +atk while one of hers stands (aphris_token_add_atk_tohost: one buff aphris_t1[atk], "不可叠加").
|
||
// The piece "不会受到攻击" (data: untargetable) and holds 无敌, 禁疗 and 孤立 (PRTS 中继器; [ASSUMED] given by this kit — the
|
||
// data's `abnormal` lacks them).
|
||
// - T2 取样优化 "攻击范围内的友方干员攻击时无视敌方10点法术抗性" (aphris[t2]: attribute 27, 法术穿透, on allied operators — profession
|
||
// mask 639 — of her attack range): resIgnoreFlat +magic_resist_penetrate_fixed on every allied operator standing on her path,
|
||
// her own tile included [ASSUMED: the selector does not drop its owner], refreshed every AURA_IV s; several sources keep the
|
||
// strongest.
|
||
// - S1 连续映射 (AUTO, 持续时间无限): ATK +atk (aphris_sk1) and the 中继器 marks above. An AUTO skill acting on herself fires at
|
||
// full SP (`trigger: 'SP_FULL'`, the owner's AUTO rule, kits/README.md checklist 5); `toggle`.
|
||
// - S2 临界瞬爆 (MANUAL, data DEFAULT, 8 s): mode 4 (aphris_sk2_switch_mode4) — her normal attack is replaced by the beam
|
||
// ability (`_preDelay` 0.167 = the Skill_2_Begin clip, then `_triggerDelta` 0.5; MAGICAL atk_scale): from BEAM_FIRST on,
|
||
// every `interval` (0.5) s, every selectable enemy on her path takes atk_scale × ATK arts (16 hits in the 8 s). The path
|
||
// meanwhile has no length limit (`_infinite`) and turns at her 中继器 and at every allied
|
||
// operator it reaches (validators: the piece, allies of profession mask 639 — every operator profession; a 孤立 one too),
|
||
// to that unit's facing. The data resolves DEFAULT: the unbounded range is no data range (no rangeId), so build-data's
|
||
// ACTIVE_RANGE rule cannot see it (reported, not hand-coded).
|
||
// - S3 混沌的本质 (MANUAL, data DEFAULT, 20 发弹药 — `ammo`, attack@trigger_time): for charge_time (3) s from the cast 强制缴械
|
||
// (aphris_sk3[sheltering], abnormal flag disarmed) and aphris_sk3[token-withdraw]: every 中继器 of hers withdrawn and its
|
||
// redeploy time cleared (ResetTokenRespawnTime — the placed piece is back on its tile at once; the two extra pieces the
|
||
// skill allows have no piece to place in the auto battle [ASSUMED: N/A]); attack interval +base_attack_time (−1.1 s on
|
||
// 2.9 s), ATK +atk, each attack attack@atk_scale × ATK on her path, and on every enemy it hits the buff aphris_sk3: 停顿
|
||
// origin_sluggish + extra_sluggish × n s and n hits of extra_atk_scale × ATK arts, n = the 中继器 the path passed before the
|
||
// enemy, at most max_times (1). It ends with its bullets; aphris_sk3[on_finish] withdraws every 中继器 and clears their
|
||
// redeploy time again (so the piece is back at once, now with its normal lifetime).
|
||
|
||
import { num, talentBb, skillRec, up, batMod, toggleBuff } from '../shared/tier1.js';
|
||
import { COLS } from '../../../constants.js';
|
||
import { dirVec, toLocal } from '../../../dir.js';
|
||
|
||
const S1 = 'skchr_aphris_1';
|
||
const S2 = 'skchr_aphris_2';
|
||
const S3 = 'skchr_aphris_3';
|
||
/** Her summon “中继器” (talent 链路协议). */
|
||
const RELAY = 'token_10070_aphris_pc';
|
||
/** Extra path length per 中继器 (the selectors' `_extendLengthEveryValidateChar`; "使该次攻击距离+2"). */
|
||
const RELAY_REACH = 2;
|
||
/** Talent-2 aura refresh period and buff length (the non-stacking aura convention of the kits). */
|
||
const AURA_IV = 0.25;
|
||
const AURA_DUR = 0.4;
|
||
/** S2 临界瞬爆: the first beam hit (skchr_aphris_2 `_preDelay`, the Skill_2_Begin clip's length), then every `interval` s. */
|
||
const BEAM_FIRST = 0.167;
|
||
/** How often a withdrawn 中继器 tries to come back once ready (her on the field, its tile free, DP paid). */
|
||
const RELAY_RETRY = 0.1;
|
||
const LINK_KEY = 'aphris:link';
|
||
const SAMPLING_KEY = 'aphris:sampling';
|
||
const CHARGE_KEY = 'aphris:charge';
|
||
const RELAY_ABNORMAL_KEY = 'aphris:relayAbnormal';
|
||
|
||
const bbOf = (chess, id) => skillRec(chess, id)?.bb ?? {};
|
||
/** A 中继器 of `unit`. */
|
||
const isRelayOf = (t, unit) => !!t && t.kind === 'token' && t.defId === RELAY && t.ownerUnit === unit;
|
||
const relaysOf = (battle, unit) => battle.allyUnits.filter((t) => isRelayOf(t, unit));
|
||
/** 连续映射 / 混沌的本质 running: a 中继器 taking the field now gets the no-withdraw mark (aphris_pc_t[with-draw]). */
|
||
const marksRelays = (unit) => !!(unit.skill && unit.skill.active && (unit.skill.id === S1 || unit.skill.id === S3));
|
||
/** Her own path length: the farthest forward tile of her data grid (5 on her E2 5-1). */
|
||
function baseReach(unit) {
|
||
let n = 0;
|
||
for (const p of unit.def?.rangeGrid ?? []) if (Array.isArray(p) && p[0] === 0 && p[1] > n) n = p[1];
|
||
return n;
|
||
}
|
||
/** A 中继器's lifetime: the last `duration` of its talents (the BLA-Y stage-3 hidden talent after the base one). */
|
||
function relayLife(t) {
|
||
let life = 0;
|
||
for (const x of t.def?.talents ?? []) if (x && x.bb && x.bb.duration != null) life = num(x.bb.duration, life);
|
||
return life;
|
||
}
|
||
|
||
/**
|
||
* Her 延伸 path (header): absolute tiles in order from her own, with `index` (tile key → position) and `relays` (the
|
||
* positions of the 中继器 tiles it passed). `beam` = S2: no length limit, every allied operator turns it too.
|
||
*/
|
||
function aphrisPath(battle, unit, beam = false) {
|
||
const tiles = [[unit.tileR, unit.tileC]];
|
||
const index = new Map([[unit.tileR * COLS + unit.tileC, 0]]);
|
||
const relays = [];
|
||
let [dr, dc] = dirVec(unit.dir);
|
||
let reach = beam ? Infinity : baseReach(unit);
|
||
let r = unit.tileR, c = unit.tileC, steps = 0;
|
||
while (steps < reach) {
|
||
const nr = r + dr, nc = c + dc;
|
||
const k = nr * COLS + nc;
|
||
if (!battle.grid.inBounds(nr, nc) || index.has(k)) break;
|
||
index.set(k, tiles.length);
|
||
tiles.push([nr, nc]);
|
||
r = nr; c = nc; steps++;
|
||
const o = battle.unitAt(nr, nc);
|
||
if (o && isRelayOf(o, unit)) {
|
||
reach += RELAY_REACH;
|
||
[dr, dc] = dirVec(o.dir);
|
||
relays.push(tiles.length - 1);
|
||
} else if (beam && o && o !== unit && o.kind === 'op' && o.side === 'ally') [dr, dc] = dirVec(o.dir);
|
||
}
|
||
return { tiles, index, relays };
|
||
}
|
||
|
||
/** Rebuild her range from the path when it changed (the relative grid of the path in her facing-RIGHT frame). */
|
||
function refreshPath(battle, unit) {
|
||
if (!up(unit)) return;
|
||
const beam = !!(unit.skill && unit.skill.active && unit.skill.id === S2);
|
||
const p = aphrisPath(battle, unit, beam);
|
||
const sig = p.tiles.map(([r, c]) => r * COLS + c).join(',');
|
||
unit.mem.aphrisPath = p;
|
||
if (unit.mem.aphrisSig === sig) return;
|
||
unit.mem.aphrisSig = sig;
|
||
unit.rangeGrid = p.tiles.map(([r, c]) => toLocal(r - unit.tileR, c - unit.tileC, unit.dir));
|
||
battle.refreshRange(unit);
|
||
}
|
||
|
||
/** How many 中继器 her path passed before `e`'s tile (S3 aphris_sk3 "extendTimes"). */
|
||
function relaysBefore(unit, e) {
|
||
const p = unit.mem.aphrisPath;
|
||
if (!p || !p.relays.length) return 0;
|
||
const i = p.index.get(Math.round(e.y) * COLS + Math.round(e.x));
|
||
return i == null ? 0 : p.relays.filter((j) => j < i).length;
|
||
}
|
||
|
||
/** Withdraw every 中继器 of hers that stands; `clear` = their redeploy time cleared (ResetTokenRespawnTime). */
|
||
function withdrawRelays(battle, unit, clear) {
|
||
for (const t of relaysOf(battle, unit)) {
|
||
if (t.alive) {
|
||
t.mem.relayCleared = clear;
|
||
battle.retreat(t, { reason: 'expired', permanent: true });
|
||
} else if (clear && !t.removed) t.mem.relayReadyAt = battle.time;
|
||
}
|
||
}
|
||
|
||
/** A 中继器 of hers took the field: its abnormal flags and redeploy rule once, its lifetime and mark every time. */
|
||
function relayDeployed(battle, unit, t) {
|
||
if (!t.mem.aphrisRelay) {
|
||
t.mem.aphrisRelay = true;
|
||
battle.addBuff(t, { key: RELAY_ABNORMAL_KEY, flags: { invulnerable: true, isolated: true, noHeal: true }, persist: true, allowDead: true });
|
||
// back on its tile its respawnTime after it left (at once when its redeploy time was cleared), while she stands,
|
||
// paying its DP — a token is otherwise removed for good: the piece is kept (`removed = false`) so its hooks survive
|
||
// and `battle.redeploy` accepts it
|
||
battle.on('death', (c) => {
|
||
if (c.unit !== t || battle.finished) return;
|
||
t.removed = false;
|
||
t.mem.relayReadyAt = battle.time + (t.mem.relayCleared ? 0 : Math.max(0, num(t.base.respawnTime)));
|
||
t.mem.relayCleared = false;
|
||
if (t.mem.relayRetry) return;
|
||
t.mem.relayRetry = battle.every(RELAY_RETRY, (b, sched) => {
|
||
const stop = () => { sched.cancel(); t.mem.relayRetry = null; };
|
||
if (t.alive || t.removed || b.finished) { stop(); return; }
|
||
if (!up(unit) || b.time + 1e-9 < t.mem.relayReadyAt) return;
|
||
if (b.redeploy(t, { free: false })) stop();
|
||
}, { owner: t });
|
||
}, { owner: t, priority: -10 });
|
||
}
|
||
t.mem.relayKept = marksRelays(unit); // aphris_pc_t[with-draw] ON_BUFF_START: aphris_sk1_token
|
||
const life = relayLife(t);
|
||
if (!(life > 0)) return;
|
||
const seq = t.deploySeq;
|
||
battle.after(life, () => {
|
||
if (!t.alive || t.deploySeq !== seq || t.mem.relayKept) return; // 连续映射's mark: "不再进行自动撤退"
|
||
battle.retreat(t, { reason: 'expired', permanent: true });
|
||
}, { owner: t });
|
||
}
|
||
|
||
export default {
|
||
char_4229_aphris: (bb, chess) => {
|
||
const t0 = talentBb(chess, 0), t1 = talentBb(chess, 1);
|
||
const b1 = bbOf(chess, S1), b2 = bbOf(chess, S2), b3 = bbOf(chess, S3);
|
||
return {
|
||
skills: {
|
||
[S1]: {
|
||
kind: 'toggle', trigger: 'SP_FULL', mods: { atkPct: num(b1.atk) },
|
||
onStart({ battle, unit }) { for (const t of relaysOf(battle, unit)) if (t.alive) t.mem.relayKept = true; }, // aphris_sk1 → aphris_sk1_token
|
||
},
|
||
[S2]: {
|
||
kind: 'duration',
|
||
attack: { noAttack: true },
|
||
onStart({ battle, unit }) {
|
||
unit.mem.beamAt = 0;
|
||
unit.mem.beamNext = BEAM_FIRST;
|
||
battle.fx('beam', { x: unit.x, y: unit.y, id: unit.id, skill: 'aphris:beam' });
|
||
},
|
||
onTick({ battle, unit, skill, dt }) {
|
||
const iv = num(b2.interval, 0.5);
|
||
if (!(iv > 0)) return;
|
||
const t = (unit.mem.beamAt = (unit.mem.beamAt ?? 0) + dt);
|
||
while (t >= unit.mem.beamNext - 1e-6 && skill.active && unit.alive) {
|
||
unit.mem.beamNext += iv;
|
||
for (const e of battle.enemiesInKeys(unit.rangeKeys, unit, unit.profile)) {
|
||
battle.dealDamage(unit, e, { amount: unit.s.atk * num(b2.atk_scale), type: 'arts', isSkill: true, tags: ['skill', 'aphris:beam'] });
|
||
}
|
||
}
|
||
},
|
||
},
|
||
[S3]: {
|
||
kind: 'ammo', ammo: num(b3['attack@trigger_time'], 20),
|
||
mods: { atkPct: num(b3.atk), batPct: batMod(b3.base_attack_time, chess) },
|
||
attack: {
|
||
atkScale: num(b3['attack@atk_scale'], 1),
|
||
onEachHit({ battle, unit, target }) { // aphris_sk3 on every enemy the attack hits
|
||
if (!target || target.side !== 'enemy' || !target.alive) return;
|
||
const max = num(b3['attack@max_times'], 1);
|
||
const n = max >= 0 ? Math.min(max, relaysBefore(unit, target)) : relaysBefore(unit, target);
|
||
const slow = num(b3['attack@origin_sluggish']) + num(b3['attack@extra_sluggish']) * n;
|
||
if (slow > 0) battle.applyStatus(target, 'sluggish', { duration: slow, source: unit });
|
||
const ex = num(b3['attack@extra_atk_scale']);
|
||
for (let i = 0; i < n && target.alive && ex > 0; i++) battle.dealDamage(unit, target, { amount: unit.s.atk * ex, type: 'arts', isSkill: true, tags: ['skill', 'aphris:relay'] });
|
||
},
|
||
},
|
||
onStart({ battle, unit }) {
|
||
const ct = num(b3.charge_time);
|
||
if (ct > 0) battle.addBuff(unit, { key: CHARGE_KEY, duration: ct, flags: { disarm: true }, tags: ['skill'] }); // 强制缴械
|
||
withdrawRelays(battle, unit, true); // aphris_sk3[token-withdraw]
|
||
},
|
||
onEnd({ battle, unit }) {
|
||
battle.removeBuff(unit, CHARGE_KEY);
|
||
withdrawRelays(battle, unit, true); // aphris_sk3[on_finish]
|
||
},
|
||
},
|
||
},
|
||
talents: [
|
||
{ install(battle, unit) { // 链路协议: ATK +atk while one of her 中继器 stands (one buff however many)
|
||
const a = num(t0.atk);
|
||
if (a) toggleBuff(battle, unit, LINK_KEY, () => relaysOf(battle, unit).some((t) => t.alive && t.deployed), { atkPct: a });
|
||
// die_to_kill_token: her pieces leave with her; charge_token[born]: her (re)deployment makes a waiting one ready
|
||
battle.on('death', (c) => { if (c.unit === unit) withdrawRelays(battle, unit, false); }, { owner: unit });
|
||
battle.on('deploy', (c) => {
|
||
if (c.unit !== unit) return;
|
||
for (const t of relaysOf(battle, unit)) if (!t.alive && !t.removed) t.mem.relayReadyAt = battle.time;
|
||
}, { owner: unit });
|
||
} },
|
||
{ install(battle, unit) { // 取样优化: 法术穿透 +v on the allied operators of her path
|
||
const v = num(t1.magic_resist_penetrate_fixed);
|
||
if (!(v > 0)) return;
|
||
battle.every(AURA_IV, () => {
|
||
if (!up(unit) || !unit.rangeKeySet) return;
|
||
for (const a of battle.alliesFor(unit)) {
|
||
if (a.kind !== 'op' || !unit.rangeKeySet.has(a.tileR * COLS + a.tileC)) continue;
|
||
const cur = a.findBuff(SAMPLING_KEY);
|
||
if (cur && cur.source !== unit && (cur.data?.v ?? 0) > v && cur.timeLeft > 0.05) continue;
|
||
battle.addBuff(a, { key: SAMPLING_KEY, duration: AURA_DUR, mods: { resIgnoreFlat: v }, source: unit, data: { v }, tags: ['aura'] });
|
||
}
|
||
}, { owner: unit, immediate: true });
|
||
} },
|
||
],
|
||
install(battle, unit) {
|
||
// the 延伸 path: rebuilt on any ally's arrival / departure (her 中继器, an operator S2 bends on), her skill's
|
||
// start / end, and checked every tick
|
||
battle.on('deploy', (c) => {
|
||
const t = c.unit;
|
||
if (t === unit) unit.mem.aphrisSig = null;
|
||
else if (isRelayOf(t, unit)) relayDeployed(battle, unit, t);
|
||
if (t && t.side === 'ally') refreshPath(battle, unit);
|
||
}, { owner: unit });
|
||
battle.on('death', (c) => { if (c.unit && c.unit.side === 'ally' && c.unit !== unit) refreshPath(battle, unit); }, { owner: unit });
|
||
battle.on('skillStart', (c) => { if (c.unit === unit) refreshPath(battle, unit); }, { owner: unit });
|
||
battle.on('skillEnd', (c) => { if (c.unit === unit) refreshPath(battle, unit); }, { owner: unit });
|
||
battle.on('tick', () => refreshPath(battle, unit), { owner: unit });
|
||
// S3's 强制缴械 from the cast on: the attack loop goes on to the attack the cast was made for (ai.js updateAlly
|
||
// checks `disarm` before the cast only) — no target, no attack, no bullet spent
|
||
battle.on('beforeAttack', (c) => { if (c.attacker === unit && unit.findBuff(CHARGE_KEY)) c.targets = []; }, { owner: unit });
|
||
},
|
||
};
|
||
},
|
||
};
|