100 lines
12 KiB
Python
100 lines
12 KiB
Python
"""Register this batch without rewriting unrelated or legacy-encoded resource descriptions."""
|
||
from pathlib import Path
|
||
import json
|
||
|
||
ROOT=Path(__file__).resolve().parents[1]
|
||
MARK='## 五敌人战场替换与技能闭环样例(2026-10-21)'
|
||
|
||
def append(path,text,marker=MARK):
|
||
target=ROOT/path
|
||
if marker.encode('utf-8') not in target.read_bytes():
|
||
with target.open('ab') as stream: stream.write(('\n\n'+text.strip()+'\n').encode('utf-8'))
|
||
|
||
def main():
|
||
validation=json.loads((ROOT/'Doc/Design/Combat/SkillSlice/validation.json').read_text(encoding='utf-8'))
|
||
play=json.loads((ROOT/'Doc/Design/Combat/SkillSlice/play.json').read_text(encoding='utf-8'))
|
||
arena=json.loads((ROOT/'Doc/Design/Combat/EnemyCombatFx/studio_arena.json').read_text(encoding='utf-8'))
|
||
assert all(r['status']=='pass' for r in (validation,play,arena))
|
||
assert validation['traceSha256']==play['traceSha256']
|
||
production='''# 火喵/岩团团技能闭环样例
|
||
|
||
2026-10-21完成独立样例。五敌人 Studio 模型已替换正式 PrototypeArena;本页的新技能逻辑则在独立验收场景运行。主线仍是 battleRules=2,未接 battle.begin/battle.events,未重启或部署开发服,未改变旧在途挑战、账号或奖励协议。
|
||
|
||
## 已完成
|
||
|
||
- `AIServer/src/combat_slice.hpp/.cpp`:纯 C++ 逻辑内核;50ms整数 tick,千分之一血量/攻击、毫米位置。实现施法通道、主动技能优先、前摇/释放/后摇、冷却、投射物、伤害/护盾、过期、死亡与取消。无 Unity、墙上时钟、数据库依赖;当前只编入独立 combat_slice_tests 目标,未编入线上 aiserver。
|
||
- `Config/Combat/skill_slice.json`:隔离样例配置;岩团团前排、火喵后排、三个小角怪。敌人血量为原配置×2以覆盖护盾与近战,属于验收场景参数,不是20关的新平衡。原玩法和技能 proposal 的冻结副本保存在 trace 中,校验其内容及 SHA256。
|
||
- `AIClient/Assets/Bundles/Combat/SkillSlice/trace.json`:由 C++ 导出,219为最后 tick、366事件、36次施法、12颗火球、30次唯一命中。客户端只播放事件,不再计算该样例的伤害。`Tools/export_skill_slice.py` 先保存输入原始字节到 `Tools/output/skill_slice_inputs/`,回执 `export.json` 记录输入及轨迹哈希;验证/Play回执绑定同一轨迹哈希,最新配置变化不会混入旧轨迹。
|
||
- `SkillSlicePlayer.cs`:前摇、释放、到达和血量变化使用同一播放时钟;原 Attack 按各角色制作配方的 attackImpact 分段采样,火球在到达事件才扣血。拱形弹道仅影响外观;逻辑到达 tick 不变。已经释放的火球不因施法者死亡而撤回;释放前死亡取消。
|
||
- 持久化 `SkillSlice.prefab`:五实体的模型/Muzzle/Animator绑定,5类特效、24个池实例、8个技能音源;复用现有敌人反馈池。蓄力按 castId、护盾按 entityId、火球按实例租约清理。音效开关、暂停/恢复、1×/2×、低帧率过期声音跳过、结束/禁用清理已实现。
|
||
- 五条 TangoFlux 音效:FireCharge 0.48s、FireLaunch 0.28s、FireHit 0.40s、RockHit 0.32s、Shield 0.65s。按项目指南英文提示、2秒原稿、50步、guidance4.5、并发≤2生成,再裁切/淡入淡出。实际 seed/请求/任务ID/哈希以 `Doc/Design/Sound/BattleSkills/Slice/generation.json` 为准;原始计划 seed 保留在计划表中,没有冒称与生成请求相同。
|
||
|
||
## 验证与查看
|
||
|
||
- C++ 30项断言:起手无伤害、延迟到达、同 tick 零前摇护盾、动作互斥、后摇、冷却边界、死亡前取消、释放后存续、丢失目标不换目标、唯一死亡、护盾刷新不叠加、过期、元素防御、确定性与输入边界。
|
||
- `Tools/verify_skill_slice.py`:事件顺序/唯一命中/火球到达 tick/冻结输入哈希、五条音效源稿及输出哈希、格式、起音、尾部淡出和零削波通过。人工听感未由自动检查代替;试听 `Doc/Design/Sound/BattleSkills/Slice/preview.wav`。
|
||
- Unity实际 Play:火球到达前后血量检查通过;1×、2×、0.73秒低帧率步进的最终血量、护盾、位置、死亡/离场状态与 C++ 一致;暂停不前进、静音、重复结束、禁用清理通过。回执 `assets.json`、`play.json`、`validation.json`。
|
||
- 直接打开 `Assets/Bundles/Scenes/Review/SkillSlice_Review.unity` 后 Play 可看独立样例。它不会载入账号或发奖励;结束后保持结果,重进 Play 可重播。未覆盖当前 Boot 场景。
|
||
- 截图 `Assets/Screenshots/skill_slice_fireball.png`、`skill_slice_shield.png`。截图为暂停时刻,粒子额外推进30ms刷新几何,仅供视觉检查,不推进逻辑时钟。
|
||
|
||
## 复现
|
||
|
||
```powershell
|
||
cmake --build AIServer/build-g1 --config Release --target combat_slice_tests
|
||
python Tools/export_skill_slice.py
|
||
python Tools/verify_skill_slice.py
|
||
python Tools/validate_skill_design.py
|
||
```
|
||
|
||
Unity经Pipeline依次运行 `BuildSkillSlice.Build`、`VerifySkillSlice.Assets`;Play中运行 `VerifySkillSlice.Play`,结束后停止。TangoFlux重制入口为 `Tools/generate_skill_slice_sfx.py`,有效源稿按请求哈希复用。
|
||
|
||
## 后续接入边界
|
||
|
||
完整25技能、治疗/减速、多路波次、trace分页和网络协议兼容仍待实现;不能把独立样例视为 battleRules=3 主线迁移完成。下一阶段将该内核接入服务端授权战斗与冻结配置,客户端加载权威轨迹,覆盖10伙伴/20关平衡及旧挑战恢复后再切换规则。当前计划音效清单为5/9已生成,Heal/Slow/Lightning/EnemyHit仍待制作。TangoFlux权重依项目指南为非商业研究用途,保留许可核查边界,未宣称商用放行。
|
||
'''
|
||
(ROOT/'Doc/Design/Combat/SkillSlice/Production.md').write_text(production,encoding='utf-8')
|
||
append('Doc/ResDesc_Model.md',MARK+'''
|
||
|
||
正式 `PrototypeArena.prefab` 的9个敌人槽×5类型已全部改为 `{Hornlet,Dashlet,Shellhorn,HornCaptain,GreatHorn}Studio/*Studio_Enemy.prefab`;保留原 Avatar/控制器及底面、身高1.2的统一战场缩放。`BuildPrototype.FormalEnemies` 已同步,后续重建也使用 Studio 版。五类型实播 Walk/Attack 通过,敌人反馈产生158次特效、122次声音;内存隔离战斗不写账号。伙伴/T2和场景未重建。旧版资源继续保留。
|
||
|
||
新增独立技能场景/Prefab `AIClient/Assets/Bundles/Combat/SkillSlice/SkillSlice.prefab`、`Scenes/Review/SkillSlice_Review.unity`,复用岩团团、火喵和小角怪 Studio 模型,不生成新角色。说明 `Doc/Design/Combat/SkillSlice/Production.md`。''')
|
||
append('Doc/ResDesc_Animation.md',MARK+'''
|
||
|
||
五敌人正式战场继续复用原 Avatar、AnimatorController 与 Idle/Walk/Attack,未改动画文件。独立 `SkillSlicePlayer` 根据逻辑释放 tick 和角色原配方 attackImpact 分段采样 Attack,将前摇/命中点/后摇对齐;动作不决定伤害。样例与正式主线规则分开,详见 `Doc/Design/Combat/SkillSlice/Production.md`。''')
|
||
append('Doc/ResDesc_Effect.md',MARK+'''
|
||
|
||
新增 `AIClient/Assets/Bundles/Combat/SkillSlice/Vfx_Skill_{FireCharge,Fireball,FireHit,RockHit,Shield}.prefab`,分别为火焰蓄力、拱形飞行火球、火焰命中、石击、低亮度护盾;Unity Pipeline制作,复用 EnemyCombat/CombatGlow.mat 和 CombatRing.asset。`FireballCore.asset` 为 Pipeline 保存的 Unity 球形网格,用作火球主体粒子。独立 SkillSlice.prefab 持久化24个特效实例,租约及 cast/entity token控制清理;未接入主线。记录 `Doc/Design/Combat/SkillSlice/Production.md`。''')
|
||
append('Doc/ResDesc_Sound.md',MARK+'''
|
||
|
||
新增 `AIClient/Assets/Bundles/Sound/SFX/BattleSkills/Sfx_{FireCharge,FireLaunch,FireHit,RockHit,Shield}.wav`,时长依次0.48/0.28/0.40/0.32/0.65秒;PCM16、44.1kHz立体声,TangoFlux 2秒/50步/guidance4.5/固定seed生成,65Hz高通、起音裁切、5ms淡入/50ms淡出,峰值约-1.94dBFS。原稿 `Tools/output/skill_slice_sfx/`;任务与实际请求哈希 `Doc/Design/Sound/BattleSkills/Slice/generation.json`,试听同目录 preview.wav。独立技能样例播放,8音源池、音效开关/暂停/切场清理通过;人工听感待试听。计划表现在5/9生成,4条仍计划。上游权重许可非商业研究,未声明商用许可。''')
|
||
append('Doc/ResDesc_Picture.md',MARK+'''
|
||
|
||
- `AIClient/Assets/Screenshots/enemy_studio_arena.png`:五敌人 Studio 版在正式 PrototypeArena 的内存隔离实战截图,Unity Pipeline渲染。
|
||
- `AIClient/Assets/Screenshots/skill_slice_fireball.png`、`skill_slice_shield.png`:独立技能样例中的火球飞行/护盾暂停时刻,Unity Pipeline渲染;用于动作/挂点/特效检查,不是业务UI。''')
|
||
append('Doc/ResDesc_Config.md',MARK+'''
|
||
|
||
- `Config/Combat/skill_slice.json`:独立火喵/岩团团样例参数,productionEnabled=false。
|
||
- `AIServer/src/combat_slice.hpp/.cpp`、`tests/combat_slice_tests.cpp`、CMake combat_slice_tests:整数tick的独立逻辑模块与30项检查/轨迹导出;未编入生产服务。
|
||
- `AIClient/Assets/HotUpdate/Scripts/Gameplay/SkillSlicePlayer.cs`:消费不可变 C++ 事件的时钟/动作/特效音效播放器,不计算伤害与奖励。
|
||
- `AIClient/AgentScripts/BuildSkillSlice.cs`、`VerifySkillSlice.cs`:Pipeline持久化制作与独立Play/截图/资源检查。
|
||
- `Tools/generate_skill_slice_sfx.py`、`export_skill_slice.py`、`verify_skill_slice.py`、`write_skill_slice_report.py`:音频制作、冻结输入导出、来源与轨迹验证、资源登记。
|
||
- `Doc/Design/Sound/BattleSkills/manifest.json`:5条产物关联实际生成回执,状态partially_generated;`Tools/validate_skill_design.py` 按文件哈希和成功任务校验,报告generatedSfxCount=5,不把9条全部标为完成。
|
||
- `BuildPrototype.FormalEnemies` 与 `VerifyFormalEnemies` 已改为五 Studio 敌人;`VerifyEnemyCombatFx.PlayBattle(true)` 使用当前T2字段初始化的内存隔离实战验收。''')
|
||
append('Doc/ResDesc.md',MARK+'''
|
||
|
||
五 Studio 敌人已替换正式战场并实播通过;火喵/岩团团独立技能样例完成动作、特效、五条TangoFlux音效与 C++ 逻辑事件闭环。完整记录 [SkillSlice/Production.md](Design/Combat/SkillSlice/Production.md),分类型登记见 Model/Animation/Effect/Sound/Picture/Config。主线仍为旧规则,样例未接网络战斗协议。''')
|
||
append('Doc/Design/Character/StudioEnemies/Production.md',MARK+'''
|
||
|
||
此后五敌人已全部替换正式 `PrototypeArena`,9槽×5类型。`BuildPrototype.FormalEnemies` 与验收检查同步;原 Avatar/控制器/动作继续复用。五类在真实 `PrototypeFlow` 内存隔离战斗观察到 Walk/Attack,攻击/受击特效音效正常(158次特效、122次声音),截图 `Assets/Screenshots/enemy_studio_arena.png`。回执 `Doc/Design/Combat/EnemyCombatFx/studio_arena.json`。''')
|
||
append('Doc/Design/Combat/SkillSystem.md',MARK+'''
|
||
|
||
火喵与岩团团已完成独立 C++ 事件轨迹→Unity 时钟/动作/5类特效/5条TangoFlux音效的可运行样例,详见 [SkillSlice/Production.md](SkillSlice/Production.md)。这一步未接主线 battle.begin/events,未切换 battleRules=3;原25技能的全关迁移仍待完成。计划音效清单现为5/9已生成、4/9待制作;正式主线与独立样例状态分别记录。''')
|
||
append('Doc/Design/G0_Implementation.md',MARK+'''
|
||
|
||
五类敌人的 Studio 低模已替换正式战场,原Avatar/控制器/三动作不变;45个模型引用、尺寸/贴地和五类型 Walk/Attack、攻击受击反馈通过。保留现有伙伴及T2,不重建整体战场。
|
||
|
||
火喵/岩团团独立技能样例实现 C++ 50ms整数tick逻辑、动作通道和阶段、投射物到达伤害、护盾及特效音效播放。C++30项检查、Unity1×/2×/暂停/低帧率最终状态一致、静音和清理通过;生成5条TangoFlux音效。生产服务器和主线仍旧规则,未进行网络轨迹协议接入或20关规则迁移。记录 `Doc/Design/Combat/SkillSlice/Production.md`。''')
|
||
print('Registered Studio arena integration and independent skill slice across six resource registries')
|
||
|
||
if __name__=='__main__':main()
|