4.5 KiB
4.5 KiB
M4 成长与 quest/combat 联动设计(2026-04-21)
更新时间:2026-04-21
0. 文档目标
本文件只冻结一件事:
把 player_progression / chapter_progression 从“可单独调用的成长基座”推进到“任务交付与战斗胜利可自动写入的最小联动闭环”。
本轮只落 turn_in_quest 和 resolve_combat_action(Victory) 两条经验链,不扩到完整章节蓝图 Rust 化、掉落分配、好感奖励或前端展示切换。
1. 本轮联动范围
本轮只接下面两条确定链路:
turn_in_quest成功后,把quest_record.reward.experience发放到player_progression。resolve_combat_action结算为Victory后,把battle_state.experience_reward发放到player_progression。
补充规则:
- 若存在
chapter_id,同时尝试把经验记到chapter_progression账本。 - 若对应
chapter_progression不存在,联动必须静默跳过,不能让任务交付或战斗结算失败。 SparComplete、Escaped、Ongoing都不发经验。
2. turn_in_quest 联动口径
2.1 经验来源
任务交付经验固定读取:
quest_record.reward.experience.unwrap_or(0)
2.2 成长写入
当经验值 > 0 时,spacetime-module::turn_in_quest 需要在任务状态切换为 TurnedIn 后调用:
upsert_player_progression_after_grant_tx
写入参数固定为:
user_id = next.actor_user_idamount = reward_experiencesource = PlayerProgressionGrantSource::Questupdated_at_micros = next.updated_at_micros
2.3 章节账本写入
若 next.chapter_id 存在,则在成长写入后继续尝试调用章节账本 helper:
granted_quest_xp = reward_experiencegranted_hostile_xp = 0hostile_defeat_increment = 0level_at_exit = Some(updated_player.level)
若章节记录不存在:
- 静默跳过
- 保留任务交付成功
- 不把“章节计划尚未初始化”视为任务错误
3. resolve_combat_action 联动口径
3.1 battle_state 新增字段
为避免在 reducer 里临时反查外部上下文,本轮给 BattleStateInput / BattleStateSnapshot / battle_state 表补两个最小字段:
chapter_id: Option<String>experience_reward: u32
设计意图:
chapter_id决定战斗胜利时是否记章节账本。experience_reward作为已编译好的确定奖励,避免本轮就把章节蓝图和敌对档位计算重新耦回 battle reducer。
3.2 胜利经验发放
当 resolve_combat_action 返回:
CombatOutcome::Victory
则 spacetime-module 需要继续执行:
upsert_player_progression_after_grant_tx
写入参数固定为:
user_id = result.snapshot.actor_user_idamount = result.snapshot.experience_rewardsource = PlayerProgressionGrantSource::HostileNpcupdated_at_micros = result.snapshot.updated_at_micros
补充规则:
- 只有
experience_reward > 0时才真正写成长表。 SparComplete不发经验,因为切磋不算敌对击杀。
3.3 章节账本写入
若 result.snapshot.chapter_id 存在,且本次为 Victory,则继续尝试:
granted_quest_xp = 0granted_hostile_xp = experience_rewardhostile_defeat_increment = 1level_at_exit = Some(updated_player.level)
同样地,若章节记录不存在:
- 静默跳过
- 仍保留 battle_state 的正常收束结果
4. reducer 分层约束
本轮保持以下分层不变:
module-combat仍只承接纯战斗状态推进,不直接依赖module-progression。module-quest仍只承接纯任务状态流转,不直接依赖module-progression。- 真正的跨域写入统一放在
crates/spacetime-modulereducer / transaction helper 中完成。
这样做的原因是:
- 领域 crate 保持纯规则,便于后续单测和重用。
- SpacetimeDB 事务内的表写顺序集中在同一层,避免跨 crate 重复持久化策略。
5. 本轮明确不做
本轮明确不扩到以下内容:
- 还不把 battle reward 在 reducer 内现场计算为经验值。
- 还不把
quest奖励里的物品、货币、好感奖励统一并入同一事务。 - 还不把
quest signal自动从战斗/剧情全量分发到任务系统。 - 还不把
chapter_progression缺失时自动补建计划记录。
6. 验证要求
本轮变更完成后,至少执行:
npm run check:encodingcargo test -p module-combatcargo test -p module-progressioncargo test -p module-questcargo check -p spacetime-module