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:
1 parent
6b489b1776
commit
1169102d46
143 files changed
+12924
-11784
No files matched your search
@@ -0,0 +1,78 @@
|
||||
// server/sim/content/kits/ops/chess_char_5_21-sntlla.js — 寒檀 (char_341_sntlla) kit, tier 5.
|
||||
// Conventions of the tier-5 kits: ../shared/tier5.js; kit contract and rules: ../README.md.
|
||||
|
||||
import { COLS } from '../../../constants.js';
|
||||
import { toLocal } from '../../../dir.js';
|
||||
import { num, talent, skillGrid, batPct, mods, lazySkills, whileOn } from '../shared/tier5.js';
|
||||
|
||||
/** 寒檀 S2 icicle splash radius (PRTS 备注 "冰凌溅射半径1.5"). */
|
||||
const ICICLE_RADIUS = 1.5;
|
||||
/** 寒檀 S2 icicle fall time (s) between the attack and the impact. [ASSUMED] */
|
||||
const ICICLE_DELAY = 0.3;
|
||||
/** 寒檀 S2 icicle lines, lateral offsets in her facing-RIGHT frame: ① the line on her left, ② on her right, ③ her own, then ①. */
|
||||
const ICICLE_ROWS = [1, -1, 0];
|
||||
|
||||
export default {
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// 寒檀 — S2 “女巫之泪” (15 s): skill range, BAT −2.4 s; attacks become icicles on random tiles of the range (cycling left
|
||||
// row → right row → own row): 65/75 % ATK arts + 1 s cold to every enemy within 1.5 of the impact. T1 生于冰寒: after 20 s on the field ATK +15 % and 抵抗 (negative
|
||||
// statuses on her last half as long).
|
||||
chess_char_5_21_a: (bb, chess, def) => {
|
||||
const t0 = talent(chess, 0);
|
||||
const scale = num(bb['attack@atk_scale'], 1), cold = num(bb['attack@cold']);
|
||||
const resist = Math.min(1, Math.max(0, -num(t0.one_minus_status_resistance))); // share of the duration removed
|
||||
return {
|
||||
// S1 迅捷打击·γ型 (duration): ATK +, ASPD +.
|
||||
skills: lazySkills({
|
||||
'skcom_quickattack[3]': () => ({ kind: 'duration', mods: mods({ atkPct: num(bb.atk), aspd: num(bb.attack_speed) }) }),
|
||||
}),
|
||||
skill: {
|
||||
kind: 'duration',
|
||||
mods: mods({ batPct: batPct(bb.base_attack_time, chess) }),
|
||||
targeting: skillGrid(chess, def) ? { rangeGrid: skillGrid(chess, def) } : undefined,
|
||||
attack: { noAttack: true },
|
||||
onStart({ unit }) { unit.mem.iceCd = 0; unit.mem.iceRow = 0; },
|
||||
onTick({ battle, unit, dt }) {
|
||||
unit.mem.iceCd -= dt;
|
||||
if (unit.mem.iceCd > 0 || !unit.canAct || unit.s.flags.disarm) return;
|
||||
if (!battle.enemiesInKeys(unit.rangeKeys, unit, { canHitFly: true }).length) return;
|
||||
unit.mem.iceCd = unit.s.interval;
|
||||
// PRTS 备注: icicles fall on a random tile of ① the row on her left, ② the row on her right, ③ her own row, …
|
||||
let k = null;
|
||||
for (let i = 0; i < ICICLE_ROWS.length && k == null; i++) {
|
||||
const idx = (num(unit.mem.iceRow) + i) % ICICLE_ROWS.length;
|
||||
const lat = ICICLE_ROWS[idx];
|
||||
const keys = (unit.rangeKeys || []).filter((x) => toLocal(Math.floor(x / COLS) - unit.tileR, x % COLS - unit.tileC, unit.dir)[0] === lat);
|
||||
if (!keys.length) continue;
|
||||
k = battle.rng.pick(keys);
|
||||
unit.mem.iceRow = (idx + 1) % ICICLE_ROWS.length;
|
||||
}
|
||||
if (k == null) return;
|
||||
const r = Math.floor(k / COLS), c = k % COLS;
|
||||
unit.lastAttackAt = battle.time;
|
||||
unit.stats.attacks++; // "攻击变为…召唤冰凌": every icicle is one of her attacks
|
||||
battle.fx('iceSpike', { x: c, y: r, id: unit.id, r: ICICLE_RADIUS });
|
||||
battle.after(ICICLE_DELAY, () => {
|
||||
const hit = [];
|
||||
for (const e of battle.foesInRadius(c, r, ICICLE_RADIUS)) {
|
||||
battle.dealDamage(unit, e, { amount: unit.s.atk * scale, type: 'arts', isAttack: true, isSkill: true, isSplash: true, tags: ['skill', 'iceSpike'] });
|
||||
if (e.alive && cold > 0) battle.applyStatus(e, 'cold', { duration: cold, source: unit });
|
||||
if (e.alive) hit.push(e);
|
||||
}
|
||||
// an icicle is an attack: "攻击时" effects (items, bonds) see it with the enemies it hit
|
||||
if (hit.length && battle.hasHook('attack')) battle.emit('attack', { attacker: unit, targets: hit, isSkill: true });
|
||||
}, { owner: unit });
|
||||
},
|
||||
},
|
||||
talents: [{ install(battle, unit) { // 生于冰寒
|
||||
const after = num(t0.interval, 20);
|
||||
// + 抵抗 (engine `resist` status: new control statuses last half as long, never compounding with other sources)
|
||||
whileOn(battle, unit, 0.5, () => {
|
||||
if (battle.time - unit.deployedAt < after - 1e-9 || unit.findBuff('sntlla:born')) return;
|
||||
battle.addBuff(unit, { key: 'sntlla:born', visible: true, mods: mods({ atkPct: num(t0.atk) }) });
|
||||
if (resist > 0) battle.applyStatus(unit, 'resist', { value: resist, source: unit });
|
||||
});
|
||||
} }],
|
||||
};
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user