Files
spellforge/docs_dev/specs/2026-07-22-boss-phases-design.md
joywayerandClaude Opus 4.8 049e5930d6 docs(spec): Boss 招式增加近战 melee(原地近身范围打击)
区别于 dash 横穿冲撞,melee 为原地劈砍/砸地:前摇预警→melee_radius
内判定扣血→后摇。同步更新范围/设计器 kind 列表/验证项。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 09:29:50 +08:00

243 lines
14 KiB
Markdown
Raw Permalink 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.
# Boss 阶段 + 弹幕/位移攻击模式系统 — 设计
> **日期**2026-07-22
> **切片**S6Boss 深度)
> **前置状态**:Mana 系统 + 元进展已完成;Boss 目前只是 EnemyManager SoA 中 `type=4`Mini/W8/`type=5`Final/W20)的厚血实体,唯一"阶段行为"是 66%/33% 跨阈值召唤 FAST 援军(`wave_manager.gd`)。本设计将其升级为数据驱动的多阶段状态机 + 攻击模式调度 + 敌方子弹子系统。
> **依赖**EnemyManager、PlayerManager、PlayerStats、EventBus、VFXManager、SettingsManager、SpatialGrid、game_designer 编辑器插件。
---
## 0. 范围
**范围内**
- 数据驱动的 Boss 多阶段状态机(血量阈值切换)。
- 攻击模式注册表:瞬时远程弹幕(环/扇/瞄准/旋转)+ 位移/蓄力/近战招式(冲刺/蓄力重击/近身劈砍),带三段式生命周期与前摇预警。
- 新增 `EnemyBulletManager` 敌方子弹子系统(伤害玩家)。
- 保留现有"进阶召唤援军"手感(作为每阶段可选字段)。
- 补齐专用事件 `BOSS_PHASE_CHANGED` / `BOSS_KILLED`
- `data/bosses.json` + 设计器新增「Boss」分页(字段化、双语、行编辑器)。
- 敌方子弹独立 MultiMesh 渲染层。
**范围外(延后)**
- 场地机制(冰墙障碍、场地收缩)。
- 通关演出 / `GAME_CLEARED` 事件 / Boss 死亡序列演出。
- 玩家无敌帧系统(本轮敌方子弹单次命中即销毁,靠数值调低保证公平)。
- `.tres` 资源(本工程纯 JSON,不引入)。
- C# 热路径(沿用 GDScript,参照 R-08)。
---
## 1. 新增自动加载 `EnemyBulletManager`(敌方子弹)
平行于 `BulletManager` 的**独立 SoA 池**,不污染玩家子弹热路径(玩家子弹检测 vs 敌人 SpatialGrid;敌方子弹检测 vs 玩家)。
- 文件:`scripts/autoloads/enemy_bullet_manager.gd`+ `.uid`),注册进 `project.godot` autoload。
- SoA `PackedFloat32Array`,命名常量 `ENEMY_BULLET_STRIDE = 7`
`[ x, y, vx, vy, lifetime, radius, damage ]`(索引 0..6)。
- `const MAX_ENEMY_BULLETS: int = 1024``_ready``resize(MAX_ENEMY_BULLETS * ENEMY_BULLET_STRIDE)` + `fill(0.0)`P6-N3:禁止热循环内 `new()`)。
- `PLAYER_HIT_RADIUS`:玩家碰撞半径常量(估算 12px,与玩家标记视觉一致)。
- `_physics_process(delta)`
- 逐弹积分:`x += vx*dt``y += vy*dt``lifetime -= dt`
- `lifetime <= 0``_swap_and_pop`
- 否则对玩家碰撞:`d² ≤ (radius + PLAYER_HIT_RADIUS)²`(读 `PlayerManager.get_position()`)→ `PlayerStats.take_damage(damage)`(走难度减伤)+ `VFXManager.play("hit_spark", pos)` + `_swap_and_pop`(单次命中即销毁)。
- 接口:
- `spawn_bullet(pos: Vector2, vel: Vector2, lifetime: float, radius: float, damage: float) -> int`(满容返回 -1)。
- `sync_multimesh(mm: MultiMesh)`SoA 位置批量写入,`visible_instance_count = active`,与 BulletManager 同法)。
- `get_active_count() -> int``reset()`
- `combat_manager``start_game` / `close_shop_and_next_wave` / `_on_player_died``EnemyBulletManager.reset()`(与 bullet/zone/minion 重置一致)。
---
## 2. 新增自动加载 `BossManager`(阶段 + 招式侧挂控制器)
Boss 实体仍留在 EnemyManager SoA(渲染/受击/HP 不变);`BossManager` 用侧 `Dictionary``boss_id` 存运行态,**并接管 Boss 的移动**。
- 文件:`scripts/autoloads/boss_manager.gd`+ `.uid`),注册进 `project.godot` autoload。
- 运行态(每个活跃 Boss 一条):
```
{
boss_type: int,
max_hp: float,
phase: int, # 当前阶段(0 起)
phase_defs: Array, # 该 boss_type 的阶段配置(来自 bosses.json
pattern_index: int, # 当前阶段招式轮询索引
cooldown: float, # 距下次起手的冷却
action: Dictionary, # 当前进行中的招式状态(见 §3),空 = 无
}
```
- `register_boss(boss_id, boss_type, max_hp)``wave_manager._spawn_boss` 生成 SoA Boss 后调用;初始化运行态、置 `phase=0` 并触发 phase 0 进入。
- `_ready` 载入 `data/bosses.json`(缺失/格式错 `push_error`,纯数据驱动无回退);订阅 `EventID.ENEMY_KILLED`。
- `_physics_process(delta)`(有活跃 Boss 才进入循环体):对每个 Boss —
1. `_check_phase_transition`:读 `EnemyManager.get_hp_percent(id)`,按 `phase_defs``hp_threshold` 降序)检查是否跨入更深阶段;跨入 → 进阶(重置 `pattern_index/cooldown`、可选召唤援军、发 `BOSS_PHASE_CHANGED`)。
2. `_tick_movement`:无进行中位移招式时,Boss 朝玩家缓慢漂移(`move_speed * phase.move_speed_mult`),经 `EnemyManager.set_entity_pos` 写回 SoA。
3. `_tick_action`:推进当前招式(§3);无招式且 `cooldown <= 0` → 选下一招式并启动。
- `_on_enemy_killed``killed_id` 是注册 Boss → 发 `BOSS_KILLED {boss_id, boss_type, wave}` 并注销运行态。
- `reset()`:清空运行态;由 `combat_manager` 在 run 重置点调用。
**EnemyManager 改动**
- 新增 `set_entity_pos(id, pos)`(写 SoA x/y)。
- `_gd_update_movement` **跳过已注册 Boss 的 slot**BossManager 全权驱动其移动)——通过 EnemyManager 侧的一个「外部驱动」id 集合(`register_external_mover(id)` / `unregister_external_mover(id)`,类比精英寻路跳过),零额外开销(无 Boss 时集合空)。
**wave_manager 改动**
- `_spawn_boss` 末尾调用 `BossManager.register_boss(...)` + `EnemyManager.register_external_mover(_boss_id)`。
- **移除** `_update_boss_phase` / `_on_boss_phase_enter`(阶段与召唤逻辑迁入 BossManager)。
- 保留 `_boss_id` / `_boss_wave` 及 `killed_id == _boss_id` 的**波次完成判定**(不变)。
---
## 3. 招式模型:带生命周期的动作
每阶段 `attack_patterns` 是招式规格列表,BossManager 轮询启动。招式分两类,均含通用字段 `kind` 与 `cooldown`(招式结束后进入的冷却秒数)。
### 3.A 瞬时远程弹幕(发一波即结束,Boss 期间继续漂移)
经 `EnemyBulletManager.spawn_bullet` 从 Boss 位置发射:
| kind | 行为 | 主要参数 |
| :--- | :--- | :--- |
| `ring` | 绕 360° 均匀 N 弹(开花/环) | `count, speed, bullet_damage, bullet_radius` |
| `spread` | 朝玩家方向 ±`half_angle` 的 N 弹扇形 | `count, half_angle, speed, bullet_damage, bullet_radius` |
| `aimed` | 瞄准玩家的单发/连发 | `count, speed, bullet_damage, bullet_radius`(连发用 burst 间隔可后续扩展) |
| `spiral` | 每次起手角度递增的旋转弹 | `count, speed, angle_step, bullet_damage, bullet_radius` |
瞬时招式的 `action` 生命周期只占一帧(发射后立即进冷却)。
### 3.B 位移 / 蓄力招式(跨多帧,三段式,接管 Boss 移动)
三段式:`windup 前摇 → active 生效 → recover 后摇`。`action` 状态含 `{ kind, stage, timer, ... }`,逐帧推进。
| kind | 行为 | 主要参数 |
| :--- | :--- | :--- |
| `dash`(冲刺) | 前摇:静立 + 播**预警 VFX**(给玩家闪避窗口);生效:以 `dash_speed` 冲向前摇锁定的玩家位置,冲刺中与玩家重叠 → `PlayerStats.take_damage(contact_damage)`(带内部命中冷却 `contact_hit_cd` 防每帧多段);后摇:静立恢复。 | `windup, dash_speed, dash_time, contact_damage, contact_hit_cd, recover, telegraph_vfx` |
| `charge_up`(蓄力) | 前摇:静立蓄力 + 播预警 VFX;生效:释放一次**重击**(大 `ring` 或多重 `spread`,由 `release_kind` + 弹幕参数决定);后摇:静立恢复。 | `windup, release_kind, count, speed, half_angle, bullet_damage, bullet_radius, recover, telegraph_vfx` |
| `melee`(近战/劈砍) | **原地近身范围打击**(区别于 `dash` 的横穿冲撞)。前摇:静立蓄势 + 播预警 VFX(预示打击范围);生效:判定玩家是否在 `melee_radius` 内 → `PlayerStats.take_damage(melee_damage)`(该次生效仅判定一次);后摇:静立恢复。 | `windup, melee_radius, melee_damage, recover, telegraph_vfx` |
**公平性约束**:所有位移/蓄力招式**必须**有 `windup` 前摇与 `telegraph_vfx` 预警,否则冲刺不可闪避。
### 招式注册表实现
`BossManager` 内置 `pattern_id/kind → 处理逻辑`(行为在代码,调参在 JSON,类比 status/spell)。瞬时招式为「发射函数」;位移/蓄力招式为「阶段推进器」。不引入独立 Resource / 注册 autoload,逻辑内聚在 `BossManager`(招式数量少,MVP 阶段)。
---
## 4. 数据与设计器
### 4.1 `data/bosses.json`
```json
{
"4": {
"move_speed": 120.0,
"phases": [
{
"hp_threshold": 1.0,
"move_speed_mult": 1.0,
"summon_adds": 0,
"attack_patterns": [
{ "kind": "spread", "count": 5, "half_angle": 40.0, "speed": 220.0, "bullet_damage": 8.0, "bullet_radius": 8.0, "cooldown": 2.5 },
{ "kind": "dash", "windup": 0.8, "dash_speed": 600.0, "dash_time": 0.5, "contact_damage": 15.0, "contact_hit_cd": 0.4, "recover": 0.8, "telegraph_vfx": "boss_telegraph", "cooldown": 3.0 }
]
},
{
"hp_threshold": 0.66,
"move_speed_mult": 1.2,
"summon_adds": 6,
"attack_patterns": [ ... ]
},
{
"hp_threshold": 0.33,
"move_speed_mult": 1.5,
"summon_adds": 8,
"attack_patterns": [ ... ]
}
]
},
"5": { "move_speed": 80.0, "phases": [ ... ] }
}
```
- 顶层键为 `boss_type`"4"=Mini、"5"=Final),与 `waves.json` 的 `boss` 字段、`balance.json` 的 `boss_hp` 键一致。
- `phases` 按 `hp_threshold` 降序(1.0 → …)。
- Boss 基础血量仍取 `balance.json` 的 `boss_hp`(不迁移,避免破坏现有平衡数据);`bosses.json` 只负责阶段/招式/移动。
- `SettingsManager` 加 getter(如 `get_boss_phase_config(boss_type) -> Array`);`BossManager._ready` 经其读取。缺失/格式错 `push_error`,无静默回退。
### 4.2 设计器「Boss」分页
按 CLAUDE.md「新增分页 3 步」:
1. `addons/game_designer/boss_tab.gd``@tool extends VBoxContainer`,复用 `designer_ui.gd` 的 `spin/line/opt/btn/header/set_status/load_json/save_json`;在 `_ready()` 构建 UI,非 `_init()`)。
2. `designer_panel.gd` 的 `_init()` 里 `_add_tab("Boss", ...)` 注册。
3. Manager `_load_json_bosses()` 于 `_ready()` 载入。
字段化要求(遵守插件规范):
- 所有枚举/选项**中英双语、中文在前**(`kind``环 ring` / `扇形 spread` / `瞄准 aimed` / `旋转 spiral` / `冲刺 dash` / `蓄力 charge_up` / `近战 melee`)。
- `OptionButton` 存 key(如 `ring`),**不**把双语显示串写进 JSON(显示标签数组与存储值数组分离,index 对齐)。
- 数值用 `SpinBox`,撑满宽度(`SIZE_EXPAND_FILL`,工厂已内置)。
- 可变长的阶段列表 / 招式列表用增删行 UI(➕加 / 🗑删);如结构过深,MVP 可先做「按 boss_type 选择 → 阶段列表 → 每阶段招式行编辑」的分层表单。**禁止手写 JSON 文本框**。
- 数据往返一致、防丢键;可选字段为默认值时可省略。
---
## 5. 事件
`event_ids.gd` 从 29 起新增(同步更新 `docs/technical/implementation_plan.md §2.1` 事件目录):
| ID | 常量 | Payload | 订阅方 |
| :-- | :-- | :-- | :-- |
| 29 | `BOSS_PHASE_CHANGED` | `{ boss_id, boss_type, phase }` | HUD(阶段提示)、AudioManager(可选 BGM|
| 30 | `BOSS_KILLED` | `{ boss_id, boss_type, wave }` | WaveManager/成就(可选)|
`GAME_CLEARED` 延后(范围外)。沿用现有 `BOSS_SPAWNED = 19`。
---
## 6. 渲染
`combat_s2.gd`
- `_setup_world_view` 增第三个 `MultiMeshInstance2D` + `_enemy_bullet_mm``instance_count = EnemyBulletManager.MAX_ENEMY_BULLETS`),modulate 用**红/紫**区分玩家子弹(黄)。
- `_process` 增 `EnemyBulletManager.sync_multimesh(_enemy_bullet_mm)`。
---
## 7. 跨切片约束(P6-N)遵守
- `ENEMY_BULLET_STRIDE` 命名常量,禁裸整数。
- 热循环(integrate/collision)不 `new()`SoA 预分配;`_swap_and_pop` 就地搬运。
- VFX `one_shot`P6-N72)。
- EventID 从常量取,禁硬编码整数;顺序递增(29/30)。
- CoreFeatureTag / StatusID 等仍用枚举,不引入裸串。
- 纯数据驱动:`bosses.json` 缺失/格式错 `push_error`,无硬编码回退副本。
---
## 8. 验证
- godot-mcp-pro `validate_script` 校验新脚本语法。
- `play_scene` 跳到 W8/W20(或直接 `WaveManager` 起 boss 波),`execute_game_script` 断言:
- 敌方子弹生成并使 `PlayerStats.hp` 下降(`ring`/`spread`/`aimed`/`spiral` 各验一次)。
- `dash` 前摇静立→冲刺位移→接触扣血(带命中冷却,不每帧多段);`charge_up` 前摇→释放弹幕;`melee` 前摇→近身范围内扣血、范围外不扣。
- HP 跨 66%/33%(或配置阈值)发 `BOSS_PHASE_CHANGED`;进阶召唤援军数正确。
- Boss 死亡发 `BOSS_KILLED`,波次照常完成。
- 设计器 Boss 分页对真实 `bosses.json` 跑回读往返(`execute_editor_script` 断言 `FAILS=0`);改 dock GDScript 后需 `EditorInterface.restart_editor(true)` 清缓存再测。
---
## 9. 目录 / 文件清单
```
scripts/autoloads/
enemy_bullet_manager.gd (+ .uid) # 新增 autoload
boss_manager.gd (+ .uid) # 新增 autoload
scripts/autoloads/enemy_manager.gd # +set_entity_pos / +register_external_mover / _gd_update_movement 跳过
scripts/autoloads/wave_manager.gd # _spawn_boss 注册 BossManager;移除旧 _update_boss_phase/_on_boss_phase_enter
scripts/autoloads/event_ids.gd # +BOSS_PHASE_CHANGED=29 / +BOSS_KILLED=30
scripts/autoloads/settings_manager.gd# +get_boss_phase_config 等 getter
scripts/domain/combat/combat_manager.gd # reset EnemyBulletManager/BossManager
scenes/main/combat_s2.gd # 敌方子弹 MMI + sync
data/bosses.json # 新增数据
addons/game_designer/boss_tab.gd (+ .uid) # 新增设计器分页
addons/game_designer/designer_panel.gd # _add_tab 注册
project.godot # 注册两个 autoload + 分页无关
docs/technical/implementation_plan.md# §2.1 事件目录 +29/+30
docs/technical/boss_design.md # 更新实现现状注记(改文档追认)
```