# Quick Design Spec: Coordinator Runtime Policy **Type**: Reference / Runtime Policy **System**: Combat Coordinator / Minion Encounter AI **Related Specs**: - `design/quick-specs/minion-formation-showcase-combat-2026-07-03.md` - `design/quick-specs/minion-combat-feel-and-weapon-roles-2026-07-03.md` **Date**: 2026-07-04 ## Change Summary 本文整理 `CoordinatorSubmodule` 改造期间形成的设计约定,用于指导后续杂兵 AI、Behavior Designer 节点、波次配置和命中反馈系统的开发。 核心目标不是让杂兵完全服务玩家,也不是让杂兵无约束地追求最优围攻,而是让 Cielonos 的杂兵战形成一种“有战术倾向,但仍然镜头友好、节奏清晰、打击稳定”的中高速 ARPG 体验。 ## Current Implementation State 当前已经完成: - `FormationProfile`:使用可配置 `Sector` 描述杂兵站位区域。 - `FormationSectorPurpose`:区分 `Action` 与 `FreeArea`。 - `FormationTacticalRoleMask`:允许 Sector 声明可接受的战术职责。 - `CoordinatorSubmodule` profile-driven sector assignment:旧固定 6 扇区逻辑已替换为 profile-driven runtime slots。 - Stable random target:敌人取得 Sector 时,会在 Sector 内抽取一个稳定随机点,避免整齐拼阵。 - `FormationFrame`:`PlayerForward` / `CameraForward` 只作为阵型初始化方向,不再逐帧拖动已分配 Sector。 - Path crossing penalty:候选 Sector 如果导致敌人从玩家附近横穿,会被显著降权。 - `RefreshFormationFrame()`:已提供手动刷新阵型方向的方法,但尚未接入自动 policy。 当前仍未完成: - 敌人 role assignment 仍主要停留在接口和数据层,Behavior Designer 尚未真正按 `FormationTacticalRole` 分流行为。 - `RefreshFormationFrame()` 尚未由波次、房间或 Coordinator policy 自动调用。 - 玩家命中敌人后的 `Movement Suppression` 尚未实现。 - `FreeArea` 敌人还缺少低威胁行为策略,例如展示位、待机位、挑衅、缓慢重定位。 - `AttackToken` 与 `FormationTacticalRole` 还没有完全合并为统一威胁预算。 ## Design Principles ### 1. Formation is Tactical Staging, Not Real-Time Rotation `FormationProfile.referenceMode` 的职责是决定阵型初始化方向,而不是让阵型每帧跟随玩家或摄像机旋转。 正确语义: ```text referenceMode = FormationFrame 初始化方向来源 FormationFrame.rotation = 当前战斗阶段的稳定阵型方向 Player position = 当前阵型中心,可持续跟随玩家 ``` 这样可以保留 `CameraForward` / `PlayerForward` 的战术意义,同时避免玩家转身或摄像机旋转导致敌人频繁横向移动。 ### 2. RefreshFormationFrame is a Combat Director Event `RefreshFormationFrame()` 不应在普通 AI Tick 中调用。它表示“当前战斗阶段需要重新定义舞台方向”,因此应由房间、波次、阶段切换或 Coordinator policy 触发。 推荐调用时机: | Trigger | Rule | |---------|------| | 新一波敌人生成 | 刷新一次,使新波次以当前镜头或玩家朝向组织阵型 | | CombatRoom / Zone 开始 | 默认由第一次 `TryAcquireSector` 创建 `FormationFrame`,通常不需要额外调用 | | 所有 `Action` Sector 空闲且敌人未处于攻击/受击/连段中 | 可作为重整期刷新 | | 玩家发生大位移 | 玩家离开原战场约 8-12 米后,可刷新一次 | | 战斗阶段切换 | 例如从玩家输出窗口切换到敌人反击窗口 | | 特殊演出或脚本事件 | 可显式刷新,但应避免打断当前受击或攻击行为 | 不推荐调用时机: - 玩家只是转身。 - 摄像机只是旋转。 - 敌人正在 `MoveToSector`。 - 玩家正在近战连段命中敌人。 - 敌人正在攻击前摇、攻击中、受击、击飞、倒地或硬直。 - Boss 战中杂兵只是辅助压力时。 推荐约束: ```text minRefreshInterval = 3.0s - 5.0s largePlayerDisplacement = 8m - 12m onlyRefreshWhenNoHighPriorityState = true ``` ### 3. Hit Ownership Overrides Formation 当玩家近战命中普通杂兵时,敌人应短时间进入玩家拥有的表现窗口。这个窗口内,敌人的主动移动和阵型重定位应被抑制。 建议机制名: ```text Movement Suppression Player-Owned Window ``` 允许保留: - 受击动画。 - 击退、浮空、牵引、倒地。 - `ReactionSubcontroller` 被动位移。 - `FuncAnimPayload` 或 `AttackArea` 施加的位移。 - Hit stop、camera shake、damage number、Wwise hit event。 应暂停: - `MoveToSector`。 - 主动重新申请 Sector。 - 主动刷新 Sector 目标点。 - 主动攻击请求。 - 绕背、归位、重新排队。 建议初始数值: | Enemy Type | Movement Suppression | |------------|----------------------| | 普通近战杂兵 | 0.6s - 1.0s | | `ShowcaseVictim` | 0.9s - 1.4s | | 远程/轻型敌人 | 0.4s - 0.7s | | 精英敌人 | 0.2s - 0.5s | | Boss / `C1_Axe` | 不走此普通规则,使用 Boss poise 机制 | ### 4. Threat Budget Must Be Explicit 杂兵战应有战术倾向,但不能让所有敌人同时成为高威胁单位。 Demo 阶段推荐: ```text maxActiveMeleeThreats = 1 maxActiveRangedThreats = 0-1 maxTotalActiveThreats = 2 defaultNonThreatRole = ShowcaseVictim / Reposition / Taunt ``` `AttackToken` 目前已经承担部分威胁预算功能,但还需要与 `FormationTacticalRole` 结合。未来应避免“没有攻击 token 的敌人仍继续强行攻击玩家”。 ### 5. FreeArea is Still Formation `FreeArea` 不是无规则闲逛区域,而是低威胁敌人的舞台调度区域。 推荐用途: - `ShowcaseVictim`:靠近玩家前场,等待被连段或压制。 - `Reposition`:填补镜头友好站位。 - `Taunt`:做低威胁展示动作或短距离摆位。 - `Staging`:等待下一次 role 切换。 `FreeArea` 可以比 `Action` 更活跃,但刷新频率仍应较低。例如每 3-6 秒、玩家大位移后、或敌人脱离镜头太久时重取目标点。 ## Runtime Priority Stack 杂兵 AI 的优先级建议如下: ```text Death / Disabled > Hit Reaction / Player-Owned Window > Current Attack / Telegraph / Recovery > AttackToken / Threat Budget > Formation Sector Assignment > FreeArea Showcase / Reposition > Idle / Taunt ``` 这意味着 Formation 不是最高优先级。它负责组织战场,但不能打断受击表现、攻击前摇、击飞、倒地或玩家连段窗口。 ## Behavior Designer Integration Notes 现有节点: - `TryAcquireSector` - `MoveToSector` - `ReleaseSector` - `TryAcquireAttackToken` - `HasAttackToken` - `ReleaseAttackToken` 建议新增或扩展: | Node | Purpose | |------|---------| | `TryAcquireSectorByRole` | 按 `FormationTacticalRole` / `FormationSectorPurpose` 申请 Sector | | `HasAssignedRole` | 行为树根据 role 分流 | | `IsMovementSuppressed` | 被玩家命中后阻止主动移动 | | `CanRequestAttackToken` | 集中检查 threat budget、受击状态、展示窗口 | | `RequestFormationRefresh` | 仅由波次或特殊行为触发,不放入普通移动循环 | | `MoveToFormationSector` | 未来替代或扩展 `MoveToSector`,支持到达后停留、低频重取点 | 现阶段可继续兼容旧节点,但下一阶段应优先让 L1/L2 行为树显式读取 role。 ## Suggested Next Implementation Order ### Step 1. Movement Suppression 实现玩家近战命中后的主动移动抑制。 最低要求: - 在敌人被玩家近战命中时记录 `movementSuppressedUntil`。 - `MoveToSector` 在抑制期间返回 `Running` 或进入等待,不刷新 destination。 - 攻击 token 请求在抑制期间失败。 - 受击、击退、浮空等被动位移仍然保留。 ### Step 2. Role-Aware Behavior Tree 让杂兵行为树不再只问“有没有 Sector”,而是问“我现在是什么职责”。 优先对象: - `L1_Assault_Pawn` - `L2_Assault_Stinger` ### Step 3. RefreshFormationFrame Policy 接入自动刷新策略,但先保守实现。 最低要求: - 房间/波次开始时允许刷新。 - 玩家大位移后允许刷新。 - 有 cooldown。 - 有敌人处于受击、攻击、硬直时不刷新。 ### Step 4. FreeArea Low-Threat Behaviors 为 `ShowcaseVictim` / `Reposition` / `Taunt` 提供简单行为。 不要求新动画和 VFX,先通过移动、停留、朝向玩家、等待、轻微重定位完成。 ### Step 5. Threat Budget Unification 把 `AttackToken`、`FormationTacticalRole`、enemy rank、当前战斗阶段统一为一套 Coordinator policy。 ## Acceptance Criteria - [ ] 玩家转身或旋转摄像机时,已分配 Sector 的敌人不会频繁横向重定位。 - [ ] 玩家近战命中普通杂兵后,敌人不会立刻为了 Formation 逃出玩家攻击范围。 - [ ] 敌人从一个 Sector 移动到另一个 Sector 时,优先避免从玩家身体附近横穿。 - [ ] `RefreshFormationFrame()` 只在战斗阶段断点、波次变化或大位移重整时调用。 - [ ] `Action` 敌人与 `FreeArea` 敌人在行为意图上可区分。 - [ ] Demo 阶段同一时间的高威胁杂兵数量可控。 - [ ] `C1_Axe` Boss 战不被普通杂兵 Coordinator 规则误削弱。 ## Open Questions - `Movement Suppression` 应放在 `StatusSubmodule`、`ReactionSubcontroller`、还是专门的 AI state 中? - 玩家攻击类型如何区分 melee hit、projectile hit、support equipment hit? - `RefreshFormationFrame()` 应由 `CombatRoomSubmodule`、`RunManager`、还是 Coordinator 自身 policy 触发? - `FreeArea` 敌人是否需要单独的 low-threat token,避免所有自由人同时轻微移动造成视觉噪声? - 高难度 profile 中,`Flanker` 是否可以绕背攻击,还是只允许绕背施压但不攻击?