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,64 @@
// server/sim/content/kits/ops/chess_char_1_07-prove.js — 普罗旺斯 (char_145_prove) kit, tier 1.
// Conventions of the tier-1 kits: ../shared/tier1.js; kit contract and rules: ../README.md.
import { sortEnemyTargets } from '../../../targeting.js';
import { frontOf } from '../../../dir.js';
import { bodyOnTile } from '../../../body.js';
import { num, talentBb, isMainHit, onHitBy, skillBbOf } from '../shared/tier1.js';
/** 普罗旺斯 杀戮嗅觉 "普通攻击不再以生命值高于80%的敌人作为目标" — the 80 % exists only in the skill text. */
const PROVE_S2_MAX_HP = 0.8;
export default {
// ---------------------------------------------------------------------------------------------------------------
// 1_07 普罗旺斯 狼眼 (passive): per hp_ratio_drop of HP the target has lost, ATK +atk_scale_up against it — CONTINUOUS,
// not in 20 % steps (PRTS note: "并非以每20%为固定节点 … 攻击力倍率提升至[100%+(100%-目标生命比例)×加成量×5]", i.e.
// ×(1 + lost / hp_ratio_drop × atk_scale_up)).
// 狩猎箭头: each attack prob (prob2 when the enemy is on the tile right in front) for ATK ×atk_scale.
// 狼眼 is an ATK increase ⇒ it scales her (ATK-based) attacks only, never fixed-value damage she sources
// (叙拉古 5000+50·L true proc, fixed item/DoT ticks…).
// Alternate S2 杀戮嗅觉: ATK +atk, but normal attacks never target an enemy above 80 % HP (with only such enemies in
// range she holds her fire). Her attacks stay normal attacks. (狼眼 is S1 only: absent when S2 is carried.)
// Elite module ARC-X only changes stats (ATK/DEF, faster redeploy).
chess_char_1_07_a: (bb, chess) => {
const t = talentBb(chess, 0);
const drop = num(bb.hp_ratio_drop), upPer = num(bb.atk_scale_up);
const S2 = 'skchr_prove_2';
const huntable = (e) => e.hpRatio <= PROVE_S2_MAX_HP + 1e-9;
const s2On = (u) => !!(u.skill && u.skill.active && u.skill.id === S2);
return {
skill: { kind: 'passive' },
skills: { [S2]: { kind: 'duration', mods: { atkPct: num(skillBbOf(chess, S2).atk) } } },
trait: {
canAttack: (battle, u) => !s2On(u) || battle.enemiesInKeys(u.rangeKeys, u, u.profile).some(huntable),
},
install(battle, unit) {
if (unit.skill?.id !== S2) return;
battle.on('beforeAttack', (ctx) => {
if (ctx.attacker !== unit || !s2On(unit) || ctx.targets.every(huntable)) return;
const prof = ctx.profile || unit.profile;
const cands = battle.enemiesInKeys(unit.rangeKeys, unit, prof).filter(huntable);
sortEnemyTargets(battle, unit, cands, prof.priority);
ctx.targets = cands.slice(0, Math.max(1, ctx.targets.length));
}, { owner: unit });
},
talents: [{ install(battle, unit) {
onHitBy(battle, unit, ({ target, dmg }) => {
if (!dmg.isAttack) return;
if (drop > 0 && upPer > 0) {
const lost = Math.max(0, Math.min(1, 1 - target.hpRatio));
if (lost > 0) dmg.amount *= 1 + (lost / drop) * upPer;
}
if (!isMainHit(dmg)) return;
const [fr, fc] = frontOf(unit.tileR, unit.tileC, unit.dir);
const front = bodyOnTile(target, fr, fc);
const p = front ? num(t.prob2) : num(t.prob);
if (p > 0 && battle.rng.chance(p)) {
dmg.amount *= num(t.atk_scale, 1);
battle.fx('crit', { x: target.x, y: target.y, id: unit.id });
}
});
} }],
};
},
};