载入中...
搜索中...
未找到
RTSEffects.cpp
浏览该文件的文档.
1#include "rts/RTSEffects.h"
2
3#include <algorithm>
4#include <cmath>
5#include <limits>
6#include <utility>
7
8namespace eve::rts {
9namespace {
10
11const char* kindTag(RTSEffectKind kind) {
12 switch (kind) {
13 case RTSEffectKind::Morale: return "rts:morale";
14 case RTSEffectKind::Suppression: return "rts:suppression";
15 case RTSEffectKind::ProductionLock: return "rts:production-lock";
16 }
17 return "rts:unknown";
18}
19
20} // namespace
21
23 if (!std::isfinite(target.morale) || target.morale < 0.0 || target.morale > 100.0)
25 "RTS morale must be in [0,100]", "target.morale"));
27}
28
30 const effects::EffectInstance& effect) const {
31 auto valid = validate(target);
32 if (!valid) return valid;
33 if (effect.hasTag("rts:production-lock")) target.productionLocked = true;
35}
36
38 effects::EffectUpdateSummary lifecycle) const {
39 auto valid = validate(target);
41 RTSEffectUpdate result;
42 result.lifecycle = std::move(lifecycle);
43 for (const auto& tick : result.lifecycle.periodicTicks) {
44 if (tick.stackCount == 0 || !std::isfinite(tick.magnitude) || tick.magnitude < 0.0)
46 eve::DiagnosticCode::InvalidArgument, "RTS periodic effect value is invalid", "periodic"));
47 const auto amount = tick.magnitude * static_cast<double>(tick.stackCount);
48 if (!std::isfinite(amount))
50 eve::DiagnosticCode::InvalidArgument, "RTS periodic effect overflows", "periodic"));
51 const auto tagged = [&tick](const char* tag) {
52 return std::find(tick.tags.begin(), tick.tags.end(), tag) != tick.tags.end();
53 };
54 if (tagged("rts:morale")) {
55 target.morale = std::max(0.0, target.morale - amount);
56 if (target.morale == 0.0) ++target.commandInterrupts;
57 } else if (tagged("rts:suppression")) {
58 if (target.suppression > std::numeric_limits<std::uint32_t>::max() - tick.stackCount)
60 eve::DiagnosticCode::InvalidArgument, "RTS suppression count overflows", "target.suppression"));
61 target.suppression += tick.stackCount;
62 } else if (tagged("rts:production-lock")) {
63 target.productionLocked = true;
64 ++result.commandInterrupts;
65 }
66 ++result.settled;
67 }
69}
70
73 if (definition.id.empty() || !subject.isValid())
75 eve::DiagnosticCode::InvalidArgument, "RTS effect requires an id and valid subject", "effect"));
77 common.id = definition.id;
78 common.stackKey = definition.id;
79 common.duration = definition.duration;
80 common.period = definition.period;
81 common.magnitude = definition.magnitude;
82 common.policy = definition.policy;
83 common.payload.setNumber("speedMultiplier", definition.speedMultiplier);
84 common.payload.setNumber("damageMultiplier", definition.damageMultiplier);
85 common.payload.setNumber("incomingDamageMultiplier", definition.incomingDamageMultiplier);
86 common.payload.setNumber("healingPerSecond", definition.healingPerSecond);
87 common.tags = definition.tags;
88 common.tags.push_back(kindTag(definition.kind));
89 auto valid = common.validate();
91 auto candidate = container_.clone();
92 auto applied = candidate.apply(common, subject.format(), definition.source);
93 if (!applied) return eve::Result<effects::EffectHandle>::failure(applied.status());
94 const std::string id = std::move(applied).takeValue();
95 const auto* instance = candidate.find(id);
96 if (!instance)
98 eve::DiagnosticCode::InvariantViolation, "RTS effect candidate disappeared", "effect"));
99 auto targetCandidate = target_;
100 auto targetValid = executor_.applyImmediate(targetCandidate, *instance);
101 if (!targetValid) return eve::Result<effects::EffectHandle>::failure(targetValid.status());
102 auto handle = candidate.handleFor(id);
104 target_ = std::move(targetCandidate);
105 container_ = std::move(candidate);
106 return eve::Result<effects::EffectHandle>::success(std::move(handle).takeValue(),
108}
109
111 auto resolved = container_.resolve(handle);
112 if (!resolved) return eve::Result<void>::failure(resolved.status());
113 return container_.remove(handle.instanceId);
114}
115
117 auto candidate = container_.clone();
118 auto lifecycle = effects::EffectExecutor{}.advance(candidate, step);
119 if (!lifecycle) return eve::Result<RTSEffectUpdate>::failure(lifecycle.status());
120 auto targetCandidate = target_;
121 auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue());
122 if (!settled) return settled;
123 auto result = std::move(settled).takeValue();
124 target_ = std::move(targetCandidate);
125 container_ = std::move(candidate);
127}
128
129RTSEffectSnapshot RTSEffectAdapter::snapshot() const { return {container_.snapshot(), target_}; }
130
132 auto valid = executor_.validate(snapshotValue.target);
133 if (!valid) return valid;
134 auto restored = container_.restore(snapshotValue.effects);
135 if (!restored) return restored;
136 target_ = snapshotValue.target;
138}
139
140std::size_t RTSEffectAdapter::count() const noexcept { return static_cast<std::size_t>(container_.effectCount()); }
141
145
146double RTSEffectAdapter::multiplier(std::string_view field) const noexcept {
147 double result = 1.0;
148 for (int index = 0; index < container_.effectCount(); ++index) {
149 const auto* effect = container_.effectAt(index);
150 if (effect == nullptr) continue;
151 const auto found = effect->payload.object().find(std::string(field));
152 if (found == effect->payload.object().end()) continue;
153 if (const auto* real = found->second.getIf<double>()) result *= *real;
154 else if (const auto* integer = found->second.getIf<std::int64_t>()) result *= static_cast<double>(*integer);
155 }
156 return result;
157}
158
159double RTSEffectAdapter::additive(std::string_view field) const noexcept {
160 double result = 0.0;
161 for (int index = 0; index < container_.effectCount(); ++index) {
162 const auto* effect = container_.effectAt(index);
163 if (effect == nullptr) continue;
164 const auto found = effect->payload.object().find(std::string(field));
165 if (found == effect->payload.object().end()) continue;
166 if (const auto* real = found->second.getIf<double>()) result += *real;
167 else if (const auto* integer = found->second.getIf<std::int64_t>()) result += static_cast<double>(*integer);
168 }
169 return result;
170}
171
173 if (!subject.isValid())
175 eve::DiagnosticCode::InvalidArgument, "RTS effect component requires a valid subject", "subject"));
176 if (subject_.isValid() && subject_ != subject && adapter_.count() != 0)
179 "RTS effect component cannot change subject while effects are active", "subject"));
180 subject_ = subject;
182}
183
185 if (!subject_.isValid())
187 eve::DiagnosticCode::InvalidArgument, "RTS effect component has no bound subject", "subject"));
188 return adapter_.apply(definition, subject_);
189}
190
192
196
197} // namespace eve::rts
LogicalId target
int subject
Definition AnimSmr.cpp:163
TokenKind kind
bool valid
const std::string * tag
std::unique_ptr< ParticleEffect > effect
PrimitiveHandle handle
RTS effect adapter with morale, suppression and production policies.
bool found
SimulationTick tick
float step
Definition TreeMesh.cpp:314
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
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Strong, domain-neutral reference to a runtime subject.
Definition SubjectRef.h:26
bool isValid() const noexcept
Return whether this reference is valid and non-nil.
Definition SubjectRef.h:38
EffectContainer snapshot() const
Capture an independent lifecycle snapshot for staging/restore.
eve::Result< const EffectInstance * > resolve(EffectHandle handle) const
Resolve a generation-qualified handle for this call.
eve::Result< void > remove(const std::string &id, const std::string &reason="removed")
Removes one active instance and emits a Removed event.
EffectContainer clone() const
Create an independent deep copy suitable for ECS staging.
int effectCount() const
Returns active instance count in deterministic creation order.
eve::Result< void > restore(const EffectContainer &snapshot)
Restore a previously captured snapshot as one observable commit.
Executes lifecycle time progression for an EffectContainer.
eve::Result< EffectUpdateSummary > advance(EffectContainer &container, double dtSeconds) const
Advances one container by a legacy seconds delta.
void setNumber(const std::string &key, double value)
Sets a JSON number field.
RTSEffectSnapshot snapshot() const
Capture effects and domain target state.
double multiplier(std::string_view field) const noexcept
Multiply one numeric payload across all active canonical effect instances.
eve::Result< const effects::EffectInstance * > resolve(effects::EffectHandle handle) const
Resolve a live handle as a borrowed observation.
std::size_t count() const noexcept
Return the number of active effect instances.
double additive(std::string_view field) const noexcept
Sum one numeric payload across all active canonical effect instances.
eve::Result< RTSEffectUpdate > advance(const eve::SimulationStep &step)
Advance and settle RTS effects atomically.
eve::Result< void > restore(const RTSEffectSnapshot &snapshot)
Restore effects and target state, invalidating prior handles.
eve::Result< void > remove(effects::EffectHandle handle)
Remove one generation-qualified effect handle.
eve::Result< effects::EffectHandle > apply(const RTSEffectDefinition &definition, eve::SubjectRef subject)
Apply one typed effect to a stable subject.
eve::Result< effects::EffectHandle > apply(const RTSEffectDefinition &definition)
Apply a typed RTS effect to this entity's canonical lifecycle.
eve::Result< void > bindSubject(eve::SubjectRef subject)
Bind the owning entity subject before applying effects.
eve::Result< RTSEffectUpdate > advance(const eve::SimulationStep &step)
Advance this entity's effect lifecycle and settle domain state.
eve::Result< void > remove(effects::EffectHandle handle)
Remove one generation-qualified effect handle.
eve::Result< RTSEffectUpdate > settle(RTSEffectTarget &target, effects::EffectUpdateSummary lifecycle) const
Settle common periodic triggers using RTS-specific policies.
eve::Result< void > applyImmediate(RTSEffectTarget &target, const effects::EffectInstance &effect) const
Apply immediate production-lock semantics to a staged target.
eve::Result< void > validate(const RTSEffectTarget &target) const
Validate the target state before a staged application.
RTSEffectKind
RTS-specific interpretation of an effect instance.
Definition RTSEffects.h:24
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158
Immutable-by-convention template used to create effect instances.
std::vector< std::string > tags
double period
Period in seconds for domain executor settlement; zero disables periodic ticks.
eve::Result< void > validate() const
Validates definition fields without changing any state.
Generation-qualified local reference to a canonical effect instance.
One owned runtime effect fact.
Summary returned by the lifecycle executor after one simulation step.
std::vector< EffectPeriodicTick > periodicTicks
Periodic triggers in deterministic container order.
Typed RTS definition projected to the common lifecycle schema.
Definition RTSEffects.h:35
Serializable in-memory RTS effect snapshot.
Definition RTSEffects.h:58
effects::EffectContainer effects
Definition RTSEffects.h:59
Mutable RTS state affected by the domain executor.
Definition RTSEffects.h:27
Result of one RTS lifecycle step and domain settlement.
Definition RTSEffects.h:51
effects::EffectUpdateSummary lifecycle
Definition RTSEffects.h:52
std::uint32_t commandInterrupts
Definition RTSEffects.h:54