22 Commits

Author SHA1 Message Date
panFD
d7cd440844 chore: 修改 git 提交规则配置中的 alwaysApply 为 false
调整 git 提交规则配置文件,将 alwaysApply 字段从 true 修改为 false
2026-10-10 08:46:32 +08:00
panFD
a242ccc2ea feat(heros): 为多个英雄预制件添加外发光材质组件 2026-10-10 08:43:55 +08:00
panFD
74d03facd9 fix(boss): keep phase outline authoritative over level tint and pool reuse
Reuse existing outline-green/outline-glow materials (P2 green / P3 red); phase outline now takes priority over level tint during boss lifetime (init/lv_up both route through BossPhaseComp), and HeroViewComp.reset clears the material back to default so pooled monster nodes never inherit a boss's red/green edge.
2026-10-10 00:40:47 +08:00
panFD
a122d0904c feat(boss): tint boss sprite outline by phase (P2 green, P3 red)
Add FlashSprite.setOutlineByBossPhase with light/dark green shade option and red fallback; BossPhaseSystem drives HeroViewComp.applyBossPhaseOutline directly after phase switch (MON faction, no hero-level event semantics, pool-node safe); extract getFlashSprite/applyOutlineByLevel to dedupe the three outline call sites.
2026-10-10 00:33:41 +08:00
panFD
24aff3baa3 refactor(hero): 移除 Boss 阶段描边相关未实现的代码
清理了 Boss 阶段描边功能的相关代码,包括移除了 resetPhaseOutline 和 setPhaseOutline 方法,删除了相关材质定义,将 onPhaseEnter 方法置空待后续迭代实现。
2026-10-10 00:27:29 +08:00
panFD
b31d870d90 feat(boss): tint boss outline green in phase 2 and red in phase 3
Add FlashSprite.setPhaseOutline (P1 default / P2 green / P3 red) and drive it from BossPhaseSystem.onPhaseEnter; reset outline on pooled boss reuse so stale tint never leaks to the next owner. Red outline material asset must be created in editor (copy green, tint red) and bound to FlashSprite.outlineMatRed.
2026-10-10 00:25:23 +08:00
panFD
11ba692f18 feat(config): add 12 boss phase plans and mount phase_ids on 6 bosses
Populate BossPhaseSet with add-style and stance-swap plans per boss (melee pool 6100-6104, ranged pool 6150-6151/6200-6204); stance-swap lists the full position ult pool in removes so random injected ults are always cleared. Mount phase_ids on 6006/6101-6106.
2026-10-09 22:15:07 +08:00
panFD
e662b63f80 docs: add boss phase content design based on shipped mechanics
Adapt external draft to existing BossPhaseSet capabilities: pool-wide removes for stance-swap semantics, ap/def-only attr mods, 30s/60s time triggers aligned with 90s battle timeout, and 13 ready-to-paste plans across 6 bosses.
2026-10-08 23:12:13 +08:00
panFD
2ab5100b41 feat: 更新BOSS阶段配置并简化方案设计
重构了兽人首领BOSS的阶段配置,将原12个阶段方案精简为1个示例方案,调整了触发条件与技能配置,优化了阶段方案的可读性与可维护性,确保大招引用近战池保证可达性。
2026-10-08 16:45:53 +08:00
panFD
f4fb561319 fix(map): 调整下一回合刷怪时机避免与英雄过场同帧移动
修改 autoNextPhase 逻辑,将 advanceToNextSection 从 PrepareStart 阶段移至 PrepareEnd 阶段,确保英雄归位待机约 1 秒后再生成怪物,使怪物随回合开始横幅同步登场,解决与英雄过场行军同帧移动的显示问题。
2026-10-08 16:44:49 +08:00
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
panFD
53a0ee1c6e refactor(hero): adjust monster prefab sizes and add boss scaling logic
调整多个怪物预制体的尺寸参数,同时在怪物基类中添加 Boss 体型放大 1.5 倍的逻辑,小怪保持原尺寸。
2026-10-07 17:29:45 +08:00
panFD
f679644bbb feat: 添加 Boss 阶段系统及测试配置
新增 BossPhaseSystem 脚本及元文件,添加阶段挂载与接管的日志记录
修改测试模式配置,调整 Boss 相关属性以适配阶段系统
2026-10-07 17:20:25 +08:00
panFD
541ca0e251 chore: add Cocos meta files for new phase scripts 2026-10-07 15:44:57 +08:00
panFD
7703b0348c fix: pass phase plan id and clear stale test skills on reuse 2026-10-07 15:44:33 +08:00
panFD
ad50e11052 feat: add BossPhaseSystem with phase switch logic 2026-10-07 15:43:17 +08:00
panFD
d5158cde52 feat: wire BossPhaseComp into monster load/destroy 2026-10-07 15:39:25 +08:00
panFD
8176ef098e feat: consume phase plans in boss generation 2026-10-07 15:36:20 +08:00
panFD
56dcdccca2 feat: add phase_ids field to boss hero config 2026-10-07 15:33:45 +08:00
panFD
31a2efee4d feat: add BossPhaseSet config with phase plan schema 2026-10-07 15:32:36 +08:00
panFD
761d0aa68e feat: add BossPhaseChange game event 2026-10-07 15:32:21 +08:00
panFD
7766909294 feat: add BossPhaseComp marker component 2026-10-07 15:32:15 +08:00
22 changed files with 978 additions and 70 deletions

View File

@@ -1,8 +1,7 @@
---
alwaysApply: true
alwaysApply: false
scene: git_message
---
## Profile
你是一个严谨的配置管理专家与代码审查员。你的核心任务是根据代码的变更内容(diff)或开发者的简短描述,生成符合业界标准(Conventional Commits / Angular 规范)的 Git 提交信息。你擅长剥离代码细节,总结出清晰的业务意图(What 和 Why)。

View File

@@ -402,6 +402,10 @@
"__uuid__": "2b8a37ee-732c-4c5b-b6a4-136f3e581cde",
"__expectedType__": "cc.Material"
},
"outlineMatGlow": {
"__uuid__": "2fcd55a9-38ca-45aa-9164-68e48aaf51ce",
"__expectedType__": "cc.Material"
},
"_id": ""
},
{

View File

@@ -402,6 +402,10 @@
"__uuid__": "2b8a37ee-732c-4c5b-b6a4-136f3e581cde",
"__expectedType__": "cc.Material"
},
"outlineMatGlow": {
"__uuid__": "2fcd55a9-38ca-45aa-9164-68e48aaf51ce",
"__expectedType__": "cc.Material"
},
"_id": ""
},
{
@@ -812,8 +816,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -921,12 +925,12 @@
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 75
"y": 50
},
"_size": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_id": ""
},

View File

@@ -402,6 +402,10 @@
"__uuid__": "2b8a37ee-732c-4c5b-b6a4-136f3e581cde",
"__expectedType__": "cc.Material"
},
"outlineMatGlow": {
"__uuid__": "2fcd55a9-38ca-45aa-9164-68e48aaf51ce",
"__expectedType__": "cc.Material"
},
"_id": ""
},
{
@@ -809,8 +813,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -918,12 +922,12 @@
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 75
"y": 50
},
"_size": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 60,
"height": 100
},
"_id": ""
},

View File

@@ -402,6 +402,10 @@
"__uuid__": "2b8a37ee-732c-4c5b-b6a4-136f3e581cde",
"__expectedType__": "cc.Material"
},
"outlineMatGlow": {
"__uuid__": "2fcd55a9-38ca-45aa-9164-68e48aaf51ce",
"__expectedType__": "cc.Material"
},
"_id": ""
},
{
@@ -812,8 +816,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -921,12 +925,12 @@
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 75
"y": 50
},
"_size": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 8,
"height": 100
},
"_id": ""
},

View File

@@ -402,6 +402,10 @@
"__uuid__": "2b8a37ee-732c-4c5b-b6a4-136f3e581cde",
"__expectedType__": "cc.Material"
},
"outlineMatGlow": {
"__uuid__": "2fcd55a9-38ca-45aa-9164-68e48aaf51ce",
"__expectedType__": "cc.Material"
},
"_id": ""
},
{
@@ -809,8 +813,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -918,12 +922,12 @@
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 75
"y": 50
},
"_size": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_id": ""
},

View File

@@ -402,6 +402,10 @@
"__uuid__": "2b8a37ee-732c-4c5b-b6a4-136f3e581cde",
"__expectedType__": "cc.Material"
},
"outlineMatGlow": {
"__uuid__": "2fcd55a9-38ca-45aa-9164-68e48aaf51ce",
"__expectedType__": "cc.Material"
},
"_id": ""
},
{
@@ -809,8 +813,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -918,12 +922,12 @@
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 75
"y": 50
},
"_size": {
"__type__": "cc.Size",
"width": 150,
"height": 150
"width": 80,
"height": 100
},
"_id": ""
},

View File

@@ -0,0 +1,268 @@
/**
* @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,按 Boss 分组,详见 docs/superpowers/specs/2026-10-06-boss-phase-content-design.md)
* 换招式技巧:removes 列满该 Boss 定位的整个大招池 id(delete 空键无副作用),
* 无论 RogueConfig 随机注入了哪个大招一律清净,再 adds 新技能组
* - 近战定位池:[6100, 6101, 6102, 6103, 6104]
* - 远程定位池:[6150, 6151, 6200, 6201, 6202, 6203, 6204]
*/
export const BossPhaseSet: Record<number, BossPhasePlan> = {
// ========== 6101 兽人首领-双刀dps(血量,近战池) ==========
/** 追加式:P2 换裂气斩、P3 加闪电斩 + 攻+20% */
10001: {
name: "狂暴进化",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6102, overrides: { ap: 180 } }], removes: [6100] },
{ trigger: { hp_pct: 40, time: 60 }, adds: [{ s_uuid: 6103, overrides: { ap: 180 } }], attr_mod: { ap_pct: 20 } },
],
},
/** 追加式:双刀越挥越快,技能只加不换 */
10002: {
name: "嗜血连斩",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6103, overrides: { ap: 160 } }], attr_mod: { ap_pct: 15 } },
{ trigger: { hp_pct: 40 }, adds: [{ s_uuid: 6104, overrides: { ap: 180 } }], attr_mod: { ap_pct: 25, defense_pct: -10 } },
],
},
/** 换招式:每阶段换一把"刀",系数逐段上调 */
10003: {
name: "换刃",
switches: [
{ trigger: { hp_pct: 70 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6101, overrides: { ap: 200 } }] },
{ trigger: { hp_pct: 40 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 20, defense_pct: -15 } },
],
},
// ========== 6102 兽人首领-坦克型(时间,近战池) ==========
/** 追加式:越久越硬,反击越痛 */
10010: {
name: "重压觉醒",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6100, overrides: { ap: 150 } }], attr_mod: { defense_pct: 20 } },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6104, overrides: { ap: 180 } }], attr_mod: { ap_pct: 20 } },
],
},
/** 换招式:外壳剥落,换进攻形态 */
10011: {
name: "崩岩",
switches: [
{ trigger: { time: 30 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6102, overrides: { ap: 200 } }], attr_mod: { ap_pct: 15 } },
{ trigger: { time: 60 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 25, defense_pct: -20 } },
],
},
// ========== 6103 兽人首领-魔法师(时间,远程池) ==========
/** 追加式:元素逐一汇入,弹幕越来越密 */
10020: {
name: "元素汇聚",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6204, overrides: { ap: 150 } }] },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6202, overrides: { ap: 150 } }], attr_mod: { ap_pct: 20 } },
],
},
/** 换招式:每阶段念诵新禁咒,旧的被遗忘 */
10021: {
name: "禁咒",
switches: [
{ trigger: { time: 30 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6201, overrides: { ap: 180 } }] },
{ trigger: { time: 60 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6204, overrides: { ap: 200 } }], attr_mod: { ap_pct: 30, defense_pct: -15 } },
],
},
// ========== 6104 兽人首领-射手(血量,远程池) ==========
/** 追加式:箭种逐段解锁 */
10030: {
name: "箭雨升级",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6150, overrides: { ap: 150 } }] },
{ trigger: { hp_pct: 40 }, adds: [{ s_uuid: 6151, overrides: { ap: 160 } }], attr_mod: { ap_pct: 20 } },
],
},
/** 换招式:放弃弹幕,换单体重炮 */
10031: {
name: "狙击姿态",
switches: [
{ trigger: { hp_pct: 70 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6203, overrides: { ap: 180 } }] },
{ trigger: { hp_pct: 40 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6204, overrides: { ap: 200 } }], attr_mod: { ap_pct: 25, defense_pct: -15 } },
],
},
// ========== 6105 亡灵首领-法师(时间,远程池) ==========
/** 追加式:毒雾与雷暴层层叠加 */
10040: {
name: "亡灵潮汐",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6202, overrides: { ap: 150 } }] },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6204, overrides: { ap: 150 } }], attr_mod: { ap_pct: 25 } },
],
},
/** 换招式:燃尽旧躯壳换新法术 */
10041: {
name: "灵魂爆发",
switches: [
{ trigger: { time: 30 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6200, overrides: { ap: 180 } }], attr_mod: { defense_pct: 15 } },
{ trigger: { time: 60 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6204, overrides: { ap: 220 } }], attr_mod: { ap_pct: 30 } },
],
},
// ========== 6106 亡灵首领-骑马战士(时间,近战池) ==========
/** 追加式:战马冲锋节奏渐强 */
10050: {
name: "冲锋陷阵",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6101, overrides: { ap: 160 } }] },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6104, overrides: { ap: 180 } }], attr_mod: { ap_pct: 20, defense_pct: -10 } },
],
},
/** 换招式:弃守转攻,每段更凶更脆 */
10051: {
name: "背水一战",
switches: [
{ trigger: { time: 30 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6103, overrides: { ap: 200 } }], attr_mod: { ap_pct: 15, defense_pct: -10 } },
{ trigger: { time: 60 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 30, defense_pct: -20 } },
],
},
// ========== 6006 骷髅领主(血量,近战池) ==========
/** 追加式:骨架越碎越硬 */
10060: {
name: "亡灵复苏",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6100, overrides: { ap: 150 } }], attr_mod: { defense_pct: 25 } },
{ trigger: { hp_pct: 40 }, adds: [{ s_uuid: 6102, overrides: { ap: 160 } }], attr_mod: { ap_pct: 15, defense_pct: 15 } },
],
},
/** 换招式:拼装新武器,攻高防低 */
10061: {
name: "骷髅狂暴",
switches: [
{ trigger: { hp_pct: 70 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6103, overrides: { ap: 180 } }] },
{ trigger: { hp_pct: 40 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 25, defense_pct: -15 } },
],
},
};
/**
* 校验阶段方案表合法性(供 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;
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "73ce05bd-3011-4b93-aa6b-f9eafa596cfe",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -116,5 +116,7 @@ export enum GameEvent {
NextSection = "NextSection",
/** 结算弹窗点击"再来一次"(payload 为空,MissionComp 据此重开本关) */
RestartMission = "RestartMission",
/** Boss 阶段切换(payload: { eid, planId, fromPhase, toPhase },表现层监听此事件实现演出;eid 定位实体,勿持裸引用) */
BossPhaseChange = "BossPhaseChange",
}

View File

@@ -1,4 +1,4 @@
import { v3 } from "cc"
import { v3 } from "cc"
import { BoxSet, FacSet, FightSet } from "./GameSet"
import { SkillOverrides, TGroup, SkillSet, FieldSkillSet, SkillConfig, SkillKind, FieldSkillConfig, FieldSkillType, mergeSkillParams, BuffTarget } from "./SkillSet"
import { BuffList, BuffConfig, ModOp } from "./BuffSet"
@@ -430,6 +430,7 @@ export interface heroInfo {
speed?: number; // 移动速度(像素/秒)
dodge?: number; // 闪避率(百分点,缺省由初始化处兜底为全局默认值)
accuracy?: number; // 命中率(百分点,缺省由初始化处兜底为全局默认值)
phase_ids?: number[]; // Boss 专属:允许的阶段方案 id 列表(引用 BossPhaseSet,生成时随机取一)
skills: Record<number, HSkillInfo>; // 携带技能ID列表
bonus?: LvBonusAttrsEntry[]; // 额外属性加成(按 lv 升序,运行时累加所有 lv ≤ 英雄等级 的档位 attrs;hp/ap/def 按"基础×品质+成长"快照百分比加成,crt/frz/stun/para 为固定百分点累加)
info: string; // 描述文案
@@ -1079,7 +1080,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 特殊位怪物
6006: {
uuid: 6006, name: "骷髅领主", path: "m6", fac: FacSet.MON, lv: 1, rarity: 3, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def, speed: 60,
uuid: 6006, name: "骷髅领主", path: "m6", fac: FacSet.MON, lv: 1, rarity: 3, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def, speed: 60, phase_ids: [10060, 10061],
skills: { 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0, overrides: { ap: 100 } } }, info: "前排MiniBoss级坦克"
},
6007: {
@@ -1093,27 +1094,27 @@ export const HeroInfo: Record<number, heroInfo> = {
// BOSS怪物 — 与英雄同一套设定(role 模板 + 品质系数),高品质承伤/输出位
6101: {
uuid: 6101, name: "兽人首领-双刀dps型", path: "mb1", fac: FacSet.MON, lv: 6, rarity: 4, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Warrior].hp, ap: HERO_ROLE_BASE[HRole.Warrior].ap, def: HERO_ROLE_BASE[HRole.Warrior].def, speed: 120,
uuid: 6101, name: "兽人首领-双刀dps型", path: "mb1", fac: FacSet.MON, lv: 6, rarity: 4, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Warrior].hp, ap: HERO_ROLE_BASE[HRole.Warrior].ap, def: HERO_ROLE_BASE[HRole.Warrior].def, speed: 120, phase_ids: [10001, 10002, 10003],
skills: { 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { ap: 100 } } }, info: "前排Boss, 攻速型"
},
6102: {
uuid: 6102, name: "兽人首领-坦克型", path: "mb2", fac: FacSet.MON, lv: 6, rarity: 5, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def, speed: 60,
uuid: 6102, name: "兽人首领-坦克型", path: "mb2", fac: FacSet.MON, lv: 6, rarity: 5, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def, speed: 60, phase_ids: [10010, 10011],
skills: { 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0, overrides: { ap: 100 } } }, info: "前排Boss, 超高HP"
},
6103: {
uuid: 6103, name: "兽人首领-魔法师", path: "mb3", fac: FacSet.MON, lv: 6, rarity: 4, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, monType: MonType.LongBoss, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap, def: HERO_ROLE_BASE[HRole.Mage].def, speed: 110,
uuid: 6103, name: "兽人首领-魔法师", path: "mb3", fac: FacSet.MON, lv: 6, rarity: 4, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, monType: MonType.LongBoss, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap, def: HERO_ROLE_BASE[HRole.Mage].def, speed: 110, phase_ids: [10020, 10021],
skills: { 6009: { uuid: 6009, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { ap: 100, hit_count: 2 } } }, info: "后排法系Boss"
},
6104: {
uuid: 6104, name: "兽人首领-射手", path: "mb4", fac: FacSet.MON, lv: 6, rarity: 4, type: HType.Long, grow_type: HGrowType.Archer, role: HRole.Archer, monType: MonType.LongBoss, hp: HERO_ROLE_BASE[HRole.Archer].hp, ap: HERO_ROLE_BASE[HRole.Archer].ap, def: HERO_ROLE_BASE[HRole.Archer].def, speed: 70,
uuid: 6104, name: "兽人首领-射手", path: "mb4", fac: FacSet.MON, lv: 6, rarity: 4, type: HType.Long, grow_type: HGrowType.Archer, role: HRole.Archer, monType: MonType.LongBoss, hp: HERO_ROLE_BASE[HRole.Archer].hp, ap: HERO_ROLE_BASE[HRole.Archer].ap, def: HERO_ROLE_BASE[HRole.Archer].def, speed: 70, phase_ids: [10030, 10031],
skills: { 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { ap: 100 } } }, info: "后排位Boss, 均衡型"
},
6105: {
uuid: 6105, name: "亡灵首领-法师", path: "mb5", fac: FacSet.MON, lv: 6, rarity: 5, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, monType: MonType.LongBoss, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap, def: HERO_ROLE_BASE[HRole.Mage].def, speed: 110,
uuid: 6105, name: "亡灵首领-法师", path: "mb5", fac: FacSet.MON, lv: 6, rarity: 5, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, monType: MonType.LongBoss, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap, def: HERO_ROLE_BASE[HRole.Mage].def, speed: 110, phase_ids: [10040, 10041],
skills: { 6011: { uuid: 6011, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { ap: 100, hit_count: 2, timed_debuff_id: 7002 } } }, info: "后排高AP Boss"
},
6106: {
uuid: 6106, name: "亡灵首领-骑马战士", path: "mb6", fac: FacSet.MON, lv: 6, rarity: 5, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def, speed: 130,
uuid: 6106, name: "亡灵首领-骑马战士", path: "mb6", fac: FacSet.MON, lv: 6, rarity: 5, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, monType: MonType.MeleeBoss, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def, speed: 130, phase_ids: [10050, 10051],
skills: { 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0, overrides: { ap: 100 } } }, info: "前排终极Boss, 最高HP+高速"
},

View File

@@ -0,0 +1,22 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
/**
* Boss 阶段标记组件:挂载即表示该实体参与多阶段状态机
* 由 Mon.load 按 phase_plan_id 条件挂载,Monster.destroy 显式 remove(触发 reset + 回组件池)
* 计时字段 elapsed 随 reset 天然归零,无全局战斗计时状态
*/
@ecs.register('BossPhase')
export class BossPhaseComp extends ecs.Comp {
/** 生效的阶段方案 id(BossPhaseSet key) */
plan_id: number = 0;
/** 当前阶段索引:0=P1 / 1=P2 / 2=P3(P3 后系统不再检测) */
index: number = 0;
/** Boss 在场战斗累计时长(秒),time 触发条件基准 */
elapsed: number = 0;
reset() {
this.plan_id = 0;
this.index = 0;
this.elapsed = 0;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "49d9ce6c-d575-4453-83f8-721a7865a3e5",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,137 @@
/**
* BossPhaseSystem — Boss 多阶段战斗状态机。
*
* 检测混合触发条件(血量阈值 OR 在场时长),命中后执行阶段切换:
* removes 移除旧技能 → adds 注入新技能(cd 兜底 + 资源预热)→ attr_mod 属性修正
* → 空钩子 onPhaseExit/onPhaseEnter(演出后续迭代)→ 广播 GameEvent.BossPhaseChange
*
* Why: 独立系统符合 M/V/S 分层;filter 按 BossPhaseComp 过滤,每局 Boss 唯一,开销可忽略。
* 计时在组件 elapsed(随 reset 归零),避免系统级计数在 per-entity update 中重复累加。
*/
import { Prefab } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { smc } from "../common/SingletonModuleComp";
import { GameEvent } from "../common/config/GameEvent";
import { BossPhaseSet, PhaseSwitchDef } from "../common/config/BossPhaseSet";
import { SkillSet } from "../common/config/SkillSet";
import { mLogger } from "../common/Logger";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { BossPhaseComp } from "./BossPhaseComp";
import { HeroViewComp } from "./HeroViewComp";
/** 阶段模块调试日志开关 */
const DEBUG = true;
/** 新注入技能缺省 CD(秒),与 Mon.ts 大招注入兜底一致 */
const DEFAULT_ADD_CD = 8;
@ecs.register('BossPhaseSystem')
export class BossPhaseSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(BossPhaseComp, HeroAttrsComp);
}
update(e: ecs.Entity): void {
const phase = e.get(BossPhaseComp);
const model = e.get(HeroAttrsComp);
if (!phase || !model) return;
// 战斗状态守卫(与 MoveComp/SCastSystem 同款模式):暂停/未开战不计时
if (!smc.mission.play || smc.mission.pause) return;
// 死亡同帧守卫:防死尸换阶段
if (model.is_dead) return;
// P3 后不再检测(固定 3 阶段)
if (phase.index >= 2) return;
const plan = BossPhaseSet[phase.plan_id];
if (!plan) return;
// 分诊日志:系统接管确认(elapsed===0 表示本组件首个有效帧,只输出一次)
if (phase.elapsed === 0) {
mLogger.log(DEBUG, "BossPhase", `${model.hero_name} 阶段系统已接管(方案 ${phase.plan_id},当前 P${phase.index + 1},hp ${Math.round(model.hp)}/${model.hp_max})`);
}
phase.elapsed += this.dt;
const def = plan.switches[phase.index];
// 混合触发:任一条件满足即切换(血量实时计算,禁止缓存绝对值——hp_max 在 load 后被外部覆盖)
const hpPct = model.hp / model.hp_max * 100;
const hpHit = def.trigger.hp_pct !== undefined && hpPct <= def.trigger.hp_pct;
const timeHit = def.trigger.time !== undefined && phase.elapsed >= def.trigger.time;
if (hpHit || timeHit) {
this.executePhaseSwitch(e, def);
}
}
/**
* 执行阶段切换:技能增量改写 + 属性修正 + 钩子与事件广播
* Why 改 skills 而非施法系统过滤:与 _testSkills/RogueConfig.ults 动态注入同模式,
* SCastSystem 零改动;施法闭包捕获 SkillSet 静态配置,进行中施法不受影响
*/
private executePhaseSwitch(e: ecs.Entity, def: PhaseSwitchDef): void {
const phase = e.get(BossPhaseComp)!;
const model = e.get(HeroAttrsComp)!;
const from = phase.index;
this.onPhaseExit(e);
// 1. 移除旧技能(仅大招键;普攻首键受键序契约保护,见 BossPhaseSet 文件头注释)
if (def.removes) {
for (const s_uuid of def.removes) {
delete model.skills[s_uuid];
}
}
// 2. 注入新技能(cd 强制兜底,防 undefined → Math.min 结果 NaN 导致大招永不释放)
if (def.adds) {
for (const add of def.adds) {
if (!add || !add.s_uuid || !SkillSet[add.s_uuid]) continue;
model.skills[add.s_uuid] = {
uuid: add.s_uuid,
lv: 1,
cd: add.cd ?? DEFAULT_ADD_CD,
ccd: 0,
overrides: add.overrides,
};
// 资源预热:新技能 prefab 首次异步加载会丢一发,切换时预载(路径与 Skill.load 对齐)
oops.res.load(`game/skill/atk/${SkillSet[add.s_uuid].sp_name}`, Prefab);
}
}
// 3. 属性修正:基于当前值链式乘算(P3 基于 P2 修正后的值,配置按此口径)
const mod = def.attr_mod;
if (mod) {
if (mod.ap_pct) model.ap = Math.round(model.ap * (1 + mod.ap_pct / 100));
if (mod.defense_pct) model.defense = Math.round(model.defense * (1 + mod.defense_pct / 100));
}
phase.index++;
this.onPhaseEnter(e);
mLogger.log(DEBUG, "BossPhase", `${model.hero_name} 阶段切换 P${from + 1}→P${phase.index + 1}(方案 ${phase.plan_id})`);
// 广播阶段切换(payload 用 eid 定位实体,防监听方持有已回收实体裸引用)
oops.message.dispatchEvent(GameEvent.BossPhaseChange, {
eid: e.eid,
planId: phase.plan_id,
fromPhase: from,
toPhase: phase.index,
});
// 直接驱动描边:Boss 是 MON 阵营,不走英雄 HeroLvUp 语义;
// 且对象池节点上挂全局事件监听易漏注销,直接调视图组件最稳
const view = e.get(HeroViewComp);
if (view && view.node && view.node.isValid) {
view.applyBossPhaseOutline(phase.index);
}
}
/** TODO: 阶段退场表现(震屏/变暗等),后续迭代实现 */
private onPhaseExit(e: ecs.Entity): void { }
/** TODO: 阶段入场表现(怒吼/无敌帧/技能横幅等),后续迭代实现 */
private onPhaseEnter(e: ecs.Entity): void { }
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "bde209ff-324b-4f07-a241-fb977cbc6c2f",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -16,6 +16,7 @@ import { Tooltip } from "../skill/Tooltip";
import { timedCom } from "../skill/timedCom";
import { oneCom } from "../skill/oncend";
import { FlashSprite } from "./hit-flash-white/scripts/FlashSprite";
import { BossPhaseComp } from "./BossPhaseComp";
const { ccclass, property } = _decorator;
@@ -89,7 +90,6 @@ export class HeroViewComp extends CCComp {
this.deadCD = 0;
this.lastBarUpdateTime = 0;
this.status_change("idle");
// 初始化头顶状态栏
const topNode = this.node.getChildByName("top");
this.topBar = topNode.getComponent(HeroTopBarComp) || topNode.addComponent(HeroTopBarComp);
@@ -98,15 +98,12 @@ export class HeroViewComp extends CCComp {
/** 方向 */
this.node.setScale(this.scale * Math.abs(this.node.scale.x), 1 * this.node.scale.y); // 确保 scale.x 为正后再乘方向
// 🔥 重置描边
if (this.model) {
let flashSprite = this.node.getComponent(FlashSprite);
if (!flashSprite) {
flashSprite = this.node.getComponentInChildren(FlashSprite);
}
if (flashSprite) {
flashSprite.setOutlineByLevel(this.model.lv);
}
// 🔥 重置描边:Boss 阶段描边优先(Boss 存活期不被等级描边覆盖),其余按等级
const phaseComp = this.ent ? this.ent.get(BossPhaseComp) : null;
if (phaseComp && phaseComp.index > 0) {
this.applyBossPhaseOutline(phaseComp.index);
} else {
this.applyOutlineByLevel();
}
}
@@ -170,16 +167,40 @@ export class HeroViewComp extends CCComp {
public lv_up() {
this.spawnTimedFx("game/skill/buff/buff_lvup", this.node, 1.0);
// 升级时同步更新描边
if (this.model) {
let flashSprite = this.node.getComponent(FlashSprite);
if (!flashSprite) {
flashSprite = this.node.getComponentInChildren(FlashSprite);
// 升级时同步更新描边:Boss 阶段描边优先(与 init 口径一致)
const phaseComp = this.ent ? this.ent.get(BossPhaseComp) : null;
if (phaseComp && phaseComp.index > 0) {
this.applyBossPhaseOutline(phaseComp.index);
} else {
this.applyOutlineByLevel();
}
}
/** 按英雄等级应用描边(升级/初始化共用入口) */
private applyOutlineByLevel() {
if (!this.model) return;
const flashSprite = this.getFlashSprite();
if (flashSprite) {
flashSprite.setOutlineByLevel(this.model.lv);
}
}
/**
* 按 Boss 阶段应用描边(P2 绿 / P3 红)
* Why: 由 BossPhaseSystem 直接调用,不走英雄 HeroLvUp 事件语义
* (Boss 是 MON 阵营,且对象池节点挂全局监听易漏注销)
* @param phase 阶段索引:1=P2 / 2=P3
*/
public applyBossPhaseOutline(phase: number) {
const flashSprite = this.getFlashSprite();
if (flashSprite) {
flashSprite.setOutlineByBossPhase(phase);
}
}
/** 查找 FlashSprite 组件(优先自身,回退子节点),描边三处调用点统一入口 */
private getFlashSprite(): FlashSprite | null {
return this.node.getComponent(FlashSprite) ?? this.node.getComponentInChildren(FlashSprite);
}
/** 攻击力提升特效 */
@@ -566,6 +587,13 @@ export class HeroViewComp extends CCComp {
this.deadCD = 0
this.lastBarUpdateTime = 0
// 清掉描边材质:防 Boss 阶段描边(绿/红)随对象池节点复用串到小怪/英雄身上
const flashSprite = this.getFlashSprite();
if (flashSprite && flashSprite.defaultMaterial) {
flashSprite.orginalFlashMaterial = flashSprite.defaultMaterial;
flashSprite.sprite?.setSharedMaterial(flashSprite.defaultMaterial, 0);
}
// 清理伤害队列
this.damageQueue.length = 0;
this.isProcessingDamage = false;

View File

@@ -10,6 +10,10 @@ import { MoveComp } from "./MoveComp";
import { GameEvent } from "../common/config/GameEvent";
import { SkillTriggerType, HType } from "../common/config/heroSet";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
import { BossPhaseSet } from "../common/config/BossPhaseSet";
import { BossPhaseComp } from "./BossPhaseComp";
import "./BossPhaseSystem";
import { mLogger } from "../common/Logger";
/** 怪物实体:负责怪物对象池复用、属性初始化、入场动画与回收 */
@ecs.register(`Monster`)
export class Monster extends ecs.Entity {
@@ -122,6 +126,10 @@ export class Monster extends ecs.Entity {
}
}
// 阶段组件显式移除:触发 reset 清空状态并回组件池(ECS 组件无自动销毁钩子)
if (this.has(BossPhaseComp)) {
this.remove(BossPhaseComp);
}
// 手动移除组件,避免 ecs 引用滞留
this.remove(HeroViewComp);
this.remove(HeroAttrsComp);
@@ -134,11 +142,11 @@ export class Monster extends ecs.Entity {
* 2) 初始化表现、属性、技能与阵营
* 3) 播放下落入场并在落地后启用碰撞与移动
*/
load(pos: Vec3 = Vec3.ZERO, scale: number = 1, uuid: number = 1001, is_boss: boolean = false, dropToY: number = pos.y, mon_lv: number = 1, laneIndex: number = 0) {
load(pos: Vec3 = Vec3.ZERO, scale: number = 1, uuid: number = 1001, is_boss: boolean = false, dropToY: number = pos.y, mon_lv: number = 1, laneIndex: number = 0, phase_plan_id: number = 0) {
// 怪物默认朝左,表现缩放固定为负向
scale = -1
// 当前怪物尺寸固定,保留变量便于后续扩展
let size = 1
// Boss 体型放大 1.5 倍,小怪保持原尺寸
let size = is_boss ? 1.5 : 1
var scene = smc.map.MapView.scene;
// 根据配置读取怪物预制体路径
var path = "game/heros/" + HeroInfo[uuid].path;
@@ -264,6 +272,21 @@ export class Monster extends ecs.Entity {
// 缓存技能射程等派生数据,减少战斗帧内重复计算
model.updateSkillDistanceCache();
// Boss 阶段初始化:P1 = 基线技能,仅挂标记组件并记录方案(方案缺失则退化为单阶段 Boss)
if (is_boss && phase_plan_id > 0) {
const plan = BossPhaseSet[phase_plan_id];
if (plan) {
const phase = this.add(BossPhaseComp);
phase.plan_id = phase_plan_id;
phase.index = 0;
// 分诊日志:确认组件挂载成功(区分"未挂载"与"系统未接管"两类故障)
mLogger.log(true, "BossPhase", `${model.hero_name} 挂载阶段方案 ${phase_plan_id}(P1 起点)`);
} else {
// 配置兜底:引用了不存在的方案,退化为单阶段并告警(不阻断战斗)
mLogger.warn(true, "BossPhase", `阶段方案 ${phase_plan_id} 未在 BossPhaseSet 中定义,Boss 按单阶段战斗`);
}
}
// 注册视图组件并重置对象池复用状态
this.add(view);
view.init();
@@ -356,6 +379,9 @@ export class Monster extends ecs.Entity {
/** 重置入口:复用 destroy 的释放流程 */
reset() {
if (this.has(BossPhaseComp)) {
this.remove(BossPhaseComp);
}
super.destroy();
}

View File

@@ -7,7 +7,7 @@ export class FlashSprite extends Component {
@property(Material)
hitFlashMaterial: Material | null = null;
@property({ type: Material, tooltip: '绿色描边材质(2级)' })
@property({ type: Material, tooltip: '绿色描边材质(2级 / Boss阶段2)' })
outlineMatGreen: Material | null = null;
@property({ type: Material, tooltip: '蓝色描边材质(3级)' })
@@ -19,6 +19,9 @@ export class FlashSprite extends Component {
@property({ type: Material, tooltip: '黄色描边材质(5级)' })
outlineMatYellow: Material | null = null;
@property({ type: Material, tooltip: '红色描边材质(Boss阶段3,即 outline-glow)' })
outlineMatGlow: Material | null = null;
orginalFlashMaterial: Material | null = null;
defaultMaterial: Material | null = null; // 缓存最原始的无描边材质
sprite: Sprite | null = null;
@@ -64,6 +67,34 @@ export class FlashSprite extends Component {
}
}
/**
* 按 Boss 阶段设置描边材质(优先级高于等级描边)
* Why: Boss 阶段是战斗强反馈(P2 绿=警告 / P3 红=危险),与英雄等级成长是两套视觉语言,
* 用独立通道避免污染 setOutlineByLevel 的等级语义;材质复用现有 outline-green / outline-glow
* @param phase 阶段索引:1=P2 绿 / 2=P3 红 / 0=P1 恢复默认
*/
public setOutlineByBossPhase(phase: number) {
if (!this.sprite) {
this.sprite = this.node.getComponent(Sprite);
}
if (!this.sprite) return;
let targetMat: Material | null = null;
if (phase === 1) {
targetMat = this.outlineMatGreen;
} else if (phase >= 2) {
targetMat = this.outlineMatGlow ?? this.outlineMatYellow;
} else {
targetMat = this.defaultMaterial;
}
if (targetMat) {
// 更新基础材质,确保闪白结束后恢复到这个阶段描边材质
this.orginalFlashMaterial = targetMat;
this.sprite.setSharedMaterial(targetMat, 0);
}
}
public clickFlash() {
if (!this.sprite || !this.hitFlashMaterial) return;

View File

@@ -582,12 +582,15 @@ export class MissionComp extends CCComp {
private autoNextPhase() {
switch (this.currentPhase) {
case MissionPhase.PrepareStart:
// 节间过场倒计时结束:推进到下一节、停背景滚动、派发 SectionStart 刷怪,进入 PrepareEnd 自动开战
// 节间过场倒计时结束:停背景滚动、英雄归位转待机,进入 PrepareEnd 缓冲
this.stopWaveCountdown();
this.advanceToNextSection();
this.changePhase(MissionPhase.PrepareEnd);
break;
case MissionPhase.PrepareEnd:
// 先生成下一回合怪物再进 BattleStart:此时英雄已待机约 1s,
// 怪物随"第 X 回合开始"横幅从右侧跑入,避免与英雄过场行军同帧移动;
// SectionStart 同步派发 NewWave,保证 changePhase(BattleStart) 的 isBossWave 就绪
this.advanceToNextSection();
this.changePhase(MissionPhase.BattleStart);
break;
case MissionPhase.BattleStart:
@@ -854,7 +857,10 @@ export class MissionComp extends CCComp {
* 2. 不清理英雄/技能实体、不重置局内数据、不重新三选一——英雄保留并自动归位(编队排队)。
* 3. 进度实时镜像到 HUD,但不写持久存档(存档只在整关通关时推进到下一关)。
*
* 调用时机:PrepareStart 过场倒计时结束(autoNextPhase)。
* 调用时机:PrepareEnd 结束、进入 BattleStart 前(autoNextPhase)。
* Why: 过场倒计时(PrepareStart)结束时英雄刚归位转待机,若同帧刷怪会出现
* 怪物与英雄一起移动的表现;推迟到 PrepareEnd 结束刷怪,英雄已待机约 1s,
* 怪物从右侧跑入与"第 X 回合开始"横幅同步登场。
* 保证:仅 1~4 回合清场后触发,末回合(BOSS_SECTION)已走 open_Victory 结算,不会推进。
*/
private advanceToNextSection() {

View File

@@ -217,11 +217,10 @@ export class MissionMonCompComp extends CCComp {
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
// 技能套装注入:通过 _testSkills 通道传递给 Mon.load()
if (monData.skills) {
(mon as any)._testSkills = monData.skills;
}
// Why 无条件赋值:实体池复用同一 JS 对象,条件赋值会让复用为小怪的实体残留上一个 Boss 的技能
(mon as any)._testSkills = monData.skills ?? undefined;
mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monData.lv, posIndex);
mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monData.lv, posIndex, monData.phase_plan_id ?? 0);
// oops.message 全局事件总线:Boss 登场(震屏/音效由表现层消费)
if (monData.isBoss) {

View File

@@ -22,6 +22,7 @@
import { HeroInfo, HType, MonType, TriggerGrouped, UltCdLv, UltCdSet } from "../common/config/heroSet";
import { SkillOverrides, SkillSet } from "../common/config/SkillSet";
import { BossPhaseSet, validateBossPhaseSet } from "../common/config/BossPhaseSet";
import { FacSet } from "../common/config/GameSet";
import {
SectionType, SectionConfig, getSectionConfig, calcMonsterAttrs,
@@ -185,6 +186,22 @@ export function validateRogueConfig(): string[] {
}
}
// 校验 Boss 阶段方案表合法性(恰 2 切换点 / trigger 条件 / hp_pct 前缀与递减 / 技能引用)
for (const err of validateBossPhaseSet()) {
errors.push(err);
}
// 校验 heroInfo.phase_ids 引用的方案存在
for (const key in HeroInfo) {
const info = HeroInfo[key];
if (!info.phase_ids) continue;
for (const pid of info.phase_ids) {
if (!BossPhaseSet[pid]) {
errors.push(`HeroInfo[${key}] phase_ids 引用的方案 ${pid} 未在 BossPhaseSet 中定义`);
}
}
}
return errors;
}
@@ -206,6 +223,8 @@ export interface GeneratedMonster {
batch: number;
/** 延迟刷出时间(秒),0 表示立即 */
delay?: number;
/** Boss 阶段方案 id(引用 BossPhaseSet,由 makeBoss 从 heroInfo.phase_ids 随机取一;未配置为 undefined) */
phase_plan_id?: number;
/**
* 怪物技能套装(技能注入通道,与 _testSkills 相同,在 Mon.load() 中写入 HeroAttrsComp)
@@ -230,12 +249,11 @@ export interface GeneratedMonster {
export const TestModeConfig = {
enable: false, // 默认关闭测试模式
baseHp: 150,
baseHp: 5000,
baseAp: 12,
monType: MonType.Melee,
monUuid: 6001,
monType: MonType.MeleeBoss,
monUuid: 6101,
spawnCount: 1,
skill: undefined as { s_uuid: number; cd?: number; overrides?: SkillOverrides } | undefined,
/** 测试模式注入的大招列表(追加,不替换普攻) */
ults: undefined as { s_uuid: number; cd?: number; overrides?: SkillOverrides }[] | undefined,
@@ -243,6 +261,11 @@ export const TestModeConfig = {
atked: undefined as TriggerGrouped | undefined,
dead: undefined as TriggerGrouped | undefined,
fstart: undefined as TriggerGrouped | undefined,
/** 测试模式:Boss 阶段方案 id(0 = 不启用阶段;需配合 isBoss: true 才会挂载组件) */
phasePlanId: 10001,
/** 测试模式:是否以 Boss 身份刷出(影响层级/金币/阶段组件挂载) */
isBoss: true,
};
// ======================== 8. 生成引擎 ========================
@@ -366,10 +389,11 @@ export class StageSpawningEngine {
type: TestModeConfig.monType,
hp: TestModeConfig.baseHp,
ap: TestModeConfig.baseAp,
isBoss: false,
isBoss: TestModeConfig.isBoss,
lv: 1,
spawnIndex: i,
batch: 0,
phase_plan_id: TestModeConfig.phasePlanId || undefined,
skills: {
skill: TestModeConfig.skill,
ults: TestModeConfig.ults,
@@ -408,6 +432,7 @@ export class StageSpawningEngine {
skills: {
ults: this.pickBossUlts(range, stage),
},
phase_plan_id: this.pickPhasePlan(baseInfo),
};
}
@@ -433,6 +458,13 @@ export class StageSpawningEngine {
return uuids[Math.floor(Math.random() * uuids.length)];
}
/** 从 Boss 允许的阶段方案中随机取一(未配置返回 undefined,Boss 退化为单阶段) */
private pickPhasePlan(baseInfo?: { phase_ids?: number[] }): number | undefined {
const ids = baseInfo?.phase_ids;
if (!ids || ids.length === 0) return undefined;
return ids[Math.floor(Math.random() * ids.length)];
}
/**
* 按定位抽取 Boss 大招(不重复),数量由关卡进度决定(getBossUltCount)
* @returns 大招配置列表;池空时返回 undefined

View File

@@ -0,0 +1,306 @@
# Boss 阶段战斗内容设计 v1
> **定位**:本文是 Boss 阶段系统的**内容设计文档**(配什么方案、什么数值),
> 技术机制见规格文档 `docs/superpowers/specs/2026-10-06-boss-phase-system-design.md`。
> **所有设计均基于现有实现能力落地,零机制新增**;外部设计稿(像素大勇者-boss-阶段设计稿.md)
> 仅作理念借鉴,凡与现有实现冲突处一律改写(对照见 §7)。
---
## §1 设计目标
- 6 个现有 Boss 各自拥有 **2~3 个阶段方案**,生成时随机抽一(肉鸽重玩性)
- 每个方案固定 **3 阶段**:P1(基线)→ P2 → P3,两个切换点
- 两种战斗气质:**追加式**(技能池越打越大,数值膨胀)与**换招式**(整组换招,机制重组),
玩家主观强度对齐
- P1→P2 触发条件全 Boss 统一,降低玩家学习成本;P3 是高潮,阈值按 Boss 气质差异化
---
## §2 现有机制能力边界(设计必须遵守)
| 能力 | 现状 | 设计含义 |
|------|------|----------|
| 触发条件 | `hp_pct`(血量≤%)/ `time`(Boss 在场秒数),同点 OR 关系 | 混合触发可直接配 |
| 技能变更 | `adds` 注入 + `removes` 按 id 删除,`delete` 不存在的键无副作用 | **换招式 = removes 列满该定位整个大招池**(见 §3.2) |
| 属性修正 | 仅 `ap_pct` / `defense_pct`,链式乘算 | **不配 hp 修正**(实现已排除,避免血量同步歧义) |
| 技能数值 | `overrides.ap` 系数,口径同 BossUltPools(140~220) | 不引用英雄技能 lv 体系 |
| 新技能 CD | `cd` 缺省 8s,注入后 `ccd: 0` 需等满一个 CD 才首放 | P3 注入技能有 8s 真空期,阈值设计须留窗口 |
| 战斗时长 | `BATTLE_TIMEOUT = 90s`,Boss 延迟 1s 刷出 | **时间触发方案的 P3 阈值 ≤ 60s**(留 ≥28s 收尾窗口) |
| 方案校验 | 恰 2 切换点;hp_pct ∈(0,100) 严格递减且须构成前缀;time 严格递增 | 配错启动期 `validateBossPhaseSet` 报错 |
| 普攻保护 | removes/adds 禁止操作普攻首键 | 只操作大招键 |
---
## §3 通用设计规则
### 3.1 触发维度按 Boss 定位分配(借鉴外部稿注 B,映射到现有 Boss)
| Boss | 定位 | 触发维度 | 理由 |
|------|------|----------|------|
| 6101 兽人首领-双刀dps | 近战 Warrior | **血量**(P2=70% / P3=40%) | 速战速决型,血量阈值直觉明显 |
| 6102 兽人首领-坦克型 | 近战 Tank | **时间**(P2=30s / P3=60s) | 消耗战,时间触发逼玩家在窗口期内输出 |
| 6103 兽人首领-魔法师 | 远程 Mage | **时间**(P2=30s / P3=60s) | 法系读招,时间阈值给玩家"看清招式再反应"的窗口 |
| 6104 兽人首领-射手 | 远程 Archer | **血量**(P2=70% / P3=40%) | 高频弹幕,时间触发会过于喧闹 |
| 6105 亡灵首领-法师 | 远程 Mage | **时间**(P2=30s / P3=60s) | 同 6103 |
| 6106 亡灵首领-骑马战士 | 近战 Tank | **时间**(P2=30s / P3=60s) | 同 6102 |
| 6006 骷髅领主 | 近战 Tank | **血量**(P2=70% / P3=40%) | 低攻速靶子型,血量节奏更稳 |
> 时间阈值为 **Boss 在场时长**(elapsed 从出生起算,暂停不计)。30s/60s 相对 90s 战斗超时
> 留出 P3 ≥28s 的收尾窗口。
### 3.2 换招式(clear_replace)的落地技巧
实现没有"清空"开关,但 `removes` 对不存在的 id 无副作用。因此:
> **换招式 = removes 列满该 Boss 定位对应的整个大招池 id**(近战池 6100~6104 / 远程池 6200~6204 + 6150/6151),
> 无论 RogueConfig 随机注入了哪个大招、之前阶段 adds 了什么,一律清净,再 adds 新技能组。
- 近战定位池:`[6100, 6101, 6102, 6103, 6104]`
- 远程定位池:`[6150, 6151, 6200, 6201, 6202, 6203, 6204]`
### 3.3 双模式强度对齐(借鉴外部稿 Q3,改写为现有口径)
| 模式 | 新鲜感来源 | 数值手段 | 输出口径 |
|------|-----------|----------|----------|
| 追加式(keep_add) | 技能池变大、轮转更密 | `attr_mod.ap_pct` 每段 +15~25% | 技能 overrides 维持标准档(ap 150~180) |
| 换招式(clear_replace) | 招式重组、机制差异 | 新技能 `overrides.ap` 逐段上调(180→200→220) | attr_mod 少量或不配 |
两模式的 P3 理论 DPS 控制在 **±10% 以内**;换招式清掉了随机注入的大招(可能很强),
故新技能给更高系数补偿。
### 3.4 方案 id 规划(10000 段,按 Boss 分组)
| id 段 | Boss | 触发 |
|-------|------|------|
| 10001~10009 | 6101 双刀dps | 血量 |
| 10010~10019 | 6102 坦克型 | 时间 |
| 10020~10029 | 6103 魔法师 | 时间 |
| 10030~10039 | 6104 射手 | 血量 |
| 10040~10049 | 6105 亡灵法师 | 时间 |
| 10050~10059 | 6106 骑马战士 | 时间 |
| 10060~10069 | 6006 骷髅领主 | 血量 |
---
## §4 各 Boss 阶段方案(可直接抄入 BossPhaseSet.ts)
> 以下 `removes` 中的整池列表即 §3.2 的换招技巧;`cd` 缺省 8s 不另写。
### 4.1 6101 兽人首领-双刀dps(血量,近战池)
```ts
// 10001 狂暴进化(已有):P2 换裂气斩、P3 加闪电斩 + 攻+20%
10001: {
name: "狂暴进化",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6102, overrides: { ap: 180 } }], removes: [6100] },
{ trigger: { hp_pct: 40, time: 60 }, adds: [{ s_uuid: 6103, overrides: { ap: 180 } }], attr_mod: { ap_pct: 20 } },
],
},
// 10002 嗜血连斩(追加式):双刀越挥越快,技能只加不换
10002: {
name: "嗜血连斩",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6103, overrides: { ap: 160 } }], attr_mod: { ap_pct: 15 } },
{ trigger: { hp_pct: 40 }, adds: [{ s_uuid: 6104, overrides: { ap: 180 } }], attr_mod: { ap_pct: 25, defense_pct: -10 } },
],
},
// 10003 换刃(换招式):每阶段换一把"刀",系数逐段上调
10003: {
name: "换刃",
switches: [
{ trigger: { hp_pct: 70 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6101, overrides: { ap: 200 } }] },
{ trigger: { hp_pct: 40 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 20, defense_pct: -15 } },
],
},
```
### 4.2 6102 兽人首领-坦克型(时间,近战池)
```ts
// 10010 重压觉醒(追加式):越久越硬,反击越痛
10010: {
name: "重压觉醒",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6100, overrides: { ap: 150 } }], attr_mod: { defense_pct: 20 } },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6104, overrides: { ap: 180 } }], attr_mod: { ap_pct: 20 } },
],
},
// 10011 崩岩(换招式):外壳剥落,换进攻形态
10011: {
name: "崩岩",
switches: [
{ trigger: { time: 30 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6102, overrides: { ap: 200 } }], attr_mod: { ap_pct: 15 } },
{ trigger: { time: 60 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 25, defense_pct: -20 } },
],
},
```
### 4.3 6103 兽人首领-魔法师(时间,远程池)
```ts
// 10020 元素汇聚(追加式):元素逐一汇入,弹幕越来越密
10020: {
name: "元素汇聚",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6204, overrides: { ap: 150 } }] },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6202, overrides: { ap: 150 } }], attr_mod: { ap_pct: 20 } },
],
},
// 10021 禁咒(换招式):每阶段念诵新禁咒,旧的被遗忘
10021: {
name: "禁咒",
switches: [
{ trigger: { time: 30 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6201, overrides: { ap: 180 } }] },
{ trigger: { time: 60 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6204, overrides: { ap: 200 } }], attr_mod: { ap_pct: 30, defense_pct: -15 } },
],
},
```
### 4.4 6104 兽人首领-射手(血量,远程池)
```ts
// 10030 箭雨升级(追加式):箭种逐段解锁
10030: {
name: "箭雨升级",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6150, overrides: { ap: 150 } }] },
{ trigger: { hp_pct: 40 }, adds: [{ s_uuid: 6151, overrides: { ap: 160 } }], attr_mod: { ap_pct: 20 } },
],
},
// 10031 狙击姿态(换招式):放弃弹幕,换单体重炮
10031: {
name: "狙击姿态",
switches: [
{ trigger: { hp_pct: 70 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6203, overrides: { ap: 180 } }] },
{ trigger: { hp_pct: 40 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6204, overrides: { ap: 200 } }], attr_mod: { ap_pct: 25, defense_pct: -15 } },
],
},
```
### 4.5 6105 亡灵首领-法师(时间,远程池)
```ts
// 10040 亡灵潮汐(追加式):毒雾与雷暴层层叠加
10040: {
name: "亡灵潮汐",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6202, overrides: { ap: 150 } }] },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6204, overrides: { ap: 150 } }], attr_mod: { ap_pct: 25 } },
],
},
// 10041 灵魂爆发(换招式):燃尽旧躯壳换新法术
10041: {
name: "灵魂爆发",
switches: [
{ trigger: { time: 30 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6200, overrides: { ap: 180 } }], attr_mod: { defense_pct: 15 } },
{ trigger: { time: 60 }, removes: [6150, 6151, 6200, 6201, 6202, 6203, 6204], adds: [{ s_uuid: 6204, overrides: { ap: 220 } }], attr_mod: { ap_pct: 30 } },
],
},
```
### 4.6 6106 亡灵首领-骑马战士(时间,近战池)
```ts
// 10050 冲锋陷阵(追加式):战马冲锋节奏渐强
10050: {
name: "冲锋陷阵",
switches: [
{ trigger: { time: 30 }, adds: [{ s_uuid: 6101, overrides: { ap: 160 } }] },
{ trigger: { time: 60 }, adds: [{ s_uuid: 6104, overrides: { ap: 180 } }], attr_mod: { ap_pct: 20, defense_pct: -10 } },
],
},
// 10051 背水一战(换招式):弃守转攻,每段更凶更脆
10051: {
name: "背水一战",
switches: [
{ trigger: { time: 30 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6103, overrides: { ap: 200 } }], attr_mod: { ap_pct: 15, defense_pct: -10 } },
{ trigger: { time: 60 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 30, defense_pct: -20 } },
],
},
```
### 4.7 6006 骷髅领主(血量,近战池)
```ts
// 10060 亡灵复苏(追加式):骨架越碎越硬
10060: {
name: "亡灵复苏",
switches: [
{ trigger: { hp_pct: 70 }, adds: [{ s_uuid: 6100, overrides: { ap: 150 } }], attr_mod: { defense_pct: 25 } },
{ trigger: { hp_pct: 40 }, adds: [{ s_uuid: 6102, overrides: { ap: 160 } }], attr_mod: { ap_pct: 15, defense_pct: 15 } },
],
},
// 10061 骷髅狂暴(换招式):拼装新武器,攻高防低
10061: {
name: "骷髅狂暴",
switches: [
{ trigger: { hp_pct: 70 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6103, overrides: { ap: 180 } }] },
{ trigger: { hp_pct: 40 }, removes: [6100, 6101, 6102, 6103, 6104], adds: [{ s_uuid: 6104, overrides: { ap: 220 } }], attr_mod: { ap_pct: 25, defense_pct: -15 } },
],
},
```
### 4.8 heroSet.ts 挂载(phase_ids)
```ts
// 6101 双刀dps: phase_ids: [10001, 10002, 10003],
// 6102 坦克型: phase_ids: [10010, 10011],
// 6103 魔法师: phase_ids: [10020, 10021],
// 6104 射手: phase_ids: [10030, 10031],
// 6105 亡灵法师: phase_ids: [10040, 10041],
// 6106 骑马战士: phase_ids: [10050, 10051],
// 6006 骷髅领主: phase_ids: [10060, 10061],
```
---
## §5 关卡节奏:前期教学 → 后期完整
**现状约束**:`pickPhasePlan` 从 `phase_ids` 均匀随机,无关卡维度;P3 阈值写死在方案里。
**本版策略(零机制改动)**:
- P3 阈值统一标准档(血量 40% / 时间 60s),不做关卡调节
- 前期体验差异由数值自然产生:低关卡玩家 DPS 低、Boss 血量成长低,战斗更快结束,
时间型 Boss 的 P3(60s)在前期自然少见 → 符合"前期教学、后期完整"的意图
- P3 新技能有 8s CD 真空期,残血/收尾阶段常来不及施放 → P3 实际强度比纸面温和,
前期偶发触发不致失控
**后续迭代项(需小改 `pickPhasePlan`,不在本版)**:
按关卡号从 `phase_ids` 过滤"苛刻版/标准版/宽松版"方案副本
(前期 P3 血量 15% / 时间 75s 的副本方案,id 同段递增)。
---
## §6 验证清单
- [ ] `validateBossPhaseSet()` 全量通过(恰 2 切换点 / hp_pct 前缀递减 / time 递增 / 技能引用存在)
- [ ] TestMode 逐个 Boss 跑一遍:追加式 P3 技能池 = 随机大招 + 2 个 adds;换招式 P3 技能池 = 仅 1 个 adds
- [ ] 时间型方案实测:30s/60s 切换,90s 超时前 P3 至少施放 1 次新技能
- [ ] 换招式验证:removes 整池后,RogueConfig 随机注入的大招确实消失(普攻不受影响)
- [ ] 数值抽查:同 Boss 追加式 vs 换招式的 P3 阶段 30s 伤害量差距 ≤10%
---
## §7 与外部设计稿的对照(借鉴 vs 放弃)
| 外部稿概念 | 本文处理 | 原因 |
|-----------|---------|------|
| 4 主题 Boss(破阵先锋/雷巫/霜羽/磐盾) | 映射到现有 6 Boss,按定位分触发维度 | Boss 池由 heroSet 实际条目决定 |
| `phase_mode: clear_replace` 字段 | 用 `removes` 列满定位大招池实现(§3.2) | 实现为纯增量,`delete` 空键无副作用,零机制新增 |
| `property_mod.hp` 血量修正 | **全部放弃**,仅 ap/def | 实现已排除 hp,避免血量同步歧义 |
| P1→P2 固定 60s(时间型) | 改为 **30s** | 适配 `BATTLE_TIMEOUT=90s`,P3 需留收尾窗口 |
| P2→P3 关卡三档调节(5%/20%/35%) | 本版统一标准档,关卡调节列后续迭代 | `pickPhasePlan` 无关卡维度,不为此改机制 |
| 英雄 lv 技能引用(lv8/lv16) | 统一 `SkillSet` 6100~6204 + overrides.ap 口径 | Boss 大招体系与英雄 lv 体系无关 |
| 阶段切换无敌帧/演出 | 维持空钩子 `onPhaseExit/onPhaseEnter` | 表现层后续迭代,接口已预留 |
| 双模式强度对齐 ±5% | 放宽到 ±10%,用 §3.3 口径对齐 | 缺数值模拟工具,先宽松后收敛 |
| Boss 命名美学(方案名) | 全部保留并适配(狂暴进化/禁咒/背水一战…) | 与机制无关,直接采纳 |
---
**版本**:v1(2026-10-06,基于已实现机制重写)
**关联文档**:
- 技术规格:`docs/superpowers/specs/2026-10-06-boss-phase-system-design.md`
- 实现计划:`docs/superpowers/plans/2026-10-06-boss-phase-system.md`
- 外部参考:`y:\构思\像素大勇者-boss-阶段设计稿.md`(仅理念借鉴)