Files
pixelheros/assets/script/game/common/config/BossPhaseSet.ts
panFD fef913bd75 feat: add 12 boss phase plans and wire to 4 bosses
Twelve plans across four bosses (melee/mage/archer/tank), clear-replace and keep-add modes interleaved, hp and time trigger dimensions. Four bosses wired with phase_ids pools for random plan selection.
2026-10-08 15:56:16 +08:00

244 lines
11 KiB
TypeScript
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.
/**
* @file BossPhaseSet.ts
* @description Boss 多阶段战斗方案配置(固定 3 阶段:P1 基线 / P2 / P3)
*
* 数据流:heroInfo.phase_ids(允许方案)→ RogueConfig.makeBoss 随机取一
* → Mon.load 第 8 参 → BossPhaseComp → BossPhaseSystem 检测切换
*
* 设计约束:
* - 方案 id 使用 10000 段(9100 段已被 TalentSet 9101-9199 / ICardSet 9101~9110 占用)
* - 增量驱动:切换 = removes 移除 + adds 注入,P1 = 基线技能(heroInfo.skills + RogueConfig 大招)无需配置
* - removes/adds 禁止操作普攻首键(SCastSystem.buildSkillCandidates 硬假设首键=普攻垫底;
* JS delete+set 会把键移到末尾,破坏普攻垫底契约)
* - 混合触发:同一切换点 hp_pct / time 任一满足即切换;
* hp_pct 切换点必须构成前缀(time-only 点之后禁止再配 hp_pct,防乱序连切)
*/
import { SkillSet, SkillOverrides } from "./SkillSet";
/** 阶段切换触发条件(混合触发:任一条件满足即切换) */
export interface PhaseTrigger {
/** 血量百分比阈值(1~99):Boss 血量比例 ≤ 该值时触发 */
hp_pct?: number;
/** Boss 在场战斗秒数:超过该时长触发 */
time?: number;
}
/** 切换瞬间一次性属性修正(基于当前值链式乘算,P3 基于 P2 修正后的值) */
export interface PhaseAttrMod {
/** 攻击 ±% */
ap_pct?: number;
/** 防御 ±%(applyDefense 除法递减模型,收益非线性,配值按公式校验) */
defense_pct?: number;
}
/** 注入技能条目(与 RogueConfig 大招条目同构,cd 缺省由系统兜底 8s) */
export interface PhaseSkillEntry {
s_uuid: number;
cd?: number;
overrides?: SkillOverrides;
}
/** 单个切换点定义(switches[0] = P1→P2,switches[1] = P2→P3) */
export interface PhaseSwitchDef {
trigger: PhaseTrigger;
/** 注入的技能(追加进 skills 表,SCastSystem 按 CD 轮转)。禁止操作普攻首键 */
adds?: PhaseSkillEntry[];
/** 移除的技能 s_uuid 列表(含 RogueConfig 注入的大招)。禁止操作普攻首键 */
removes?: number[];
attr_mod?: PhaseAttrMod;
}
/** 阶段方案:固定 2 个切换点 = 3 阶段,P1 为基线 */
export interface BossPhasePlan {
name: string;
switches: [PhaseSwitchDef, PhaseSwitchDef];
}
/**
* 阶段方案表(id 段 10000)
* 12 方案矩阵:4 Boss × 3 方案(清空/保留交错),P1=基线(heroInfo.skills + RogueConfig 大招)
* P2→P3 触发条件关卡调节:前期苛刻(血量 5% / 时间 120s)→ 中期标准(20% / 90s)→ 后期易触发(35% / 75s)
* 技能 s_uuid 引用 RogueConfig.BossUltPools 保证可达(近战 6100~6104 / 中程 6150~6151,6203~6204 / 远程 6200~6204)
*/
export const BossPhaseSet: Record<number, BossPhasePlan> = {
// ========== 破阵先锋(近战 DPS,血量阈值) ==========
// 方案 1:断剑回锋(清空+新)——每阶段全新招式集,玩家每阶段重学
10011: {
name: "断剑回锋",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6104, overrides: { ap: 350 } }], removes: [6100] },
{ trigger: { hp_pct: 5 }, adds: [{ s_uuid: 6103, overrides: { ap: 400 } }], removes: [6104], attr_mod: { ap_pct: 50, defense_pct: -30 } },
],
},
// 方案 2:破阵三式(保留+加)——数值膨胀,技能池越打越大
10012: {
name: "破阵三式",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6102, overrides: { ap: 250 } }] },
{ trigger: { hp_pct: 5 }, adds: [{ s_uuid: 6104, overrides: { ap: 350 } }], attr_mod: { ap_pct: 50, defense_pct: -20 } },
],
},
// 方案 3:破阵三式·逆鳞(清空变体)——P3 高爆发脆皮
10013: {
name: "破阵三式·逆鳞",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6103, overrides: { ap: 300 } }], removes: [6100] },
{ trigger: { hp_pct: 5 }, adds: [{ s_uuid: 6104, overrides: { ap: 400 } }], removes: [6103], attr_mod: { ap_pct: 50, defense_pct: -30 } },
],
},
// ========== 雷巫·荒火(法师,时间阈值) ==========
// 方案 1:雷云·蓄(清空+新)——法系读招,时间触发给玩家反应窗口
10021: {
name: "雷云·蓄",
switches: [
{ trigger: { time: 60 }, adds: [{ s_uuid: 6201, overrides: { ap: 300 } }], removes: [6200], attr_mod: { ap_pct: 30, defense_pct: -10 } },
{ trigger: { time: 120 }, adds: [{ s_uuid: 6204, overrides: { ap: 400 } }], removes: [6201], attr_mod: { ap_pct: 50, defense_pct: -15 } },
],
},
// 方案 2:雷云·蓄·散(保留+加)——雷→火→碎甲,机制叠加
10022: {
name: "雷云·蓄·散",
switches: [
{ trigger: { time: 60 }, adds: [{ s_uuid: 6151, overrides: { ap: 350 } }], attr_mod: { ap_pct: 25 } },
{ trigger: { time: 120 }, adds: [{ s_uuid: 6203, overrides: { ap: 250 } }], attr_mod: { ap_pct: 50, defense_pct: -15 } },
],
},
// 方案 3:雷云·散(清空变体)——P3 超高爆发
10023: {
name: "雷云·散",
switches: [
{ trigger: { time: 60 }, adds: [{ s_uuid: 6150, overrides: { ap: 250 } }], removes: [6200], attr_mod: { ap_pct: 20 } },
{ trigger: { time: 120 }, adds: [{ s_uuid: 6204, overrides: { ap: 400 } }], removes: [6150], attr_mod: { ap_pct: 60 } },
],
},
// ========== 霜羽·影哨(射手,血量阈值) ==========
// 方案 1:单羽破阵(保留+加)——高频弹幕,血量阈值避免每秒判定
10031: {
name: "单羽破阵",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6151, overrides: { ap: 220 } }], attr_mod: { ap_pct: 20 } },
{ trigger: { hp_pct: 5 }, adds: [{ s_uuid: 6150, overrides: { ap: 100 } }], attr_mod: { ap_pct: 50, defense_pct: -15 } },
],
},
// 方案 2:羽阵·三重(清空+新)——冰→火→雷,元素轮换
10032: {
name: "羽阵·三重",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6151, overrides: { ap: 220 } }], removes: [6150], attr_mod: { ap_pct: 30 } },
{ trigger: { hp_pct: 5 }, adds: [{ s_uuid: 6204, overrides: { ap: 150 } }], removes: [6151], attr_mod: { ap_pct: 50 } },
],
},
// 方案 3:羽阵·三重·影哨(保留+加变体)——P3 脆皮化
10033: {
name: "羽阵·三重·影哨",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6151, overrides: { ap: 220 } }], attr_mod: { ap_pct: 25 } },
{ trigger: { hp_pct: 5 }, adds: [{ s_uuid: 6150, overrides: { ap: 100 } }], attr_mod: { ap_pct: 50, defense_pct: -15 } },
],
},
// ========== 磐盾·吞岩(坦克,时间阈值) ==========
// 方案 1:磐盾·重压(清空+新)——消耗战,时间触发逼玩家固定窗口打完
10041: {
name: "磐盾·重压",
switches: [
{ trigger: { time: 60 }, adds: [{ s_uuid: 6102, overrides: { ap: 350 } }], removes: [6100], attr_mod: { ap_pct: 30, defense_pct: 20 } },
{ trigger: { time: 120 }, adds: [{ s_uuid: 6104, overrides: { ap: 400 } }], removes: [6102], attr_mod: { ap_pct: 40, defense_pct: 40 } },
],
},
// 方案 2:磐盾·三叠(保留+加)——护盾→自疗→爆发,越打越硬
10042: {
name: "磐盾·三叠",
switches: [
{ trigger: { time: 60 }, adds: [{ s_uuid: 6101, overrides: { ap: 300 } }], attr_mod: { defense_pct: 20 } },
{ trigger: { time: 120 }, adds: [{ s_uuid: 6103, overrides: { ap: 300 } }], attr_mod: { ap_pct: 30, defense_pct: 50 } },
],
},
// 方案 3:磐盾·三叠·崩岩(清空变体)——P3 反击+减攻
10043: {
name: "磐盾·三叠·崩岩",
switches: [
{ trigger: { time: 60 }, adds: [{ s_uuid: 6101, overrides: { ap: 350 } }], removes: [6100], attr_mod: { ap_pct: 30 } },
{ trigger: { time: 120 }, adds: [{ s_uuid: 6104, overrides: { ap: 400 } }], removes: [6101], attr_mod: { defense_pct: 30 } },
],
},
};
/**
* 校验阶段方案表合法性(供 validateRogueConfig 追加调用,建议启动期执行一次)
* @returns 错误信息数组,空数组表示校验通过
*/
export function validateBossPhaseSet(): string[] {
const errors: string[] = [];
for (const key in BossPhaseSet) {
const plan = BossPhaseSet[key];
const tag = `BossPhaseSet[${key}](${plan.name})`;
if (plan.switches.length !== 2) {
errors.push(`${tag} 切换点必须恰为 2 个(固定 3 阶段),实际 ${plan.switches.length}`);
continue;
}
let seenTimeOnly = false;
let lastHpPct = 101;
let lastTime = 0;
plan.switches.forEach((def, i) => {
const t = def.trigger;
const stag = `${tag} switches[${i}]`;
// 条件完备性与取值范围
if (t.hp_pct === undefined && t.time === undefined) {
errors.push(`${stag} trigger 至少需要 hp_pct 或 time 之一`);
return;
}
if (t.hp_pct !== undefined && (t.hp_pct <= 0 || t.hp_pct >= 100)) {
errors.push(`${stag} hp_pct 必须在 (0,100) 区间,实际 ${t.hp_pct}`);
}
if (t.time !== undefined && t.time <= 0) {
errors.push(`${stag} time 必须 > 0,实际 ${t.time}`);
}
// hp_pct 前缀规则:time-only 点之后禁止再配 hp_pct(防"P2 time / P3 hp_pct"乱序连切)
if (seenTimeOnly && t.hp_pct !== undefined) {
errors.push(`${stag} hp_pct 切换点必须构成前缀:time-only 切换点之后禁止再配置 hp_pct`);
}
if (t.hp_pct === undefined && t.time !== undefined) {
seenTimeOnly = true;
}
// hp_pct 严格递减 / time 严格递增
if (t.hp_pct !== undefined) {
if (t.hp_pct >= lastHpPct) {
errors.push(`${stag} hp_pct=${t.hp_pct} 未严格小于前一档 ${lastHpPct}`);
}
lastHpPct = t.hp_pct;
}
if (t.time !== undefined) {
if (t.time <= lastTime) {
errors.push(`${stag} time=${t.time} 未严格大于前一档 ${lastTime}`);
}
lastTime = t.time;
}
// 技能引用存在性(error 级)
for (const add of def.adds ?? []) {
if (!add.s_uuid || !SkillSet[add.s_uuid]) {
errors.push(`${stag} adds 技能 ${add.s_uuid} 未在 SkillSet 中定义`);
}
}
for (const s_uuid of def.removes ?? []) {
if (!SkillSet[s_uuid]) {
errors.push(`${stag} removes 技能 ${s_uuid} 未在 SkillSet 中定义`);
}
}
});
}
return errors;
}