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,125 @@
|
||||
// server/sim/content/kits/ops/chess_char_5_09-cetsyr.js — 魔王 (char_4134_cetsyr) kit, tier 5 (hidden).
|
||||
// Conventions of the tier-5 kits: ../shared/tier5.js; kit contract and rules: ../README.md.
|
||||
|
||||
import { COLS } from '../../../constants.js';
|
||||
import { rotateOffset } from '../../../dir.js';
|
||||
import { AURA_IV, AURA_DUR, num, talent, traitBb, skillGrid, isOp, leaderOf, whileOn } from '../shared/tier5.js';
|
||||
|
||||
/** 魔王 T1 微尘: collision radius of one mote (PRTS 备注 0.4); orbit radius / speed come from the talent bb. */
|
||||
const MOTE_HIT_RADIUS = 0.4;
|
||||
|
||||
export default {
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// 魔王 — bard (aura heal 10 % ATK/s). S3 编织重构现世 (30 s): skill range, aura 65 %, motes never vanish, other allies in
|
||||
// range get 鼓舞 = +65 % of her max HP, HP of everyone in range is equalised every 2 s.
|
||||
// T1 过往尘埃: 3 motes orbit her; one colliding with an operator vanishes and gives it ×1.5 aura healing for 6 s, then
|
||||
// respawns after 6 s.
|
||||
// T2 魔王残响: allies take −10 % damage from Sarkaz enemies. Module (elite): ≥2 other ops in range → ATK +8 %.
|
||||
chess_char_5_09_a: (bb, chess, def) => {
|
||||
const t0 = talent(chess, 0), t1 = talent(chess, 1), tb = traitBb(chess), tm = talent(chess, -1);
|
||||
const baseRatio = num(tb['attack@atk_to_hp_recovery_ratio'], 0.1);
|
||||
const skillRatio = num(bb['attack@atk_to_hp_recovery_ratio'], baseRatio);
|
||||
const inspire = num(bb.max_hp);
|
||||
const shareIv = Math.max(0.1, num(bb['attack@cetsyr_s_3[cal_hp_ratio].interval'], 2));
|
||||
const inspireAll = (battle, unit) => {
|
||||
if (!(inspire > 0)) return;
|
||||
const hp = unit.s.maxHp * inspire;
|
||||
for (const a of battle.alliesInGrid(unit)) {
|
||||
if (a !== unit) battle.addBuff(a, { key: 'cetsyr:inspire', duration: AURA_DUR, visible: true, status: 'inspire', mods: { hpFlat: hp }, data: { src: unit } });
|
||||
}
|
||||
};
|
||||
return {
|
||||
skill: {
|
||||
kind: 'duration',
|
||||
targeting: skillGrid(chess, def) ? { rangeGrid: skillGrid(chess, def) } : undefined,
|
||||
onStart({ battle, unit }) {
|
||||
if (unit.profile) unit.profile.auraRatio = skillRatio;
|
||||
unit.mem.shareAcc = 0;
|
||||
unit.mem.inspireAcc = 0;
|
||||
inspireAll(battle, unit);
|
||||
battle.fx('healAoe', { x: unit.x, y: unit.y, id: unit.id, r: 2.5 });
|
||||
},
|
||||
onTick({ battle, unit, dt }) {
|
||||
unit.mem.inspireAcc += dt;
|
||||
if (unit.mem.inspireAcc >= AURA_IV) { unit.mem.inspireAcc = 0; inspireAll(battle, unit); }
|
||||
unit.mem.shareAcc += dt;
|
||||
if (unit.mem.shareAcc < shareIv - 1e-9) return;
|
||||
unit.mem.shareAcc -= shareIv;
|
||||
const group = battle.alliesInGrid(unit).filter((a) => a.alive && a.kind !== 'device');
|
||||
let hp = 0, max = 0;
|
||||
for (const a of group) { hp += a.hp; max += a.s.maxHp; }
|
||||
if (!(max > 0) || group.length < 2) return;
|
||||
const ratio = hp / max;
|
||||
for (const a of group) a.hp = Math.max(1, Math.min(a.s.maxHp, a.s.maxHp * ratio));
|
||||
battle.fx('hpShare', { x: unit.x, y: unit.y, id: unit.id, ratio: Math.round(ratio * 1000) / 1000 });
|
||||
},
|
||||
onEnd({ battle, unit }) {
|
||||
if (unit.profile) unit.profile.auraRatio = baseRatio;
|
||||
for (const a of battle.allyUnits) {
|
||||
const b = a.findBuff('cetsyr:inspire');
|
||||
if (b && b.data.src === unit) battle.removeBuff(a, b);
|
||||
}
|
||||
},
|
||||
},
|
||||
talents: [
|
||||
{ install(battle, unit) { // 过往尘埃: cnt motes orbit her (radius range_radius, dynamic_spd °/s, PRTS hit radius 0.4)
|
||||
const cnt = Math.max(0, Math.floor(num(t0.cnt, 3))), orbit = num(t0.range_radius, 1.15);
|
||||
const spd = (num(t0.dynamic_spd, 30) * Math.PI) / 180;
|
||||
const cd = num(t0.cooldown, 6), dur = num(t0.talent_duration, 6), mul = num(t0['attack@trait_mul'], 1.5);
|
||||
const motes = new Array(cnt).fill(0); // per orbit slot: the time its mote is back (vanished → respawn after cd)
|
||||
const reach = orbit + MOTE_HIT_RADIUS + 0.01;
|
||||
battle.on('deploy', (c) => { if (c.unit === unit) motes.fill(0); }, { owner: unit });
|
||||
whileOn(battle, unit, AURA_IV, () => {
|
||||
if (!cnt) return;
|
||||
const keep = !!unit.skill?.active; // S3: "微尘"不再消失
|
||||
// "微尘"碰撞友方干员: operators only; a mote never collides with an operator already under a mote effect
|
||||
const cands = battle.alliesInRadius(unit.x, unit.y, reach, null).filter((a) => a !== unit && isOp(a) && !a.findBuff('cetsyr:mote'));
|
||||
if (!cands.length) return;
|
||||
const t = battle.time - num(unit.deployedAt);
|
||||
for (let k = 0; k < cnt; k++) {
|
||||
if (motes[k] > battle.time + 1e-9) continue;
|
||||
const ang = spd * t + (2 * Math.PI * k) / cnt;
|
||||
// the orbit turns with his direction: angle 0 = straight ahead, π/2 = his left hand
|
||||
const [fr, fc] = unit.fwd, [lr, lc] = rotateOffset(1, 0, unit.dir);
|
||||
const mx = unit.x + orbit * (fc * Math.cos(ang) + lc * Math.sin(ang)), my = unit.y + orbit * (fr * Math.cos(ang) + lr * Math.sin(ang));
|
||||
let hit = null, hd = Infinity;
|
||||
for (const a of cands) {
|
||||
if (a.findBuff('cetsyr:mote')) continue;
|
||||
const d = Math.hypot(a.x - mx, a.y - my);
|
||||
if (d <= MOTE_HIT_RADIUS + 1e-9 && d < hd) { hd = d; hit = a; }
|
||||
}
|
||||
if (!hit) continue;
|
||||
battle.addBuff(hit, { key: 'cetsyr:mote', duration: dur, visible: true, data: { src: unit, mul } });
|
||||
if (!keep) motes[k] = battle.time + cd;
|
||||
battle.fx('mote', { x: hit.x, y: hit.y, id: hit.id });
|
||||
}
|
||||
});
|
||||
battle.on('heal', (c) => {
|
||||
if (c.source !== unit || !c.opts?.aura) return;
|
||||
const b = c.target.findBuff('cetsyr:mote');
|
||||
if (b) c.amount *= num(b.data.mul, 1);
|
||||
}, { owner: unit });
|
||||
} },
|
||||
{ install(battle, unit) { // 魔王残响
|
||||
const dr = num(t1.damage_resistance);
|
||||
if (!(dr > 0)) return;
|
||||
battle.on('hit', (c) => {
|
||||
if (!c.target || c.target.side !== 'ally' || !c.source || c.source.side !== 'enemy') return;
|
||||
if (!(c.source.def?.tags || []).includes('sarkaz') || leaderOf(battle, unit) !== unit) return;
|
||||
c.dmg.mul *= 1 - dr;
|
||||
}, { owner: unit });
|
||||
} },
|
||||
],
|
||||
install(battle, unit) { // module: ≥ cnt other ops in the (base) range → ATK +8 %
|
||||
const atk = num(tm.atk), need = num(tm.cnt, 2);
|
||||
if (!chess?.isGolden || !atk) return;
|
||||
whileOn(battle, unit, AURA_IV, () => {
|
||||
const keys = new Set(unit.baseRangeKeys || []);
|
||||
let n = 0;
|
||||
for (const a of battle.allies()) if (a !== unit && isOp(a) && keys.has(a.tileR * COLS + a.tileC)) n++;
|
||||
if (n >= need) battle.addBuff(unit, { key: 'cetsyr:module', duration: AURA_DUR, mods: { atkPct: atk } });
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user