- 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.
125 lines
8.5 KiB
JavaScript
125 lines
8.5 KiB
JavaScript
// 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 });
|
||
}
|
||
},
|
||
};
|
||
},
|
||
};
|