载入中...
搜索中...
未找到
AbilitySystem.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <utility>
5
6namespace eve::action {
7namespace {
8
9bool terminal(ActionPhase phase) {
11}
12
13template <typename Id>
14Result<Id> takeNext(Id& next, std::string_view domain) {
15 const Id value = next;
16 const auto incremented = next.incremented();
17 if (!incremented)
19 Diagnostic::error(DiagnosticCode::Conflict, std::string(domain) + " identity space exhausted", {}));
20 next = *incremented;
22}
23
24} // namespace
25
27 if (!id.isValid()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Ability id is invalid", "id"));
29 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Ability cooldown must be non-negative", "cooldown"));
30 auto actionValid = action.validate();
31 if (!actionValid) return Result<void>::failure(actionValid.status());
32 for (std::size_t i = 0; i < triggers.size(); ++i) {
33 if (!tags::isValidGameplayTagName(triggers[i].gameplayTag))
34 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Ability trigger tag is invalid", "triggers[" + std::to_string(i) + "].gameplayTag"));
35 }
36 return Result<void>::success();
37}
38
40 auto valid = definition.validate();
41 if (!valid) return valid;
42 const std::string key = definition.id.format();
43 if (definitions_.contains(key))
44 return Result<void>::failure(Diagnostic::error(DiagnosticCode::AlreadyExists, "Ability definition is already registered", key));
45 definitions_.emplace(key, std::move(definition));
47}
48
50 auto valid = definition.validate();
51 if (!valid) return valid;
52 const std::string key = definition.id.format();
53 const auto found = definitions_.find(key);
54 if (found == definitions_.end())
55 return Result<void>::failure(Diagnostic::error(DiagnosticCode::NotFound, "Ability definition is not registered", key));
56 if (found->second.instancing != definition.instancing) {
57 const bool hasGrant = std::any_of(grants_.begin(), grants_.end(), [&](const auto& item) {
58 return item.second.state.definitionId == definition.id;
59 });
60 if (hasGrant)
61 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Conflict, "Granted ability cannot change its instancing policy during hot reload", "instancing"));
62 }
63 found->second = std::move(definition);
65}
66
67Result<AbilityGrantId> AbilityRuntime::grant(std::string ownerId, const LogicalId& definitionId) {
68 if (ownerId.empty())
70 Diagnostic::error(DiagnosticCode::InvalidArgument, "Ability owner is empty", "ownerId"));
71 const auto definition = definitions_.find(definitionId.format());
72 if (definition == definitions_.end())
74 Diagnostic::error(DiagnosticCode::NotFound, "Ability definition is not registered", definitionId.format()));
75 auto grantId = nextGrantId();
76 if (!grantId) return grantId;
77 AbilityInstanceId ownerInstance;
78 if (definition->second.instancing == AbilityInstancingPolicy::PerOwner) {
79 auto instance = nextInstanceId();
80 if (!instance) return Result<AbilityGrantId>::failure(instance.status());
81 ownerInstance = instance.value();
82 }
83 AbilityGrantState state{grantId.value(), std::move(ownerId), definitionId, Duration::zero()};
84 grants_.emplace(grantId.value(), GrantRecord{std::move(state), ownerInstance});
86}
87
89 const auto found = grants_.find(grantId);
90 if (found == grants_.end()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::NotFound, "Ability grant was not found", "grantId"));
91 if (grantHasActiveActivation(grantId))
92 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Conflict, "Active ability grant cannot be revoked", "grantId"));
93 grants_.erase(found);
95}
96
98 const auto found = grants_.find(grantId);
99 if (found == grants_.end())
101 Diagnostic::error(DiagnosticCode::NotFound, "Ability grant was not found", "grantId"));
102 return Result<AbilityGrantState>::success(found->second.state);
103}
104
106 auto grant = grants_.find(grantId);
107 if (grant == grants_.end())
109 Diagnostic::error(DiagnosticCode::NotFound, "Ability grant was not found", "grantId"));
110 const auto definition = definitions_.find(grant->second.state.definitionId.format());
111 if (definition == definitions_.end())
113 Diagnostic::error(DiagnosticCode::NotFound, "Granted ability definition was removed", "definitionId"));
114 if (grant->second.state.cooldownRemaining > Duration::zero())
116 Diagnostic::error(DiagnosticCode::PreconditionViolation, "Ability is on cooldown", "cooldown"));
117 if (request.actionId != definition->second.action.id)
119 Diagnostic::error(DiagnosticCode::InvalidArgument, "Ability request action id does not match", "actionId"));
120 if (definition->second.instancing != AbilityInstancingPolicy::PerExecution && grantHasActiveActivation(grantId))
122 Diagnostic::error(DiagnosticCode::Conflict, "Ability instance is already active", "grantId"));
123
124 std::vector<AbilityActivationId> replaceable;
125 if (definition->second.activationGroup != AbilityActivationGroup::Independent) {
126 for (const auto& [id, active] : activations_) {
127 if (active.ownerId != grant->second.state.ownerId || active.group == AbilityActivationGroup::Independent)
128 continue;
129 const auto* execution = runtime_.find(active.executionId);
130 if (!execution || terminal(execution->phase())) continue;
133 DiagnosticCode::Conflict, "An exclusive-blocking ability is active", "activationGroup"));
134 replaceable.push_back(id);
135 }
136 }
137
138 auto execution = runtime_.submit(definition->second.action, std::move(request));
139 if (!execution) return Result<AbilityActivation>::failure(execution.status());
140 for (const auto replacedId : replaceable) {
141 auto cancelled = runtime_.cancel(activations_.at(replacedId).executionId, tick);
142 if (!cancelled) {
143 auto rollback = runtime_.cancel(execution.value(), tick);
144 if (!rollback) return Result<AbilityActivation>::failure(rollback.status());
146 DiagnosticCode::Conflict, "Could not replace the active exclusive ability", "activationGroup"));
147 }
148 activations_.erase(replacedId);
149 }
150
151 auto activationId = nextActivationId();
152 if (!activationId) {
153 auto rollback = runtime_.cancel(execution.value(), tick);
154 if (!rollback) return Result<AbilityActivation>::failure(rollback.status());
155 return Result<AbilityActivation>::failure(activationId.status());
156 }
157 AbilityInstanceId instanceId;
158 if (definition->second.instancing == AbilityInstancingPolicy::PerOwner) {
159 instanceId = grant->second.ownerInstanceId;
160 } else if (definition->second.instancing == AbilityInstancingPolicy::PerExecution) {
161 auto instance = nextInstanceId();
162 if (!instance) {
163 auto rollback = runtime_.cancel(execution.value(), tick);
164 if (!rollback) return Result<AbilityActivation>::failure(rollback.status());
165 return Result<AbilityActivation>::failure(instance.status());
166 }
167 instanceId = instance.value();
168 }
169 AbilityActivation activation{activationId.value(),
170 grantId,
171 instanceId,
172 execution.value(),
173 grant->second.state.ownerId,
174 definition->second.activationGroup};
175 activations_.emplace(activation.id, activation);
176 grant->second.state.cooldownRemaining = definition->second.cooldown;
178}
179
180std::vector<AbilityGrantId> AbilityRuntime::matchingGrants(std::string_view ownerId,
181 std::string_view gameplayEventTag) const {
182 std::vector<AbilityGrantId> result;
183 for (const auto& [id, grant] : grants_) {
184 if (grant.state.ownerId != ownerId) continue;
185 const auto definition = definitions_.find(grant.state.definitionId.format());
186 if (definition == definitions_.end()) continue;
187 const bool matches = std::any_of(
188 definition->second.triggers.begin(), definition->second.triggers.end(), [&](const auto& trigger) {
189 return tags::gameplayTagMatches(gameplayEventTag, trigger.gameplayTag, trigger.match);
190 });
191 if (matches) result.push_back(id);
192 }
193 return result;
194}
195
197 if (delta < Duration::zero())
198 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Ability cooldown delta must be non-negative", "delta"));
199 for (auto& [id, grant] : grants_) {
200 const auto remaining = grant.state.cooldownRemaining.nanoseconds();
201 grant.state.cooldownRemaining =
202 Duration::fromNanoseconds(delta.nanoseconds() >= remaining ? 0 : remaining - delta.nanoseconds());
203 }
205}
206
208 const std::size_t before = activations_.size();
209 std::erase_if(activations_, [&](const auto& item) {
210 const auto* execution = runtime_.find(item.second.executionId);
211 return !execution || terminal(execution->phase());
212 });
213 const std::size_t removed = before - activations_.size();
216}
217
218std::vector<AbilityActivation> AbilityRuntime::activeActivations() const {
219 std::vector<AbilityActivation> result;
220 result.reserve(activations_.size());
221 for (const auto& [id, activation] : activations_) result.push_back(activation);
222 return result;
223}
224
225Result<AbilityGrantId> AbilityRuntime::nextGrantId() { return takeNext(nextGrant_, "ability grant"); }
226Result<AbilityActivationId> AbilityRuntime::nextActivationId() {
227 return takeNext(nextActivation_, "ability activation");
228}
229Result<AbilityInstanceId> AbilityRuntime::nextInstanceId() { return takeNext(nextInstance_, "ability instance"); }
230
231bool AbilityRuntime::grantHasActiveActivation(AbilityGrantId grantId) const {
232 return std::any_of(activations_.begin(), activations_.end(), [&](const auto& item) {
233 if (item.second.grantId != grantId) return false;
234 const auto* execution = runtime_.find(item.second.executionId);
235 return execution && !terminal(execution->phase());
236 });
237}
238
239} // namespace eve::action
Grants, cooldowns, instancing, activation groups and gameplay-event triggers.
double value
bool & active
float phase
Definition CaveMesh.cpp:58
const GltfImportRequest & request
std::uint32_t key
bool valid
bool found
int removed
SimulationTick tick
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
Signed, fixed-resolution simulation duration in nanoseconds.
Definition Time.h:43
constexpr std::int64_t nanoseconds() const noexcept
Return the exact signed nanosecond representation.
Definition Time.h:69
static constexpr Duration fromNanoseconds(std::int64_t nanoseconds) noexcept
Construct an exact duration from nanoseconds.
Definition Time.h:55
static constexpr Duration zero() noexcept
Return zero duration.
Definition Time.h:60
constexpr bool isZero() const noexcept
Whether this duration is exactly zero.
Definition Time.h:75
Human-readable, scoped identifier in the form namespace:name.
Definition Identity.h:411
const std::string & format() const noexcept
Returns the canonical namespace:name representation.
Definition Identity.h:436
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
Result< AbilityActivation > activate(AbilityGrantId grantId, ActionRequest request, SimulationTick tick)
Submit an activation after cooldown, instancing and group checks.
Result< AbilityGrantState > findGrant(AbilityGrantId grantId) const
Return an owning grant-state snapshot.
Result< std::size_t > synchronize()
Remove links whose ActionRuntime execution reached a terminal phase.
Result< void > revoke(AbilityGrantId grantId)
Revoke an inactive grant. Active grants must be synchronized/cancelled first.
Result< void > registerDefinition(AbilityDefinition definition)
Register one canonical definition; duplicate ids are rejected.
Result< void > advanceCooldowns(Duration delta)
Decrease every cooldown using injected deterministic time.
std::vector< AbilityActivation > activeActivations() const
Return owning active-link snapshots in activation-id order.
Result< void > replaceDefinition(AbilityDefinition definition)
Transactionally replace one registered definition for future activations.
Result< AbilityGrantId > grant(std::string ownerId, const LogicalId &definitionId)
Grant a registered definition to a non-empty owner identity.
std::vector< AbilityGrantId > matchingGrants(std::string_view ownerId, std::string_view gameplayEventTag) const
Return grants whose declared gameplay-event triggers match an event tag.
Result< void > cancel(ActionExecutionId id, SimulationTick tick)
Cancel a non-terminal execution before its Active transaction.
Definition Action.cpp:514
const ActionExecution * find(ActionExecutionId id) const noexcept
Find a borrowed read-only execution view, or null when absent.
Definition Action.cpp:168
Result< ActionExecutionId > submit(ActionDefinition definition, ActionRequest request)
Submit a validated definition/request pair in Requested phase.
Definition Action.cpp:148
constexpr std::uint64_t value() const noexcept
Returns the underlying value at an explicit protocol boundary.
ActionPhase
Lifecycle phase shared by every gameplay action adapter.
Definition Action.h:40
eve::detail::StrongUint64< AbilityGrantIdTag > AbilityGrantId
Opaque identity of one owner-to-definition grant.
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
bool isValidGameplayTagName(std::string_view name) noexcept
Returns whether a name is a canonical dot-separated gameplay tag.
Owning link between an ability activation and its action execution.
Canonical immutable-by-convention definition of a grantable ability.
std::vector< AbilityTrigger > triggers
Result< void > validate() const
Validate identity, action, cooldown and gameplay-event trigger invariants.
Owning snapshot of one granted ability's mutable state.
Result< void > validate() const
Validate definition invariants without changing the definition.
Definition Action.cpp:56
One caller-owned request to instantiate an action execution.
Definition Action.h:127