Files
ark-stronghold-protocol/server/sim/content/kits/ops/chess_char_1_07-prove.js
T
sganggs 1169102d46 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.
2026-10-05 16:13:56 +08:00

65 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 });
}
});
} }],
};
},
};