后端重写提交
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
# M4 module-combat SpacetimeDB 基线设计(2026-04-21)
|
||||
|
||||
更新时间:`2026-04-22`
|
||||
|
||||
## 0. 文档目标
|
||||
|
||||
本文件只冻结一件事:
|
||||
|
||||
**把 `module-combat` 从“只有 README 占位”推进到“首版 battle_state 与 resolve_combat_action 可真实编码、可编译、可继续扩展”的工程基线。**
|
||||
|
||||
本轮不宣称完成完整 `runtime story action` 迁移,也不把 `inventory / npc / story AI 续写` 直接耦进战斗 reducer;跨子域写入继续收敛在 `spacetime-module` 聚合层。
|
||||
|
||||
---
|
||||
|
||||
## 1. 本轮落地范围
|
||||
|
||||
本轮只做下面 5 件事:
|
||||
|
||||
1. 新增 `server-rs/crates/module-combat/` 真实 crate。
|
||||
2. 冻结 `battle_state` 的首版领域类型、枚举、输入结构与字段校验 helper。
|
||||
3. 冻结 `resolve_combat_action` 的首版输入、输出与纯规则推进逻辑。
|
||||
4. 在 `server-rs/crates/spacetime-module/` 中新增 `battle_state` 表。
|
||||
5. 在 `spacetime-module` 中新增 `create_battle_state`、`resolve_combat_action` 两个 reducer。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前冻结的实现边界
|
||||
|
||||
### 2.1 首版必须支持的战斗 function
|
||||
|
||||
首版与 [../prd/AI_NATIVE_BATTLE_SINGLE_ACTION_FUNCTION_PRD_2026-04-18.md](../prd/AI_NATIVE_BATTLE_SINGLE_ACTION_FUNCTION_PRD_2026-04-18.md) 保持一致,只支持以下单行为入口:
|
||||
|
||||
1. `battle_attack_basic`
|
||||
2. `battle_recover_breath`
|
||||
3. `battle_use_skill`
|
||||
4. `battle_escape_breakout`
|
||||
5. 旧兼容攻击类:
|
||||
- `battle_all_in_crush`
|
||||
- `battle_guard_break`
|
||||
- `battle_probe_pressure`
|
||||
- `battle_feint_step`
|
||||
- `battle_finisher_window`
|
||||
|
||||
本轮刻意不接入:
|
||||
|
||||
1. `inventory_use`
|
||||
2. 技能与物品的正式外部明细读取
|
||||
3. 与 `quest_record`、`npc_state` 的联动写入
|
||||
4. 脱战后 `story_event` 追加与 AI 续写触发
|
||||
|
||||
### 2.2 为什么先不做 `inventory_use`
|
||||
|
||||
当前 Rust 侧还没有 `inventory_slot` 正式表,也没有稳定的战斗内物品快照输入。
|
||||
|
||||
如果现在把 `inventory_use` 硬塞进 `module-combat`,只会出现两种坏结果:
|
||||
|
||||
1. reducer 内部引入并不存在的 inventory 真相依赖;
|
||||
2. 退回成“让 Axum 先算完再写 battle_state”的伪迁移。
|
||||
|
||||
因此本轮明确冻结为:
|
||||
|
||||
1. `module-combat` 先完成纯战斗状态推进;
|
||||
2. `inventory_use` 留到 `inventory_slot` 与 runtime snapshot projection 口径稳定后再接。
|
||||
|
||||
---
|
||||
|
||||
## 3. `battle_state` 首版字段
|
||||
|
||||
首版 `battle_state` 冻结为以下字段:
|
||||
|
||||
1. `battle_state_id`
|
||||
2. `story_session_id`
|
||||
3. `runtime_session_id`
|
||||
4. `actor_user_id`
|
||||
5. `target_npc_id`
|
||||
6. `target_name`
|
||||
7. `battle_mode`
|
||||
8. `status`
|
||||
9. `player_hp`
|
||||
10. `player_max_hp`
|
||||
11. `player_mana`
|
||||
12. `player_max_mana`
|
||||
13. `target_hp`
|
||||
14. `target_max_hp`
|
||||
15. `chapter_id`
|
||||
16. `experience_reward`
|
||||
17. `reward_items`
|
||||
18. `turn_index`
|
||||
19. `last_action_function_id`
|
||||
20. `last_action_text`
|
||||
21. `last_result_text`
|
||||
22. `last_damage_dealt`
|
||||
23. `last_damage_taken`
|
||||
24. `last_outcome`
|
||||
25. `version`
|
||||
26. `created_at`
|
||||
27. `updated_at`
|
||||
|
||||
### 3.1 设计意图
|
||||
|
||||
首版只解决下面这些真相问题:
|
||||
|
||||
1. 当前战斗是否存在、是否仍在进行中;
|
||||
2. 玩家与当前目标的 HP / MP 最小数值状态;
|
||||
3. 当前是 `fight` 还是 `spar`;
|
||||
4. 当前战斗归属哪个章节;
|
||||
5. 本场战斗若胜利应发多少经验;
|
||||
6. 本场战斗若胜利应发哪些已编译好的 reward item;
|
||||
7. 最近一次动作结算了什么;
|
||||
8. 当前 battle reducer 是否发生过版本推进。
|
||||
|
||||
### 3.2 当前刻意不放入的字段
|
||||
|
||||
本轮明确不放:
|
||||
|
||||
1. 多目标列表
|
||||
2. 技能冷却 map
|
||||
3. build buff 详情
|
||||
4. 掉落预算、好感预算、剧情上下文大对象
|
||||
5. 大型 `rawGameState` 镜像字段
|
||||
|
||||
原因很直接:这些都属于后续跨子域联动层,不适合在 `battle_state` 首版里重新堆一个大 JSON。
|
||||
|
||||
---
|
||||
|
||||
## 4. 枚举与动作口径
|
||||
|
||||
### 4.1 `BattleMode`
|
||||
|
||||
只保留两种:
|
||||
|
||||
1. `Fight`
|
||||
2. `Spar`
|
||||
|
||||
### 4.2 `BattleStatus`
|
||||
|
||||
只保留三种:
|
||||
|
||||
1. `Ongoing`
|
||||
2. `Resolved`
|
||||
3. `Aborted`
|
||||
|
||||
说明:
|
||||
|
||||
1. `Resolved` 表示战斗已正常收束,包括胜利、切磋结束、成功逃脱。
|
||||
2. `Aborted` 预留给后续 session 中断、外部清理、投影回滚等异常收束场景。
|
||||
|
||||
### 4.3 `CombatOutcome`
|
||||
|
||||
首版冻结:
|
||||
|
||||
1. `Ongoing`
|
||||
2. `Victory`
|
||||
3. `SparComplete`
|
||||
4. `Escaped`
|
||||
|
||||
这与当前共享契约里的 `RuntimeBattlePresentation.outcome` 一致,避免首版就制造新的枚举翻译成本。
|
||||
|
||||
---
|
||||
|
||||
## 5. `resolve_combat_action` 首版规则
|
||||
|
||||
### 5.1 输入
|
||||
|
||||
首版 reducer 输入只包含:
|
||||
|
||||
1. `battle_state_id`
|
||||
2. `function_id`
|
||||
3. `action_text`
|
||||
4. `base_damage`
|
||||
5. `mana_cost`
|
||||
6. `heal`
|
||||
7. `mana_restore`
|
||||
8. `counter_multiplier`
|
||||
9. `updated_at_micros`
|
||||
|
||||
### 5.2 为什么允许输入 `base_damage`
|
||||
|
||||
本轮 `module-combat` 的职责是把战斗推进规则固定到 SpacetimeDB。
|
||||
|
||||
但玩家技能、装备 build、物品 buff、成长曲线这些正式真相仍未迁完,因此首版允许上游把已算好的 `base_damage / mana_cost / heal / mana_restore` 作为确定输入传进 reducer。
|
||||
|
||||
这意味着当前模块边界是:
|
||||
|
||||
1. `module-combat` 负责状态推进、反击、逃跑、战斗收束规则;
|
||||
2. 更高层的 build / skill / item 数值来源仍可在后续模块中逐步收敛;
|
||||
3. 等 `inventory / progression / runtime build` 真相表稳定后,再继续把这些输入收得更窄。
|
||||
|
||||
### 5.3 动作规则
|
||||
|
||||
#### A. `battle_escape_breakout`
|
||||
|
||||
直接结束战斗:
|
||||
|
||||
1. `status = Resolved`
|
||||
2. `last_outcome = Escaped`
|
||||
3. `last_damage_dealt = 0`
|
||||
4. `last_damage_taken = 0`
|
||||
|
||||
#### B. `battle_recover_breath`
|
||||
|
||||
恢复类动作:
|
||||
|
||||
1. 玩家回复 `heal`
|
||||
2. 玩家回复 `mana_restore`
|
||||
3. 若战斗仍持续,则按 `counter_multiplier` 吃一次敌方反击
|
||||
|
||||
#### C. `battle_attack_basic` / 旧兼容攻击类 / `battle_use_skill`
|
||||
|
||||
攻击类动作:
|
||||
|
||||
1. 目标扣除 `base_damage`
|
||||
2. 若目标已收束,则按 `battle_mode` 进入 `Victory / SparComplete`
|
||||
3. 若目标未收束,则玩家按 `counter_multiplier` 吃一次敌方反击
|
||||
|
||||
### 5.4 反击规则
|
||||
|
||||
首版固定:
|
||||
|
||||
1. `fight` 下敌方基础反击伤害 = `max(4, round(target_max_hp * 0.14 * counter_multiplier))`
|
||||
2. `spar` 下敌方基础反击伤害固定为 `1`
|
||||
|
||||
这是对当前 Node 逻辑的直接收敛,先保证行为方向不漂移,不在本轮发明新的战斗公式。
|
||||
|
||||
### 5.5 HP 下限规则
|
||||
|
||||
1. `fight` 下正常下限为 `0`
|
||||
2. `spar` 下双方 HP 最低保留为 `1`
|
||||
|
||||
这样能保留当前“切磋点到为止”的旧行为,不把 `spar` 错结算成死亡战斗。
|
||||
|
||||
---
|
||||
|
||||
## 6. `spacetime-module` 接线口径
|
||||
|
||||
### 6.1 battle_state 表
|
||||
|
||||
`spacetime-module` 首版只新增一张 private 真相表:
|
||||
|
||||
1. `battle_state`
|
||||
|
||||
建议索引:
|
||||
|
||||
1. `by_story_session_id`
|
||||
2. `by_runtime_session_id`
|
||||
3. `by_actor_user_id`
|
||||
|
||||
### 6.2 reducer
|
||||
|
||||
当前仍只保留两个战斗 reducer:
|
||||
|
||||
1. `create_battle_state`
|
||||
2. `resolve_combat_action`
|
||||
|
||||
职责:
|
||||
|
||||
1. `create_battle_state` 只负责插入 battle 真相,不负责故事会话编排。
|
||||
2. `resolve_combat_action` 负责推进 battle 真相。
|
||||
3. 当 `Victory` 收束时,由 `spacetime-module` 聚合层继续把 `experience_reward` 联动写入 `player_progression / chapter_progression`。
|
||||
4. 当 `Victory` 收束且 `reward_items` 非空时,由 `spacetime-module` 聚合层继续把战利品写入 `inventory_slot`。
|
||||
5. `resolve_combat_action` 仍不负责 AI 续写和 quest signal 全量分发。
|
||||
|
||||
---
|
||||
|
||||
## 7. 与后续子域的边界
|
||||
|
||||
### 7.1 与 `story`
|
||||
|
||||
当前关系:
|
||||
|
||||
1. `story` 负责更高层 action 路由与后续 story_event 追加;
|
||||
2. `combat` 只返回 battle 真相推进结果。
|
||||
|
||||
后续再补:
|
||||
|
||||
1. 战斗结束时的 `story_event`
|
||||
2. 脱战后的 `continue_story` / `resolve_story_action`
|
||||
|
||||
### 7.2 与 `inventory`
|
||||
|
||||
当前不直接耦合到 `module-combat` reducer。
|
||||
|
||||
后续再补:
|
||||
|
||||
1. 战斗内 `inventory_use`
|
||||
2. 消耗品扣减
|
||||
3. 战斗 buff 写入
|
||||
|
||||
当前已存在的聚合层联动:
|
||||
|
||||
1. `Victory` 时可把 `battle_state.reward_items` 写入 `inventory_slot`
|
||||
|
||||
### 7.3 与 `progression`
|
||||
|
||||
当前不直接在 `module-combat` reducer 内发经验与等级变更。
|
||||
|
||||
后续再补:
|
||||
|
||||
1. hostile scaling 与 reward 编译口径
|
||||
|
||||
当前已存在的聚合层联动:
|
||||
|
||||
1. `fight_victory` 的经验发放
|
||||
2. 章节账本写入
|
||||
|
||||
### 7.4 与 `npc`
|
||||
|
||||
当前不直接改好感。
|
||||
|
||||
后续再补:
|
||||
|
||||
1. `spar_complete` 的 affinity 变化
|
||||
2. `fight / spar` 与 encounter 状态同步
|
||||
|
||||
---
|
||||
|
||||
## 8. 本轮验收口径
|
||||
|
||||
满足以下条件,视为本轮 `module-combat` 基线完成:
|
||||
|
||||
1. `server-rs/crates/module-combat` 已从 README 占位升级为真实 crate。
|
||||
2. `battle_state`、`BattleMode`、`BattleStatus`、`CombatOutcome`、`ResolveCombatActionInput` 已冻结到代码。
|
||||
3. `spacetime-module` 已新增 `battle_state` 表。
|
||||
4. `spacetime-module` 已新增 `create_battle_state` 与 `resolve_combat_action` reducer。
|
||||
5. `cargo check -p module-combat -p spacetime-module` 通过。
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步建议
|
||||
|
||||
在本轮基线稳定后,下一步按以下顺序推进最稳:
|
||||
|
||||
1. 设计 `inventory_slot` 与战斗内 `inventory_use` 的最小真相输入。
|
||||
2. 设计 `resolve_story_action` 如何编排 `story + combat + npc + quest + inventory`。
|
||||
3. 把 `battle_state` 结束事件接入 `story_event`。
|
||||
4. 再把 Axum facade 与 `RuntimeStoryActionResponse.battle` 真正打通。
|
||||
Reference in New Issue
Block a user