载入中...
搜索中...
未找到
SettlementEffectProjection.cpp
浏览该文件的文档.
2
4
5#include <cmath>
6#include <string>
7#include <utility>
8
9namespace eve::settlement {
10namespace {
11
12template <class T>
13eve::Result<T> invalid(std::string message, std::string path) {
16}
17
18const eve::Value* field(const eve::Value::Object& object, std::string_view name) {
19 const auto found = object.find(std::string(name));
20 return found == object.end() ? nullptr : &found->second;
21}
22
23eve::Result<std::string> stringField(const eve::Value::Object& object, std::string_view name, std::string path,
24 bool required = false) {
25 const auto* value = field(object, name);
26 if (value == nullptr) {
27 if (required) return invalid<std::string>("required string field is missing", std::move(path));
29 }
30 const auto* string = value->getIf<std::string>();
31 if (string == nullptr) return invalid<std::string>("field must be a string", std::move(path));
33}
34
35eve::Result<double> numberField(const eve::Value::Object& object, std::string_view name, double fallback,
36 std::string path) {
37 const auto* value = field(object, name);
38 if (value == nullptr) return eve::Result<double>::success(fallback);
39 double number = 0.0;
40 if (const auto* real = value->getIf<double>())
41 number = *real;
42 else if (const auto* integer = value->getIf<std::int64_t>())
43 number = static_cast<double>(*integer);
44 else
45 return invalid<double>("field must be a number", std::move(path));
46 if (!std::isfinite(number)) return invalid<double>("field must be finite", std::move(path));
48}
49
50eve::Result<std::vector<std::string>> stringsField(const eve::Value::Object& object, std::string_view name,
51 std::string path) {
52 const auto* value = field(object, name);
53 if (value == nullptr) return eve::Result<std::vector<std::string>>::success({});
54 const auto* array = value->getIf<eve::Value::Array>();
55 if (array == nullptr) return invalid<std::vector<std::string>>("field must be an array of strings", path);
56 std::vector<std::string> result;
57 result.reserve(array->size());
58 for (std::size_t index = 0; index < array->size(); ++index) {
59 const auto* string = (*array)[index].getIf<std::string>();
60 if (string == nullptr)
61 return invalid<std::vector<std::string>>("array item must be a string",
62 path + "[" + std::to_string(index) + "]");
63 result.push_back(*string);
64 }
65 return eve::Result<std::vector<std::string>>::success(std::move(result));
66}
67
68eve::Result<StageKind> parseStage(std::string_view value, std::string path) {
70 if (value == "source_modifiers") return eve::Result<StageKind>::success(StageKind::SourceModifiers);
71 if (value == "target_mitigation") return eve::Result<StageKind>::success(StageKind::TargetMitigation);
74 return invalid<StageKind>("unknown settlement rule stage", std::move(path));
75}
76
77eve::Result<RuleOperation> parseOperation(std::string_view value, std::string path) {
88 return invalid<RuleOperation>("unknown settlement rule operation", std::move(path));
89}
90
91eve::Result<eve::Value::Object> ruleObject(const effects::EffectInstance& effect, std::string path) {
92 const auto* encoded = field(effect.payload.object(), "settlement.rule");
93 if (encoded == nullptr) return eve::Result<eve::Value::Object>::success({});
94 if (const auto* object = encoded->getIf<eve::Value::Object>())
96 if (const auto* json = encoded->getIf<std::string>()) {
97 auto parsed = eve::Value::fromJson(*json);
98 if (!parsed) return eve::Result<eve::Value::Object>::failure(parsed.status());
99 auto value = std::move(parsed).takeValue();
100 const auto* object = value.getIf<eve::Value::Object>();
101 if (object == nullptr) return invalid<eve::Value::Object>("settlement.rule JSON must be an object", path);
103 }
104 return invalid<eve::Value::Object>("settlement.rule must be an object or JSON object string", std::move(path));
105}
106
107} // namespace
108
110 if (scope.empty()) return invalid<SettlementRuleSet>("effect rule scope must not be empty", "scope");
111 std::vector<SettlementRule> rules;
112 for (int index = 0; index < effects.effectCount(); ++index) {
113 const auto* effect = effects.effectAt(index);
114 if (effect == nullptr)
115 return invalid<SettlementRuleSet>("effect container returned an empty active slot",
116 "effects[" + std::to_string(index) + "]");
117 if (!effect->payload.has("settlement.rule")) continue;
118 const auto path = "effects[" + std::to_string(index) + "].payload.settlement.rule";
119 auto objectResult = ruleObject(*effect, path);
120 if (!objectResult) return eve::Result<SettlementRuleSet>::failure(objectResult.status());
121 auto object = std::move(objectResult).takeValue();
122
123 auto stageName = stringField(object, "stage", path + ".stage", true);
124 if (!stageName) return eve::Result<SettlementRuleSet>::failure(stageName.status());
125 auto operationName = stringField(object, "operation", path + ".operation", true);
126 if (!operationName) return eve::Result<SettlementRuleSet>::failure(operationName.status());
127 auto stage = parseStage(stageName.value(), path + ".stage");
128 if (!stage) return eve::Result<SettlementRuleSet>::failure(stage.status());
129 auto operation = parseOperation(operationName.value(), path + ".operation");
131 auto value = numberField(object, "value", effect->magnitude, path + ".value");
133 auto perStack = numberField(object, "value_per_extra_stack", 0.0, path + ".value_per_extra_stack");
134 if (!perStack) return eve::Result<SettlementRuleSet>::failure(perStack.status());
135 auto when = stringField(object, "when", path + ".when");
136 if (!when) return eve::Result<SettlementRuleSet>::failure(when.status());
137 auto kinds = stringsField(object, "kinds", path + ".kinds");
138 if (!kinds) return eve::Result<SettlementRuleSet>::failure(kinds.status());
139 auto required = stringsField(object, "required_tags", path + ".required_tags");
141 auto excluded = stringsField(object, "excluded_tags", path + ".excluded_tags");
142 if (!excluded) return eve::Result<SettlementRuleSet>::failure(excluded.status());
143
144 SettlementRule rule;
145 rule.id = std::string(scope) + "." + effect->id;
146 rule.source = effect->source.empty() ? effect->type : effect->source;
147 rule.stage = stage.value();
148 rule.priority = effect->priority;
149 rule.operation = operation.value();
150 rule.value = value.value();
151 rule.valuePerExtraStack = perStack.value();
152 rule.stacks = effect->stackCount;
153 rule.filter.kinds = std::move(kinds).takeValue();
154 rule.filter.requiredTags = std::move(required).takeValue();
155 rule.filter.excludedTags = std::move(excluded).takeValue();
156 rule.when = std::move(when).takeValue();
157 rules.push_back(std::move(rule));
158 }
159 SettlementRuleSet result;
160 auto configured = result.configure(std::move(rules));
161 if (!configured) return eve::Result<SettlementRuleSet>::failure(configured.status());
162 return eve::Result<SettlementRuleSet>::success(std::move(result));
163}
164
165} // namespace eve::settlement
ActionParameterOperation operation
double value
Owning effect storage and lifecycle-only executor.
std::string message
JobScope scope
bool required
std::string name
std::unique_ptr< ParticleEffect > effect
std::string path
Definition PlayHost.cpp:110
double number
bool found
Declarative, domain-neutral rules for buffs and gameplay effects.
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
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
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
std::vector< Value > Array
Definition Value.h:33
Owns active effect instances and their deterministic lifecycle events.
EffectInstance * effectAt(int index)
Returns an active instance by deterministic index, or nullptr. @ownership Borrowed from this containe...
int effectCount() const
Returns active instance count in deterministic creation order.
Validated immutable-style rule collection installable into pipelines.
eve::Result< void > configure(std::vector< SettlementRule > rules)
Atomically replace all rules after validating ids, operands and phases.
Result< int > invalid(std::string message)
Invalid.
const EditorValue * field(const EditorValue &value, const char *name)
eve::Result< SettlementRuleSet > projectEffectRules(const effects::EffectContainer &effects, std::string_view scope)
Project active effect payloads into an owning settlement rule snapshot.
std::vector< std::string > requiredTags
std::vector< std::string > kinds
std::vector< std::string > excludedTags
One owning, serializable projection of a buff/effect contribution.
std::string when
Optional side-effect-free expression compiled by configure().