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