载入中...
搜索中...
未找到
QuestReward.cpp
浏览该文件的文档.
1#include "rpg/QuestReward.h"
2
4#include "rpg/GameState.h"
5#include "rpg/Quest.h"
6#include "rpg/QuestSystem.h"
7#include "rpg/Tracker.h"
8
9#include <climits>
10#include <cmath>
11#include <optional>
12
13namespace eve::rpg {
14namespace {
15
16eve::Result<int> rewardFailure(eve::DiagnosticCode code, std::string message,
17 std::string path, std::string questId) {
19 code, message, path, {{"questId", questId}},
20 "rpg.quest-reward"));
21}
22
23} // namespace
24
26 inventory::Bag &bag, const std::string &questId) {
27 const QuestDefinition *definition = QuestRegistry::find(questId);
28 if (!definition)
29 return rewardFailure(eve::DiagnosticCode::NotFound, "quest reward claim references an unknown quest",
30 "questId", questId);
31 if (tracker.getState(questId) != "ready")
33 "quest must be ready before rewards can be claimed", "quest.state", questId);
34
35 std::vector<inventory::InventoryItemGrant> itemGrants;
36 itemGrants.reserve(definition->rewards.size());
37 for (std::size_t index = 0; index < definition->rewards.size(); ++index) {
38 const RewardSpec &reward = definition->rewards[index];
39 const std::string path = "quest.rewards[" + std::to_string(index) + "]";
40 if (reward.id.empty() || !std::isfinite(reward.amount) || reward.amount <= 0.0)
41 return rewardFailure(eve::DiagnosticCode::InvalidArgument,
42 "quest reward requires an id and finite positive amount", path, questId);
43 if (reward.type == "item") {
44 if (reward.amount > static_cast<double>(INT_MAX) || std::floor(reward.amount) != reward.amount)
45 return rewardFailure(eve::DiagnosticCode::InvalidArgument,
46 "item reward amount must be a positive integer", path + ".amount", questId);
47 itemGrants.push_back({reward.id, static_cast<int>(reward.amount)});
48 } else if (reward.type == "attribute") {
49 const double committed = gameState.getVariable(reward.id) + reward.amount;
50 if (!std::isfinite(committed))
51 return rewardFailure(eve::DiagnosticCode::InvalidArgument,
52 "attribute reward would produce a non-finite value", path + ".amount", questId);
53 } else {
54 return rewardFailure(eve::DiagnosticCode::Unsupported,
55 "quest reward type is not supported by the canonical claim transaction",
56 path + ".type", questId);
57 }
58 }
59
60 std::optional<inventory::PreparedInventoryAdd> preparedInventory;
61 if (!itemGrants.empty()) {
62 auto prepared = inventory::InventorySystem::prepareAddBatch(&bag, itemGrants);
63 if (!prepared.ok()) return eve::Result<int>::failure(prepared.status());
64 preparedInventory.emplace(std::move(prepared).takeValue());
65 }
66
67 auto gameStateBefore = gameState.snapshotJson();
68 if (!gameStateBefore.ok()) return eve::Result<int>::failure(gameStateBefore.status());
69 std::string gameStateSnapshot = std::move(gameStateBefore).takeValue();
70 auto entriesBefore = tracker.entries;
71 auto orderBefore = tracker.order;
72 auto pendingBefore = tracker.pending;
73 auto polledBefore = tracker.polled;
74
75 for (const RewardSpec &reward : definition->rewards)
76 if (reward.type == "attribute") gameState.addVariable(reward.id, reward.amount);
77
78 std::string claimReason;
79 if (!QuestSystem::claim(&tracker, questId, &claimReason)) {
80 auto restored = gameState.restoreSnapshotJson(gameStateSnapshot);
81 const bool restoredOk = restored.ok();
82 tracker.entries = std::move(entriesBefore);
83 tracker.order = std::move(orderBefore);
84 tracker.pending = std::move(pendingBefore);
85 tracker.polled = std::move(polledBefore);
86 if (!restoredOk)
87 return rewardFailure(eve::DiagnosticCode::InvariantViolation,
88 "quest reward rollback could not restore GameState", "rollback", questId);
89 return rewardFailure(eve::DiagnosticCode::Conflict,
90 "quest state changed before reward commit: " + claimReason,
91 "quest.state", questId);
92 }
93
94 if (preparedInventory) {
95 auto committed = inventory::InventorySystem::commitAddBatch(std::move(*preparedInventory));
96 if (!committed.ok()) {
97 auto restored = gameState.restoreSnapshotJson(gameStateSnapshot);
98 const bool restoredOk = restored.ok();
99 tracker.entries = std::move(entriesBefore);
100 tracker.order = std::move(orderBefore);
101 tracker.pending = std::move(pendingBefore);
102 tracker.polled = std::move(polledBefore);
103 if (!restoredOk)
104 return rewardFailure(eve::DiagnosticCode::InvariantViolation,
105 "quest reward rollback could not restore GameState", "rollback", questId);
106 return eve::Result<int>::failure(committed.status());
107 }
108 }
109
110 return eve::Result<int>::success(static_cast<int>(definition->rewards.size()),
112}
113
114} // namespace eve::rpg
全局游戏状态:开关(bool)、变量(数字)、独立变量(按作用域)。
std::string message
DiagnosticCode code
std::string path
Definition PlayHost.cpp:110
Atomic orchestration for claiming data-driven quest rewards.
目标追踪器的状态机与匹配行为。
任务 / 成就 / 教程的通用定义模板与进程级注册表。
目标追踪器运行时实例(玩家日志 / 成就 / 教程可各建一份)。
uint32_t index
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:480
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
格子型物品容器(脚本可直接操作)。
Definition Bag.h:21
static eve::Result< int > commitAddBatch(PreparedInventoryAdd prepared)
Commit one prepared addition and then publish its change events.
static eve::Result< PreparedInventoryAdd > prepareAddBatch(Bag *bag, const std::vector< InventoryItemGrant > &grants)
Validate a multi-item addition against a private candidate Bag.
一份游戏状态(开关/变量/独立变量)。
Definition GameState.h:25
double getVariable(const std::string &name) const
Returns the variable.
Definition GameState.cpp:62
void addVariable(const std::string &name, double delta)
Adds variable.
Definition GameState.cpp:67
eve::Result< void > restoreSnapshotJson(std::string_view json)
Validate and atomically restore a canonical RPG game-state JSON document.
eve::Result< std::string > snapshotJson() const
Serialize this state as the canonical versioned RPG game-state JSON document.
static const QuestDefinition * find(const std::string &id)
Find a registered quest definition by id.
Definition Quest.cpp:160
static eve::Result< int > claim(Tracker &tracker, GameState &gameState, inventory::Bag &bag, const std::string &questId)
Validate and atomically claim every reward declared by one ready quest.
static bool claim(Tracker *t, const std::string &id, std::string *reason=nullptr)
仅 state==ready 时成功 → completed 并触发解锁;否则 reject(compatibility facade (脚本兼容门面))。
一份任务进度 + 本实例事件队列。
Definition Tracker.h:26
std::unordered_map< std::string, QuestRuntime > entries
运行时条目(QuestSystem 读写;公开以便测试与脚本查询)。
Definition Tracker.h:123
std::vector< std::string > order
Definition Tracker.h:124
std::vector< QuestEvent > pending
Definition Tracker.h:125
std::string getState(const std::string &id) const
Returns the state.
Definition Tracker.cpp:172
std::vector< QuestEvent > polled
Definition Tracker.h:126
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
数据驱动的任务模板。
Definition Quest.h:24
奖励规格(定义侧数据)。引擎不发奖,仅提供查询。
Definition QuestTypes.h:26