载入中...
搜索中...
未找到
Skill.cpp
浏览该文件的文档.
1#include "rpg/Skill.h"
2
3#include "common/Json.h"
4#include "common/Value.h"
6
7#include <algorithm>
8#include <cmath>
9#include <cstdint>
10#include <unordered_map>
11
12namespace eve::rpg {
13
15
16namespace {
17
18eve::definitions::DefinitionRegistry& canonicalSkillRegistry() {
20 return registry;
21}
22
23eve::Result<std::string> canonicalJson(const SkillDefinition& def) {
25 object.emplace("id", eve::Value(def.id));
26 object.emplace("cooldown", eve::Value(static_cast<double>(def.cooldown)));
27 object.emplace("castTime", eve::Value(static_cast<double>(def.castTime)));
28 object.emplace("targetType", eve::Value(def.targetType));
29
30 auto condition = encodeSkillCondition(def.castCondition);
32 object.emplace("castCondition", std::move(condition).takeValue());
33
34 eve::Value::Array effects;
35 for (const auto& effect : def.grantedEffects) effects.emplace_back(effect);
36 object.emplace("grantedEffects", eve::Value(std::move(effects)));
37
39 for (const auto& tag : def.tags) tags.emplace_back(tag);
40 object.emplace("tags", eve::Value(std::move(tags)));
41
43 for (const auto& [key, value] : def.extra) extra.emplace(key, eve::Value(value));
44 object.emplace("extra", eve::Value(std::move(extra)));
45
47 if (def.cost) {
48 for (const auto& item : def.cost->items()) {
49 costs.emplace_back(eve::Value(eve::Value::Object{
50 {"attribute", eve::Value(item.resource.value())},
51 {"amount", eve::Value(item.amount.value())},
52 }));
53 }
54 }
55 object.emplace("costs", eve::Value(std::move(costs)));
56 return eve::Value(std::move(object)).toJson();
57}
58
59} // namespace
60
61bool SkillDefinition::hasTag(const std::string &tag) const {
62 return std::find(tags.begin(), tags.end(), tag) != tags.end();
63}
64
65std::unordered_map<std::string, SkillDefinition> &table() {
66 static std::unordered_map<std::string, SkillDefinition> t;
67 return t;
68}
69
71
73 if (def.id.empty()) return;
74 auto json = canonicalJson(def);
75 if (!json) {
76 json.ignore("legacy skill registration cannot serialize invalid definition");
77 return;
78 }
79 auto& registry = canonicalSkillRegistry();
80 auto existing = registry.resolve("rpg.skill", def.id);
82 existing.ok() ? registry.replace("rpg.skill", def.id, 1, std::move(json).takeValue())
83 : existing.status().code() == eve::StatusCode::NotFound
84 ? registry.insert("rpg.skill", def.id, 1, std::move(json).takeValue())
86 if (!result) return;
87 table()[def.id] = def;
88}
89
90const SkillDefinition *SkillRegistry::find(const std::string &id) {
91 auto &t = table();
92 auto it = t.find(id);
93 return it == t.end() ? nullptr : &it->second;
94}
95
96bool SkillRegistry::remove(const std::string& id) {
97 auto& registry = canonicalSkillRegistry();
98 auto result = registry.remove("rpg.skill", id);
99 if (!result) return false;
100 return table().erase(id) > 0;
101}
102
104 auto& skills = table();
105 auto& registry = canonicalSkillRegistry();
106 for (const auto& [id, unused] : skills) {
107 (void)unused;
108 auto result = registry.remove("rpg.skill", id);
109 result.ignore("legacy SkillRegistry::clear removes canonical skill definitions");
110 }
111 skills.clear();
112}
113
114int SkillRegistry::count() { return int(table().size()); }
115
116namespace {
117
118SkillDefinition parseSkillObject(Value o, std::string* error) {
119 SkillDefinition def;
120 if (!o.isObject()) return def;
121 def.id = o.getString("id");
122 def.cooldown = o.getFloat("cooldown", 0.f);
123 def.castTime = o.getFloat("castTime", 0.f);
124 def.targetType = o.getString("targetType", "self");
125 def.grantedEffects = o.getStringArray("grantedEffects");
126 def.tags = o.getStringArray("tags");
127
128 const Value costs = o.get("costs");
129 std::vector<eve::resource::ResourceCost> costItems;
130 for (size_t i = 0; i < costs.size(); ++i) {
131 const Value co = costs.at(i);
132 const std::string resource = co.getString("attribute");
133 const double amount = co.getDouble("amount", 0.0);
134 // Keep the legacy JSON shape, but do not silently truncate a value
135 // into the canonical integral resource Amount type.
136 if (resource.empty()) continue;
137 if (!std::isfinite(amount) || amount <= 0.0 || std::floor(amount) != amount || amount >= std::ldexp(1.0, 63)) {
138 if (error) *error = "skill cost must be a finite positive integer: " + resource;
139 return {};
140 }
141 auto item = eve::resource::ResourceCost::create(resource, static_cast<std::int64_t>(amount));
142 if (!item) {
143 if (error) *error = item.status().describe();
144 return {};
145 }
146 costItems.push_back(std::move(item).takeValue());
147 }
148 if (!costItems.empty()) {
149 auto canonical = eve::resource::CostSpec::create(std::move(costItems));
150 if (!canonical) {
151 if (error) *error = canonical.status().describe();
152 return {};
153 }
154 def.cost = std::move(canonical).takeValue();
155 }
156
157 def.extra = o.getStringMap("extra");
158 return def;
159}
160
161} // namespace
162
163int SkillRegistry::loadFromJson(const std::string &json, std::string *error) {
164 std::string err;
165 const eve::json::Document doc = eve::json::Document::parse(json, &err);
166 if (!doc.valid()) {
167 if (error) *error = err.empty() ? "invalid json" : err;
168 return 0;
169 }
170
171 const Value root = doc.root();
172 int n = 0;
173 if (root.isArray()) {
174 for (size_t i = 0; i < root.size(); ++i) {
175 SkillDefinition def = parseSkillObject(root.at(i), error);
176 if (def.id.empty()) continue;
177 registerSkill(def);
178 ++n;
179 }
180 } else if (root.isObject()) {
181 SkillDefinition def = parseSkillObject(root, error);
182 if (!def.id.empty()) {
183 registerSkill(def);
184 ++n;
185 }
186 }
187 return n;
188}
189
190} // namespace eve::rpg
double value
int root
Definition AnimSmr.cpp:119
eve::resource::CostSpec cost
eve::Value condition
std::uint32_t key
glm::vec3 n
Definition Grass.cpp:63
const std::string * tag
std::string error
Definition Package.cpp:60
std::unique_ptr< ParticleEffect > effect
float t
std::string resource
RPG-owned Value codec for the shared decision condition tree.
Json object
float size
Definition TreeMesh.cpp:156
Owning, renderer-independent dynamic values.
Move-only operation result carrying either a value or Status.
Definition Result.h:155
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:255
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
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
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Deterministic registry of topic-neutral, versioned JSON definitions.
Definition Definitions.h:58
EVENGINE_API_FOUNDATION public API.
Definition Json.h:136
bool valid() const
Valid.
Definition Json.h:157
static Document parse(const std::string &text, std::string *error=nullptr)
Parse.
Definition Json.cpp:581
Value root() const
Root.
Definition Json.h:159
EVENGINE_API_FOUNDATION public API.
Definition Json.h:38
bool isObject() const
True when object.
Definition Json.cpp:331
Value get(const char *key) const
Returns the value.
Definition Json.cpp:336
size_t size() const
Returns the size of .
Definition Json.cpp:351
Value at(size_t index) const
At.
Definition Json.cpp:358
float getFloat(const char *key, float fallback=0.f) const
Returns the float.
Definition Json.cpp:423
std::string getString(const char *key, const std::string &fallback={}) const
Returns the string.
Definition Json.cpp:427
std::vector< std::string > getStringArray(const char *key) const
Returns the string array.
Definition Json.cpp:439
double getDouble(const char *key, double fallback=0.0) const
Returns the double.
Definition Json.cpp:424
std::unordered_map< std::string, std::string > getStringMap(const char *key) const
Definition Json.cpp:461
static eve::Result< CostSpec > create(std::vector< ResourceCost > items)
Validate and canonicalize resource items.
static const SkillDefinition * find(const std::string &id)
Find a registered skill definition by id.
Definition Skill.cpp:90
static int loadFromJson(const std::string &json, std::string *error=nullptr)
从 JSON 数组批量注册技能定义,返回成功注册数量。元素形如: { "id": "fireball", "cooldown": 4, "castTime": 1....
Definition Skill.cpp:163
static eve::definitions::DefinitionRegistry & definitionRegistry()
Return the common registry that owns canonical skill JSON.
Definition Skill.cpp:70
static void registerSkill(const SkillDefinition &def)
Definition Skill.cpp:72
static bool remove(const std::string &id)
Removes .
Definition Skill.cpp:96
static void clear()
Clears .
Definition Skill.cpp:103
static int count()
Returns the number of .
Definition Skill.cpp:114
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
eve::Result< eve::Value > encodeSkillCondition(const eve::decision::Condition &condition)
Encode one validated Skill condition tree as an owning Value.
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
static eve::Result< ResourceCost > create(std::string_view resourceId, std::int64_t amountValue)
Construct and validate one cost item.
SkillDefinition public API.
Definition Skill.h:28
std::vector< std::string > tags
Definition Skill.h:36
float castTime
0 = 瞬发
Definition Skill.h:31
bool hasTag(const std::string &tag) const
True when tag.
Definition Skill.cpp:61
std::string targetType
Definition Skill.h:32
std::vector< std::string > grantedEffects
命中/释放后施加给目标的效果 id 列表
Definition Skill.h:35
std::unordered_map< std::string, std::string > extra
游戏自定义附加数据
Definition Skill.h:39
std::optional< eve::resource::CostSpec > cost
Canonical resource cost; AttributeSet is an adapter, not a second cost model.
Definition Skill.h:34