Add rank, friends, fishing, and camp treasure with T2 studio models and skill presentation.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"realFlow": true,
|
||||
"isolatedInMemory": true,
|
||||
"networkRewardRequests": false,
|
||||
"allFiveTypes": true,
|
||||
"studio": true,
|
||||
"walked": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"attacked": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"effects": 158,
|
||||
"sounds": 122,
|
||||
"captured": true,
|
||||
"Defeated": 9
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
# 伙伴技能特效与音效制作记录
|
||||
|
||||
2026-10-21:十位伙伴的技能表现已接入正式 `PrototypeArena`。本批补齐四条 TangoFlux 音效,九条计划音效均有真实产物及生成回执。
|
||||
|
||||
## 已制作资源
|
||||
|
||||
特效目录:`AIClient/Assets/Bundles/Combat/Partners/`。各行文件名均以 `Vfx_` 开头、`.prefab` 结尾。
|
||||
|
||||
| 伙伴 | 特效文件名 | 触发与形态 |
|
||||
| --- | --- | --- |
|
||||
| 石球 | RockBallGuard | shield:琥珀护环与石甲颗粒,跟随目标直到护盾耗尽/到期 |
|
||||
| 火喵 | FireCatFlare | hit:火花与橙色冲击环;复用火焰蓄力、发射与弹道 |
|
||||
| 芽芽 | BudBudBloom | heal:绿环、粉色花瓣与治疗十字 |
|
||||
| 闪闪雀 | SparkBirdBolt | hit:连接施法挂点与命中目标的蓝色折线雷电 |
|
||||
| 泡泡龟 | BubbleTurtleBubbles | slow:围住目标的蓝色泡泡 |
|
||||
| 岩甲犀 | RockRhinoQuake | slow:贴地震波与飞散石块;石击沿用岩石命中 |
|
||||
| 烈焰狮 | FlameLionBreath | ability release:面向目标的锥形喷焰;hit:火焰命中 |
|
||||
| 藤灵 | VineSpriteVines | heal:上升藤蔓、叶片与绿环 |
|
||||
| 雷纹豹 | ThunderLeopardClaw | hit:紫色雷电连接与三道爪痕 |
|
||||
| 潮汐鲸 | TideWhaleTide | shield:半球水罩与涟漪,状态结束清理 |
|
||||
|
||||
Pipeline 同时制作六个共用低多边形网格:`SkillStone`、`SkillLeaf`、`SkillHealCross`、`SkillBubble`、`SkillWaterDome`、`SkillVineHelix`(`.asset`)。`PartnerSkillGlow.mat` 使用现有 `AIGame/EnemyCombatParticle` Shader,白纹理及顶点色;不修改敌人共用材质。所有 Prefab、材质、网格均在编辑器内持久化。
|
||||
|
||||
新增音效位于 `AIClient/Assets/Bundles/Sound/SFX/BattleSkills/`:
|
||||
|
||||
| 文件 | 用途 | 成品长度 | 实际 seed |
|
||||
| --- | --- | --- | --- |
|
||||
| Sfx_Heal.wav | 治疗 | 0.55s | 52005 |
|
||||
| Sfx_Slow.wav | 减速 | 0.50s | 52007 |
|
||||
| Sfx_Lightning.wav | 雷电命中 | 0.38s | 52008 |
|
||||
| Sfx_EnemyHit.wav | 软受击 | 0.26s | 52009 |
|
||||
|
||||
按 `D:/UD/AI/XModel/TangoFlux_AI调用指南.md` 调用本地 `127.0.0.1:8012`。每条源音频2秒、50步、guidance 4.5,最多2条并发;65Hz高通、起音裁切、5ms淡入/50ms淡出、峰值约 -1.94dBFS。成品 PCM16/44.1kHz/双声道,Unity 导入为 PCM、DecompressOnLoad、保留采样率。源文件与请求缓存:`Tools/output/partner_skill_sfx/`;回执:`Doc/Design/Sound/BattleSkills/Partners/generation.json`;九条连听文件:同目录 `preview_all_nine.wav`。前五条保留 Slice 原始回执和真实 seed。本批未声称完成主观听审;发布授权审查沿用原 manifest 的 pending 状态。
|
||||
|
||||
## 标准制作与接入步骤
|
||||
|
||||
1. 在 `Config/Combat/partner_presentations_v1.json` 配置角色、效果索引、声音索引、地面效果、持续时间与池容量。数据效果仍由 `skill_runtime_v1.json` 的服务器逻辑决定。
|
||||
2. 用 `BuildPartnerSkillFx.Build` 经 Unity Pipeline 制作/更新持久化美术资产,再用 `BuildBattleSkills.Build` 构建正式表现池并嵌入已有战场。
|
||||
3. 声音按 TangoFlux 指南生成,保留 request/job/source/output 哈希;补登记 manifest,设置 Unity 导入参数。维护共用音效优先于按角色重复生成。
|
||||
4. `BattleSkillPresentation` 仅消费已结算的 cast_start、release、projectile、hit、shield、heal、slow 等事件;原 Attack 仍按既有15种角色的 attackImpact 对齐。19个既有 SkillMuzzle 随角色朝向与T2缩放;表现组件不计算伤害/治疗/减速。
|
||||
5. 验证索引与文件、音频格式及回执、真实轨迹、播放清理和画面,追加资源登记后完成。
|
||||
|
||||
正式池当前15类效果、76个根粒子实例、8个音源、9条音效、10个伙伴配置。雷电折线复用池中的 LineRenderer;运行时不创建材质、粒子或声音对象。雷电/群体治疗/减速同一次 cast 同种声音只播一次;普通软受击避免旧敌人声音与新伙伴声音重复叠播,硬壳/护盾反馈仍保留。
|
||||
|
||||
暂停与倍速作用于表现时钟及粒子;声音保留原始音高。过期的一次性事件跳过,未到期护盾和弹道可恢复。取消/死亡/目标消失/状态到期/battle_end/禁用均清理对应租约,代数标识避免旧租约停止刚复用的粒子。战斗结束保留计数供验收、清空播放对象。
|
||||
|
||||
## 验收证据
|
||||
|
||||
- `audio_config_validation.json`:十个配置与 Prefab、九条音效的源/请求/成品哈希、成功任务、起音/淡出/响度/零削波通过。
|
||||
- `behavior.json`:十种专属效果与九种声音均触发;连接端点、多目标音效去重、超池复用、过期跳过、暂停/2倍速/静音、死亡/到期/取消/结束/禁用清理通过。23份既有服务器轨迹覆盖十个伙伴,全部回放至末尾无残留。
|
||||
- `battle_visual.json`:真实 `PrototypeFlow` 消费服务器第20关T2轨迹并截图,验证正式战场、既有挂点和雷电命中。测试夹具不写真实账号。
|
||||
- `Assets/Screenshots/partner_skills_overview.png`:十角色独立特效总览;`partner_skills_battle.png`:正式场景命中。已目视检查颜色、形态、源点到目标及亮背景可见度。
|
||||
- `VerifyBattleRuntime.Assets`:正式战场76实例/8音源/9音效、19个局部-Z挂点、10伙伴/5敌人动作相位绑定通过。
|
||||
- `python -m unittest discover -s Tools/tests -p test_skill_design.py`:8项通过;`python Tools/verify_partner_skills.py`、`python Tools/validate_skill_runtime.py` 通过。
|
||||
|
||||
尚未进行微信真机帧耗、内存与混音主观验收;本次为编辑器功能及视觉验收。本批不改服务器逻辑、数值或现有账号,未重启8080服务。收尾退出测试 Play,保留原编辑场景。
|
||||
|
||||
重建入口:
|
||||
|
||||
```text
|
||||
python Tools/generate_partner_skill_sfx.py
|
||||
Unity Pipeline: BuildPartnerSkillFx.Build -> BuildBattleSkills.Build
|
||||
python Tools/verify_partner_skills.py
|
||||
Unity Pipeline (Play): VerifyPartnerSkillFx.Behavior / ContactSheet
|
||||
Unity Pipeline (Play): VerifyBattleRuntime.VisualSkills
|
||||
Unity Pipeline (Stop): VerifyPartnerSkillFx.Finish / VerifyBattleRuntime.Assets
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"profiles": 10,
|
||||
"signaturePrefabs": 10,
|
||||
"poolRoots": 76,
|
||||
"voices": 8,
|
||||
"audio": [
|
||||
{
|
||||
"id": "Sfx_FireCharge",
|
||||
"seconds": 0.48,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -14.407821284878423,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "686f9799eb264fcf310edb63dd69f75d16be94092bda40b3c341b0df18765217",
|
||||
"actualSeed": 62100
|
||||
},
|
||||
{
|
||||
"id": "Sfx_FireLaunch",
|
||||
"seconds": 0.28,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -14.786814954199185,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "e6f40a6d9a7e57f6fd56051c2393315c0610554921f5e26459eab2cbde40bbd2",
|
||||
"actualSeed": 62101
|
||||
},
|
||||
{
|
||||
"id": "Sfx_FireHit",
|
||||
"seconds": 0.4,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -12.397172372455682,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "d7c39fe8d2a270ab1241ee655f13d775f0e9c7cc0c3d38fdd6da2b33da493039",
|
||||
"actualSeed": 62102
|
||||
},
|
||||
{
|
||||
"id": "Sfx_RockHit",
|
||||
"seconds": 0.32,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -17.7350875982902,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "092735b139b22e0e5a1cd851c493fb1d6c9ff320cfdd33fdefc73db725a37a59",
|
||||
"actualSeed": 62103
|
||||
},
|
||||
{
|
||||
"id": "Sfx_Heal",
|
||||
"seconds": 0.55,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -14.50466159857536,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "61ab8a7cd7feed49646752ad921c50dbfbec33ce8d3b534d4451f3638ff5df89",
|
||||
"actualSeed": 52005
|
||||
},
|
||||
{
|
||||
"id": "Sfx_Shield",
|
||||
"seconds": 0.65,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -14.618990028368117,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "52119154552ac4fbbde0d94d34011be1749c019dec3764c31ecb7fae1b8fff37",
|
||||
"actualSeed": 62104
|
||||
},
|
||||
{
|
||||
"id": "Sfx_Slow",
|
||||
"seconds": 0.5,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -18.172440503372695,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "e191cc6baa46fa6e1c46f77eaa072495c52b4c75d47be98cfd7a81ca14a134c9",
|
||||
"actualSeed": 52007
|
||||
},
|
||||
{
|
||||
"id": "Sfx_Lightning",
|
||||
"seconds": 0.38,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -15.270460646000476,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "a8e6f806b3e62e8916929d39b63f9d6cfd1f534dc44f49f79d99a490753ed00a",
|
||||
"actualSeed": 52008
|
||||
},
|
||||
{
|
||||
"id": "Sfx_EnemyHit",
|
||||
"seconds": 0.26,
|
||||
"peakDb": -1.9386641491007355,
|
||||
"rmsDb": -17.505279728308555,
|
||||
"clippedSamples": 0,
|
||||
"sha256": "2cdb0a6f272f1bd5fac3ffab34fffaa52104a38056debe944e7913d7cf6ef93f",
|
||||
"actualSeed": 52009
|
||||
}
|
||||
],
|
||||
"subjectiveListening": "not_asserted",
|
||||
"releaseLicenseReview": "pending_upstream_review_per_guide"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"authoritative": true,
|
||||
"persistentMuzzles": 19,
|
||||
"t2ScaleApplied": true,
|
||||
"flight": false,
|
||||
"signature": true,
|
||||
"effects": 14,
|
||||
"networkAccountWrites": false
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"profiles": 10,
|
||||
"effectKinds": 15,
|
||||
"poolRoots": 76,
|
||||
"voices": 8,
|
||||
"audioClips": 9,
|
||||
"signatureEffectPlays": [
|
||||
3,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
17,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"soundPlays": [
|
||||
3,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
15,
|
||||
1
|
||||
],
|
||||
"chainSoundOnce": true,
|
||||
"beamEndpoints": true,
|
||||
"poolReuse": true,
|
||||
"pause": true,
|
||||
"speed2": true,
|
||||
"staleSkip": true,
|
||||
"death": true,
|
||||
"expiry": true,
|
||||
"cancel": true,
|
||||
"mute": true,
|
||||
"endCleanup": true,
|
||||
"disableCleanup": true,
|
||||
"traceCases": 23,
|
||||
"traceActors": 10,
|
||||
"rows": [
|
||||
{
|
||||
"trace": "stage_10_t1_f1.json",
|
||||
"events": 2894,
|
||||
"effects": 225,
|
||||
"sounds": 156
|
||||
},
|
||||
{
|
||||
"trace": "stage_11_t1_f2.json",
|
||||
"events": 2225,
|
||||
"effects": 143,
|
||||
"sounds": 121
|
||||
},
|
||||
{
|
||||
"trace": "stage_12_t1_f0.json",
|
||||
"events": 2338,
|
||||
"effects": 174,
|
||||
"sounds": 157
|
||||
},
|
||||
{
|
||||
"trace": "stage_13_t1_f1.json",
|
||||
"events": 3380,
|
||||
"effects": 285,
|
||||
"sounds": 196
|
||||
},
|
||||
{
|
||||
"trace": "stage_14_t1_f2.json",
|
||||
"events": 2458,
|
||||
"effects": 184,
|
||||
"sounds": 158
|
||||
},
|
||||
{
|
||||
"trace": "stage_15_t1_f0.json",
|
||||
"events": 2587,
|
||||
"effects": 215,
|
||||
"sounds": 198
|
||||
},
|
||||
{
|
||||
"trace": "stage_16_t1_f1.json",
|
||||
"events": 3626,
|
||||
"effects": 361,
|
||||
"sounds": 262
|
||||
},
|
||||
{
|
||||
"trace": "stage_17_t1_f2.json",
|
||||
"events": 2512,
|
||||
"effects": 195,
|
||||
"sounds": 167
|
||||
},
|
||||
{
|
||||
"trace": "stage_18_t1_f0.json",
|
||||
"events": 2866,
|
||||
"effects": 240,
|
||||
"sounds": 210
|
||||
},
|
||||
{
|
||||
"trace": "stage_19_t1_f1.json",
|
||||
"events": 3321,
|
||||
"effects": 311,
|
||||
"sounds": 221
|
||||
},
|
||||
{
|
||||
"trace": "stage_1_t1_f1.json",
|
||||
"events": 2920,
|
||||
"effects": 185,
|
||||
"sounds": 133
|
||||
},
|
||||
{
|
||||
"trace": "stage_20_t1_f2.json",
|
||||
"events": 2738,
|
||||
"effects": 237,
|
||||
"sounds": 210
|
||||
},
|
||||
{
|
||||
"trace": "stage_20_t2_f0.json",
|
||||
"events": 2476,
|
||||
"effects": 193,
|
||||
"sounds": 179
|
||||
},
|
||||
{
|
||||
"trace": "stage_20_t2_f1.json",
|
||||
"events": 3099,
|
||||
"effects": 254,
|
||||
"sounds": 177
|
||||
},
|
||||
{
|
||||
"trace": "stage_20_t2_f2.json",
|
||||
"events": 2488,
|
||||
"effects": 180,
|
||||
"sounds": 159
|
||||
},
|
||||
{
|
||||
"trace": "stage_2_t1_f2.json",
|
||||
"events": 2001,
|
||||
"effects": 110,
|
||||
"sounds": 105
|
||||
},
|
||||
{
|
||||
"trace": "stage_3_t1_f0.json",
|
||||
"events": 1758,
|
||||
"effects": 98,
|
||||
"sounds": 88
|
||||
},
|
||||
{
|
||||
"trace": "stage_4_t1_f1.json",
|
||||
"events": 3005,
|
||||
"effects": 198,
|
||||
"sounds": 133
|
||||
},
|
||||
{
|
||||
"trace": "stage_5_t1_f2.json",
|
||||
"events": 2188,
|
||||
"effects": 134,
|
||||
"sounds": 120
|
||||
},
|
||||
{
|
||||
"trace": "stage_6_t1_f0.json",
|
||||
"events": 2171,
|
||||
"effects": 131,
|
||||
"sounds": 111
|
||||
},
|
||||
{
|
||||
"trace": "stage_7_t1_f1.json",
|
||||
"events": 2770,
|
||||
"effects": 204,
|
||||
"sounds": 139
|
||||
},
|
||||
{
|
||||
"trace": "stage_8_t1_f2.json",
|
||||
"events": 2004,
|
||||
"effects": 116,
|
||||
"sounds": 97
|
||||
},
|
||||
{
|
||||
"trace": "stage_9_t1_f0.json",
|
||||
"events": 2199,
|
||||
"effects": 151,
|
||||
"sounds": 133
|
||||
}
|
||||
],
|
||||
"networkAccountWrites": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"status": "captured",
|
||||
"profiles": 10,
|
||||
"screenshot": "AIClient/Assets/Screenshots/partner_skills_overview.png",
|
||||
"sampleSeconds": 0.14,
|
||||
"networkAccountWrites": false
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
# 权威技能协议与正式战场接入
|
||||
|
||||
2026-10-21完成。独立火喵/岩团团样例已推广到正式战斗:十伙伴的普攻/主动技能及五敌人普攻,共25个技能;覆盖20主线关卡和5个属性挑战。8080本地开发服已更新,health返回combatProtocol=1;未发布Player或生产服。
|
||||
|
||||
## 版本与配置
|
||||
|
||||
- `Config/Combat/skill_runtime_v1.json` 是正式逻辑表,protocol=1、logicVersion=1,新战斗快照标记rulesVersion=3。原proposal保持design_only/runtimeEnabled=false,两个校验器分别报告设计和正式运行时状态,避免把旧设计文件当成正式入口。
|
||||
- `greenfield.json` 保持battleRules=2,兼容旧客户端及旧挑战。新版客户端只在battle.begin/challenge.begin发送combatProtocol=1;没有该字段的旧客户端仍得到原规则。新客户端连接旧服务端时,也能接收旧规则战斗。
|
||||
- CMake将正式技能表编入程序。开战原子保存原玩法配置、技能定义、gameplayHash、skillsHash、权威结果及完整事件轨迹;旧进行中战斗不会因新配置或程序重启重新计算。
|
||||
|
||||
## 逻辑内核
|
||||
|
||||
`combat_slice` 已由独立样例内核推广为共享纯数据内核,`combat_runtime` 从冻结玩法参数与正式技能表构造战斗。单位id为伙伴id或100+敌人槽;时间50ms整数tick、血量/攻击千分之一单位、位置毫米、倍率万分比。数值量化及前摇会改变新规则的节奏,不宣称与旧规则结果等价。
|
||||
|
||||
三波刷怪、多路前排阻挡/移动/漏怪、主动技能优先的单动作通道、前摇/释放/后摇、绝对冷却、投射物到达伤害、伤害/治疗/护盾/减速类型化行为、首领控制折扣、护盾和减速的刷新/过期均已接入。行为数组按actionId顺序执行;同cast/action/target不能重复命中。释放前死亡取消,释放后的火球独立存续,目标丢失不换目标,死亡和战斗结束只发一次。
|
||||
|
||||
开局预计算仍由服务端授权。battle.finish继续使用已冻结结果,忽略客户端won和elapsed字段;最早结算时间由readyAt约束,放弃/重试/首通奖励沿用原幂等事务。没有战中手动输入。
|
||||
|
||||
## 分页与恢复
|
||||
|
||||
| 操作 | 行为 |
|
||||
| --- | --- |
|
||||
| battle.begin/challenge.begin | 新客户端声明combatProtocol=1;返回snapshot和不含events的trace manifest |
|
||||
| GET /game/state | 返回原场次的traceId/traceHash/初态/终态摘要,供重连恢复 |
|
||||
| POST /game/command:battle.events | 按本玩家pendingBattle、activityId、traceId鉴权;cursor与limit读取事件,limit≤256 |
|
||||
| battle.finish/abandon | 原事务结算;关闭后删除活动轨迹,后续分页返回activity_missing |
|
||||
|
||||
分页读取不修改日历、revision、钱包或operation log。每页返回原始JSON payload字符串及SHA256;客户端逐页校验、按原顺序拼接并核对整条traceHash,再开始播放。错误页、缺页、重复/乱序事件或不支持的版本不会回退成本地算伤害。请求可重试,持久化命令队列与分页读取分开。恢复当前实现从头重播原轨迹,沿用同一battleId及readyAt,不重新开战或发奖。
|
||||
|
||||
事件上限50000,正式内核最多2400tick。当前120组矩阵最大3626事件/299734字节(事件JSON),按需分页预载后开始播放;不是把全事件塞进每次存档响应。
|
||||
|
||||
## 客户端与表现
|
||||
|
||||
- `CombatTrace.cs`:协议DTO、版本/实体/事件顺序校验;`PrototypeApi.LoadCombat`:授权分页、哈希校验与有界重试。
|
||||
- `PrototypeBattle.Trace.cs`:只读战斗模型。Tick只应用事件,不运行旧伤害/技能计算;血量、护盾、减速、死亡、漏怪、技能次数和统计都取服务器轨迹。
|
||||
- `PrototypeFlow`:新旧战斗分支共存。新分支由同一时钟驱动动作、弹道、VFX/SFX与数据;低帧率仍消费全部数据事件,跳过过期一次性声音。结束、放弃、换页与禁用清理池。
|
||||
- `BattleSkillPresentation.prefab`:5类特效、36个池实例、8个技能音源,复用已有5条TangoFlux音效和敌人反馈池。火焰伙伴有蓄力/发射/命中,其他投射物按元素着色;治疗/减速沿用现有标识。各伙伴更丰富的专属美术及余下4条计划音效未在本批补制。
|
||||
- 15种角色的attackImpact读取各自原制作配方,分段采样原Attack以对齐逻辑释放点;零前摇护盾保持Idle并播放护盾。AnimationEvent不授权伤害。
|
||||
- 战场持久化19个`SkillMuzzle`(10伙伴+9敌人槽),局部-Z为前,随角色朝向和T2体型缩放,可在Prefab调节;不在运行时创建挂点/材质/粒子对象。BuildPrototype重建、后续敌人替换均保留此约定。
|
||||
|
||||
## 验证
|
||||
|
||||
- 三组CTest全部通过:原样例、旧事务/规则5192项断言、正式技能协议与120组关卡/进化/阵容矩阵(覆盖25技能,超过30万项事件断言)。涵盖治疗相邻路线、首领减速、复合行为、未来波次、确定性、分页只读、账号隔离、快照变化不重算、旧客户端、属性挑战及幂等奖励。
|
||||
- 23份C++导出轨迹→C#只读模型,覆盖全部20关及3套T2终章阵容;最终血量、护盾、减速、位置、死亡/漏怪、统计完全一致,错误序列被拒绝。
|
||||
- 真正EventSystem UI+HTTP,18083专用数据库的1-5全T2六人阵容,1876事件。服务端进程重启后恢复相同battleId与traceHash,2×实播后最终状态与服务器奖励/统计一致;暂停、静音、火球、护盾、敌人攻击和结束清理通过。
|
||||
- 最终Prefab挂点实播通过,T2火喵发射点与持久化挂点世界坐标一致;截图 `Assets/Screenshots/battle_skills_anchors.png`。完整UI截图 `battle_skills_network.png`。
|
||||
- 真实账号与偏好已恢复。开发服更新前一致性SQLite备份及旧程序保留;重启后全部账号game_states与备份比较一致,包括已有冻结战斗。期间Windows旧进程文件锁曾延迟替换,确认进程完全退出后更新成功。部署回执 `deployment.json`。
|
||||
|
||||
矩阵用于验证执行、确定性和代表阵容可通关;首日实际解锁阵容的细致平衡、微信真机帧耗/内存、全部伙伴专属特效音效仍需后续验收。
|
||||
|
||||
## 入口
|
||||
|
||||
```powershell
|
||||
cmake --build AIServer/build-g1 --config Release --target aiserver combat_runtime_tests game_tests combat_slice_tests
|
||||
ctest --test-dir AIServer/build-g1 -C Release --output-on-failure
|
||||
AIServer/build-g1/Release/combat_runtime_tests.exe Doc/Design/Combat/Runtime/server_validation.json Tools/output/combat_runtime/traces
|
||||
python Tools/validate_skill_runtime.py
|
||||
```
|
||||
|
||||
Unity Pipeline:`BuildBattleSkills.Build`(制作/挂点)、`VerifyBattleRuntime.Assets`/`Domain`(检查);实战测试使用专用18083账号,Begin→重启专用服务→ResumeAndFinish→Cleanup,勿对真实账号运行测试入口。开发服实际程序在AIServer/build/Release,运行中Windows会锁定exe;不要直接构建覆盖运行中的文件。新版本应先在其他输出目录验证,等待旧进程完全退出后替换。
|
||||
|
||||
|
||||
## 后续完成:伙伴专属技能表现(2026-10-21)
|
||||
上文记录的“专属特效/余四条音效待补”已完成:新增十种伙伴专属Prefab、四条TangoFlux音效,正式池现为15类/76根实例/8音源/9音效/10角色配置。治疗与减速也消费权威事件播放,真实轨迹23例、复用/去重/清理与正式战场截图通过。详见 `../PartnerSkills/Production.md`。微信真机性能、内存与主观混音仍待验收。
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"status": "formal_runtime_contract_validated",
|
||||
"runtimeIntegrated": true,
|
||||
"generatedAudio": true,
|
||||
"generatedSfxCount": 9,
|
||||
"skills": 25,
|
||||
"pets": 10,
|
||||
"enemies": 5,
|
||||
"stages": 20,
|
||||
"presentationProfiles": 12,
|
||||
"existingAssets": 2,
|
||||
"plannedAssets": 18,
|
||||
"sfxRecipes": 9,
|
||||
"note": "Formal runtime table is separate from disabled proposals; nine audio clips generated; ten formal partner presentation profiles validated by verify_partner_skills.py",
|
||||
"formalPartnerProfiles": 10,
|
||||
"formalEffectKinds": 15,
|
||||
"formalAudioClips": 9,
|
||||
"protocol": 1,
|
||||
"logicVersion": 1,
|
||||
"rulesVersion": 3,
|
||||
"runtimeSource": "Config/Combat/skill_runtime_v1.json",
|
||||
"runtimeSourceSha256": "6125b71e519c9c35579e36be0d9b1f9810d9de898643b8eb9ba81d9f342e5ddb"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"combatProtocol": 1,
|
||||
"pid": 62304,
|
||||
"previousExe": "D:\\UD\\AI\\AIGame\\Tools\\output\\combat_runtime\\aiserver-before-combat-20261021-213915.exe",
|
||||
"executableSha256": "173fd1c4df0e087f36a4ecb8b85b5857d1bba17e40f118f54b0a086d24dc1c6c",
|
||||
"status": "enabled",
|
||||
"backup": "D:\\UD\\AI\\AIGame\\AIServer\\data\\aiserver-before-combat-20261021-213915.db",
|
||||
"gameStatesUnchanged": true,
|
||||
"accounts": 1
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"authoritative": true,
|
||||
"persistentMuzzles": 19,
|
||||
"t2ScaleApplied": true,
|
||||
"flight": true,
|
||||
"effects": 3,
|
||||
"networkAccountWrites": false
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"effects": 15,
|
||||
"profiles": 10,
|
||||
"audioClips": 9,
|
||||
"poolInstances": 76,
|
||||
"voices": 8,
|
||||
"anchors": 19,
|
||||
"petPhases": 10,
|
||||
"enemyPhases": 5
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"cases": 23,
|
||||
"all20Stages": true,
|
||||
"rows": [
|
||||
{
|
||||
"stage": 10,
|
||||
"events": 2894,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 11,
|
||||
"events": 2225,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 12,
|
||||
"events": 2338,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 13,
|
||||
"events": 3380,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 14,
|
||||
"events": 2458,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 15,
|
||||
"events": 2587,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 16,
|
||||
"events": 3626,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 17,
|
||||
"events": 2512,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 18,
|
||||
"events": 2866,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 19,
|
||||
"events": 3321,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 1,
|
||||
"events": 2920,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 20,
|
||||
"events": 2738,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 20,
|
||||
"events": 2476,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 20,
|
||||
"events": 3099,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 20,
|
||||
"events": 2488,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 2,
|
||||
"events": 2001,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 3,
|
||||
"events": 1758,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 4,
|
||||
"events": 3005,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 5,
|
||||
"events": 2188,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 6,
|
||||
"events": 2171,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 7,
|
||||
"events": 2770,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 8,
|
||||
"events": 2004,
|
||||
"won": true
|
||||
},
|
||||
{
|
||||
"stage": 9,
|
||||
"events": 2199,
|
||||
"won": true
|
||||
}
|
||||
],
|
||||
"corruptTraceRejected": true
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"endpoint": "http://127.0.0.1:18083",
|
||||
"stage": 5,
|
||||
"tier": 2,
|
||||
"authoritative": true,
|
||||
"hashVerified": true,
|
||||
"serverRestartRecovery": true,
|
||||
"pause": true,
|
||||
"mute": true,
|
||||
"speed2": true,
|
||||
"flight": true,
|
||||
"shield": true,
|
||||
"enemyAttack": true,
|
||||
"finalStateExact": true,
|
||||
"statsMatch": true,
|
||||
"events": 1876,
|
||||
"won": true,
|
||||
"sounds": 83,
|
||||
"cleanup": true,
|
||||
"realAccountWrites": false
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# 火喵/岩团团技能闭环样例
|
||||
|
||||
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权重依项目指南为非商业研究用途,保留许可核查边界,未宣称商用放行。
|
||||
|
||||
|
||||
## 权威技能协议与正式战场接入(2026-10-21)
|
||||
|
||||
后续已将独立样例推广到正式技能协议与全关战场,见 [../Runtime/Production.md](../Runtime/Production.md)。本页保留独立样例的原始制作记录;正式配置、分页、恢复与客户端入口以Runtime文档为准。
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"prefab": "Assets/Bundles/Combat/SkillSlice/SkillSlice.prefab",
|
||||
"actors": 5,
|
||||
"effectKinds": 5,
|
||||
"poolInstances": 24,
|
||||
"voices": 8,
|
||||
"audioClips": 5
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"status": "exported",
|
||||
"inputs": [
|
||||
{
|
||||
"source": "Config/Gameplay/greenfield.json",
|
||||
"snapshot": "Tools/output/skill_slice_inputs/greenfield.json",
|
||||
"field": "frozenGameplay",
|
||||
"sourceSha256": "2787887803fd365816e0d7d3f3a06386daddb1bbf08d0e7e40a9ec5393d6d5e2",
|
||||
"payloadSha256": "5c2229d1c4701d889b239ff036966bd1dab872eeccf202c0e1726f5d7a6a32a4"
|
||||
},
|
||||
{
|
||||
"source": "Config/Combat/skill_system_v1.proposal.json",
|
||||
"snapshot": "Tools/output/skill_slice_inputs/skill_system_v1.proposal.json",
|
||||
"field": "skillDefinitions",
|
||||
"sourceSha256": "9749629b3914ec9135389f2f6ccaffb072010c26feb7d2680e90ae9f4994fc6c",
|
||||
"payloadSha256": "5b64a277ee3d246e864f20285406ec8087756a6d7ae4a66c0779377c6e594df6"
|
||||
},
|
||||
{
|
||||
"source": "Config/Combat/skill_slice.json",
|
||||
"snapshot": "Tools/output/skill_slice_inputs/skill_slice.json",
|
||||
"field": "scenario",
|
||||
"sourceSha256": "2af74ebfca1dd556b5512efcc18983aa99a3ebca34b0f22776dc4aef1b178607",
|
||||
"payloadSha256": "5e429157decd5ad6a1ef7d3c16f9cc08498942ff09c29450d85dbf841b7c0de8"
|
||||
}
|
||||
],
|
||||
"traceSha256": "cb5e34b8c625d58700cff87aec357764f475bb1db19208a006320eb209124827"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"name": "Vfx_Skill_Fireball",
|
||||
"count": 1,
|
||||
"position": "(200.00, 1.80, 0.08)",
|
||||
"bounds": "Center: (199.99, 1.79, 0.11), Extents: (0.32, 0.32, 0.32)",
|
||||
"layer": 30
|
||||
},
|
||||
{
|
||||
"name": "BrightCore",
|
||||
"count": 1,
|
||||
"position": "(200.00, 1.80, 0.08)",
|
||||
"bounds": "Center: (200.03, 1.82, 0.07), Extents: (0.14, 0.14, 0.14)",
|
||||
"layer": 30
|
||||
},
|
||||
{
|
||||
"name": "Vfx_Skill_Shield",
|
||||
"count": 1,
|
||||
"position": "(200.00, 0.80, 0.48)",
|
||||
"bounds": "Center: (199.99, 0.81, 0.45), Extents: (1.46, 1.46, 1.46)",
|
||||
"layer": 30
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"name": "Vfx_Skill_FireHit",
|
||||
"count": 14,
|
||||
"position": "(200.00, 0.80, 0.77)",
|
||||
"bounds": "Center: (200.01, 0.81, 0.76), Extents: (0.18, 0.17, 0.16)",
|
||||
"layer": 30
|
||||
},
|
||||
{
|
||||
"name": "ImpactRing",
|
||||
"count": 1,
|
||||
"position": "(200.00, 0.80, 0.77)",
|
||||
"bounds": "Center: (200.01, 0.76, 0.78), Extents: (1.31, 1.31, 1.31)",
|
||||
"layer": 30
|
||||
},
|
||||
{
|
||||
"name": "Vfx_Skill_Shield",
|
||||
"count": 1,
|
||||
"position": "(200.00, 0.80, 0.48)",
|
||||
"bounds": "Center: (200.00, 0.77, 0.48), Extents: (1.46, 1.46, 1.46)",
|
||||
"layer": 30
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"traceSha256": "cb5e34b8c625d58700cff87aec357764f475bb1db19208a006320eb209124827",
|
||||
"authoritativeCppTrace": true,
|
||||
"productionRulesChanged": false,
|
||||
"events": 366,
|
||||
"won": true,
|
||||
"pause": true,
|
||||
"speed2": true,
|
||||
"lowFrameRate": true,
|
||||
"arrivalDamage": true,
|
||||
"finalStateExact": true,
|
||||
"mute": true,
|
||||
"disableCleanup": true,
|
||||
"liveSounds": 44,
|
||||
"fireballScreenshot": true,
|
||||
"shieldScreenshot": true,
|
||||
"networkAccountWrites": false
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"status": "pass",
|
||||
"events": 366,
|
||||
"casts": 36,
|
||||
"projectiles": 12,
|
||||
"hits": 30,
|
||||
"deaths": 3,
|
||||
"traceSha256": "cb5e34b8c625d58700cff87aec357764f475bb1db19208a006320eb209124827",
|
||||
"frozenInputs": {
|
||||
"Config/Gameplay/greenfield.json": "2787887803fd365816e0d7d3f3a06386daddb1bbf08d0e7e40a9ec5393d6d5e2",
|
||||
"Config/Combat/skill_system_v1.proposal.json": "9749629b3914ec9135389f2f6ccaffb072010c26feb7d2680e90ae9f4994fc6c",
|
||||
"Config/Combat/skill_slice.json": "2af74ebfca1dd556b5512efcc18983aa99a3ebca34b0f22776dc4aef1b178607"
|
||||
},
|
||||
"currentSourceChanged": [],
|
||||
"audio": [
|
||||
{
|
||||
"id": "Sfx_FireCharge",
|
||||
"seconds": 0.48,
|
||||
"rmsDb": -14.407821284878423,
|
||||
"clippedSamples": 0
|
||||
},
|
||||
{
|
||||
"id": "Sfx_FireLaunch",
|
||||
"seconds": 0.28,
|
||||
"rmsDb": -14.786814954199185,
|
||||
"clippedSamples": 0
|
||||
},
|
||||
{
|
||||
"id": "Sfx_FireHit",
|
||||
"seconds": 0.4,
|
||||
"rmsDb": -12.397172372455682,
|
||||
"clippedSamples": 0
|
||||
},
|
||||
{
|
||||
"id": "Sfx_RockHit",
|
||||
"seconds": 0.32,
|
||||
"rmsDb": -17.7350875982902,
|
||||
"clippedSamples": 0
|
||||
},
|
||||
{
|
||||
"id": "Sfx_Shield",
|
||||
"seconds": 0.65,
|
||||
"rmsDb": -14.618990028368117,
|
||||
"clippedSamples": 0
|
||||
}
|
||||
],
|
||||
"subjectiveListening": "not_asserted",
|
||||
"productionIntegrated": false
|
||||
}
|
||||
@@ -207,3 +207,17 @@ proposal文件显式 `runtimeEnabled=false`,现有CMake和客户端加载器
|
||||
## 已落地的敌人表现批次(2026-10-21)
|
||||
|
||||
五类敌人的攻击与普通/硬甲/护盾受击表现已作为现有battleRules=1/2的适配层接入。实际完成8套特效、8条TangoFlux短音效、32实例/8音源播放池;真实PrototypeFlow隔离战斗及暂停/倍速/静音通过。完整制作记录见 [EnemyCombatFx/Production.md](EnemyCombatFx/Production.md)。此前BattleSkills的9条计划音效清单仍保持原计划状态,不能与本批8条敌人音效混算;battleRules=3技能逻辑内核、动作冲击相位与伤害同步、伙伴元素技能仍按上文阶段实施。
|
||||
|
||||
|
||||
## 五敌人战场替换与技能闭环样例(2026-10-21)
|
||||
|
||||
火喵与岩团团已完成独立 C++ 事件轨迹→Unity 时钟/动作/5类特效/5条TangoFlux音效的可运行样例,详见 [SkillSlice/Production.md](SkillSlice/Production.md)。这一步未接主线 battle.begin/events,未切换 battleRules=3;原25技能的全关迁移仍待完成。计划音效清单现为5/9已生成、4/9待制作;正式主线与独立样例状态分别记录。
|
||||
|
||||
|
||||
## 权威技能协议与正式战场接入(2026-10-21)
|
||||
|
||||
本阶段已完成正式 `skill_runtime_v1.json`、25技能权威内核、分页协议和客户端正式战场接入;8080开发服已启用。原proposal仍为设计样例,正式验证器为 `Tools/validate_skill_runtime.py`。旧挑战不重算,新客户端使用快照rulesVersion3;新旧规则数值节奏不等价。详细边界、测试、部署和待补美术见 [Runtime/Production.md](Runtime/Production.md)。
|
||||
|
||||
|
||||
## 正式伙伴技能表现补齐(2026-10-21)
|
||||
十伙伴的专属效果与九条计划技能声音已接入正式权威战斗表现池,配置 `Config/Combat/partner_presentations_v1.json`,制作用 `BuildPartnerSkillFx`、`BuildBattleSkills`。每次cast同种声音去重、雷电连接源目标、治疗/减速按已结算事件播放;不改逻辑内核。见 `PartnerSkills/Production.md` 的资源、流程和验证回执。
|
||||
|
||||
Reference in New Issue
Block a user