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,103 @@
// server/sim/content/kits/ops/chess_char_5_22-nymph.js — 妮芙 (char_4146_nymph) kit, tier 5.
// Conventions of the tier-5 kits: ../shared/tier5.js; kit contract and rules: ../README.md.
import { hasHp } from '../../../damage.js';
import {
num, on, inRange, talent, traitBb, maxCharges, mods, selectedId, lazySkills, skillRange, burstSpUp, elementHit,
burstDamageUp,
} from '../shared/tier5.js';
export default {
// ---------------------------------------------------------------------------------------------------------------
// 妮芙 — S2 怵然震爆 (1/2 charges): next attack 270/300 % ATK arts + 4/5 s fear on the target and an equal arts splash
// (projectile_range), every hit adding 18/22 % of its damage as apoptosis; targets in an apoptosis burst raise T1 to 70/85 %.
// T1 失魂: hitting an enemy in an apoptosis burst → 40 % ATK elemental damage per second until the burst ends.
// T2 窥心钥: an apoptosis burst in range → ATK +2 % (×10). Module (elite): ×damage_scale vs enemies in a burst.
// S1 笞心击 (duration): ATK +; attacks add attack@ep_damage_ratio of their damage as 凋亡损伤 and, on a target in an
// apoptosis burst, attack@extra_ep_damage_scale × ATK elemental damage. S3 心防溃决 (duration): skill range, ATK +,
// ASPD +, 2 targets; attacks on a target in an apoptosis burst deal elemental damage (split_atk_scale × ATK, no arts).
// Module PRI-Y (elite): SP +0.2/s while an enemy in range is in an element burst.
chess_char_5_22_a: (bb, chess, def) => {
const t0 = talent(chess, 0), t1 = talent(chess, 1), tb = traitBb(chess), tm = talent(chess, -1);
const sid = selectedId(chess, def);
const ep = num(bb.ep_damage_ratio, num(bb['attack@ep_damage_ratio'])), skillSoul = num(bb.element_atk_scale), soulBase = num(t0.element_atk_scale);
const soul = (battle, unit, e, scale) => {
const burst = e.findBuff('apoptosisBurst');
if (!burst || !(scale > 0)) return;
const key = `nymph:soul:${unit.id}`;
const cur = e.findBuff(key);
if (cur) { cur.data.scale = Math.max(num(cur.data.scale), scale); return; }
battle.addBuff(e, {
key, duration: Math.max(0.1, burst.timeLeft), interval: 1, data: { scale },
onTick: ({ unit: x, buff }) => {
if (!x.findBuff('apoptosisBurst')) { battle.removeBuff(x, buff); return; }
elementHit(battle, unit, x, unit.s.atk * num(buff.data.scale), 'nymphSoul', 'apoptosis');
},
});
battle.fx('soul', { x: e.x, y: e.y, id: e.id });
};
return {
skills: lazySkills({
skchr_nymph_1: () => ({ kind: 'duration', mods: mods({ atkPct: num(bb.atk) }) }),
skchr_nymph_3: () => ({
kind: 'duration', mods: mods({ atkPct: num(bb.atk), aspd: num(bb.attack_speed) }),
targeting: skillRange(chess, def, { maxTargets: Math.max(1, num(bb['attack@max_target'], 2)) }),
attack: { atkScale: num(bb['attack@split_atk_scale'], 1) },
}),
}),
skill: {
kind: maxCharges(chess, def) > 1 ? 'charges' : 'instant',
attack: {
atkScale: num(bb.atk_scale, 1), splashRadius: num(bb.projectile_range, 1.5),
onHit({ battle, unit, target, x, y }) {
if (target && target.alive) battle.applyStatus(target, 'fear', { duration: num(bb.fear), source: unit });
battle.fx('aoe', { x, y, id: unit.id, r: num(bb.projectile_range, 1.5), skill: 'nymph' });
},
},
},
talents: [
{ install(battle, unit) { // 失魂 (+ the skill's apoptosis rider and 70 % upgrade)
battle.on('damaged', (c) => {
if (c.source !== unit || c.type === 'element' || c.target.side !== 'enemy' || !c.target.alive || !c.dmg) return;
// (S1 has no attack override: its attacks are the skill's while it runs)
const skillAtk = c.dmg.isSkill || (sid === 'skchr_nymph_1' && !!unit.skill?.active);
// (not on a killing blow: the target is at 0 HP here — no burst on the corpse)
if (skillAtk && c.dmg.isAttack && ep > 0 && c.amount > 0 && hasHp(c.target)) {
battle.dealDamage(unit, c.target, { type: 'element', element: 'apoptosis', amount: c.amount * ep, tags: ['skill', 'nymph'] });
}
if (c.dmg.isAttack && c.target.alive) soul(battle, unit, c.target, c.dmg.isSkill ? Math.max(skillSoul, soulBase) : soulBase);
}, { owner: unit });
} },
{ install(battle, unit) { // 窥心钥
battle.on('elementBurst', (c) => {
if (c.element !== 'apoptosis' || c.target.side !== 'enemy' || !on(unit) || !inRange(unit, c.target)) return;
battle.addBuff(unit, { key: 'nymph:key', refresh: 'stack', maxStacks: Math.max(1, num(t1.max_stack_cnt, 10)), mods: { atkPct: num(t1.atk) } });
}, { owner: unit });
} },
],
install(battle, unit) {
burstDamageUp(battle, unit, num(tb.damage_scale));
burstSpUp(battle, unit, 'nymph:module', num(tm.sp_recovery_per_sec));
if (sid === 'skchr_nymph_1') { // S1: extra elemental damage on burst targets (the 凋亡 rider is T1's `ep`)
const extra = num(bb['attack@extra_ep_damage_scale']);
battle.on('damaged', (c) => {
if (c.source !== unit || !unit.skill?.active || !c.dmg?.isAttack || c.type === 'element' || c.type === 'elemental') return;
const e = c.target;
if (e.side !== 'enemy' || !e.alive || !e.findBuff('apoptosisBurst') || !(extra > 0)) return;
elementHit(battle, unit, e, unit.s.atk * extra, 'nymphLash', 'apoptosis');
}, { owner: unit });
}
if (sid === 'skchr_nymph_3') { // S3: attacks on a burst target become elemental damage
battle.on('hit', (c) => {
if (c.source !== unit || !unit.skill?.active || !c.dmg.isAttack || !c.target || !c.target.findBuff('apoptosisBurst')) return;
if (c.dmg.type !== 'arts' && c.dmg.type !== 'phys') return;
c.dmg.type = 'elemental';
c.dmg.element = 'apoptosis';
c.dmg.canDodge = false;
(c.dmg.tags ||= []).push('nymphBreak');
}, { owner: unit, priority: -10 });
}
},
};
},
};