tools/build-data.mjs TRIGGER_ALLY_RULES (PRTS 特殊策略 "技能范围内存在可治疗的我方单位时释放技能") writes the row as the engine's SKILL_RANGE on the skill range with allies: true; simdata normalizeSkill passes allies into the def's trigger (skills.js reads trigger.allies). Regenerated offline: 9 paths, the S3 trigger of 黍's three forms. op-shu.js no longer sets the trigger. Golden: no scenario moved.
289 lines
20 KiB
JavaScript
289 lines
20 KiB
JavaScript
// server/sim/content/kits/ops/op-shu.js — 黍 (char_2025_shu) 自选 operator kit: 6★ 守护者 (重装), an owned-6★ pick of the
|
||
// tier-5 and tier-6 自选 slots; every skill, both talents, the trait and her module at every form.
|
||
// Kit contract and the 自选 rules: ../README.md ("How to add an operator (自选)").
|
||
//
|
||
// Forms (data/backups.json units.char_2025_shu, the DIY slot statuses): normal = E2 Lv1, skills at rank 4, no module;
|
||
// elite = E2 Lv60, rank 7, GUA-X at stage 1 (tier 5) or 3 (tier 6). Potential 0 [ASSUMED: no account].
|
||
// Sources: character_table / skill_table / battle_equip_table (zh_CN, as built into backups.json); PRTS 黍 (百谷长青 备注
|
||
// "本天赋效果为永久地块效果,自身退场时清空所有地块的播种效果,不对装置职业的目标生效。生命恢复的提供方式为增加位于地块上干员的
|
||
// “生命回复速度”属性,不受治疗加成和禁疗影响"; 天有四时 修正 "在场时" / "编入队伍且"; S1 修正 "小于等于一半"; S2 备注 "实际效果为技能
|
||
// 期间普通攻击改为治疗友方单位(受攻速和缴械影响,非停止攻击),“特殊间隔”指攻击间隔+1.3"; S3 备注 "技能持续期间视野扩大至治疗范围,
|
||
// 自身普通攻击同时触发治疗,无攻击目标时普通攻击可直接进行治疗", the 【播种标记】 and its teleport, "攻击力/攻击速度提升效果无视孤立");
|
||
// PRTS 卫戍协议/帮助 §技能操作 (黍【离离枯荣】 "技能范围内存在可治疗的我方单位时释放技能"); the client's battle data: buff templates
|
||
// shu_t_1 / shu_t_1[char] / shu_farm_buff[char] / shu_s_2 / shu_s_3 / shu_s_3[in_range] / shu_s_3[heal_in_attack] /
|
||
// shu_s_3[enemy][enter|leave|mark] / heal_scale_up[hpratio][LE], her charpack (the S2 / S3 modes' Attack / Heal
|
||
// abilities, DoFarm / EnemyListener tile abilities and their selectors, the 天有四时 auras and deck part) and the skill
|
||
// prefabs skchr_shu_1..3; Arknights Terra Wiki "Shu".
|
||
// - Trait (守护者) "技能可以治疗友方单位": her normal attack is a melee physical strike (ground-only, block 3, range 0-1 — her own
|
||
// tile); only her skills heal. Module GUA-X “钦天司时” "治疗生命值低于50%的友方单位时治疗量提升15%" (trait bb heal_scale /
|
||
// hp_ratio): every heal of hers on a target at or below hp_ratio ×heal_scale (the client template is heal_scale_up
|
||
// [hpratio][LE]: at or below — the text says 低于), never an HP-regen tick.
|
||
// - T1 百谷长青 (shu_t_1): every heal she outputs (S1 / S2 / S3; never a regen tick) sows the healed unit's tile and the four
|
||
// next to it (DoFarm, range x-5) — tiles that can be deployed on or walked (the tile selector: buildable or passable). A
|
||
// sown tile stays sown until she leaves the field (death / retreat clears them all). Every allied unit standing on one —
|
||
// operators and summons, not 装置 (professionMask 767) nor a 孤立 unit (no ignoreTargetFree) — has 生命回复速度
|
||
// +hp_recovery_per_sec (an hpRegen buff, checklist 11: 禁疗 / 无法被友方治疗 do not stop it — 斥罪 too) and 庇护
|
||
// damage_resistance (the shared applyStrongest key PROTECT), refreshed every FARM_EVERY s (and at once on a sowing and at
|
||
// S2's start / end); both × S2's extra_extend_scale while S2 runs (shu_s_2 sets DoFarm's extra_extend_scale). Two 黍 of a
|
||
// shared field: the stronger regeneration and 庇护 hold. GUA-X stage 3: 80 / s, 15 % and "部署时立即给所处地块播种该效果"
|
||
// (bbStr born_range_id 0-1: her own tile at every deployment).
|
||
// - T2 天有四时: while she is deployed, ≥ PROF_NEEDED different professions among the allied operators on the field ⇒ every
|
||
// allied operator's max HP +max_hp; ≥ PROF_NEEDED operators of one profession ⇒ ASPD +attack_speed (charpack auras:
|
||
// operators only, 孤立 included); if her squad (her player's operators, deployed or not) holds ≥ SUI_NEEDED 【岁】 operators
|
||
// (character_table groupId 'sui': 年 夕 令 重岳 黍 余 望) ⇒ every operator of the squad ATK +atk for the battle and +sp SP
|
||
// every `interval` s of each deployment (modify_sp[trigger]; an SP gift — a 决战者's 阻回 refuses it), whether she is
|
||
// deployed or not (Terra). The counts 3 / 4 exist in the text and the charpack only (minCount / minCntToTrig).
|
||
// - S1 化被草木 (AUTO, time SP, 可充能 1 / 2 次; data DEFAULT): the client prefab is 塞雷娅 S1 急救's (a SkillTrigger on an
|
||
// ally of HP ≤ half — _maxHpRatio 0.5 — _cancelIfSearchTargetFailed), whose PRTS 备注 reads "此技能仅在周围有符合血量条件的友方
|
||
// 单位时可触发,触发时会替换当次攻击": the engine's DEFAULT with `allies` / `hpAtMost` on the skill range x-4 — at her attack,
|
||
// with an ally of x-4 at ≤ half HP, that attack heals the lowest such ally for heal_scale × ATK instead (as 塞雷娅's kit).
|
||
// - S2 嘉禾盈仓 (MANUAL; data DEFAULT — the owner's 重装 exception of 2026-10-05, rawRule TAKE_DAMAGE): `duration` s, her
|
||
// attacks become heals of up to attack@max_target injured allied units on the skill range x-1 (her range meanwhile:
|
||
// _rangeIdModeIndex 1) for ATK, ATK +atk, block +block_cnt, attack interval +base_attack_time s (a flat +1.3 on her 1.2
|
||
// s; ASPD and 缴械 still count), T1 ×extra_extend_scale.
|
||
// - S3 离离枯荣 (MANUAL): her range becomes the skill range x-2 (_rangeIdModeIndex 2; "视野扩大至治疗范围"), ATK +atk; every
|
||
// attack on an enemy also heals the most injured allied unit of that range other than herself for ATK
|
||
// (shu_s_3[heal_in_attack]: the Heal selector excluding her); with no enemy to attack her attack heals the most injured
|
||
// allied unit of the range, herself included (the mode's Attack-else-Heal ability). While a ground enemy stands on a
|
||
// sown tile every allied operator of her range — 孤立 ones too — ATK +e_atk and ASPD +e_attack_speed (× min(enemies,
|
||
// e_max_stack_cnt)); a ground enemy that enters a sown tile is marked with that tile (once) and each time it then enters
|
||
// a tile at a Manhattan distance ≥ max_distance from it, it is put back on that tile's centre (unblocked, its route
|
||
// re-planned from there; waypoints already passed stay passed), stealth or 无敌 notwithstanding; a 消失 (hidden) enemy loses
|
||
// its mark and every mark ends with the skill (the client attaches it to shu_s_3). No enemy of the mode holds
|
||
// 免疫传送 (data otherImmunities). Trigger: the data's — her own row TRY_SEARCH_ALLY_SKILL (PRTS "技能范围内存在可治疗的我方
|
||
// 单位时释放技能"), which tools/build-data.mjs (TRIGGER_ALLY_RULES) writes as the engine's SKILL_RANGE + `allies` on the
|
||
// skill range x-2 (an injured, healable allied unit there, herself included; 古米 S1's option).
|
||
|
||
import { num, traitBb, skillRec, batMod, up, giveSp } from '../shared/tier1.js';
|
||
import { COLS } from '../../../constants.js';
|
||
import { effectiveProfile, acquireTargets, performAttack } from '../../../ai.js';
|
||
|
||
const S1 = 'skchr_shu_1';
|
||
const S2 = 'skchr_shu_2';
|
||
const S3 = 'skchr_shu_3';
|
||
/** The skill ranges when the data carries none: x-4 (S1, 周围), x-1 (S2), x-2 (S3) — range_table. */
|
||
const X4 = Object.freeze([[1, -1], [1, 0], [1, 1], [0, -1], [0, 0], [0, 1], [-1, -1], [-1, 0], [-1, 1]]);
|
||
const X1 = Object.freeze([[2, 0], [1, -1], [1, 0], [1, 1], [0, -2], [0, -1], [0, 0], [0, 1], [0, 2], [-1, -1], [-1, 0], [-1, 1], [-2, 0]]);
|
||
const X2 = Object.freeze([[2, -1], [2, 0], [2, 1], [1, -2], [1, -1], [1, 0], [1, 1], [1, 2], [0, -2], [0, -1], [0, 0], [0, 1], [0, 2],
|
||
[-1, -2], [-1, -1], [-1, 0], [-1, 1], [-1, 2], [-2, -1], [-2, 0], [-2, 1]]);
|
||
/** shu_t_1 sows the healed unit's tile and the four next to it (range x-5, symmetric). */
|
||
const SOW = Object.freeze([[0, 0], [1, 0], [-1, 0], [0, 1], [0, -1]]);
|
||
/** Range ids a talent's bbStr may name (GUA-X stage 3 born_range_id '0-1': her own tile). */
|
||
const RANGE_IDS = Object.freeze({ '0-1': Object.freeze([[0, 0]]) });
|
||
/** S1 "血量不足一半" — PRTS 修正 小于等于, the prefab's _maxHpRatio 0.5. */
|
||
const HALF_HP = 0.5;
|
||
/** T1's tile effect refresh and hold. */
|
||
const FARM_EVERY = 0.25;
|
||
const FARM_HOLD = 0.35;
|
||
/** T2 "三名不同 / 相同职业干员", "四名【岁】干员" (the charpack's minCount / minCntToTrig; no blackboard key). */
|
||
const PROF_NEEDED = 3;
|
||
const SUI_NEEDED = 4;
|
||
/** 【岁】: character_table groupId 'sui' (data/backups.json diy.operators powers carry it for the 自选 picks). */
|
||
const SUI = new Set(['char_2014_nian', 'char_2015_dusk', 'char_2023_ling', 'char_2024_chyue', 'char_2025_shu', 'char_2026_yu', 'char_2027_wang']);
|
||
/** 庇护 (ba.protect): one "同名效果取最高" effect per unit, whoever grants it (battle.applyStrongest). */
|
||
const PROTECT = 'protect';
|
||
const protectMods = (v) => ({ physTakenMul: 1 - v, artsTakenMul: 1 - v });
|
||
const FARM = 'talent:shu:farm';
|
||
const HARVEST = 'skill:shu:harvest';
|
||
|
||
const bbOf = (chess, id) => skillRec(chess, id)?.bb ?? {};
|
||
const gridOf = (chess, id, fallback) => { const g = skillRec(chess, id)?.rangeGrid; return g?.length ? g : fallback; };
|
||
/** The talent record with data index `i` (null when absent). */
|
||
const talentRec = (chess, i) => (chess?.talents ?? []).find((t) => t && t.index === i) ?? null;
|
||
/** The tile key of a unit's position (an enemy: its 判定中心). */
|
||
const tileKey = (u) => Math.round(u.y) * COLS + Math.round(u.x);
|
||
/** An allied unit her heals and tiles may take: on the field, not a device. */
|
||
const onField = (a) => !!a && a.alive && a.deployed && !a.hidden && a.kind !== 'device';
|
||
|
||
export default {
|
||
char_2025_shu: (bb, chess) => {
|
||
const tb = traitBb(chess);
|
||
const r0 = talentRec(chess, 0), t0 = r0?.bb ?? {}, t1 = talentRec(chess, 1)?.bb ?? {};
|
||
const born = RANGE_IDS[r0?.bbStr?.born_range_id] ?? null;
|
||
const b1 = bbOf(chess, S1), b2 = bbOf(chess, S2), b3 = bbOf(chess, S3);
|
||
const s1 = skillRec(chess, S1);
|
||
const g1 = gridOf(chess, S1, X4), g2 = gridOf(chess, S2, X1), g3 = gridOf(chess, S3, X2);
|
||
|
||
/** The sown tiles of her current deployment (a Set of tile keys). */
|
||
const sown = (unit) => {
|
||
if (unit.mem.shuSownSeq !== unit.deploySeq || !unit.mem.shuSown) { unit.mem.shuSown = new Set(); unit.mem.shuSownSeq = unit.deploySeq; }
|
||
return unit.mem.shuSown;
|
||
};
|
||
/** T1's tile effect on every allied unit standing on a sown tile (× S2's extra_extend_scale while it runs). */
|
||
const farm = (battle, unit) => {
|
||
if (!up(unit)) return;
|
||
const tiles = sown(unit);
|
||
if (!tiles.size) return;
|
||
const scale = unit.skill?.active && unit.skill.id === S2 ? num(b2.extra_extend_scale, 1) : 1;
|
||
const hp = num(t0.hp_recovery_per_sec) * scale, dr = num(t0.damage_resistance) * scale;
|
||
for (const a of battle.allyUnits) {
|
||
if (!onField(a) || !tiles.has(a.tileR * COLS + a.tileC) || !battle.allySelectable(a, unit)) continue;
|
||
const cur = a.findBuff(FARM);
|
||
if (hp > 0 && !(cur && cur.source !== unit && (cur.data?.v ?? 0) > hp && cur.timeLeft > 0.05)) {
|
||
battle.addBuff(a, { key: FARM, duration: FARM_HOLD, mods: { hpRegen: hp }, source: unit, data: { v: hp }, tags: ['talent'] });
|
||
}
|
||
if (dr > 0) battle.applyStrongest(a, PROTECT, { duration: FARM_HOLD, value: dr, mods: protectMods, source: unit });
|
||
}
|
||
};
|
||
/** Sow the tiles of `grid` around (r, c) — only tiles that can be deployed on or walked. Returns the new ones. */
|
||
const sow = (battle, unit, r, c, grid) => {
|
||
const tiles = sown(unit);
|
||
let n = 0;
|
||
for (const [dr, dc] of grid) {
|
||
const rr = r + dr, cc = c + dc;
|
||
if (!battle.grid.inRect(rr, cc)) continue;
|
||
const t = battle.grid.tile(rr, cc);
|
||
if (!t || (t.build === 'NONE' && t.pass !== 'ALL')) continue;
|
||
const k = rr * COLS + cc;
|
||
if (!tiles.has(k)) { tiles.add(k); n++; }
|
||
}
|
||
if (n > 0) { battle.fx('healField', { x: c, y: r, id: unit.id }); farm(battle, unit); }
|
||
return n;
|
||
};
|
||
/** S3: the most injured healable allied unit of her current range (`others`: not herself). */
|
||
const healTarget = (battle, unit, others) => {
|
||
const list = battle.injuredAlliesInKeys(unit.rangeKeySet || new Set(unit.rangeKeys || []), unit);
|
||
return others ? list.find((a) => a !== unit) ?? null : list[0] ?? null;
|
||
};
|
||
|
||
return {
|
||
skills: {
|
||
[S1]: {
|
||
kind: num(s1?.maxChargeTime, 1) > 1 ? 'charges' : 'instant',
|
||
trigger: { rule: 'DEFAULT', grid: g1, allies: true, hpAtMost: HALF_HP },
|
||
targeting: { rangeGrid: g1 },
|
||
attack: { dmgType: 'heal', heal: { mode: 'single', hpAtMost: HALF_HP }, healScale: num(b1.heal_scale, 1), projectile: 'none' },
|
||
},
|
||
[S2]: {
|
||
kind: 'duration',
|
||
mods: { atkPct: num(b2.atk), blockCnt: num(b2.block_cnt), batPct: batMod(b2.base_attack_time, chess) },
|
||
targeting: { rangeGrid: g2, maxTargets: Math.max(1, Math.floor(num(b2['attack@max_target'], 2))) },
|
||
attack: { dmgType: 'heal', heal: { mode: 'single' }, healScale: 1, projectile: 'none' },
|
||
onStart({ battle, unit }) { farm(battle, unit); },
|
||
onEnd({ battle, unit }) { farm(battle, unit); },
|
||
},
|
||
[S3]: {
|
||
kind: 'duration',
|
||
mods: { atkPct: num(b3.atk) },
|
||
targeting: { rangeGrid: g3 },
|
||
onStart({ unit }) { unit.mem.shuMarks = new Map(); },
|
||
onAttack({ battle, unit, targets }) {
|
||
// shu_s_3[heal_in_attack]: an attack on an enemy also heals the most injured allied unit of her range but her
|
||
if (!targets?.length || targets[0].side !== 'enemy') return;
|
||
const a = healTarget(battle, unit, true);
|
||
if (a) battle.heal(unit, a, unit.s.atk * unit.s.atkScaleMul);
|
||
},
|
||
onTick({ battle, unit }) {
|
||
if (!up(unit)) return;
|
||
const tiles = sown(unit);
|
||
// 有地面敌人处于播种地块时: the allied operators of her range ATK / ASPD + (孤立 ones too)
|
||
let onSown = 0;
|
||
for (const e of battle.enemies) if (e.alive && !e.hidden && !e.isFlying && tiles.has(tileKey(e))) onSown++;
|
||
const stacks = Math.min(onSown, Math.max(1, Math.floor(num(b3.e_max_stack_cnt, 1))));
|
||
if (stacks > 0) {
|
||
const keys = unit.rangeKeySet || new Set(unit.rangeKeys || []);
|
||
for (const a of battle.allyUnits) {
|
||
if (a.kind !== 'op' || !onField(a) || !keys.has(a.tileR * COLS + a.tileC)) continue;
|
||
battle.addBuff(a, { key: HARVEST, duration: 0.1, mods: { atkPct: num(b3.e_atk) * stacks, aspd: num(b3.e_attack_speed) * stacks }, source: unit, tags: ['skill'] });
|
||
}
|
||
}
|
||
// 【播种标记】: a ground enemy entering a sown tile records it; ≥ max_distance (Manhattan) away it is put back there
|
||
const marks = unit.mem.shuMarks ?? (unit.mem.shuMarks = new Map());
|
||
const far = Math.max(1, num(b3.max_distance, 4));
|
||
for (const e of battle.enemies) {
|
||
const m = marks.get(e.id);
|
||
if (!e.alive || e.hidden) { if (m) marks.delete(e.id); continue; }
|
||
const k = tileKey(e);
|
||
if (!m) {
|
||
if (!e.isFlying && tiles.has(k)) marks.set(e.id, { r: Math.round(e.y), c: Math.round(e.x), last: k });
|
||
continue;
|
||
}
|
||
if (k === m.last) continue;
|
||
m.last = k;
|
||
const dist = Math.abs(Math.floor(k / COLS) - m.r) + Math.abs((k % COLS) - m.c);
|
||
if (dist < far - 1e-9) continue;
|
||
e.x = m.c;
|
||
e.y = m.r;
|
||
battle._unblock(e);
|
||
if (e.route) e.route.pts = null;
|
||
m.last = m.r * COLS + m.c;
|
||
battle.fx('teleport', { x: m.c, y: m.r, id: e.id, src: unit.id });
|
||
}
|
||
// no enemy to attack: her attack heals the most injured allied unit of her range (herself included)
|
||
if (unit.atkCd > 0 || !unit.canAct || unit.s.flags.disarm || !unit.profile) return;
|
||
const prof = effectiveProfile(unit);
|
||
if (acquireTargets(battle, unit, prof).length) return;
|
||
const a = healTarget(battle, unit, false);
|
||
if (!a) return;
|
||
performAttack(battle, unit, { ...prof, dmgType: 'heal', heal: { mode: 'single' }, atkScale: 1, healScale: 1, projectile: 'none', splashRadius: 0, chain: null, hits: 1 }, [a]);
|
||
unit.atkCd = Math.max(unit.atkCd, unit.s.interval);
|
||
},
|
||
onEnd({ unit }) { unit.mem.shuMarks = null; },
|
||
},
|
||
},
|
||
talents: [
|
||
{ install(battle, unit) { // 百谷长青: every heal she outputs sows the healed unit's x-5; the sown tiles regenerate / 庇护
|
||
battle.on('heal', (c) => {
|
||
if (c.source !== unit || !up(unit) || c.opts?.regen || !(c.amount > 0) || !onField(c.target)) return;
|
||
sow(battle, unit, c.target.tileR, c.target.tileC, SOW);
|
||
}, { owner: unit, priority: -20 });
|
||
battle.on('deploy', (ctx) => {
|
||
if (ctx.unit !== unit) return;
|
||
sown(unit).clear();
|
||
if (born) sow(battle, unit, unit.tileR, unit.tileC, born); // GUA-X stage 3: 部署时立即给所处地块播种该效果
|
||
}, { owner: unit });
|
||
battle.on('death', (ctx) => { // 自身退场时清空所有地块的播种效果
|
||
if (ctx.unit !== unit) return;
|
||
sown(unit).clear();
|
||
for (const a of battle.allyUnits) if (a.findBuff(FARM)?.source === unit) battle.removeBuff(a, FARM);
|
||
}, { owner: unit });
|
||
battle.every(FARM_EVERY, () => farm(battle, unit), { owner: unit, immediate: true });
|
||
} },
|
||
{ install(battle, unit) { // 天有四时
|
||
const hpUp = num(t1.max_hp), asUp = num(t1.attack_speed);
|
||
battle.every(FARM_EVERY, () => {
|
||
if (!up(unit)) return;
|
||
const ops = battle.allyUnits.filter((a) => a.kind === 'op' && onField(a));
|
||
const per = new Map();
|
||
for (const a of ops) { const p = a.def?.profession ?? '?'; per.set(p, (per.get(p) ?? 0) + 1); }
|
||
const many = per.size >= PROF_NEEDED, same = Math.max(0, ...per.values()) >= PROF_NEEDED;
|
||
for (const a of ops) {
|
||
if (many && hpUp) battle.addBuff(a, { key: 'talent:shu:seasons:hp', duration: FARM_HOLD, mods: { hpPct: hpUp }, source: unit, tags: ['talent'] });
|
||
if (same && asUp) battle.addBuff(a, { key: 'talent:shu:seasons:aspd', duration: FARM_HOLD, mods: { aspd: asUp }, source: unit, tags: ['talent'] });
|
||
}
|
||
}, { owner: unit, immediate: true });
|
||
// 编入队伍且编队中有四名【岁】干员: her squad's operators ATK + for the battle, +sp SP every `interval` s deployed
|
||
const squadOf = () => battle.allyUnits.filter((a) => a.kind === 'op' && a.ownerId === unit.ownerId);
|
||
const atk = num(t1.atk), sp = num(t1.sp), iv = num(t1.interval);
|
||
const spBuff = (a) => {
|
||
if (sp > 0 && iv > 0) battle.addBuff(a, { key: 'talent:shu:suiSp', interval: iv, onTick: ({ unit: x }) => giveSp(x, sp), source: unit, tags: ['talent'] });
|
||
};
|
||
const apply = () => {
|
||
if (unit.mem.shuSuiDone) return;
|
||
unit.mem.shuSuiDone = true;
|
||
const squad = squadOf();
|
||
if (squad.filter((a) => SUI.has(a.def?.charId)).length < SUI_NEEDED) return;
|
||
const members = new Set(squad);
|
||
for (const a of squad) {
|
||
if (atk) battle.addBuff(a, { key: 'talent:shu:sui', mods: { atkPct: atk }, persist: true, allowDead: true, source: unit, tags: ['talent'] });
|
||
if (onField(a)) spBuff(a);
|
||
}
|
||
battle.on('deploy', (ctx) => { if (members.has(ctx.unit)) spBuff(ctx.unit); }, { owner: unit });
|
||
};
|
||
if (battle.started) apply();
|
||
else battle.on('battleStart', apply, { owner: unit });
|
||
} },
|
||
],
|
||
install(battle, unit) {
|
||
// GUA-X: 治疗生命值低于50%的友方单位时治疗量提升15% (heal_scale_up[hpratio][LE]: at or below hp_ratio)
|
||
const hs = num(tb.heal_scale, 1), hr = num(tb.hp_ratio);
|
||
if (hs !== 1 && hr > 0) {
|
||
battle.on('heal', (c) => {
|
||
if (c.source === unit && !c.opts?.regen && c.target && c.target.hpRatio <= hr + 1e-9) c.amount *= hs;
|
||
}, { owner: unit });
|
||
}
|
||
},
|
||
};
|
||
},
|
||
};
|