载入中...
搜索中...
未找到
Tracker.cpp
浏览该文件的文档.
1#include "rpg/Tracker.h"
2
3#include "rpg/Quest.h"
4#include "rpg/QuestSystem.h"
5#include "common/Value.h"
6
7#include <algorithm>
8#include <unordered_set>
9#include <utility>
10
11namespace eve::rpg {
12
14
16
17bool Tracker::activate(const std::string &id) { return QuestSystem::activate(this, id); }
18
19bool Tracker::canActivate(const std::string &id) { return QuestSystem::canActivate(this, id); }
20
21std::string Tracker::canActivateReason(const std::string &id) {
22 return QuestSystem::canActivateReason(this, id);
23}
24
25void Tracker::notify(const std::string &topic, const std::string &target, int amount) {
26 QuestSystem::notify(this, topic, target, amount);
27}
28
29bool Tracker::claim(const std::string &id) { return QuestSystem::claim(this, id); }
30
31bool Tracker::reset(const std::string &id) { return QuestSystem::reset(this, id); }
32
33bool Tracker::abandon(const std::string &id) { return QuestSystem::abandon(this, id); }
34
35bool Tracker::fail(const std::string &id, const std::string &reason) {
36 return QuestSystem::fail(this, id, reason);
37}
38
40 polled = std::move(pending);
41 pending.clear();
42}
43
45 eve::Value::Array encodedEntries;
46 encodedEntries.reserve(order.size());
47 for (const auto &id : order) {
48 const auto found = entries.find(id);
49 if (found == entries.end())
51 eve::DiagnosticCode::InvariantViolation, "quest tracker order references a missing entry", id));
53 objectives.reserve(found->second.objectives.size());
54 for (const auto &objective : found->second.objectives) {
55 eve::Value::Object encodedObjective;
56 encodedObjective.emplace("current", eve::Value(objective.current));
57 encodedObjective.emplace("id", eve::Value(objective.id));
58 objectives.emplace_back(std::move(encodedObjective));
59 }
60 eve::Value::Object encodedEntry;
61 encodedEntry.emplace("id", eve::Value(found->second.id));
62 encodedEntry.emplace("objectives", eve::Value(std::move(objectives)));
63 encodedEntry.emplace("state", eve::Value(found->second.state));
64 encodedEntries.emplace_back(std::move(encodedEntry));
65 }
67 root.emplace("entries", eve::Value(std::move(encodedEntries)));
68 root.emplace("schema", eve::Value("eve.rpg.quest-tracker"));
69 root.emplace("version", eve::Value(1));
70 return eve::Value(std::move(root)).toJson();
71}
72
74 auto parsed = eve::Value::fromJson(json);
75 if (!parsed.ok()) return eve::Result<void>::failure(parsed.status());
76 const eve::Value &root = parsed.value();
77 auto fail = [](eve::DiagnosticCode code, std::string message, std::string path) {
79 };
80 if (!root.isObject())
81 return fail(eve::DiagnosticCode::ParseError, "quest tracker root must be an object", "$");
82 const eve::Value *schema = root.find("schema");
83 if (!schema || !schema->isString() || schema->asString() != "eve.rpg.quest-tracker")
84 return fail(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to RPG Tracker", "$.schema");
85 const eve::Value *version = root.find("version");
86 if (!version || !version->isInt64() || version->asInt() != 1)
87 return fail(eve::DiagnosticCode::UnknownVersion, "unsupported RPG Tracker snapshot version", "$.version");
88 const eve::Value *encodedEntries = root.find("entries");
89 if (!encodedEntries || !encodedEntries->isArray())
90 return fail(eve::DiagnosticCode::ParseError, "quest tracker entries must be an array", "$.entries");
91
92 Tracker candidate;
93 if (encodedEntries->arraySize() != candidate.order.size())
95 "quest tracker snapshot does not match the current quest registry", "$.entries");
96 std::unordered_set<std::string> seenEntries;
97 for (std::size_t index = 0; index < candidate.order.size(); ++index) {
98 const eve::Value &encoded = encodedEntries->at(index);
99 const std::string path = "$.entries[" + std::to_string(index) + "]";
100 if (!encoded.isObject())
101 return fail(eve::DiagnosticCode::ParseError, "quest tracker entry must be an object", path);
102 const eve::Value *id = encoded.find("id");
103 const eve::Value *state = encoded.find("state");
104 const eve::Value *objectives = encoded.find("objectives");
105 if (!id || !id->isString() || !state || !state->isString() || !objectives || !objectives->isArray())
106 return fail(eve::DiagnosticCode::ParseError, "quest tracker entry fields have invalid types", path);
107 if (!seenEntries.emplace(id->asString()).second)
108 return fail(eve::DiagnosticCode::Conflict, "quest tracker entry id is duplicated", path + ".id");
109 if (id->asString() != candidate.order[index])
111 "quest tracker entry order or id does not match the current registry", path + ".id");
112 auto entryIt = candidate.entries.find(id->asString());
113 if (entryIt == candidate.entries.end())
114 return fail(eve::DiagnosticCode::NotFound, "quest definition is not registered", path + ".id");
115 const std::string &stateName = state->asString();
116 if (stateName != "locked" && stateName != "inactive" && stateName != "active" &&
117 stateName != "ready" && stateName != "completed" && stateName != "failed")
118 return fail(eve::DiagnosticCode::InvalidArgument, "quest tracker state is invalid", path + ".state");
119 if (objectives->arraySize() != entryIt->second.objectives.size())
121 "quest objective list does not match the current definition", path + ".objectives");
122 bool allDone = true;
123 for (std::size_t objectiveIndex = 0; objectiveIndex < entryIt->second.objectives.size(); ++objectiveIndex) {
124 const eve::Value &encodedObjective = objectives->at(objectiveIndex);
125 const std::string objectivePath = path + ".objectives[" + std::to_string(objectiveIndex) + "]";
126 if (!encodedObjective.isObject())
127 return fail(eve::DiagnosticCode::ParseError, "quest objective must be an object", objectivePath);
128 const eve::Value *objectiveId = encodedObjective.find("id");
129 const eve::Value *current = encodedObjective.find("current");
130 auto &runtime = entryIt->second.objectives[objectiveIndex];
131 if (!objectiveId || !objectiveId->isString() || !current || !current->isInt64())
132 return fail(eve::DiagnosticCode::ParseError, "quest objective fields have invalid types", objectivePath);
133 if (objectiveId->asString() != runtime.id)
135 "quest objective id or order does not match the current definition", objectivePath + ".id");
136 if (current->asInt() < 0 || current->asInt() > runtime.count)
138 "quest objective progress is outside its definition bounds", objectivePath + ".current");
139 runtime.current = static_cast<int>(current->asInt());
140 runtime.done = runtime.current >= runtime.count;
141 allDone = allDone && runtime.done;
142 }
143 if ((stateName == "ready" || stateName == "completed") && !allDone)
145 "ready or completed quest must have every objective complete", path + ".state");
146 if ((stateName == "locked" || stateName == "inactive") &&
147 std::any_of(entryIt->second.objectives.begin(), entryIt->second.objectives.end(),
148 [](const ObjectiveRuntime &objective) { return objective.current != 0; }))
150 "locked or inactive quest cannot contain progress", path + ".state");
151 if (allDone && stateName != "ready" && stateName != "completed" && stateName != "failed")
153 "fully progressed quest must be ready, completed, or failed", path + ".state");
154 entryIt->second.state = stateName;
155 }
156 candidate.pending.clear();
157 candidate.polled.clear();
158 entries.swap(candidate.entries);
159 order.swap(candidate.order);
160 pending.swap(candidate.pending);
161 polled.swap(candidate.polled);
163}
164
165int Tracker::getCount() const { return int(entries.size()); }
166
167std::string Tracker::getId(int index) const {
168 if (index < 0 || size_t(index) >= order.size()) return {};
169 return order[size_t(index)];
170}
171
172std::string Tracker::getState(const std::string &id) const {
173 auto it = entries.find(id);
174 return it == entries.end() ? std::string{} : it->second.state;
175}
176
177bool Tracker::hasTag(const std::string &id, const std::string &tag) const {
178 const QuestDefinition *def = QuestRegistry::find(id);
179 return def && def->hasTag(tag);
180}
181
182std::string Tracker::getExtra(const std::string &id, const std::string &key) const {
183 const QuestDefinition *def = QuestRegistry::find(id);
184 return def ? def->getExtra(key, "") : std::string{};
185}
186
187int Tracker::getObjectiveCount(const std::string &id) const {
188 auto it = entries.find(id);
189 return it == entries.end() ? 0 : int(it->second.objectives.size());
190}
191
192std::string Tracker::getObjectiveId(const std::string &id, int index) const {
193 auto it = entries.find(id);
194 if (it == entries.end()) return {};
195 if (index < 0 || size_t(index) >= it->second.objectives.size()) return {};
196 return it->second.objectives[size_t(index)].id;
197}
198
199int Tracker::getObjectiveCurrent(const std::string &id, int index) const {
200 auto it = entries.find(id);
201 if (it == entries.end()) return 0;
202 if (index < 0 || size_t(index) >= it->second.objectives.size()) return 0;
203 return it->second.objectives[size_t(index)].current;
204}
205
206int Tracker::getObjectiveCountRequired(const std::string &id, int index) const {
207 auto it = entries.find(id);
208 if (it == entries.end()) return 0;
209 if (index < 0 || size_t(index) >= it->second.objectives.size()) return 0;
210 return it->second.objectives[size_t(index)].count;
211}
212
213bool Tracker::isObjectiveDone(const std::string &id, int index) const {
214 auto it = entries.find(id);
215 if (it == entries.end()) return false;
216 if (index < 0 || size_t(index) >= it->second.objectives.size()) return false;
217 return it->second.objectives[size_t(index)].done;
218}
219
220int Tracker::getRewardCount(const std::string &id) const {
221 const QuestDefinition *def = QuestRegistry::find(id);
222 return def ? int(def->rewards.size()) : 0;
223}
224
225std::string Tracker::getRewardType(const std::string &id, int i) const {
226 const QuestDefinition *def = QuestRegistry::find(id);
227 if (!def || i < 0 || size_t(i) >= def->rewards.size()) return {};
228 return def->rewards[size_t(i)].type;
229}
230
231std::string Tracker::getRewardId(const std::string &id, int i) const {
232 const QuestDefinition *def = QuestRegistry::find(id);
233 if (!def || i < 0 || size_t(i) >= def->rewards.size()) return {};
234 return def->rewards[size_t(i)].id;
235}
236
237double Tracker::getRewardAmount(const std::string &id, int i) const {
238 const QuestDefinition *def = QuestRegistry::find(id);
239 if (!def || i < 0 || size_t(i) >= def->rewards.size()) return 0.0;
240 return def->rewards[size_t(i)].amount;
241}
242
243int Tracker::getEventCount() const { return int(polled.size()); }
244
245std::string Tracker::getEventEntryId(int i) const {
246 if (i < 0 || size_t(i) >= polled.size()) return {};
247 return polled[size_t(i)].entryId;
248}
249
250std::string Tracker::getEventObjectiveId(int i) const {
251 if (i < 0 || size_t(i) >= polled.size()) return {};
252 return polled[size_t(i)].objectiveId;
253}
254
255std::string Tracker::getEventAction(int i) const {
256 if (i < 0 || size_t(i) >= polled.size()) return {};
257 return polled[size_t(i)].action;
258}
259
260std::string Tracker::getEventTopic(int i) const {
261 if (i < 0 || size_t(i) >= polled.size()) return {};
262 return polled[size_t(i)].topic;
263}
264
265std::string Tracker::getEventTarget(int i) const {
266 if (i < 0 || size_t(i) >= polled.size()) return {};
267 return polled[size_t(i)].target;
268}
269
270int Tracker::getEventAmount(int i) const {
271 if (i < 0 || size_t(i) >= polled.size()) return 0;
272 return polled[size_t(i)].amount;
273}
274
275std::string Tracker::getEventReason(int i) const {
276 if (i < 0 || size_t(i) >= polled.size()) return {};
277 return polled[size_t(i)].reason;
278}
279
280} // namespace eve::rpg
LogicalId target
int root
Definition AnimSmr.cpp:119
std::string message
DiagnosticCode code
std::uint32_t key
const std::string * tag
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
目标追踪器的状态机与匹配行为。
任务 / 成就 / 教程的通用定义模板与进程级注册表。
bool found
double current
Battle::Objectives objectives
目标追踪器运行时实例(玩家日志 / 成就 / 教程可各建一份)。
uint32_t index
Owning, renderer-independent dynamic values.
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
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
Result< std::string > toJson() const
Serialize this value as deterministic compact JSON.
Definition Value.cpp:67
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
Definition Value.cpp:87
bool isInt64() const noexcept
Return true when this value is an Int64.
Definition Value.h:91
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::map< std::string, Value > Object
Definition Value.h:34
bool isArray() const noexcept
Return true when this value is an array.
Definition Value.h:97
bool isString() const noexcept
Return true when this value is a string.
Definition Value.h:95
std::int64_t asInt() const
Return the Int64 payload; the caller must have checked the kind.
Definition Value.cpp:69
std::vector< Value > Array
Definition Value.h:33
std::size_t arraySize() const
Return the number of array elements.
Definition Value.cpp:100
bool isObject() const noexcept
Return true when this value is an object.
Definition Value.h:99
Value & at(std::size_t index)
Return a bounds-checked array element.
Definition Value.cpp:106
Value * find(const std::string &key) noexcept
Return an object member, or nullptr when absent.
Definition Value.cpp:124
static const QuestDefinition * find(const std::string &id)
Find a registered quest definition by id.
Definition Quest.cpp:160
static bool canActivate(Tracker *t, const std::string &id, std::string *reason=nullptr)
是否可激活(谓词查询,可返回 reason)。
static bool reset(Tracker *t, const std::string &id)
进度清零并按当前 requires 回落 locked/inactive/active;不级联(compatibility facade (脚本兼容门面))。
static bool claim(Tracker *t, const std::string &id, std::string *reason=nullptr)
仅 state==ready 时成功 → completed 并触发解锁;否则 reject(compatibility facade (脚本兼容门面))。
static void syncAuto(Tracker *t)
补建 Registry 里尚不在 Tracker 上的条目,并解开前置已齐的 locked 条目。
static std::string canActivateReason(Tracker *t, const std::string &id)
不可激活的原因("" 可激活,否则 unknown/locked/alreadyActive/alreadyCompleted/failed/ready)。
static bool fail(Tracker *t, const std::string &id, const std::string &reason)
Fail.
static bool activate(Tracker *t, const std::string &id, std::string *reason=nullptr)
激活:仅 inactive 且前置已齐时成功;否则 push reject 并返回 false(compatibility facade (脚本兼容门面))。
static void notify(Tracker *t, const std::string &topic, const std::string &target, int amount)
报告事实 (topic, target, amount);只推进 state==active 的条目。
static bool abandon(Tracker *t, const std::string &id)
abandon/fail → failed;对 completed/failed/locked/未知 id 为 reject(compatibility facade (脚本兼容门面))。
一份任务进度 + 本实例事件队列。
Definition Tracker.h:26
int getObjectiveCountRequired(const std::string &id, int index) const
Returns the objective count required.
Definition Tracker.cpp:206
bool isObjectiveDone(const std::string &id, int index) const
True when objective done.
Definition Tracker.cpp:213
std::string getRewardType(const std::string &id, int i) const
Returns the reward type.
Definition Tracker.cpp:225
bool reset(const std::string &id)
重置进度(compatibility facade (脚本兼容门面))。
Definition Tracker.cpp:31
std::string getEventEntryId(int i) const
Returns the event entry id.
Definition Tracker.cpp:245
std::string getEventAction(int i) const
Returns the event action.
Definition Tracker.cpp:255
std::unordered_map< std::string, QuestRuntime > entries
运行时条目(QuestSystem 读写;公开以便测试与脚本查询)。
Definition Tracker.h:123
void syncAuto()
补建 Registry 里尚未出现的条目并按前置解锁。
Definition Tracker.cpp:15
std::string getExtra(const std::string &id, const std::string &key) const
Returns the extra.
Definition Tracker.cpp:182
bool abandon(const std::string &id)
放弃任务(compatibility facade (脚本兼容门面))。
Definition Tracker.cpp:33
int getEventAmount(int i) const
Returns the event amount.
Definition Tracker.cpp:270
int getRewardCount(const std::string &id) const
Returns the reward count.
Definition Tracker.cpp:220
std::vector< std::string > order
Definition Tracker.h:124
int getEventCount() const
Returns the event count.
Definition Tracker.cpp:243
std::string getEventTarget(int i) const
Returns the event target.
Definition Tracker.cpp:265
std::string getId(int index) const
Returns the id.
Definition Tracker.cpp:167
eve::Result< std::string > snapshotJson() const
Serialize persistent quest progress as deterministic versioned JSON.
Definition Tracker.cpp:44
Tracker()
Tracker.
Definition Tracker.cpp:13
int getObjectiveCount(const std::string &id) const
Returns the objective count.
Definition Tracker.cpp:187
bool activate(const std::string &id)
激活任务(compatibility facade (脚本兼容门面))。
Definition Tracker.cpp:17
std::string getEventObjectiveId(int i) const
Returns the event objective id.
Definition Tracker.cpp:250
bool hasTag(const std::string &id, const std::string &tag) const
True when tag.
Definition Tracker.cpp:177
bool canActivate(const std::string &id)
Can activate.
Definition Tracker.cpp:19
void pollEvents()
把 pending 移到 polled 并清空 pending;随后 getEvent* 读 polled。
Definition Tracker.cpp:39
eve::Result< void > restoreSnapshotJson(std::string_view json)
Validate and atomically restore quest progress against the current registry.
Definition Tracker.cpp:73
std::string getEventTopic(int i) const
Returns the event topic.
Definition Tracker.cpp:260
std::string getEventReason(int i) const
Returns the event reason.
Definition Tracker.cpp:275
std::vector< QuestEvent > pending
Definition Tracker.h:125
double getRewardAmount(const std::string &id, int i) const
Returns the reward amount.
Definition Tracker.cpp:237
bool claim(const std::string &id)
完成任务(compatibility facade (脚本兼容门面))。
Definition Tracker.cpp:29
bool fail(const std::string &id, const std::string &reason)
失败任务(compatibility facade (脚本兼容门面))。
Definition Tracker.cpp:35
void notify(const std::string &topic, const std::string &target, int amount)
Notify.
Definition Tracker.cpp:25
std::string getState(const std::string &id) const
Returns the state.
Definition Tracker.cpp:172
int getCount() const
Definition Tracker.cpp:165
std::string getRewardId(const std::string &id, int i) const
Returns the reward id.
Definition Tracker.cpp:231
std::vector< QuestEvent > polled
Definition Tracker.h:126
std::string getObjectiveId(const std::string &id, int index) const
Returns the objective id.
Definition Tracker.cpp:192
int getObjectiveCurrent(const std::string &id, int index) const
Returns the objective current.
Definition Tracker.cpp:199
std::string canActivateReason(const std::string &id)
Can activate reason.
Definition Tracker.cpp:21
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
单条目标的运行时进度。
Definition QuestTypes.h:33
数据驱动的任务模板。
Definition Quest.h:24
bool hasTag(const std::string &tag) const
True when tag.
Definition Quest.cpp:15
std::vector< RewardSpec > rewards
Definition Quest.h:33
std::string getExtra(const std::string &key, const std::string &fallback={}) const
Returns the extra.
Definition Quest.cpp:19