feedback5 O9: 自选 operator kits 帕拉斯, 止颂 and 贝洛内 (with his 牵绊) — every skill, talent, trait and module (stage 1 / 3) at every form

- op-pallas.js (char_485_pallas, 教官): 英雄的诞生 精力充沛 on every 米诺斯 operator (one buff per ally, the
  highest active bonus — her S3's too), 女神的振奋 on every damage instance (her 1-1, through 禁疗; INS-Y stage 3 the
  米诺斯 extra), S1 two strikes, S2 range +1 / ATK / stun (data DEFAULT: build-data's ATTACK_RANGE_CHANGE misses
  "攻击范围向前延伸一格" — reported, not worked around), S3 three targets and the front-holder buff (low ground,
  operators only, passed on retreat / deploy). INS-Y's "可以额外部署在远程位" is a prep placement rule (owner's
  decision of 2026-10-04): unchanged, reported.
- op-lessng.js (char_4011_lessng, 无畏者): 苦痛专注 (hit-time cut while blocking, 无来源 / allies / himself
  included), 痛楚砺刃 (any damage aimed at him, dodged too), S2 the deploy window (2 strikes, ATK, talent ×2), S3
  status immunity, blocked-target scale, the cast under a 异常状态 (600 arts first, cleanse, then HP; 沉默 does
  not hold it back), DRE-X ×1.15 on a blocked target, DRE-Y once-per-deployment save and stage 3's 12 % DEF ignore.
- op-demetr.js (char_4037_demetr, 斗士): 【手段】 (one per enemy, stacks before the hit, 8 and 停顿 under S2, back
  to 5 on the 0.1 s update), the HP-ratio damage bonus, 街头直觉 from every deployment (a 【移动】 too), S1 two
  strikes, S2 3-13 / three targets, S3 the 【移动】s (x-2 around his located tile, buildable low-ground tiles,
  in-place move without a target, re-target after 1 s without damage or when the elite he hit falls), 牵绊
  (无敌 / untargetable / 孤立), the fatal-hit 不死 + end, the 【返回】 (SP emptied, a short 不死) and the forced
  retreat; FGT-Y ASPD above half HP.
- kits/index.js: the three files appended to OPERATOR_KIT_FILES.
- Tests: test/content/op_pallas.test.js (9), op_lessng.test.js (10), op_demetr.test.js (10).
- Golden: only diy moved — diy-049 … 054 now field the three operators (every form × module × skill), the
  prototype / reserve battles moved down to diy-054 … 058 (new ids, same packing); diy-001 … 048 and every other
  family unchanged.
This commit is contained in:
sganggs committed 2026-10-06 01:30:26 +08:00
1 parent fef4ea3d4b
commit c454b61075
8 files changed
+2062 -118

No files matched your search

+168
View File
@@ -0,0 +1,168 @@
// server/sim/content/kits/ops/op-pallas.js — 帕拉斯 (char_485_pallas) 自选 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_485_pallas, 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 帕拉斯 (英勇的祝福 备注
// "持有增益的干员撤退时,增益立刻转交至帕拉斯;自身持有增益,满足条件的干员部署时,帕拉斯的增益立刻转交给对方"; 女神的振奋 备注
// "本天赋无视自身获得的禁疗,且必定进行治疗"); gamedata_const ba.strong 精力充沛 "生命值高于一定比例时获得一定属性加成(同类属性取
// 最高)"; the client's battle data — charpack char_485_pallas (英雄的诞生: an aura on every ally tagged `minos`; S3: an aura on
// her 1-1 for operators only, professionMask without TOKEN / TRAP), skchr_pallas_1 (`_additionalTimes` 1), buff_template_data
// `pallas_t_2` / `pallas_t_2[heal]`, `pallas_s_3[peak]`, `pallas_e_t_2[trigger]` and [uc]equips pallas_equip_2_x_p2.
// - Trait (教官) "可以攻击到较远敌人,攻击自身未阻挡的敌人时攻击力提升至120%": the profession profile (professions.js
// instructor: ×atk_scale on a target she does not block, her attacks only; range 2-2). Module INS-X “故乡的山岩” raises
// it to 130% (its traitOverride bb atk_scale, read by the profile through the composed record).
// - T1 英雄的诞生 "在场时,所有【米诺斯】干员生命值高于80%时获得+25%攻击力的精力充沛" (bb peak_performance.hp_ratio / .atk):
// while she is deployed, every allied operator of nation minos (her included — the aura has no self exclusion) gets ATK
// +atk while its HP ratio is above hp_ratio. INS-X stage 3: above 50 %, +30 % (the module's talent change). 精力充沛 of
// several sources is "同类属性取最高": one buff per ally carrying the highest ATK bonus whose HP condition holds (her T1
// and her S3 alike, several 帕拉斯 of a shared field too), re-evaluated every tick. 帕拉斯 is the only minos operator of
// the data (chess.json and backups.json), so in practice the talent is hers.
// - T2 女神的振奋 "每攻击一名敌人时为自身与身前一格的我方干员恢复40点生命值" (bb value): pallas_t_2 is ON_OUTPUT_DAMAGE — every
// damage instance she deals (S1's two strikes, each S3 target) heals every ally on her 1-1 (her tile and the tile in
// front, rotated with her) `value` HP, a heal that ignores 禁疗 (FixedValueHeal _ignoreHealFree; PRTS 备注) but not
// "不成为其他角色的治疗目标" (the heal's selector is HEAL-purpose). INS-Y “赫里亚之辉” stage 2+: value 60 and "并使所有【米诺斯】干员
// 额外恢复40点生命值" (talent bb pallas_e_t_2.value): pallas_e_t_2[trigger] (ON_OUTPUT_DAMAGE) uses pallas_e_heal on every
// ally its aura marks — tags minos, _selfOption INCLUDE, so herself too — for that much more (also ignoring 禁疗).
// - Module INS-Y "可以额外部署在远程位" (hidden module talent buildable_type 2) is a placement rule of the prep, not of the
// battle: by the owner's decision of 2026-10-04 (shared/highGround.js) only elite 歌蕾蒂娅 + HOK-Y may take a 高台, so a
// 帕拉斯 piece stays on the melee tiles — an open question for the owner (DESIGN draft 0.2.0.o9), nothing in this kit.
// - S1 胜利的连击 (AUTO, attack SP 3; data DEFAULT): the next attack strikes its target twice for atk_scale × ATK physical
// (skchr_pallas_1 `_additionalTimes` 1, `_triggerDelta` 0.1 s: the second strike 0.1 s after the first — both land in the
// attack here [ASSUMED: the 0.1 s is not modelled]); the instructor ×1.2 applies to each strike.
// - S2 信念的长鞭 (MANUAL, 20 / 22 s): attack range +ability_range_forward_extend tile forward (2-2 ⇒ 4 tiles: targeting
// rangeExtend, the card's live range follows), ATK +atk, every attack attack@buff_prob to stun its target attack@stun s.
// Its trigger is the data's DEFAULT: tools/build-data.mjs ATTACK_RANGE_CHANGE does not read "攻击范围向前延伸一格", so the
// owner's ACTIVE_RANGE rule (a running range that strictly contains the own one) does not reach it — reported, not
// worked around (the brief: report a trigger that looks wrong).
// - S3 英勇的祝福 (MANUAL, 30 s; data DEFAULT): ATK +atk, attacks hit attack@max_target (3) targets; the operator deployed
// on the tile in front of her, when that tile is low ground (pallas_s_3[peak] CheckHeightTypeOfRootTile LOWLAND: on any
// other tile the buff is finished on it), holds "生命值高于80%获得+35%攻击力的精力充沛、防御力+20%、阻挡数+1"
// (attack@peak_performance.hp_ratio / .atk, attack@def, attack@block_cnt) — else she holds it herself; the holder is
// re-read every tick, so it passes to her the moment the front operator leaves and back when one deploys there (PRTS
// 备注). Summons do not hold it (the aura's professionMask). The DEF / block part is per source (independentCharacterSource).
// Melee, ground-only (data canHitFly false: no anti-air); ground enemies target her normally (no 起飞 / 迷彩 / 隐匿).
import { num, talentBb, skillRec, up, alliesInGridOf } from '../shared/tier1.js';
import { frontOf } from '../../../dir.js';
const S1 = 'skchr_pallas_1';
const S2 = 'skchr_pallas_2';
const S3 = 'skchr_pallas_3';
/** The nation of 英雄的诞生 / pallas_e_heal (the client's `minos` tag = character_table nationId minos). */
const MINOS = 'minos';
/** T2's heal area: her 1-1 (pallas_t_2 `_rangeId` 1-1) — her tile and the tile in front. */
const RANGE_1_1 = Object.freeze([[0, 0], [0, 1]]);
/** The one 精力充沛 buff of an ally (ba.strong "同类属性取最高"). */
export const PEAK_KEY = 'talent:pallas:peak';
/** The S3 holder's DEF / block buff, per 帕拉斯 (independentCharacterSource). */
export const holderKey = (unit) => `skill:pallas:blessing#${unit.id}`;
const bbOf = (chess, id) => skillRec(chess, id)?.bb ?? {};
const isMinos = (a) => !!a && a.kind === 'op' && (a.def?.raw?.nationId ?? a.def?.nationId) === MINOS;
/**
* The 精力充沛 sources of a battle (a Set of `(ally) => ATK bonus`, 0 when one does not apply); the first registration
* installs the battle-wide tick that keeps, on every deployed ally, one buff with the highest bonus whose HP condition
* holds (never a sum: "同类属性取最高").
*/
const PEAKS = new WeakMap();
function peakSources(battle) {
let set = PEAKS.get(battle);
if (set) return set;
PEAKS.set(battle, (set = new Set()));
battle.on('tick', () => {
for (const a of battle.allyUnits) {
if (!a.alive || !a.deployed || a.kind === 'device') continue;
let v = 0;
for (const f of set) v = Math.max(v, f(a));
const cur = a.findBuff(PEAK_KEY);
if ((cur?.data?.v ?? 0) === v) continue;
if (v > 0) battle.addBuff(a, { key: PEAK_KEY, mods: { atkPct: v }, data: { v }, tags: ['talent'] });
else battle.removeBuff(a, PEAK_KEY);
}
});
return set;
}
/**
* The holder of 英勇的祝福's buff: the operator deployed on the tile in front of her when it stands on low ground (and her
* aura may select it), else herself.
*/
export function blessingHolder(battle, unit) {
const [fr, fc] = frontOf(unit.tileR, unit.tileC, unit.dir, 1);
const a = battle.grid.inRect(fr, fc) ? battle.unitAt(fr, fc) : null;
if (a && a !== unit && a.side === 'ally' && a.kind === 'op' && a.deployed && a.ground !== false && battle.grid.isLow(fr, fc)
&& battle.allySelectable(a, unit)) return a;
return unit;
}
export default {
char_485_pallas: (bb, chess) => {
const t0 = talentBb(chess, 0); // 英雄的诞生 (INS-X stage 3: hp_ratio 0.5, atk 0.3)
const t1 = talentBb(chess, 1); // 女神的振奋 (INS-Y stage 3: value 60, pallas_e_t_2.value 40)
const b1 = bbOf(chess, S1), b2 = bbOf(chess, S2), b3 = bbOf(chess, S3);
const blessDef = num(b3['attack@def']), blessBlock = num(b3['attack@block_cnt']);
const blessHp = num(b3['attack@peak_performance.hp_ratio'], 0.8), blessAtk = num(b3['attack@peak_performance.atk']);
/** Move 英勇的祝福 to the current holder (DEF / block here; its 精力充沛 goes through the shared tick). */
const bless = ({ battle, unit }) => {
const st = unit.mem.pallasBless ?? (unit.mem.pallasBless = { holder: null });
const h = unit.skill?.active && up(unit) ? blessingHolder(battle, unit) : null;
if (st.holder === h) return;
if (st.holder) battle.removeBuff(st.holder, holderKey(unit));
st.holder = h;
if (h) battle.addBuff(h, { key: holderKey(unit), source: unit, mods: { defPct: blessDef, blockCnt: blessBlock }, tags: ['skill'], visible: true });
};
return {
skills: {
[S1]: { kind: 'instant', attack: { atkScale: num(b1.atk_scale, 1), hits: 2 } },
[S2]: {
kind: 'duration',
mods: { atkPct: num(b2.atk) },
targeting: { rangeExtend: Math.max(0, Math.round(num(b2.ability_range_forward_extend))) },
attack: {
onHit({ battle, unit, target }) {
if (target && target.alive && target.side === 'enemy' && battle.rng.chance(num(b2['attack@buff_prob']))) {
battle.applyStatus(target, 'stun', { duration: num(b2['attack@stun']), source: unit });
}
},
},
},
[S3]: {
kind: 'duration',
mods: { atkPct: num(b3.atk) },
targeting: { maxTargets: Math.max(1, num(b3['attack@max_target'], 3)) },
onStart: bless,
onTick: bless,
onEnd: bless,
},
},
talents: [
{ install(battle, unit) { // 英雄的诞生 (and S3's 精力充沛, same 同类取最高 buff)
const hp0 = num(t0['peak_performance.hp_ratio'], 0.8), atk0 = num(t0['peak_performance.atk']);
peakSources(battle).add((a) => {
if (!up(unit) || !battle.allySelectable(a, unit)) return 0;
let v = 0;
if (atk0 > 0 && isMinos(a) && a.hpRatio > hp0) v = atk0;
if (blessAtk > 0 && unit.mem.pallasBless?.holder === a && unit.skill?.active && a.hpRatio > blessHp) v = Math.max(v, blessAtk);
return v;
});
} },
{ install(battle, unit) { // 女神的振奋 (+ INS-Y stage 2+: the minos extra heal)
const value = num(t1.value), extra = num(t1['pallas_e_t_2.value']);
if (!(value > 0) && !(extra > 0)) return;
battle.on('damaged', (c) => {
if (c.source !== unit || !c.target || c.target.side !== 'enemy' || c.type === 'element' || !up(unit)) return;
if (value > 0) {
for (const a of alliesInGridOf(battle, unit, RANGE_1_1)) battle.heal(unit, a, value, { self: a === unit, ignoreHealFree: true, tags: ['talent'] });
}
if (extra > 0) {
for (const a of battle.alliesFor(unit)) if (isMinos(a)) battle.heal(unit, a, extra, { self: a === unit, ignoreHealFree: true, tags: ['talent', 'module'] });
}
}, { owner: unit });
} },
],
};
},
};