100 Commits
Author SHA1 Message Date
joywayer 9aaa41e420 meta 2026-08-03 14:53:45 +08:00
joywayerandClaude Opus 5 d61f10ee80 merge: DataHub 敌人能力总览表格
补回删除旧 Boss 轨时丢掉的能力资产编辑器视图(spec §6.4)。
做成跨敌人对比表而非标准列表+详情:能力按敌人分目录存放,
逐个点开看不出问题,需要的是横向比射程/权重/冷却。

问题列复用资产自己的 Validate(),规则只有一处;不提供新建,
类型化子类与目标目录由角色向导保证。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:38:25 +08:00
joywayerandClaude Opus 5 bb577f01a8 feat(editor): DataHub 新增敌人能力总览表格,补回删旧 Boss 轨时丢的编辑器视图
删除 BossSkillModule / BossSkillSequenceWindow 后能力资产没有任何编辑器总览
(spec §6.4 记为待补)。本次补上,但不照搬「列表 + 详情」的标准形态:

能力资产按敌人分散在 Data/Enemies/{敌人}/Abilities/ 下,逐个点开看不出问题;
真正需要的是横向对比——同一敌人的招之间射程/权重/冷却是否成比例、有没有
Attack 类却射程为 0 的死招。所以做成一张跨敌人的对比表,表格本身就是导航:
点行即选中资产,Inspector 出现在表格下方。列均可点击排序。

两点刻意的设计:

- 问题列直接调资产自己的 Validate(),不在表格里另写一份判定规则。否则 SO 侧
  改了规则,表格会继续按旧规则报。
- 不提供新建。能力有类型化子类且必须落到对应敌人目录,这两件事由角色向导保证
  (CLAUDE.md 第 2 条不裸建);模块只给一个跳转向导的按钮。

取数与排序抽成纯函数(EnemyAbilityOverview)与 UI 分离:UIElements 构建不可单测,
而表格真正会出错的是「归属怎么从路径推导」「问题从哪来」「排序对不对」,
这三处都是纯逻辑,抽出来即可覆盖。

spec 里还提到「阶段一览」,本次不做且不打算做:阶段现在归 BossPhaseAbilityGate
(预制体上的组件),不在 SO 上,配方/资产层看不到它——与 ApproachAttackEngagement
无法在配方层校验选招器是同一类边界。

验证:编译 0 错;EditMode 279/279(273 + 新增 6)。
UI 路径另做程序化冒烟(单测覆盖不到):模块被 DataHubWindow 自动发现并排在
「敌人」之后,两个面板构建无异常,表格 18 行 = 项目 18 个能力资产;
过滤实测 18 / 仅 Attack 5 / 仅有问题 0(与 SO 校验"全部合法"一致),
归属降序首行为 E006 符合序数排序。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 13:09:05 +08:00
joywayerandClaude Opus 5 bf7899f91a feat(combat): 接通弹反反制——弹反成功让发起攻击的敌人硬直
EnemyBase.ReceiveParry 早就存在且形态正确(强制 Stagger + 打断全部能力 +
定时恢复,小怪 Boss 通用),但一直零调用者:弹反判定点拿不到攻击者引用。
硬直时长的权威也早就存在——ParryConfigSO.StaggerDuration,注释写着
「被弹反敌人的受击硬直时长」,同样零消费者。本次只是把这两端接上,没有新造语义。

接缝选在 DamageInfo 而非"弹反成功时回调 HitBox"(spec §6.2 留的两个选项):
攻击者引用与既有的 SourceProjectile 完全同构——都是"攻击从哪来"的引用,
供弹反分支反向作用于来源。落到代码上就是弹反分支里并列的两行,不引入第二条回调链路。

- Combat 新增 IParryable(攻击被弹反时的反制承受方)。用接口是因为程序集方向:
  Combat 不能反向依赖 Enemies,而 HurtBox.ReceiveDamage 是唯一同时握有
  弹反结果与 DamageInfo 的地方。与 IDamageable 同款做法。
- DamageInfo 加 Attacker 字段([NonSerialized],Builder 与 From 工厂同步支持)。
- HitBox 在 Activate 时按宿主解析并缓存 IParryable——与 _ownerRigidbody 同源、
  与 _ownerProjectile 同手法。放 Activate 而非 Awake:attacker 可由调用方传入。
- ParrySystem 暴露 StaggerDuration,HurtBox 在弹反成功时读它,不另立常量。
- EnemyBase 声明实现 IParryable(方法签名本就匹配,无需改实现)。

顺带确认旧的全局误伤路径确实已随 Boss 单轨合并删净:全库不再有
HandleParrySuccess,ParryInfoEventChannelSO 只剩 ParrySystem 自己 Raise,
不存在"弹反小怪导致场上 Boss 一起硬直"的双重路径。

验证:编译 0 错;EditMode 276/276(273 + 新增 3)。
测试走完整 HurtBox 流水线:反射仅用于喂配置与跑真实 Awake,被测逻辑全程生产代码
(与 BossPhaseAbilityGateTests 同一手法)。硬直时长断言取 1.25f 这个非默认值,
才能证明读的是配置而非常量。用例把完美弹反阈值设为负数以避开会改 Time.timeScale
的子弹时间协程(编辑模式下它 yield 后不会恢复),TearDown 另有兜底还原。
变异验证——注释掉 HurtBox 里那一行后,恰好只有反制用例变红;还原后复验 276/276。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 11:36:52 +08:00
joywayerandClaude Opus 5 1e3c060c8d fix(core): 对象池真正通知 IPoolable,复用的敌人不再以死亡状态出场
IPoolable 的接口文档写着「由 PooledObject 子类或同 GameObject 上的其他
MonoBehaviour 实现,并在 PooledObject.OnSpawn/OnDespawn 中手动驱动」,
但那两个方法是空的 public virtual,全库无任何 PooledObject 子类,
GlobalObjectPool 也从不查找 IPoolable 组件——这份契约从未生效过。

全库 .OnSpawn(/.OnDespawn( 的调用点只有 4 处,真正驱动 EnemyBase
(唯一的 IPoolable 实现者)的只有 EnemyRespawner 一处手动补调。后果:

- EnemyBase.OnDespawn 在生产中从未被调用,其中的 _nav?.Stop() 是死代码;
- 更严重的是取出侧:PerformDeath 会 ForceState(Dead) 并禁用全部碰撞体后归池,
  而 EnemySpawnerOnEvent / ChaoFengBoss 召唤 / RangedEnemy 这些取用方
  没有补调 OnSpawn,拿到的复用敌人仍停在 Dead 态、碰撞体全关、HP 为 0——
  一个不可交互也不会动的幽灵。EnemyRespawner 路径侥幸没暴露,只因为它手动补了。

修法是让池履行它自己声明的契约,而不是让各调用方继续手动补调:
PooledObject.OnSpawn/OnDespawn 转发给本物体上的 IPoolable。只扫本物体、
不向下扫子物体——接口文档限定的就是「同 GameObject」,向下扫会让嵌套的
可池化对象被两个 PooledObject 各通知一次。组件集惰性解析一次并缓存
(池化本就是为省开销,不能每次进出都遍历组件)。

同时统一两条归池路径的顺序:GlobalObjectPool.Despawn 原本先 SetActive(false)
再 OnDespawn,而 PooledObject.ForceReturnToPool 是反过来的。转发接通后这个
顺序就成了可观察行为,两条路径必须给出同一份契约——统一为「先通知再停用」,
清理才跑在还活跃的对象上。

EnemyRespawner 那次手动 OnSpawn 随之改为只在兜底实例化路径调用:池化路径
现在由转发完成,重复调用会让 OnSpawn 末尾的 Spawned?.Invoke() 触发两次,
即重复执行出生能力。(今天重复触发会被 Execute() 的 !_isRunning 门挡住,
但那是运气不是设计。)

验证:编译 0 错;EditMode 273/273(269 + 新增 4)。
分阶段观察过失败面:只加转发时,2 条转发测试转绿、顺序测试仍红在
WasActiveOnDespawn 上;补顺序后全绿。
变异验证——把扫描范围改成 GetComponentsInChildren 后,边界测试恰好变红
(该测试在修复前是平凡通过的,需确认它真有约束力),还原后复验 273/273。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:03:57 +08:00
joywayer 5178b73637 Merge branch 'master' of ssh://git.joywaygames.cn:2222/basegames/zeling_v2 2026-07-31 15:01:07 +08:00
joywayerandClaude Opus 5 43ffc8736b merge: Boss AI 双轨统一 — 决策层与执行层一并并入小怪轨
核心洞察:Boss 侧那层"富编排"从未真正存在——全项目 0 个 AttackPatternSO/
SkillSequenceSO 资产、5 个 BossSkillSO 全空、BossSkillExecutor 跑一遍纯空转、
UseBossSkillWeighted 零调用者、Boss 预制体上没有 EnemyAiBrain。唯一的 Boss(嘲风)
实际用的就是动画事件驱动,即小怪轨的做法。所以"按复杂度分层"的前提不成立,
双轨只是让机制原语出现平行实现并已开始漂移。

- 决策归 AI 图:Boss 走 [AiDefinition] AiScript 手写图(不建 Boss 骨架/配方——
  Boss 间差异远大于小怪,骨架会退化成开关面板)
- 阶段 = 换招池:BossPhaseAbilityGate 按阶段启停能力组件,不在 SO 上写阶段字段
- 招走 EnemyAbilitySO + EnemyAttackSO;判定/生成/音效/无敌帧全挂动画事件
- Boss 专属只剩 IBossControl facet(非 Boss 访问即抛)+ 几个旁挂 MonoBehaviour
- 删旧轨 9 个脚本与 5 个空能力资产;两条脚手架创建链路改挂新轨
- 选招器补防重复折扣(WeightedRandomAntiRepeat),取代旧轨同名逻辑

顺带修掉三个波及全体敌人的缺陷:
- EnemyHurtState/EnemyStaggerState 在无对应动画 Clip 时守卫式早退却丢掉恢复安排,
  敌人被打一次即永久失能(美术未接入时影响大部分敌人)
- 池化复活带着上一条命的能力冷却:OnSpawn 那行 InterruptAll 的两道 IsRunning 门
  让它恒为空转,注释所称的"重置冷却"从未发生
- BrainBuilder.Build() 只做正向校验,已声明却无出边的非终态会让敌人永久卡住且
  零报错;现加"非终态必须有出口"校验,部分图另走 BuildPartial()

净变化:139 文件,+3808/-4211;删 10 个脚本、加 14 个。
验证:编译 0 错;EditMode 269/269。
未做:进 Play 实跑嘲风战的人工验收(嘲风动画 Clip 仍缺,卡美术资源)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 14:56:07 +08:00
joywayerandClaude Opus 5 0f676b0e88 feat(ai): Build() 校验「非终态必须有出口」,堵住零报错的永久卡死
BrainBuilder.Build() 此前只做正向校验——边指向的态是否已声明(RequireState
的同款关切);反向从不检查。一个已声明却没有任何出边的非终态,会让敌人进去
就永久停在那里,且全程零报错。计划里点名的风险点是 BossFragments.PhaseTransition:
它按设计不自带出边,出口全靠调用方补挂,漏一条就把 Boss 锁死。

做法:AiState 加 IsTerminal,StateBuilder 加 .Terminal() 显式标记,
AiStateFragments.Terminal() 自带该标记;Build() 对「非终态且无出口」抛异常。

「有出口」的判定比计划原话严一档,两点都是照 AiRuntime 的实际行为定的:
- 自转换不算——Switch 对 Target == 当前态直接 return false,根本出不去;
- 全局转换不算——全局事件边要外部推信号才触发,不是自主出口。把它算作出口,
  等于让「只有死了才出得去」的死角通过校验,而那恰是本校验要暴露的东西。

校验放在 Build() 而非各生产入口:生产的完整图装配点是 AiScript.GetOrBuildGraph
与 AiRecipeSO 两处,放 Build() 能让将来新增的入口默认受保护。代价是模块单测
需要一条别的路——单个 IUnawareModule / IEngagementModule 按设计只声明自己的态,
升级边由 PerceptionSkeleton 事后挂,单独构建时的死角是正常中间态而非缺陷。
为此加 BuildPartial():跳过本项校验,其余照旧。

测试侧按语义分两类处理,没有一刀切:
- 概念上确实是终态的(Dead)标 .Terminal(),保留 Build() 的全量校验;
- 隔离验证单个模块/片段的部分图改用 BuildPartial();
- AiScriptTests / AiDefinitionRegistryTests 的夹具经 GetOrBuildGraph 走生产
  Build(),给桩汇态补了回边——这两条本就该验完整图。
- 断言 Build() 因别的原因抛异常的用例原样保留。
PerceptionSkeleton 与嘲风图建的都是完整图,无需改动即通过——这本身就是覆盖。

验证:编译 0 错;EditMode 269/269(264 + 新增 5)。
加校验后曾有 49 条既有测试变红,全部为上述两类夹具,逐条按语义修正后转绿。

变异验证:注释掉 ChaoFengAi 里 PhaseTx 的 txDone 出边后,ChaoFengAiTests
8 条全部变红,报错精确点名 'PhaseTransition';还原后复验 269/269。
本改动之前,同样的删除是零报错的——Boss 会静默锁死在过渡态,
这正是本校验存在的理由。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 13:21:07 +08:00
joywayer 30a880c922 场景 2026-07-31 11:23:49 +08:00
joywayerandClaude Opus 5 6414fa2dfe fix(enemy): 池化复活的敌人不再带着上一条命的能力冷却
EnemyBase.OnSpawn 里 InterruptAll 那行的注释写着「重置能力冷却」,
三处都不成立:

1. EnemyAbilityRegistry.InterruptAll 有 `if (ab.IsRunning)` 门;
2. EnemyAbilityBase.Interrupt 里还有一道 `if (!_isRunning) return`;
   出生时没有能力在跑,循环体一次都不执行;
3. 即便执行到,末行是 `_cooldownEndTime = Time.time + cooldown * 0.5f`——
   那是「中断后计半程冷却」的写入语义,本就不是清零。

冷却以绝对 Time.time 记时,于是上一条命的剩余冷却原样活到下一条命。
活路径是 EnemyRespawner.SpawnEnemy():敌人死亡归池、延迟复活后取出复用,
按 1.5–10 秒的冷却量级,新生的敌人有数秒出不了招。

修法是补一条真正的清除路径而非改动中断语义:
EnemyAbilityBase.ResetCooldown() 把 _cooldownEndTime 清回出生态的 -1,
EnemyAbilityRegistry.ResetAllCooldowns() 不看 IsRunning 逐个调用,
OnSpawn 显式调用它。原 InterruptAll 保留为兜底中断,但注释改为陈述它
实际做的事——那句失真的注释正是这个缺陷藏了这么久的原因。

测试的冷却全程由生产代码写入:Execute() 启真实协程(编辑模式下跑到首个
yield 即挂起,_isRunning 留 true),再由 Interrupt() 经真实路径写冷却,
不反射直写字段,否则测到的是伪造状态而非真实时序的产物。

验证:编译 0 错 0 警;EditMode 264/264。
修复前该用例是唯一变红的一条(263/264),失败值 5.0f 恰为
cooldown(10) × 0.5,精确指向上述第 3 点。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 11:13:49 +08:00
joywayer 9120964be0 fix(enemy): 无受击/僵直动画时不再永久卡死
EnemyHurtState 与 EnemyStaggerState 的 Enter 在对应 Clip 为空时守卫式早退,
**同时丢掉了恢复安排**:_currentState 永远停在该态,IsControllable 恒假,
AiRuntime 的让位门永久挡住条件边求值——敌人被打一次即永久失能。
美术未接入时这影响的是大部分敌人。

TakeDamage 是分发点:霸体被破进 Stagger、否则进 Hurt,两条都可达,
所以这是两个活 bug 而非一个。EnemyKnockUpState 本就有 else 分支走
ScheduleStateRecovery,是正确范式;本次把该分支补给另外两个,并加一条
KnockUp 回归测试守住它。

时长权威没有新造:
- Hurt 接上 DamageSourceSO.HitStunDuration——该字段一路铺到 DamageInfo
  却零消费者,本就是「这一击让对方僵多久」的既有权威;经 PendingHitStun
  传给状态,形状与既有的 PendingLaunchDir 一致。
- Stagger 用 HitTierConfig.staggerDuration——该结构 tooltip 本就写着
  「Stagger / KnockUp 参数」却只有 KnockUp 的字段,属补齐而非发明。

顺带修 ScheduleStateRecovery 不取消前一次安排的问题:两个重叠安排里较短的
会先把状态改回 Controlled,静默缩短调用方想要的时长(ReceiveParry 接线后
就会撞上)。并在 OnSpawn 清掉跨池化周期残留的协程句柄。

注:Knockback 目前零调用者,其中的 PendingHitStun 赋值今天是死代码,
但它是 public virtual 且会进 Hurt 态,不设则将来接线时会用到陈旧时长。

验证:编译 0 错;EditMode 263/263(260 + 新增 3)。
变异验证——移除两个 else 分支后恰好 Hurt/Stagger 两条测试变红、
KnockUp 回归测试保持绿,恢复后复验 263/263。
2026-07-31 09:40:47 +08:00
joywayer 691b19fd11 setting 2026-07-31 09:20:32 +08:00
joywayerandClaude Opus 5 6d3816d7ef docs(enemy): Boss AI 单轨落地——新增作者指南,清理描述已删除系统的长期文档
新增 Docs/Guides/09_BossAi_Authoring_Guide.md:
定制路径写法([AiDefinition] + AiScript)、建态原语(AiStateFragments +
BossFragments + IBossControl)、阶段=换招池、一招怎么配(EnemyAbilitySO +
EnemyAttackSO + 动画事件)、开战触发、死亡归物理层,
以已落地的 ChaoFengAi 为逐段范例,附 Boss 专属陷阱与已知缺口。
08 号指南加交叉引用。

删除两份整份描述已删除且从未实现的体系的文档:
Docs/Architecture/23_BossSkillModule.md、Docs/Design/47_BossSkillSystem.md。
删前已确认全库无 Markdown 链接残留,索引行与关联文档行改指新指南或 spec。

修订:25_CharacterArchitectureOverview §4 Boss 章节按实际代码重写;
07_EnemyModule 加过时提示、§11 改重定向;02_EventSystem 修正 Boss 事件发送方;
19_BossPatternLibrary 加废止提示(设计意图仍有效,类型名已废);
AssetFolderSpec 删旧 Boss 技能编辑器条目;三处 README/索引同步。
Docs_Dev/ 历史记录保留不动。

spec 标记为已实施,并记录实施期偏差(无 Boss 骨架、Telegraph 枚举保留、
阶段直查不走黑板、BossSkillEvent 一并删除)与八项已知未完成项,
其中 EnemyHurtState 无受击动画永久卡死为最高优先级、且与 Boss 轨无关。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 17:14:32 +08:00
joywayerandClaude Opus 5 cf0ac60fad refactor(enemy): 清全库行为树残留,删无人使用的 Boss 技能事件频道
Task 21 Step 2 全库扫描后的收尾:

注释与 Tooltip 里的旧决策层措辞逐处改为只描述功能——
EnemyControlledState / RangedEnemy / FlyingEnemy / SensorSlotNames /
EnemyPoiseComponent / EnemyAnimationConfigSO / EnemyAiBrain。

EnemyQuotaManager 的序列化字段 _maxActiveBehaviorTrees 改名 _maxActiveBrains
(它裁剪的实际是 EnemyAiBrain),带 FormerlySerializedAs。复核确认目前
无任何场景 / 预制体挂载该组件,故无值可丢;属未雨绸缪。

BossSkillEventChannelSO + BossEvents.BossSkillEvent + EVT_BossSkill.asset 删除:
唯一发送方 BossSkillExecutor 已随旧轨删除,复核确认全项目零发送方、零订阅方,
资产 guid 在场景 / 预制体 / Addressables 分组中均无引用。

自检:编译 0 error;EditMode 260/260 通过;
SO 校验 0 error 0 warning;层矩阵 25/25 正确。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 17:14:09 +08:00
joywayer 2c569dcf0d docs(enemy): Task 21 补 Step 2.5——处理描述已删除系统的长期文档
Task 19 发现约 20 份文档仍在描述旧 Boss 轨,含两份整份失效的架构/设计文档。
按第 7 条,Docs/ 是长期文档,留着描述不存在的类型比没有更糟。
分类处理:两份整份删除、AssetFolderSpec 局部修订、Guides 随新章节改写、
Docs_Dev 的历史记录保留不动(那是过程记录,改了反而失真)。
2026-07-30 16:47:51 +08:00
joywayerandClaude Opus 5 ad41da0410 refactor(enemy): 删预警旧路径,预警统一走动画
telegraphVfxKey / telegraphDuration / TelegraphRoutine 删除。
预警的正确载体是 clip 本身——姿态在动画里,音效与特效在动画事件上
(PlaySFX / TriggerFeedback 已于阶段一补接)。旧路径会在动画之前
插一段无动画的静止等待,是同一件事的第二条平行路。

AbilityRunState.Telegraph 枚举值保留:BlinkStrikeAbility 现身闪光段
仍在自己协程里显式置位(计划原文假设它零消费者,实测不成立),
只删了基类的默认预警协程。顺带清掉该文件里遗留的行为树插件措辞。

18 个 ABL_ 资产经 AssetDatabase.ForceReserializeAssets 重写,
去掉已删字段的死键;顺带补齐了这些旧资产缺失的当前字段默认值
(designNote / category / weight / rangeRadius / rangeOffset / maxDashDuration),
无任何已有取值被改动。SOValidationRunner:0 错误 0 警告。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:45:51 +08:00
joywayerandClaude Opus 5 d3013b1053 refactor(enemy)!: 删除旧 Boss 轨 9 个脚本
BossSkillSO / AttackPatternSO / SkillSequenceSO / BossSkillExecutor /
BossSkillTypes / WeakPointSystem / TelegraphSystem / BossSkillModule /
BossSkillSequenceWindow,连同 Patterns 独立程序集(BaseGames.Enemies.Boss.Patterns.asmdef,
已核实无任何 asmdef 按名或按 GUID 引用它)整包移除。

删前先清两处引用:
- ChaoFengKnockdownCounter 击落打断改走 _boss.Abilities.InterruptAll(ExternalRequest)
- EditorScaffoldUtils 命名前缀表删掉两条指向已删类型的死条目(还与 AssetFolderSpec 的 ABL_ 冲突)

判据「有更优替代才删」逐项对应:
- 编排 → EnemyAbilitySO + EnemyAttackSO(归一化时机 / 槽位 HitBox)
- 阶段门 → BossPhaseAbilityGate
- 选招 → EnemyAttackSelector + WeightedRandomAntiRepeat
- 可弹反 → DamageSourceSO.Flags(CanBeParried),HurtBox 已在判定
- 弱点倍率 → HurtBox 自带倍率(原实现的 GetDamageMultiplier 零调用者)
- 预警 → clip 姿态 + 动画事件(TriggerFeedback / PlaySFX 已补接)
- 竞技场联动 → EnemySpawnerOnEvent
- 霸体窗口 → EnemyAttackSO.hasPoiseWindow 四字段

能力资产的编辑器总览视图随 BossSkillModule 一并消失,
后续补 EnemyAbilityModule(优先级低于 AiDefinitionValidator)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:39:57 +08:00
joywayer ef1998db2b docs(enemy): Task 19 补 Step 0——两处计划遗漏的引用,不清则删除任务必然编译失败
ChaoFengKnockdownCounter.cs:82 有 BossSkillExecutor 的编译期类型引用,
该文件既不在删除清单也未安排改动,却仍被 ChaoFengBoss 使用。
EditorScaffoldUtils 的命名前缀表里有 BossSkillSO/SkillSequenceSO 两条死条目。

后续任务再添一项:池化敌人复活带着上一条命的冷却——OnSpawn 里
InterruptAll 的注释声称重置冷却,实际 Interrupt 在未运行时提前返回、
运行时又是设置而非清零冷却。影响全体池化敌人。
2026-07-30 16:28:21 +08:00
joywayerandClaude Opus 5 822dc90c21 refactor(enemy): 清行为树债——StopBehaviorTree 改名,删 ConsumeParryEvent
StopBehaviorTree → NotifyDecisionStop(方法体早已只是发 Died 信号)。
EnemyDeathSequence 的 _stopBehaviorTree 字段同步改名,带 FormerlySerializedAs 保值。
ConsumeParryEvent 及其 TTL 状态删除:零调用者,且 ForceState(Stagger) +
IsControllable 让位门已是更优表达,不需要第二条受击通道。

顺带清掉这三个文件里残留的行为树插件措辞(BD_* / BD Task / 停行为树)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:23:47 +08:00
joywayerandClaude Opus 5 a44aafee3a refactor(enemy): BossBase 瘦身——删技能执行与弹反反制两套
技能选取/执行归 EnemyAttackSelector + EnemyAbilityBase(阶段一已就位),
BossBase 不再持有 BossSkillExecutor。阶段切换改用能力注册表打断在跑的招。

弹反反制整套删除:它订阅的是全局弹反成功频道,无来源过滤——
玩家弹反任意小怪都会让场上 Boss 硬直。正确形态是 EnemyBase.ReceiveParry
(谁被弹反谁硬直),其接线为独立后续任务。

调试面板的 BossSkillExec 行换为阶段与过渡状态。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:19:21 +08:00
joywayerandClaude Opus 5 635ab69bb6 refactor(enemy): BossResource 去掉自动放招,触发权交回 AI 图
满值时由 BossResource 直接调 UseBossSkill 是绕过决策层执行,
违反「AI 只决策、能力负责实现」。现在只维护数值并暴露 IsFull,
AI 图挂 When(x => x.Boss.ResourceFull) 边进专属招态。
顺带解除对即将删除的 BossSkillSO 的依赖。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:13:51 +08:00
joywayer 02c3a764a5 docs(enemy): 记录 Task 15 实测结论——含一个项目级严重 bug
后续任务新增最高优先级项:EnemyHurtState.Enter 在无受击动画时提前返回
且不安排回 Controlled,敌人被打一次即永久卡在 Hurt 态、AI 永久停摆。
美术未接入的当下等于大部分敌人一挨打就废。与 Boss 轨无关,需单独立项。

Task 16 加警示:不得删掉 Task 15 新增的 BossBase.Start()——它是场景直接
放置的 Boss 唯一一次应用阶段招池的时机(EnterPhase 只在换阶段触发、
OnSpawn 只在池化复用触发,开局都不跑)。

顺带更正计划头部的 Unity 版本(实际 2022.3.62f3c1,非 Unity 6)。
2026-07-30 16:05:26 +08:00
joywayerandClaude Opus 5 877d2f5fef feat(enemy): 嘲风迁移到新轨——能力资产 + 对象树 + 开战触发
删 5 个空 BossSkillSO,用向导重建 6 个 EnemyAbilitySO(含入场演出)。
预制体经放置工具重新产出:EnemyAiBrain(_definitionId=ChaoFeng) +
BossPhaseAbilityGate + 6 个能力组件,不再有 BossSkillExecutor。
TestRoomA 加开战触发区。三件自检 0 error,EditMode 260/260。

首次真正跑通两条脚手架,暴露并修掉三个它们的缺陷:

1. 放置工具漏挂 EnemyLocomotion(PlaceChaoFeng 及其余 7 个敌人放置入口)。
   EnemyBase.Awake 找不到它即报错且 Locomotion 为 null,
   AiStateFragments.ApplyLocomotion 直接 NRE、AiRuntime 构造失败——
   Boss 建不出决策层,完全不动。已在所有敌人放置入口补齐。
2. 向导把空中阶段的 wind_stone 建成 requiresGrounded=true(SO 默认值)。
   Boss 浮空后 IsGrounded 恒 false,选招器把它滤掉,空中阶段站着不打。
   能力定义表加 grounded 列,阶段 1 的招显式置 false。
3. BossPhaseAbilityGate.ApplyPhase 只在 EnterPhase 与 OnSpawn 调用,
   直接摆场景里的 Boss 两条路径都不走,开局后续阶段的招仍在候选池里。
   BossBase.Start 补一次初始阶段应用。

另:Phase1_Tornado_HitBox 确认零消费者(龙卷判定随弹体走),
从放置工具删除其创建与伤害源绑定;
EnemyBrainContext.UseAbility 对解析不到的能力 id 改为 LogError,
不再静默返回 false(CLAUDE.md 第 6 条);
新增 PlaceBossFightTrigger 脚手架(此前无对应创建入口,属裸建)。

播放模式实测状态序列(TestRoomA,玩家走进触发区):
Wait →(Engaged) Intro → Ground⇄GroundAttack →(hp<50%) PhaseTransition
→ Air⇄AirAttack,浮空生效,阶段门按阶段换池,全程 0 报错。
动画 Clip 未接入,PlayClipAbility 空 clip 立即完成,故只验证了状态流转,
判定时序待美术接入后再校。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:02:14 +08:00
joywayer bdeefca75b docs(enemy): Task 15 补 Phase1_Tornado_HitBox 去留判定
Task 14 发现该 HitBox 在 _hitBoxes 数组随 BossSkillExecutor 移除后已无任何引用。
龙卷由动画事件生成弹体、判定自带,Boss 身上这个 HitBox 很可能多余。
要么删掉其创建与伤害源绑定,要么绑进对应能力的槽位——不得以无引用状态留在对象树上。
2026-07-30 15:19:57 +08:00
joywayerandClaude Opus 5 ba2e22cd58 refactor(editor): PlaceChaoFeng 改挂新轨——决策层 + 阶段门 + 能力组件
BossSkillExecutor 换为 EnemyAiBrain(_definitionId=ChaoFeng) +
BossPhaseAbilityGate + 6 个能力组件(一招一子物体,绕开
DisallowMultipleComponent)。HitBox 改绑命名槽位,阶段招池经反射写入。
顺带删掉 Place Boss Enemy 报告里的 BossSkillExecutor 旧轨提示,
以及已无引用的 using BaseGames.Boss。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:17:52 +08:00
joywayerandClaude Opus 5 a64449d213 refactor(editor): 角色向导 Boss 页改产出 EnemyAbilitySO
该向导是 5 个空 BossSkillSO 资产的创建链路——不改则会持续产出旧轨资产。
招式表扩为带 soType / rangeRadius / cooldown / weight 的形式(与
GetEnemyAbilityDefs 对齐),阶段可用性移交 BossPhaseAbilityGate。
产出时强制 rangeRadius > 0,不把责任推给下游作者。
状态面板改查 EnemyAbilitySO,并新增 AI 图注册状态一行。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:08:10 +08:00
joywayer e488255315 docs(enemy): Task 15 补两项——UseAbility 静默失败与频道资产实名
Task 11 发现 EnemyBrainContext.UseAbility 对找不到的能力 id 静默返回 false:
入场演出资产漏配会被静默跳过且零报错,是第 6 条禁止的静默通过。
建图期已挡掉空 id,故运行期解析不到必是配置错误,改为 LogError(非抛异常,
因为这是每帧路径)。放 Task 15 因为那是第一次能观察到配齐/漏配两种表现的时点。

Task 12 实测:EVT_BossFightToggled 资产不存在,实际是 EVT_BossFightEnded
被当开关用(BGMController 与 BossHPBar 都绑它)。资产名与用途不符是既存问题,
本次只记录不改名。
2026-07-30 14:58:08 +08:00
joywayer 990f90d383 feat(enemy): BossFightTrigger——补上缺失的开战触发
开战事件频道此前全项目零发送方,Boss 永远不开战。
触发时广播开战事件并向 Boss 决策层发 AiSignal.Engaged。
漏配目标 Brain / bossId / 两个频道 / 玩家层掩码 / isTrigger 一律逐条点名报错并停用组件,
不做部分执行——半开的战斗比彻底不开更难查。
2026-07-30 14:55:05 +08:00
joywayer 0d81d29527 feat(enemy): 嘲风 AI 图(BrainGraph 定制路径)
Wait →(Engaged) Intro → Ground⇄GroundAttack →(hp<50%) PhaseTx → Air⇄AirAttack。
Boss 不脱战(rest 指回自身)、阶段单向推进、起手就打完。
死亡不在图上:全局 Died 边进 Death 终态,演出归物理层。
8 条 EditMode 断言覆盖状态序列、过渡只发起一次、过渡结束必须转出、
脱离感知区不脱战、血量回升不回退阶段。
2026-07-30 14:51:08 +08:00
joywayer d172f903b6 docs(enemy): 记录 BrainBuilder 缺少「态是否有出边」校验
Task 9+10 执行时发现:Build() 只做 RequireState 的正向校验(边指向的态
是否已声明),不校验反向——已声明却无出边的非终态会让敌人永久卡住且零报错。
BossFragments.PhaseTransition 按设计不自带出边,正是这类风险点。
列入后续任务,并记录 MoveToAnchor 只在 OnEnter 采样锚点这一隐含假设。
2026-07-30 14:34:32 +08:00
joywayerandClaude Opus 5 eaba54ea76 feat(ai): Boss 图共享片段 + 竞技场锚点 + AiSignal.Engaged
BossFragments:阶段过渡 / 逼近选招 / 移动到锚点三个静态建态片段。
ApproachAttackEngagement 的建态逻辑提为 internal static Declare(态名参数化),
模块路径与 Boss 图路径共用同一份行为,Boss 可建多组(地面组/空中组)。

锚点坐标经 IBossControl.AnchorAt / DistanceToAnchor 暴露,不走黑板——
EnemyAiBrain._context 是私有的(BossBase 无从写入),且 ResetScratch 会清黑板
带来隐式时序约束。锚点本就是 Boss 专属知识,归 Boss facet 更直。
锚点漏配 / 下标越界显式抛,不回退到自身位置。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:32:32 +08:00
joywayerandClaude Opus 5 8c56d207bd feat(enemy): EnemyAbilitySO 补设计备注,命名前缀对齐规范
+ designNote(目标体量下能力数量大,设计意图需就近记录)。
fileName 由 EAB_ 改为 ABL_——AssetFolderSpec 第 298 行、两个向导
以及 PlayClipAbilitySO 等子类都用 ABL_,此前只有基类是 EAB_。
顺带清掉 abilityId 注释里的旧插件残留字样。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:23:41 +08:00
joywayerandClaude Opus 5 c05583a365 feat(animation): 敌人侧动画事件补接无敌帧与反馈四件
EnableIFrame/DisableIFrame → HurtBox.SetInvincible(与玩家侧同形态,
无敌时长由动画时间轴上两事件间距表达);TriggerFeedback/PlaySFX →
IFeedbackPlayer,用于音效预警。此前敌人侧只接了 7/20 个事件类型。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:20:47 +08:00
joywayer 5cc93483d3 feat(combat): HurtBox 自带受击伤害倍率
倍率归 HurtBox 自己(弱点部位就是一个倍率 > 1 的 HurtBox),
取代 WeakPointSystem 那份写进私有字段却零调用者的倍率——
其注释称由 BossStats 调用,而项目里根本没有 BossStats 类型。

倍率 1 时数值原样返回而非"乘完再钳最低 1":本改动先于任何消费者落地,
若默认路径引入钳制会静默改变游戏里每一次伤害计算。
测试用 ApplyDamageMultiplier(0)==0 守住这条——10 在两种实现下都得 10,
只有 0 能把二者区分开。变异验证:去掉该捷径后恰好该断言变红。
2026-07-30 14:15:12 +08:00
joywayer 5046125acb docs(enemy): 执行须知第 5 条——RequireComponent 指向抽象类型时 AddComponent 返回 null
HurtBox 标了 [RequireComponent(typeof(Collider2D))],Collider2D 是抽象类,
Unity 无法自动补齐,AddComponent<HurtBox>() 返回真 null。建测试对象须先挂
具体碰撞体。Task 6 实测证实;HurtBoxEditor.cs:97 早有同样注释,说明这个坑
项目里踩过但没写进规范。
2026-07-30 14:04:42 +08:00
joywayer c577d6baa6 docs(enemy): Task 16 的行为树残留清理已在 Task 5 顺带完成 2026-07-30 13:52:47 +08:00
joywayer 50f505279d feat(enemy): BossPhaseAbilityGate——阶段即换招池
按阶段启停能力组件,被禁用的招经 CanUse 自动退出选招候选,
阶段门不需要碰选招器。取代 BossSkillSO.availablePhaseIndices。
EnterPhase 先换池后广播;OnSpawn 回阶段 0(对象池复用)。
2026-07-30 13:50:34 +08:00
joywayer 24c612930e docs(enemy): 记录 EditMode 不触发 Awake 的坑,并分派两处越界发现
执行须知第 4 条:EditMode 下 AddComponent 不调用 Awake(Task 4 实测证实)。
后果是 EnemyAbilityBase._enemy 留空使 CanUse 恒假——依赖它的断言在修复前
也会通过,成为永远不会失败的测试。做法是给测试子类加 RunAwake() 显式触发,
不要反射直写字段。

Task 5 追加:IAttackCandidate 的 CanUse 注释补上 enabled 这一维。
Task 21 追加:EnemyQuotaManager._maxActiveBehaviorTrees 改名须带
FormerlySerializedAs,否则丢场景配置值。
2026-07-30 13:30:44 +08:00
joywayer 64d2087adc fix(enemy): 禁用的能力组件不再报告可用
CanUse 纳入 enabled。此前禁用组件仍返回 true,选招器会选中一个
StartCoroutine 必然失败的招。这也是阶段门(按阶段启停能力)的前提。
2026-07-30 13:28:23 +08:00
joywayer d8d99dd1ce docs(enemy): 计划补三条实测坑与两处修正
执行须知新增:EnemyBase.Abilities 属性遮蔽同名命名空间(成员访问位置须写全名,
Task 16/17 会再遇到);unity_get_compilation_errors 在编译成功时不报警告,
'0 warnings' 是上报口径而非真值;MCP 测试运行器忽略过滤参数。

Task 5:选招器防重复的重置移出 BossBase.OnSpawn(Task 3 审查确认应归
EnemyBase.OnSpawn 以覆盖全部池化敌人)。
Task 21:残留扫描的预期由「无命中」改为「BD_/行为树 仍有约 20 处待清」。
2026-07-30 13:02:52 +08:00
joywayerandClaude Opus 5 0a92d21ce1 fix(enemy): 应用 Task 3 代码审查意见
补上池化路径的防重复记忆重置——ResetRepeatMemory 此前文档承诺了
「对象池复用时清除」却无任何调用点,而它要取代的 BossBase.LastUsedSkillId
恰好在 OnSpawn 里有重置。放 EnemyBase.OnSpawn 以覆盖全部池化敌人。

折扣系数改为每次选招前刷新,与 attackSelectionMode 的读取时机对齐——
此前策划在播放模式下调系数无效、调模式却立即生效,容易误判为功能没生效。
默认值收敛为 EnemyAttackSelector.DefaultAntiRepeatFactor 单一常量。

测试:IneligiblePrevious 用例改用系数 0.5——原先用 0 时,把折扣错写在
eligibility 归零之前也照样通过,是个永远不会失败的测试;统计用例边界由
7σ 收紧到 5σ 使其真正验证该系数;补 ResetRepeatMemory 的确定性用例、
系数 1.0 无惩罚、三候选三条。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:00:28 +08:00
joywayerandClaude Opus 5 eb3f4888a4 feat(enemy): 选招器支持防重复折扣——WeightedRandomAntiRepeat
上一次选中的招按 EnemyStatsSO.attackAntiRepeatFactor 折扣权重(默认 0.3)。
折扣把唯一候选压到 0 时退化为 Priority 路径,不会出现敌人永不出手。
阶段切换清空防重复记忆。取代 BossBase.UseBossSkillWeighted 里的同名逻辑。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 12:43:01 +08:00
joywayer b0fd040904 docs(enemy): 按 Task 1+2 审查结论修订 spec 与计划
spec:ResourceFull 由「无组件时恒 false」改为显式抛,与 Boss facet、AnchorAt
统一为「依赖组件缺失一律抛,不返默认值」。
计划:Task 16 补上 BossBase 残余三处行为树残留的清理项(含运行时告警字符串);
Task 19 记录 Patterns/ 是零外部引用的独立程序集,可整目录删。
2026-07-30 12:32:43 +08:00
joywayerandClaude Opus 5 2a1ada4388 fix(ai): 应用 Task 1+2 代码审查意见
ResourceFull 未挂 BossResource 时改为显式抛——原先静默返回 false,
是 CLAUDE.md 第 6 条禁止的下游兜底,且与紧邻的 Boss facet 行为不一致。
FakeBossControl.DistanceToAnchor 不再忽略 index。测试改用 TearDown 清理
(本项目关闭了 Domain/Scene Reload,失败断言会把物体永久留在打开的场景里)。
BeginPhaseTransition 去掉死的默认参数;接口补充重入契约说明。

顺带去重:FakeAiContext 实现 IEnemyActor,删除两个测试内的本地 Ctx 副本
——一个接口成员改动要同步三份是复发性成本。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 12:31:39 +08:00
joywayerandClaude Opus 5 3665504a58 feat(ai): 新增 IBossControl facet——Boss 阶段状态经 IAiContext 供图读取
非 Boss 敌人访问 IAiContext.Boss 显式抛 InvalidOperationException,
不返回空对象掩盖小怪图误挂 Boss 边的错误。BossResource 补 IsFull 供图条件边判定。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 12:15:54 +08:00
joywayer 5839b423ae docs(enemy): Boss AI 双轨统一实施计划——21 个任务,分两阶段
阶段一 Task 1-15 只加不删(每 Task 编译通过、测试全绿,末尾嘲风能用新轨打起来);
阶段二 Task 16-21 删旧轨(先改引用方,再删被引用类型;先删资产,再删脚本)。

编写期核查坐实并更正 spec 两处:
- 取消 EnemyBase.SetInvincible(float):HurtBox.SetInvincible(bool) 已存在,
  玩家侧 EnableIFrame/DisableIFrame 就走它,无敌时长由动画上两事件间距表达。
  新增件由六件减为五件。
- 竞技场锚点改挂 IBossControl,不走黑板:EnemyAiBrain._context 是私有字段
  且无公开访问器(BossBase 无从写入),且 ResetScratch 会清黑板带来隐式时序约束。

另确认 EditorScaffoldUtils.CreateSOAsset(Type,...) 重载已存在,无需新增辅助。
2026-07-30 11:01:46 +08:00
joywayer c176423103 docs(enemy): Boss AI 双轨统一设计——决策层与执行层一并归并到小怪轨
竞品调研 + 代码现状核查后推翻 2026-07-27 §5 的「执行层按复杂度分层」:
Boss 侧那层富编排从未真正存在(0 个 AttackPatternSO/SkillSequenceSO 资产、
5 个 BossSkillSO 除 skillId/weight 外全空、执行器跑一遍纯空转、
UseBossSkillWeighted 零调用者、预制体上无 EnemyAiBrain)。

决策:
- 决策层单轨 —— 每 Boss 一个 AiScript 定制类,不建 Boss 骨架/配方
- 执行层单轨 —— Boss 招走 EnemyAbilitySO + EnemyAttackSO,编排权威=动画时间轴
- 阶段=换招池 —— BossPhaseAbilityGate 按阶段启停能力组件
- 选招策略是可换小构件 —— AttackSelectionMode 加防重复值,不抽策略接口
- 旧 Boss 轨退役 9 脚本 + 5 资产,判据「有更优替代才删」

同时记录 WeakPointSystem.GetDamageMultiplier 零调用者、
BossBase.HandleParrySuccess 订阅全局弹反频道导致的误伤 bug、
敌人侧动画事件只接了 7/20 三处历史遗漏,以及两条脚手架创建链路
(CharacterWizardWindow / SceneObjectPlacerTool.PlaceChaoFeng)必须同改。
2026-07-30 10:29:49 +08:00
joywayer aaca138774 fix(editor): EnemyAiBrain 检视器支持多选——mixed 值不再误写,违规对象逐个点名 2026-07-30 08:46:41 +08:00
joywayer b36ac96ac7 feat(editor): EnemyAiBrain 检视器——二选一不变量提前到编辑期,定义 id 改下拉 2026-07-30 08:38:13 +08:00
joywayer afcc0b10ee fix(enemy): 配方校验改用一次性 builder 探测,不再干扰共享图缓存 2026-07-30 08:33:53 +08:00
joywayer da217af113 feat(enemy): AI 配方接入 SO 校验——漏配/歧义/零冷却 Committed 在编辑期与构建期报错 2026-07-29 17:20:12 +08:00
joywayer 6df4a259b7 docs(enemy): 修正作者指南与两处注释残留的死亡层表述 2026-07-29 16:34:21 +08:00
joywayer db6f6c5f77 docs(enemy): 新增敌人 AI 作者指南(三条路径 + 模块清单 + 六个范例 + 陷阱) 2026-07-29 15:57:23 +08:00
joywayer 44412adfc7 refactor(enemy): E001 AI 迁移为配方资产,退役定制脚本
用 EnemyAiRecipeWizard 建 ENM_E001_Ai.asset,配置未发现层
DisguiseThenPatrol + 交战层 RushEngagement(e001_chase, Committed);
TestRoomA 场景 EnemyAiBrain 改绑 _recipe,_definitionId 清空;
删除 E001CaoZhiAi.cs;SceneObjectPlacerTool 的 E001 挂载分支与其余
AI(BrainGraph) 提示文案统一改为指向配方向导路径。
2026-07-29 15:38:44 +08:00
joywayer ecdb0040cb feat(editor): 敌人 AI 配方脚手架向导;AssignModules 改名并失效缓存
- AiRecipeSO 新增 protected InvalidateGraph(),子类装配模块后调用以丢弃缓存图,
  避免改动要等到下次进 Play 才生效的静默陈旧图问题。
- PerceptionRecipeSO.SetModulesForTests 改名为 AssignModules(向导也要用它装配模块,
  旧名字具有误导性),并在装配后调用 InvalidateGraph。
- 新增 EnemyAiRecipeWizard:按 AssetFolderSpec 定名定路径创建感知型 AI 配方资产
  (Assets/_Game/Data/Enemies/{EnemyID}/ENM_{EnemyID}_Ai.asset),已存在则选中不覆盖。
- PerceptionRecipeSoTests 同步改名调用点,并新增测试钉住"装配后缓存必须失效"契约。
2026-07-29 15:28:25 +08:00
joywayer 426fa3601d fix(enemy): EnemyAiBrain 配置守卫不随重新启用蒸发,注册表消重 2026-07-29 15:23:49 +08:00
joywayer 57cbaacf2c feat(enemy): EnemyAiBrain 支持配方资产/AiScript id 二选一解析 2026-07-29 14:56:54 +08:00
joywayerandClaude Opus 5 c76eba8a0e docs(enemy): T15 并入 T13 审查遗留——AssignModules 改名 + InvalidateGraph
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 14:53:54 +08:00
joywayerandClaude Opus 5 6540560c13 docs(enemy): 计划同步——未配置配方应显式报错,而非"默认必须可建图"
T13 执行中发现原稿的 DefaultModules_ProduceValidGraph 断言了一条不成立的契约:
建资产到配好模块之间没有任何东西会调 GetOrBuildGraph(),它只在
EnemyAiBrain.Start() 运行时被调,所以"新建资产即报错"的场景不存在。
按该断言把默认改成零配置可建图的模块,会把"必然响亮报错"换成
"建图成功但敌人永不出手、零报错"——方向与第 6 条相反。改为钉相反契约。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 14:48:19 +08:00
joywayer 825cae7977 feat(enemy): PerceptionRecipeSO 配方资产 + SubclassSelector 模块下拉
未发现层/交战层各用 [SerializeReference]+[SubclassSelector] 下拉挑选模块,
新增模块类自动出现在下拉里,无需改枚举或已有文件。默认交战模块仍为
RushEngagement——未配能力时建图必须显式报错,而非静默产出打不出手的图。
2026-07-29 14:46:41 +08:00
joywayerandClaude Opus 5 04f644969b fix(ai): AiRecipeSO.OnEnable 改 protected virtual,防子类静默遮蔽
留作 private 时,子类另写一个 OnEnable 会遮蔽它(Unity 只调最派生的),
PlayModeResetHook 注册从此不发生且零报错——关闭域重载下即表现为
"改了配方进 Play 仍跑旧图"。T13 的 PerceptionRecipeSO 是第一个子类,
在它出现前先堵住。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 14:36:22 +08:00
joywayer 6d8db95046 feat(ai): IAiDefinition + AiRecipeSO 配方基类(含关闭域重载的缓存重置) 2026-07-29 14:32:45 +08:00
joywayer 7327a5fe09 refactor(enemy): 退役 PerceptionStateMachine,E001 切换到两层模块组装 2026-07-29 14:24:48 +08:00
joywayer 9dc10e0398 test(enemy): 补测骨架 × 另两个模块的组合,以及入口覆盖契约 2026-07-29 14:21:36 +08:00
joywayer bf8101b3f5 feat(enemy): PerceptionSkeleton——两层可插拔的感知骨架,自声明死亡终态 2026-07-29 14:10:46 +08:00
joywayer faa210c789 revert(enemy): 收掉死亡层——死亡归物理层,AI 只需一个终态 2026-07-29 14:02:13 +08:00
joywayerandClaude Opus 5 6a885ec105 docs(enemy): 计划同步取消死亡层——T9 作废、新增 T9R,T10/T11/T13 去掉 death 参数
T9 原稿折叠保留作历史记录,附必然卡死的机制说明。
PerceptionSkeleton.Add 由三参收为两参并自声明 Death 终态;
PerceptionRecipeSO 去掉死亡层字段。T5 段落保持原样(它记录的是已发生的事实,
IDeathModule 由 T9R 删除)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:59:54 +08:00
joywayerandClaude Opus 5 023a4a4ec6 docs(enemy): 取消死亡层——死亡归物理层,四层收为三层
T9 质量审查发现 TwoStageDeath 在真实运行中必然卡死:PerformDeath 先
ForceState(Dead) 再 Send(Died),IsControllable 此后永久为假,AiRuntime
的让位门使死亡链里的条件边永不被求值。追根后确认整层放错位置——
死亡完全归物理层(PerformDeath/EnemyDeathSequence/EnemySpawnerOnEvent),
AI 图只需要一个终态让全局 Died 边有去处。

初稿做成一层的由来:旧 Config 的 DeathAbilityId 字段,而其唯一使用者
E001 留空并注明"演出走物理状态机"。投机字段被放大成了三模块的层。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:54:40 +08:00
joywayer 5a9c1fd569 chore(enemy): 补齐死亡层模块的 .meta 文件 2026-07-29 13:46:45 +08:00
joywayer 3bc7eb1855 feat(enemy): 死亡层三模块(终态/单段/两段) 2026-07-29 13:46:20 +08:00
joywayer 3d00c66677 test(enemy): 补测 Attack 单出边的自愈路径,写明边序依赖的射程前提
Approach 边序注释:attackInRange 先于 leftAllZones 的前提是攻击射程恒小于
感知区(EnemyAttackSelector.Eligible 的 InAttackRange 门),故两条件不会相争;
若未来出现射程更大的招式,应由 AI 配方校验器报错,而非靠调边序绕开。

FakeCombat 新增 BestAttackFails 开关(与 Eligible 解耦),补测 Attack 唯一出边
attackDone 的自愈不变量:UseBestAttack 触发失败时不留下运行中能力,下一帧即可
自愈回 Approach,不会永久卡死。
2026-07-29 13:08:46 +08:00
joywayer 9d632cd103 fix(enemy): 退回 Approach 边序——脱战优先属假件失真,改由测试贴合真实射程语义
HasEligibleAttack() 只看攻击射程圆(远小于追击/视野区),脱离全部感知区在真实系统里
必然已脱离攻击射程,原顺序(attackInRange 先于 leftAllZones)本就正确。
FakeCombat.Eligible 是裸字段不会随 Chase/Vision 联动,才让上一版看似需要调边序。
改法:边序退回原样,测试里玩家离开感知区时手工同步清 Eligible,贴合真实耦合关系。
2026-07-29 12:59:07 +08:00
joywayer a700bef352 feat(enemy): ApproachAttackEngagement——攻击改为起手即打完,不中途收招
寻路逼近↔到射程选招攻击的交战模块。招式射程/冷却/权重归 EnemyAttackSelector,
本模块只决定何时逼近、何时出手。

有意的行为变更:Attack 态去掉 leftAllZones 边,玩家在前摇中跑出感知区不再
中途打断招式,招式完整打完后经 Approach 自然脱战。
2026-07-29 12:53:49 +08:00
joywayer 5771ac414e test(enemy): 补测 Committed 冲锋的防抖动闭环,RushExit 写明冷却前提 2026-07-29 12:45:43 +08:00
joywayerandClaude Opus 5 d013fe8e2e docs(enemy): 写明 RushExit.Committed 防抖动的前提是能力 cooldown > 0
质量审查发现:Committed 的冷却门若遇上 cooldown==0 的能力,
CanUseAbility 结束下一帧即恢复,Rush↔Rest 每帧抖动照旧出现。
这属能力侧配置错误,不在模块内兜底,纳入 AiDefinitionValidator 校验项。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:41:09 +08:00
joywayer bb1e2f7705 feat(enemy): RushEngagement——两个耦合 flag 合并为 RushExit 语义枚举 2026-07-29 12:32:50 +08:00
joywayerandClaude Opus 5 1c6ce57648 test(enemy): 修正测试名——该用例只验证空 Link 不加边,不涉及单向不可逆
原名 DisguiseThenPatrol_HasNoInternalEdges_DisguiseIsOneWay 承诺了
"伪装态不可逆",但那需要骨架的升级边才能验证(T10 覆盖)。
此用例实际只证明 Link 为空时状态不会自行跑掉,按实测内容改名。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:26:42 +08:00
joywayer acfba66c5d feat(enemy): 未发现层三模块(单一态/单向降级/定时交替) 2026-07-29 12:19:26 +08:00
joywayer 894ae99984 test(ai): 补测经 AiStateFragments 的跨模块状态撞名,State() 补引导注释 2026-07-29 12:11:07 +08:00
joywayer 80fdc36762 feat(ai): BrainBuilder.DeclareState——重复声明状态即报错,防模块间同名静默覆盖
State() 仍保持宽松(有则取),用于给已声明的态挂边;DeclareState() 只用于
首次声明行为,重复声明同名状态会抛异常。AiStateFragments 的四个建态原语
(Locomotion/Ability/AbilityOnce/Terminal)改用 DeclareState。同时给
IEngagementModule/IDeathModule/IUnawareModule 补充不变量说明。
2026-07-29 12:01:19 +08:00
joywayerandClaude Opus 5 5dcbce6019 docs(enemy): 计划新增 T5b——BrainBuilder.DeclareState 防模块间状态名冲突
契约质量审查提出:模块化后 State() 的"有则取"语义使两个独立模块
取同名状态时后者静默覆盖前者回调。与 T1 的 RequireState 是同一类
失败的两面,趁 0 个模块实现时补代价最低。附带补充交战/死亡层
"为何单阶段 Build 足够"的不变量说明。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 11:57:57 +08:00
joywayer 17e71dac39 feat(enemy): 未发现层/交战层/死亡层三个模块契约 2026-07-29 11:47:29 +08:00
joywayerandClaude Opus 5 2643f58b08 docs(enemy): 计划同步——AiStateFragments 空能力 id 改建图期报错
原稿沿用旧代码的静默跳过写法。AiStateFragments 成为公开原语后,
定制 AiScript 漏配 id 会产出"存在但什么都不做"的状态且无报错,
与 T1 引入 BrainBuilder.RequireState 的理由是同一类失败,不应双标。
校验放建图期而非每帧回调,兼顾快速失败与热路径开销。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 11:43:15 +08:00
joywayer 68f615e9c1 fix(enemy): AiStateFragments 空能力 id 建图期报错,补 OnExit 收尾测试
Ability/AbilityOnce 建图期校验 abilityId 非空,与 BrainBuilder.RequireState
同一类"漏配即快速失败"约束保持一致,避免定制 AiScript 漏配 id 时产出静默
不作为的状态。同时补 4 个测试:Locomotion/Ability 的 OnExit 收尾行为,以及
两个原语对空 id 的报错行为。
2026-07-29 11:42:47 +08:00
joywayer 77a32cbf8a feat(enemy): AiStateFragments 建态原语,新增 AbilityOnce 修死亡态重触发隐患
从 PerceptionStateMachine 的 private static 辅助方法提升为公开原语,供后续
模块化重构(骨架+可插拔模块)与定制 AiScript 复用。新增 AbilityOnce:
只在进入时触发能力、不每帧重触发,修复用 Ability() 驱动死亡态时演出播完
被 Tick 无限重播的隐患(现有 PerceptionStateMachine 因 E001 死亡能力 id
为空未暴露,本次不改它,留待 T11 整体退役)。
2026-07-29 11:35:47 +08:00
joywayer 084354fa6c test(enemy): AbilityRef 补测资产已挂但 abilityId 为空的误配路径 2026-07-29 11:29:47 +08:00
joywayer 474e89de3a feat(enemy): AbilityRef——AI 层能力引用资产化,字符串仅作定制脚本兜底 2026-07-29 11:22:15 +08:00
joywayer fd238b71f1 refactor(ai): AiSignal 8→1,删除零发送方零消费方的死枚举 2026-07-29 11:14:29 +08:00
joywayer e76d6bc118 feat(ai): BrainBuilder.RequireState——未声明状态挂边时构建期报错 2026-07-29 11:08:23 +08:00
joywayerandClaude Opus 5 8d63525b12 chore(scene): TestRoomA 敌人感知槽改 Box 型并配尺寸,微调物体位置
aggro/los 两个感知槽由点/圆改为 Box(type 7),offset y=0.31,
分别配 8.1x1.38 / 11.65x1.38;顺带清掉随 NavDriving 死代码移除的序列化残留字段。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 11:05:15 +08:00
joywayerandClaude Opus 5 f3c17de223 docs(enemy): 敌人 AI 组合式模块架构实施计划(17 个任务,TDD)
含计划阶段发现的三处修正:
- 未发现层拆 Declare/Link 两阶段,保证骨架升级边优先于内部计时边;
- 模块参数校验与委托缓存移出构造函数([SerializeReference] 不保证走 ctor);
- 新增 AiStateFragments.AbilityOnce,修死亡态演出被 Tick 无限重播的隐患。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:51:07 +08:00
joywayerandClaude Opus 5 e59e24cca0 docs(enemy): 修正 AI 配方解析方式——直接引用替代注册表双源
实施计划阶段发现两处硬阻塞:
1. BaseGames.AI 只引用 BaseGames.Core,无法收集依赖 Enemies 的 AiRecipeSO;
2. 项目 database SO 走 [SerializeField] 直接引用而非 Addressables,静态注册表取不到。

改为 EnemyAiBrain 上 _recipe 资产引用 / _definitionId 脚本 id 二选一。
净减 AiRecipeDatabaseSO、Addressables 加载、AddressKeys 改动、asmdef 改动;
flyweight 与配方路径零字符串 id 均保持。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:38:07 +08:00
joywayerandClaude Opus 5 1c32d8c87e docs(enemy): 敌人 AI 组合式模块架构设计(替换 PerceptionStateMachine)
四层架构:固定感知骨架 + 可插拔的 未发现层/交战层/死亡层;
两种定义源:PerceptionRecipeSO 配方资产(约 95% 敌人零代码)+ AiScript 定制脚本;
消除 Config 开关面板(EngagementStyle/PatrolBetweenChases → RushExit 语义枚举);
能力引用资产化(AbilityRef);AiSignal 8→1 清理死枚举。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:20:22 +08:00
joywayer c4e80afe9e chore(enemy): 清除随寻路库失效的 NavDriving 死代码与两处过时注释
NavDriving 原由已删除的寻路适配层设置,现无人写入(全项目 C# 零引用),
连同其调试字段一并移除;场景/预制体里的序列化残值由 Unity 下次保存自动丢弃。
2026-07-28 13:28:52 +08:00
joywayerandClaude Opus 5 d46ba67385 merge: 移除第三方 2D 寻路库,敌人导航改为直接移动方案
核心洞察:可达性由 EnemyMovement 的墙/悬崖夹紧天然表达——走过去、被夹停就是边界,
无需重建任何图/段结构。'agent 脱离导航段导致寻路全面静默失效'这一整类问题随之消失。

- IPathAgent(20+成员) → IEnemyNavigator(7成员);GroundNavigator/FlyingNavigator/NullEnemyNavigator
- 三处重复的卡死检测收敛为 StallDetector;修复 Pace 持续受阻无法掉头的死锁
- 修复 Wander 挑点失败退化为永久播 Idle 的缺陷
- 删除寻路库整包(112脚本/21069行)及对其源码的 4 处项目改动
- 脚手架/编辑器面板/Boss 预制体同步清理

净变化:删约 21500 行,加约 350 行。
验证:编译 0 错误;EditMode 206/206;PlayMode 无寻路库环境下三种巡逻+追击实测通过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 13:20:05 +08:00
joywayer a220b5c0d8 删除多余 2026-07-28 13:18:41 +08:00
joywayer 02ea0bb576 docs(enemy): 标注脱图脱困设计已作废(被移除寻路库方案取代,未实施) 2026-07-28 13:03:54 +08:00
joywayer 878e2c12cc chore(enemy): Boss 预制体移除随寻路库失效的 3 个导航组件(Boss 本就不使用导航) 2026-07-28 13:02:52 +08:00
joywayer 7934513b05 chore: 删除第三方 2D 寻路库整包(112 脚本/21069 行,含 4 处对其源码的项目改动)
敌人导航已改为 GroundNavigator/FlyingNavigator 直接移动方案:
可达性由 EnemyMovement 的墙/悬崖夹紧天然决定,不再需要烘焙导航图,
'agent 脱离导航段导致寻路全面静默失效'这一整类问题随之消失。
2026-07-28 11:50:33 +08:00
joywayer 3c622a69f2 refactor(enemy): NavLinkType 迁入 INavLinkHandler 并改述为能力驱动的地形穿越;补交新脚本 .meta 2026-07-28 11:50:17 +08:00