Files
Genarrative/docs/technical/M4_COMBAT_REWARD_INVENTORY_INTEGRATION_2026-04-22.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

4.4 KiB
Raw Permalink Blame History

M4 Combat Reward Inventory Integration2026-04-22

更新时间:2026-04-22

0. 文档目标

本文件只冻结一件事:

resolve_combat_action(Victory) 从“只发经验”推进到“经验与战利品可在同一事务内结算”的最小主链口径。

本轮不回收完整 runtime item 导演层,也不在 module-combat 内直接做 AI 语义生成;只承接已经编译好的 reward item 快照。


1. 本轮落地范围

本轮只落实下面 4 件事:

  1. module-combat 中为 battle_state 补充 reward_items 字段。
  2. 允许 BattleStateInput 在初始化时携带已经编译好的战利品快照。
  3. spacetime-module::resolve_combat_action 中,当结果为 Victory 时同步把 reward_items 写入 inventory_slot
  4. 保持 module-combat 仍然是纯规则 crate不直接依赖 module-inventory

2. 当前冻结的战利品口径

2.1 battle_state.reward_items

首版字段固定复用 module-runtime-item::RuntimeItemRewardItemSnapshot

原因:

  1. 宝箱链已经用这套 reward item contract 打通到 inventory_slot
  2. 任务奖励当前仍有独立 QuestRewardItem,但战斗奖励更接近 runtime item 导演层。
  3. 先复用现有 reward item 快照,避免本轮再发明第三套 combat 专属掉落结构。

2.2 battle 初始化来源

当前 battle_state.reward_items 不在战斗 reducer 内生成,只允许由上游在创建 battle 时传入:

  1. resolve_npc_battle_interaction_and_return
  2. 后续 Axum façade / runtime story orchestration
  3. 其它明确的 battle create 聚合入口

也就是说:

  1. module-combat 只消费已确定的 reward item 快照
  2. 不在 reducer 内做随机、提示词、外部世界图谱推导

当前已接通:

  1. resolve_npc_battle_interaction_and_return
  2. POST /api/story/npc/battle
  3. POST /api/story/battles

这几条入口都只负责透传已编译奖励,不负责现场生成掉落。


3. Victory 发物规则

resolve_combat_action 结算结果满足:

  1. outcome == Victory

spacetime-module 需要继续执行:

  1. experience_reward > 0 时写 player_progression / chapter_progression
  2. reward_items.len() > 0 时写 inventory_slot

3.1 发物方式

当前固定规则:

  1. 每个 reward item 显式映射成一条 InventoryMutation::GrantItem
  2. source_kind = CombatDrop
  3. source_reference_id = battle_state_id
  4. 同一 battle_state_id 只允许发放一次

3.2 幂等约束

本轮先采用与 quest / treasure 一致的“按来源引用查重”思路:

  1. 若当前 actor 的 inventory_slot 中已经存在 source_reference_id = battle_state_id
  2. 视为该 battle reward 已发放
  3. Victory 再次重放时跳过发物,但不影响 battle_state 已收束结果

4. 与既有链路的边界

4.1 与 module-combat

module-combat 继续只负责:

  1. battle_state 结构
  2. resolve_combat_action 状态推进
  3. 胜负结果收束

不负责:

  1. inventory 写表
  2. progression 写表
  3. runtime item 生成

4.2 与 module-runtime-item

本轮不把战斗奖励映射 helper 上提到 module-runtime-item

原因:

  1. 当前 RuntimeItemRewardItemSnapshot -> InventoryItemSnapshot 的 helper 语义固定为 TreasureReward
  2. 若直接复用,会把 source_kind 写错成 TreasureReward
  3. 本轮先在 spacetime-module 里补一个 combat 专用映射,后续再统一抽象

5. 当前刻意未做

本轮明确不做下面这些扩张:

  1. 不把 Node 版 monster_drop AI 导演层整体迁到 Rust
  2. 不在 resolve_npc_battle_interaction_and_return 里现场计算掉落
  3. 不处理 battle reward 的货币、好感、情报
  4. 不处理战斗内 inventory_use
  5. 不把掉落展示或 Battle Reward 面板接到前端

6. 验证要求

本轮完成后至少执行:

  1. npm run check:encoding
  2. cargo test -p module-combat --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml
  3. cargo check -p module-combat -p spacetime-module --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml

7. 下一步建议

按当前节奏,后续应继续按下面顺序推进:

  1. 把 Node 侧 monster_drop runtime item 编译逻辑迁到 Rust 聚合层。
  2. 视复用收益决定是否把 battle / treasure 的 reward item 归一化 helper 上提到 module-runtime-item
  3. 最后再把 battle reward 展示、story patch 和前端接口切到新后端。