5.5 KiB
5.5 KiB
战斗直结算与表现修复记录(2026-04-27)
更新时间:2026-04-27
1. 问题背景
本次修复针对 RPG 运行态战斗里两类直接可感知的问题:
- 战斗面板中的“普通攻击”“技能释放”等选项点击后,体验上像是又走了一次剧情/模型推演,而不是立刻播动作并结算伤害。
- 伤害飘字与敌方头顶血条在战斗里没有稳定可见,尤其是最后一击和脱战前后的几帧容易直接丢失。
2. 本次约束
为避免后续回归,本次明确补充以下工程约束:
- 只要已经处于
inBattle = true的战斗面板阶段,battle_*与战斗内inventory_use都必须走前端现有的本地确定性战斗播放链。 - 这类动作只负责“播放动作 -> 扣血/扣蓝/冷却 -> 刷新下一轮 options / 脱战收尾”,不能再回到服务端剧情推演分流。
- 脱战前最后一拍的画布表现必须保留,不能因为
inBattle提前变成false就把伤害飘字、敌方血条和受击反馈一起关掉。
3. 落地改动
3.1 战斗动作分流
文件:src/hooks/rpg-runtime-story/choiceActions.ts
调整:
- 新增“战斗直结算动作”判定。
- 当运行态已经处于战斗中时,
battle_*和inventory_use不再命中runServerRuntimeChoiceAction()。 - 这些动作统一回到
runLocalStoryChoiceContinuation(),沿用现有buildBattlePlan + playback本地确定性链路。
结果:
- 普通攻击、技能释放、调息、逃跑、战斗内用物品都会直接播动作并结算。
- UI 不再把这类按钮误导成“剧情推演中”的体验。
3.2 战斗画布表现保留
文件:src/components/game-canvas/GameCanvasEntityLayer.tsx
调整:
- 新增
hasCombatAfterimage判定,用于识别“虽然inBattle已经开始收尾,但敌方仍处于受击/死亡帧或刚产生过伤害反馈”的阶段。 - 伤害反馈样本采集、飘字事件保留、敌我血条展示统一改为依赖
shouldRenderCombatPresentation,而不是只看inBattle。
结果:
- 伤害数值飘字不会在最后一击前被提前清空。
- 敌方头顶血条能跟着受击与死亡过渡完整显示出来。
3.3 面板文案修正
文件:src/components/rpg-runtime-panels/RpgAdventurePanel.tsx
调整:
- 运行态仍需锁输入的短暂阶段,如果当前处于战斗中,加载提示改为“战斗结算中...”。
- 非战斗态保留“剧情推演中...”。
结果:
- 战斗按钮点击后的反馈语义与真实执行链路一致。
- 用户不会再误判为“点击普通攻击/技能后又触发了一次模型推理”。
4. 回归关注点
后续若继续改 server-rs / Runtime Story 的战斗 option 下发,需要继续遵守:
- 服务端可以负责下发合法战斗选项和最终快照结构。
- 但战斗内直接动作的逐帧播放与即时结算,默认仍以前端确定性链路为准。
- 若未来要把这条链路整体迁回
server-rs,必须先补齐“无推理、可逐帧播放、可保留最终受击帧”的等价表现方案,再替换当前实现。
5. 追加修复(2026-04-27 晚)
本日后续排查又发现一处更直接的战斗跳过根因:
src/hooks/combat/battlePlan.ts旧实现会在一次点击里继续跑完整段turnOrder,而不是只结算“玩家一次声明动作 + 最多一次敌方反击”。battle_attack_basic还会复用技能挑选逻辑,导致“普通攻击”可能被本地随机映射成别的技能动作。
本轮已追加修复:
- 本地战斗计划严格收束为单回合结算,不再一次点击连跑多轮。
battle_attack_basic固定走基础攻击,不再随机选技能。- 战斗内
inventory_use在本地计划中按单次动作消费物品、应用恢复/冷却收益,不再落回攻击型分支。
追加验收口径:
- 战斗中点击一次普通攻击 / 技能 / 物品 / 调息,不会直接把整场战斗过程跳过。
- 若敌人未被这一击打死,当前次结算结束后仍停留在战斗态,并刷新下一轮战斗选项。
- “普通攻击”不会显示成其他技能的动作与耗蓝结果。
6. 再追加修复(2026-04-27 夜间二次排查)
用户复测后又暴露出一类更隐蔽的问题:
- 面板展示层可能仍在显示战斗选项,但逻辑态
gameState.inBattle已短暂回落为false。 - 旧实现里,
src/hooks/rpg-runtime-story/choiceActions.ts只要看到inBattle === false,就会把battle_*/inventory_use重新分流回服务端 runtime。 - 这会让用户在“视觉上仍处于战斗”的窗口点击选项时,直接命中服务端快照结果,体感仍然是“点一下就把战斗过程跳过”。
本轮追加修正:
- 战斗直结算判定不再只依赖
gameState.inBattle。 - 只要当前选项本身属于
battle_*/inventory_use,并且任一战斗上下文仍然存在currentBattleNpcId、currentNpcBattleMode、存活敌人、当前故事仍在显示战斗选项 就必须继续走本地逐帧战斗链。 - 为此补充了回归测试,覆盖“
inBattle已短暂变成false,但战斗展示仍在”的残留窗口。
新增验收口径:
- 即使
inBattle出现一帧级别的提前回落,只要玩家看到的仍是战斗选项,点击后也不会跳回服务端直结算。 - 战斗面板残留显示期间点击
battle_*/inventory_use,仍应播放本地战斗过程,而不是直接跳到结果快照。