Files
ark-stronghold-protocol/server/sim/content/kits/ops/op-helage.js
T
sganggs a62728c188 feedback5 O4: 自选 operator kits for 陈, 艾丽妮 and 赫拉格 — every skill, talent, trait and module (stage 1 and 3) at every form
- op-chen.js (char_010_chen, 剑豪): S1 鞘击 one sheath strike (her Skill clip: one OnAttack) + stun; S2 赤霄·拔刀 up to
  max_target enemies of 3-12, flyers too, arts then physical (PRTS 备注); S3 赤霄·绝影 10 slashes on the nearest ground
  enemy of x-1 at the Skill_3 clip's OnAttack times, the last stuns, 2.933 s of 无敌 / 无法阻挡 / stun and freeze
  immunity, ends at once with no target before the 10th (PRTS 备注), not opened with only flyers in range [ASSUMED];
  呵斥 per-deployment timer +1 attack / hurt SP to the field's allies (SWO-X 3: every 3 s, +1 more for her); 持刀格斗术;
  SWO-X x1.1 during her skill (PRTS 修正 技能期间), SWO-Y ignores 70 DEF.
- op-irene.js (char_4009_irene, 剑豪): 审判之火 as PRTS 备注 (a 物理穿透 charge: air units always and without a draw,
  浮空 enemies count, ground at 50 %, spent only by a landing instance); SWO-X 3: 55 % and +6 SP after a skill that knocked
  out an air unit; 净化之剑 doubled while a 【海怪】 is on the field; S1 起风 strike + 浮空 + second strike; S2 裂潮 2
  charges, ground only, 重量 <= 3 float, not opened with only flyers in range [ASSUMED]; S3 判决 3.5 s (PRTS): opening
  strike + 浮空, 10 bombardments every 0.3 s, radius 1.1, air too; SWO-X x1.1 skill damage, SWO-Y 70 DEF, ISW-A
  attributes only (its talent parts are 集成战略-only).
- op-helage.js (char_188_helage, 武者): S1 新月 two hits, S2 弦月 二连击 + 75 % dodge, S3 满月 range +1 / 3 targets;
  the 武者 heal per damage instance (the kit adds one per further hit of an attack); 坚忍 ASPD (SBL-X 3: +130 at 50 %);
  运筹帷幄 生命回复速度 while not blocking [ASSUMED: no PRTS 备注], SBL-Y 3 also below 30 % HP (80 / s, never both);
  SBL-X 庇护 25 % below 50 % HP; SBL-Y once per deployment back to 30 % HP.
- kits/index.js OPERATOR_KIT_FILES: the three files appended. Tests: op_chen / op_irene / op_helage (10 each, every form x
  module x skill, numbers from data/backups.json).
- Golden: only the diy family moved — the packing now fields the new kits: diy-003 … diy-006 (the prototypes before) and
  the new diy-007 … diy-009 field 陈 / 艾丽妮 / 赫拉格 in every form x module x skill, the prototypes moved to the new
  diy-010 … diy-013; diy-001 / diy-002 (推进之王) and roster / bonds / fields / matches / standins unchanged.
  test/golden/README.md: the diy row no longer counts battles or names 推进之王 alone.
2026-10-05 23:51:27 +08:00

125 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// server/sim/content/kits/ops/op-helage.js — 赫拉格 (char_188_helage) 自选 operator kit: 6★ 武者 (近卫), an owned-6★ pick of
// the tier-5 and tier-6 自选 slots; every skill, both talents, the trait and both modules at every form.
// Kit contract and the 自选 rules: ../README.md ("How to add an operator (自选)").
//
// Forms (data/backups.json units.char_188_helage, 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].
// Sources: character_table / skill_table / battle_equip_table (zh_CN, as built into backups.json), PRTS 赫拉格 (talents,
// skills, modules: no 备注), PRTS 分支特性信息 §武者 ("常态持有禁疗;通过自身特性/天赋/技能产生的作用于自身的治疗效果会无视自身的
// 禁疗"; "特性治疗于干员每次输出伤害时触发(不局限于攻击)"), gamedata_const ba.berserk 坚忍 ("根据已损失的生命值获得相应比例的
// 属性加成,损失一定比例时达最大加成(同类属性取最高)") and ba.protect 庇护 ("受到的物理和法术伤害降低相应比例(同名效果取最高)"), his
// battle skeleton char_188_helage.skel (Skill: two OnAttack, Skill_2: two, Skill_3_Loop: one).
// - Trait (武者) "不成为其他角色的治疗目标,每次攻击到敌人后回复自身70生命": the profession default (professions.js `musha`: no
// heal from others, value HP per enemy an attack hits), ground-only melee on his 1-1, block 1. The branch heals on every
// damage instance he deals: the engine heals once per enemy an attack strikes, so the second hit of S1 / S2's 二连击 on
// an enemy heals once more here (the trait's `value`, a self heal).
// - T1 月盈星亏 "在场时,自身获得最高+100攻击速度的坚忍(损失70%生命值时达到最大加成)": ASPD + min_attack_speed × the share of
// the HP lost up to 1 − min_hp_ratio (linear, 坚忍), refreshed every tick. SBL-X stage 3: +130 at 50 % lost (the module
// talent change).
// - T2 运筹帷幄 "未阻挡敌人时每秒回复60生命": hp_recovery_per_sec is the 生命回复速度 attribute (the stat hpRecoveryPerSec;
// SIM.md §4: an hpRegen buff, not a heal — no 治疗加成, never stopped by 禁疗), on while he blocks nobody [ASSUMED: PRTS
// has no 备注 on it; the blackboard key is the attribute's, as 角峰 S1 / 宴 S1 in their kits — the 0.2.0 生命回复速度 audit
// lists such 「每秒回复」 + hp_recovery_per_sec effects as open; a heal would reach him too, 武者's own heals ignore his
// 禁疗, and only 治疗加成 would differ]. SBL-Y stage 3
// "未阻挡敌人或生命值低于30%时每秒回复80生命": 80 / s and the hidden module talent (index 2, hp_ratio / hp_recovery_per_sec):
// the same regeneration below hp_ratio while he blocks [ASSUMED: the text's 或 — 80 / s when either holds, never both
// parts at once].
// - Module SBL-X “藏锋” "生命值低于50%时,获得25%的庇护" (a display trait part; the effect is the hidden talent part hp_ratio /
// damage_resistance, merged into talent 0's blackboard): below hp_ratio of his max HP the physical and arts damage he takes
// ×(1 − damage_resistance) (as 宴's elite module).
// - Module SBL-Y “热的雪” "被击倒时不撤退且回复30%生命(单次部署只触发1次)" (trait bb hp_ratio): the first lethal blow of a
// deployment leaves him on the field at hp_ratio of his max HP (a kit saver, priority −50 as 斯卡蒂's module).
// - S1 新月 (AUTO, attack SP, DEFAULT): the next attack at atk_scale × ATK, twice (Skill clip: two OnAttack).
// - S2 弦月 (MANUAL, time SP, DEFAULT): `duration` s — ATK +atk, every attack two hits (二连击), 物理闪避 +prob.
// - S3 满月 (MANUAL, time SP, data ACTIVE_RANGE on the running 1-1 + 1): `duration` s — ATK +atk, attack range +1 tile
// forward (ability_range_forward_extend), up to attack@max_target targets at once; back to 1-1 after.
import { num, traitBb, skillRec, toggleBuff, up, onHitOn } from '../shared/tier1.js';
const S1 = 'skchr_helage_1';
const S2 = 'skchr_helage_2';
const S3 = 'skchr_helage_3';
const bbOf = (chess, id) => skillRec(chess, id)?.bb ?? {};
/** The blackboard of the talent with data index `i` ({} when absent; module parts with a new index included). */
const talentAt = (chess, i) => (chess?.talents ?? []).find((t) => t && t.index === i)?.bb ?? {};
export default {
char_188_helage: (bb, chess) => {
const t0 = talentAt(chess, 0), t1 = talentAt(chess, 1), t2 = talentAt(chess, 2);
const tb = traitBb(chess);
const b1 = bbOf(chess, S1), b2 = bbOf(chess, S2), b3 = bbOf(chess, S3);
return {
skills: {
[S1]: {
kind: num(skillRec(chess, S1)?.maxChargeTime, 1) > 1 ? 'charges' : 'instant',
attack: { atkScale: num(b1.atk_scale, 1), hits: 2 },
},
[S2]: {
kind: 'duration',
mods: { atkPct: num(b2.atk), dodgePhys: num(b2.prob) },
attack: { hits: 2 },
},
[S3]: {
kind: 'duration',
mods: { atkPct: num(b3.atk) },
targeting: { rangeExtend: num(b3.ability_range_forward_extend), maxTargets: Math.max(1, Math.floor(num(b3['attack@max_target'], 1))) },
},
},
talents: [
{ install(battle, unit) { // 月盈星亏: 坚忍 ASPD up to min_attack_speed, reached at min_hp_ratio HP (linear in the HP lost)
const maxAs = num(t0.min_attack_speed), minHp = num(t0.min_hp_ratio);
if (!(maxAs > 0) || !(minHp < 1)) return;
const key = 'talent:helage:berserk';
battle.on('tick', () => {
const cur = unit.findBuff(key);
if (!up(unit)) { if (cur) battle.removeBuff(unit, key); return; }
const v = maxAs * Math.max(0, Math.min(1, (1 - unit.hpRatio) / (1 - minHp)));
if (cur && Math.abs((cur.data?.v ?? 0) - v) < 0.05) return;
if (v < 0.05) { if (cur) battle.removeBuff(unit, key); return; }
battle.addBuff(unit, { key, mods: { aspd: v }, data: { v }, tags: ['talent'] });
}, { owner: unit });
} },
{ install(battle, unit) { // 运筹帷幄: 生命回复速度 while blocking nobody; SBL-Y stage 3: or below hp_ratio HP
const regen = num(t1.hp_recovery_per_sec);
if (regen > 0) toggleBuff(battle, unit, 'talent:helage:regen', () => unit.blocking.length === 0, { hpRegen: regen });
const lowHp = num(t2.hp_ratio), lowRegen = num(t2.hp_recovery_per_sec);
if (lowRegen > 0 && lowHp > 0) {
toggleBuff(battle, unit, 'talent:helage:regenLow', () => unit.blocking.length > 0 && unit.hpRatio < lowHp, { hpRegen: lowRegen });
}
} },
],
install(battle, unit) {
// 武者: the trait heal on every damage instance — the engine heals once per enemy an attack strikes, so each further
// hit of the same attack on that enemy (S1, S2's 二连击) heals once more
battle.on('damaged', (c) => {
const d = c.dmg;
if (c.source !== unit || !unit.alive || !d || !d.isAttack || !d.attackId || !c.target || c.target.side !== 'enemy') return;
const m = unit.mem;
if (m.helageAttackId !== d.attackId) { m.helageAttackId = d.attackId; m.helageStruck = new Set(); }
if (!m.helageStruck.has(c.target.id)) { m.helageStruck.add(c.target.id); return; }
battle.heal(unit, unit, num(unit.profile?.selfHeal, num(tb.value, 70)), { self: true });
}, { owner: unit, priority: -10 });
// SBL-X “藏锋”: 生命值低于50%时,获得25%的庇护 (physical and arts damage taken)
const dr = num(t0.damage_resistance), drBelow = num(t0.hp_ratio);
if (dr > 0 && drBelow > 0) {
onHitOn(battle, unit, ({ dmg }) => {
if ((dmg.type === 'phys' || dmg.type === 'arts') && unit.hpRatio < drBelow) dmg.mul *= 1 - dr;
});
}
// SBL-Y “热的雪”: 被击倒时不撤退且回复30%生命(单次部署只触发1次)
const rise = num(tb.hp_ratio);
if (rise > 0) {
battle.on('fatal', (ctx) => {
if (ctx.unit !== unit || ctx.prevented || unit.mem.helageRoseSeq === unit.deploySeq) return;
unit.mem.helageRoseSeq = unit.deploySeq;
ctx.prevented = true;
unit.hp = Math.max(1, unit.s.maxHp * rise);
battle.fx('undying', { x: unit.x, y: unit.y, id: unit.id });
}, { owner: unit, priority: -50 });
}
},
};
},
};