feedback5 R4: one file per operator kit — kits/tier1..6.js split into kits/ops/<chessId>-<codename>.js (129 kits), kits/shared/tier1..6.js (the helpers two or more kits of a tier use, and each tier's header notes) and kits/index.js (the registry, same keys in the same order); the tier files become re-export shims

Pure move: every line of a kit, of a helper and of the tier headers is copied verbatim (only the headers' first
line names the new file); a helper used by one kit moved into that kit's file. Glue only: the generated imports,
`export default { … }` around each registry entry, `export { … }` lists in shared/tier2..6.js, and tier4's
`(bb, chess, def)` default-argument wrapper as shared/tier4.js `withDefaults` (applied in each tier-4 kit file).
kits/index.js loads every kit file with a guarded dynamic import (browser-safe, no directory listing); tierN.js
re-export the tier registry (TIER_KITS) and their old named exports (tier1 helpers + tinmanKit, tier6
PITHST_ELEMENTS). The merged registry, every tier default and named export have the same keys, order, frozenness
and function sources as before. test/docs-consistency.test.js reads the moved code at its new paths.
Golden: GOLDEN_FULL=1 node --test test/golden.test.js unchanged.
This commit is contained in:
sganggs committed 2026-10-05 16:13:56 +08:00
1 parent 6b489b1776
commit 1169102d46
143 files changed
+12924 -11784

No files matched your search

@@ -0,0 +1,118 @@
// server/sim/content/kits/ops/chess_char_4_19-flamtl.js — 焰尾 (char_420_flamtl) kit, tier 4.
// Conventions of the tier-4 kits: ../shared/tier4.js; kit contract and rules: ../README.md.
import { canTargetEnemy, sortEnemyTargets } from '../../../targeting.js';
import {
AURA, num, tbb, grid, nationOf, targetsInGrid, whileDeployed, pulse, toggleBuff, skillActive, isSel, alt,
withDefaults,
} from '../shared/tier4.js';
export default withDefaults({
// ===== 焰尾 (pioneer) S3 焰心 — 8 DP over the skill, faster, +ATK, block +1, 60 % dodge; talents 前锋剑术 / 红松骑士团团长
// S1 迅敏直觉 (+6 DP, dodges the next physical attack); S2 “红松林” (+11/12 DP; ≤ 6 enemies around: 2 × 180 %/210 %
// phys + 0.5 s stun, air units too — PRTS 备注 "※可对空"; allies around +40 %/45 % physical dodge for 10 s);
// module SOL-X (她们的未来): ATK/DEF +8 % while blocking. Her dodges (any source) feed talent 前锋剑术.
chess_char_4_19_a: (bb, chess, def) => {
const t1 = tbb(def, 1);
const tb = def.traitBb || {};
const total = Math.max(0, Math.floor(num(bb.value, 8))), per = num(bb.cost, 1);
const p = num(bb.prob, 0.6);
const S1 = isSel(def, 'skchr_flamtl_1'), S3 = isSel(def, 'skchr_flamtl_3');
const g = grid(def.skill?.rangeGrid);
return {
skills: alt(def, {
// (自动触发: an AUTO skill takes no 技能策略 — an AUTO DP skill fires as soon as it is ready, like 伺夜 S1)
skchr_flamtl_1: () => ({
kind: 'instant',
trigger: 'SP_FULL',
onStart({ battle, unit }) {
battle.addDp(unit.ownerId, num(bb.cost, 6));
battle.addBuff(unit, { key: 'flamtl:evade', visible: true });
battle.fx('dp', { x: unit.x, y: unit.y, id: unit.id, n: num(bb.cost, 6) });
},
}),
skchr_flamtl_2: () => ({
kind: 'instant',
onStart({ battle, unit }) {
battle.addDp(unit.ownerId, num(bb.cost, 11));
const area = g || [[0, 0], [0, 1]];
const foes = targetsInGrid(battle, unit, area);
sortEnemyTargets(battle, unit, foes, null);
for (const e of foes.slice(0, Math.max(1, Math.floor(num(bb.max_target, 6))))) {
for (let i = 0; i < 2 && e.alive; i++) battle.dealDamage(unit, e, { amount: unit.s.atk * num(bb.atk_scale, 1.8), type: 'phys', isSkill: true, tags: ['skill', 'redPine'] });
if (e.alive) battle.applyStatus(e, 'stun', { duration: num(bb.stun, 0.5), source: unit });
}
for (const a of battle.unitsInGrid(unit, area, { side: 'ally' })) {
if (a.kind === 'device' || !battle.allySelectable(a, unit)) continue;
battle.addBuff(a, { key: 'flamtl:redPine', duration: num(bb['flamtl_s_2.duration'], 10), mods: { dodgePhys: num(bb['flamtl_s_2.prob'], 0.4) }, visible: true, source: unit });
}
battle.fx('aoe', { x: unit.x, y: unit.y, id: unit.id });
},
}),
}),
skill: {
kind: 'duration',
// the 60 % dodge is rolled on its own in the `hit` hook below (independent of other dodge sources such as her
// talent's 22 %: 1 − (1−p)(1−q), never an additive 80 % + 22 % = 100 % immunity)
mods: { atkPct: num(bb.atk), batPct: Math.max(-0.9, num(bb.base_attack_time, 1) - 1), blockCnt: num(bb.block_cnt, 1) },
onStart({ battle, unit, skill }) { unit.mem.flame = { acc: 0, given: 0, step: skill.duration / Math.max(1, total) }; battle.fx('flame', { x: unit.x, y: unit.y, id: unit.id }); },
onTick({ battle, unit, dt }) {
const F = unit.mem.flame;
if (!F) return;
F.acc += dt;
while (F.acc + 1e-9 >= F.step && F.given < total) { F.acc -= F.step; F.given++; battle.addDp(unit.ownerId, per); }
},
onEnd({ battle, unit, reason }) {
const F = unit.mem.flame;
unit.mem.flame = null;
if (F && reason === 'duration' && F.given < total) battle.addDp(unit.ownerId, per * (total - F.given));
},
},
install(battle, unit) {
if (S1) {
battle.on('hit', (c) => { // S1: "闪避下次物理攻击"
const d = c.dmg;
if (c.target !== unit || d.cancel || d.type !== 'phys' || !d.isAttack || !c.source || c.source.side !== 'enemy' || !unit.findBuff('flamtl:evade')) return;
battle.removeBuff(unit, 'flamtl:evade');
d.cancel = true;
battle.fx('dodge', { x: unit.x, y: unit.y, id: unit.id });
battle.emit('dodge', { source: c.source, target: unit, dmg: d });
}, { owner: unit, priority: 20 });
}
const ma = num(tb.atk, 0), md = num(tb.def, 0); // module SOL-X: 阻挡敌人时攻击力和防御力各+8%
if (ma || md) whileDeployed(battle, unit, 0.1, () => toggleBuff(battle, unit, 'flamtl:module', unit.blocking.length > 0, { atkPct: ma, defPct: md }));
if (!S3) return;
battle.on('hit', (c) => { // S3: "获得60%的物理和法术闪避"
const d = c.dmg;
if (c.target !== unit || !skillActive(unit) || d.cancel || !d.canDodge || (d.type !== 'phys' && d.type !== 'arts')) return;
if (!battle.rng.chance(p)) return;
d.cancel = true;
battle.fx('dodge', { x: unit.x, y: unit.y, id: unit.id });
battle.emit('dodge', { source: c.source, target: unit, dmg: d });
}, { owner: unit, priority: 20 });
},
talents: [
{ install(battle, unit) { // 前锋剑术: after a dodge the next attack hits twice and every blocked enemy
battle.on('dodge', (c) => { if (c.target === unit) unit.mem.riposte = true; }, { owner: unit });
battle.on('death', (c) => { if (c.unit === unit) unit.mem.riposte = unit.mem.riposteNow = false; }, { owner: unit });
battle.on('beforeAttack', (c) => {
if (c.attacker !== unit || !unit.mem.riposte) return;
unit.mem.riposte = false;
unit.mem.riposteNow = true;
const extra = unit.blocking.filter((e) => e.alive && canTargetEnemy(unit, e, c.profile) && !c.targets.includes(e));
c.targets = [...c.targets, ...extra];
}, { owner: unit });
battle.on('attack', (c) => {
if (c.attacker !== unit || !unit.mem.riposteNow) return;
unit.mem.riposteNow = false;
for (const e of c.targets) if (e.alive) battle.dealDamage(unit, e, { amount: unit.s.atk * unit.s.atkScaleMul, type: 'phys', isAttack: true, tags: ['riposte'] });
battle.fx('riposte', { x: unit.x, y: unit.y, id: unit.id });
}, { owner: unit });
} },
{ install(battle, unit) { // 红松骑士团团长: 卡西米尔 operators +22 % physical dodge
whileDeployed(battle, unit, AURA, () => { for (const a of battle.allies(unit.ownerId)) if (nationOf(a) === 'kazimierz') pulse(battle, a, 'flamtl:dodge', { dodgePhys: num(t1.prob, 0.22) }); });
} },
],
};
},
});