载入中...
搜索中...
未找到
CombatAttributes.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4#include "tags/GameplayTag.h"
5
6#include <algorithm>
7#include <cmath>
8#include <utility>
9
10namespace eve::combat {
11namespace {
12
13} // namespace
14
18 Diagnostic::error(DiagnosticCode::InvalidArgument, "Combat attribute tag is invalid", "tag"));
19 if (!std::isfinite(initialValue) || !std::isfinite(minimumValue) || !std::isfinite(maximumValue) ||
20 !std::isfinite(regenerationPerSecond))
22 Diagnostic::error(DiagnosticCode::InvalidArgument, "Combat attribute values must be finite", tag));
25 Diagnostic::error(DiagnosticCode::InvalidArgument, "Combat attribute minimum exceeds maximum", tag));
26 if (initialValue < minimumValue || initialValue > maximumValue)
28 "Combat attribute initial value is outside bounds", tag));
29 if (regenerationPerSecond < 0.0)
31 "Combat attribute regeneration must be non-negative", tag));
32 return Result<void>::success();
33}
34
36 auto valid = definition.validate();
37 if (!valid) return valid;
38 if (definitions_.contains(definition.tag))
40 Diagnostic::error(DiagnosticCode::AlreadyExists, "Combat attribute is already registered", definition.tag));
41 const std::string tag = definition.tag;
42 definitions_.emplace(tag, std::move(definition));
43 values_.setBase(tag, definitions_.at(tag).initialValue);
45}
46
48 const auto definition = definitions_.find(tag);
49 if (definition == definitions_.end())
51 Diagnostic::error(DiagnosticCode::NotFound, "Combat attribute is not registered", std::string(tag)));
52 return Result<double>::success(values_.getBase(definition->first));
53}
54
55Result<std::vector<CombatAttributeEvent>> CombatAttributeRuntime::commit(
56 const CombatAttributeDefinition& definition, double newValue) {
57 if (!std::isfinite(newValue))
59 DiagnosticCode::InvalidArgument, "Combat attribute value must be finite", definition.tag));
60 const double previous = values_.getBase(definition.tag);
61 const double clamped = std::clamp(newValue, definition.minimumValue, definition.maximumValue);
62 if (clamped == previous)
64
65 values_.setBase(definition.tag, clamped);
66 std::vector<CombatAttributeEvent> events;
68 events.push_back({definition.tag, kind, previous, clamped, clamped - previous});
69 if (clamped == definition.minimumValue && previous > definition.minimumValue)
70 events.push_back({definition.tag, CombatAttributeEventKind::Depleted, previous, clamped, clamped - previous});
71 return Result<std::vector<CombatAttributeEvent>>::success(std::move(events),
73}
74
76 const auto definition = definitions_.find(tag);
77 if (definition == definitions_.end())
79 Diagnostic::error(DiagnosticCode::NotFound, "Combat attribute is not registered", std::string(tag)));
80 return commit(definition->second, value);
81}
82
84 if (!std::isfinite(delta))
86 DiagnosticCode::InvalidArgument, "Combat attribute delta must be finite", std::string(tag)));
87 const auto definition = definitions_.find(tag);
88 if (definition == definitions_.end())
90 Diagnostic::error(DiagnosticCode::NotFound, "Combat attribute is not registered", std::string(tag)));
91 return commit(definition->second, values_.getBase(definition->first) + delta);
92}
93
95 if (delta < Duration::zero())
97 Diagnostic::error(DiagnosticCode::InvalidArgument, "Combat attribute delta must be non-negative", "delta"));
98 if (delta.isZero())
100
101 struct Candidate {
102 const CombatAttributeDefinition* definition = nullptr;
103 double value = 0.0;
104 };
105 std::vector<Candidate> candidates;
106 candidates.reserve(definitions_.size());
107 for (const auto& [tag, definition] : definitions_) {
108 const double amount = definition.regenerationPerSecond * delta.seconds();
109 if (!std::isfinite(amount))
111 Diagnostic::error(DiagnosticCode::InvalidArgument, "Combat attribute regeneration overflowed", tag));
112 candidates.push_back({&definition, values_.getBase(tag) + amount});
113 }
114
116 for (const Candidate& candidate : candidates) {
117 auto committed = commit(*candidate.definition, candidate.value);
118 if (!committed) return Result<CombatAttributeAdvance>::failure(committed.status());
119 auto events = std::move(committed).takeValue();
120 result.events.insert(result.events.end(), std::make_move_iterator(events.begin()),
121 std::make_move_iterator(events.end()));
122 }
123 return Result<CombatAttributeAdvance>::success(std::move(result));
124}
125
126std::vector<CombatAttributeDefinition> CombatAttributeRuntime::definitions() const {
127 std::vector<CombatAttributeDefinition> result;
128 result.reserve(definitions_.size());
129 for (const auto& [tag, definition] : definitions_) result.push_back(definition);
130 return result;
131}
132
133} // namespace eve::combat
double value
Tag-addressed combat attributes, regeneration and change events.
Stable, structured diagnostics shared by engine modules.
Stable hierarchical gameplay-tag definitions and registry.
TokenKind kind
bool valid
const std::string * tag
graphics::Canvas * previous
Battle::Events events
LogicalId definition
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
static constexpr Duration zero() noexcept
Return zero duration.
Definition Time.h:60
double seconds() const noexcept
Return this duration as seconds for legacy/presentation APIs.
Definition Time.cpp:28
constexpr bool isZero() const noexcept
Whether this duration is exactly zero.
Definition Time.h:75
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
void setBase(const AttributeId &attribute, double value)
Set or replace a base value. Empty attribute ids are ignored for compatibility.
double getBase(const AttributeId &attribute, double fallback=0.0) const
Return a base value, or fallback when absent.
Result< std::vector< CombatAttributeEvent > > setValue(std::string_view tag, double value)
Replace a value after clamping and return its owning gain/loss/depletion event, if changed.
Result< CombatAttributeAdvance > advance(Duration delta)
Regenerate all attributes in lexical tag order using injected deterministic time.
Result< double > value(std::string_view tag) const
Return the current base value, or NotFound for an unknown tag.
std::vector< CombatAttributeDefinition > definitions() const
Return owning registered definitions in lexical tag order.
Result< std::vector< CombatAttributeEvent > > modify(std::string_view tag, double delta)
Add a signed delta after clamping and return owning change events.
Result< void > registerAttribute(CombatAttributeDefinition definition)
Register one tag-addressed definition and initialize its value atomically.
bool isValidGameplayTagName(std::string_view name) noexcept
Returns whether a name is a canonical dot-separated gameplay tag.
Owning result of a deterministic regeneration step.
std::vector< CombatAttributeEvent > events
Data-driven bounds and deterministic regeneration for one combat attribute.
Result< void > validate() const
Validate the gameplay-tag identity, finite bounds and regeneration rate.