载入中...
搜索中...
未找到
WeaponControl.cpp
浏览该文件的文档.
2
3#include "common/Capability.h"
7
8#include <algorithm>
9#include <limits>
10
11namespace eve::weapon {
12namespace {
13
14LogicalId id(std::string_view text) { return LogicalId::parse(text).value(); }
15
16Result<const Value::Object*> object(const Value& value) {
17 const auto* result = value.getIf<Value::Object>();
18 if (!result)
20 Diagnostic::error(DiagnosticCode::InvalidArgument, "weapon parameters must be an object", "parameters"));
22}
23
24Result<double> number(const Value::Object& value, std::string_view name, double fallback) {
25 const auto found = value.find(std::string(name));
26 if (found == value.end()) return Result<double>::success(fallback);
27 if (!found->second.isNumeric())
29 DiagnosticCode::InvalidArgument, "weapon parameter must be numeric", "parameters." + std::string(name)));
30 return Result<double>::success(found->second.isInt64()
31 ? static_cast<double>(found->second.asInt())
32 : found->second.asDouble());
33}
34
35Result<int> integer(const Value::Object& value, std::string_view name, int fallback) {
36 const auto found = value.find(std::string(name));
37 if (found == value.end()) return Result<int>::success(fallback);
38 if (!found->second.isInt64() || found->second.asInt() < std::numeric_limits<int>::min() ||
39 found->second.asInt() > std::numeric_limits<int>::max())
41 DiagnosticCode::InvalidArgument, "weapon parameter must be an integer", "parameters." + std::string(name)));
42 return Result<int>::success(static_cast<int>(found->second.asInt()));
43}
44
45} // namespace
46
50 : instance_(instance),
51 wielder_(wielder),
52 definition_(std::move(definition)),
53 account_(&account),
54 effect_(&effect) {
55 cap::addListener<IGameplayControlProvider>(this);
56 cap::addListener<IGameplayInstanceCatalog>(this);
57}
58
60 cap::removeListener<IGameplayInstanceCatalog>(this);
61 cap::removeListener<IGameplayControlProvider>(this);
62}
63
64std::string_view WeaponControl::gameplayDomain() const noexcept { return "weapon"; }
65
66std::vector<SubjectRef> WeaponControl::gameplayInstances() const {
67 // This adapter serves exactly one instance: the identity it was constructed
68 // with is the only one it can answer for.
69 if (!instance_.isValid()) return {};
70 return {instance_};
71}
72
73bool WeaponControl::controls(const GameplaySession& session) const {
75 std::find(session.controlledSubjects.begin(), session.controlledSubjects.end(), wielder_) !=
76 session.controlledSubjects.end();
77}
78
80 SubjectRef instance) const {
81 if (instance != instance_ || !instance_.isValid())
83 Diagnostic::error(DiagnosticCode::NotFound, "weapon gameplay instance was not found", "instance"));
84 if (!controls(session))
86 DiagnosticCode::PreconditionViolation, "session does not control the weapon wielder", "instance"));
87 if (!account_ || !effect_ || definition_.id.empty())
89 Diagnostic::error(DiagnosticCode::StaleHandle, "weapon gameplay participants are invalid", "instance"));
90 auto cost = WeaponActionAdapter::resourceCost(definition_);
91 if (!cost) return Result<GameplayObservation>::failure(cost.status());
92 bool affordable = true;
93 if (cost.value()) {
94 auto answer = account_->canAfford(*cost.value());
95 if (!answer) return Result<GameplayObservation>::failure(answer.status());
96 affordable = answer.value().isAffordable();
97 }
98 GameplayObservation observation;
99 observation.domain = id("gameplay:weapon");
100 observation.instance = instance_;
101 observation.tick = tick_;
102 observation.revision = revision_;
103 observation.state = Value(Value::Object{{"affordable", Value(affordable)},
104 {"damage", Value(static_cast<double>(definition_.damage))},
105 {"range", Value(static_cast<double>(definition_.range))},
106 {"weaponId", Value(definition_.id)}});
107 return Result<GameplayObservation>::success(std::move(observation));
108}
109
111 const GameplaySession& session, SubjectRef instance, SubjectRef subject) const {
112 auto observed = observeGameplay(session, instance);
113 if (!observed) return Result<std::vector<GameplayActionDescriptor>>::failure(observed.status());
114 std::move(observed).takeValue();
115 if (subject != wielder_)
117 DiagnosticCode::PreconditionViolation, "weapon action subject must be its wielder", "subject"));
118 const Value numberType(Value::Object{{"type", Value("number")}});
119 const Value integerType(Value::Object{{"type", Value("integer")}});
121 {id("weapon:fire"),
122 Value(Value::Object{{"pitch", numberType}, {"shooterId", integerType},
123 {"targetX", numberType}, {"targetY", numberType},
124 {"targetZ", numberType}, {"yaw", numberType}})},
125 });
126}
127
129 SubjectRef instance,
130 const GameplayCommand& command) {
131 if (instance != instance_)
133 Diagnostic::error(DiagnosticCode::NotFound, "weapon gameplay instance was not found", "instance"));
134 if (!controls(session) || command.subject != wielder_)
136 DiagnosticCode::PreconditionViolation, "session does not control the weapon wielder", "command.subject"));
137 if (command.id.empty())
139 Diagnostic::error(DiagnosticCode::InvalidArgument, "command id must not be empty", "command.id"));
140 if (command.action != id("weapon:fire"))
142 Diagnostic::error(DiagnosticCode::Unsupported, "unsupported weapon action", "command.action"));
143 if (command.observedTick != tick_ || command.expectedRevision != revision_)
145 DiagnosticCode::Conflict, "weapon command was based on a stale observation", "command.expectedRevision"));
146 auto values = object(command.parameters);
148 auto x = number(*values.value(), "targetX", 0.0);
149 auto y = number(*values.value(), "targetY", 0.0);
150 auto z = number(*values.value(), "targetZ", 0.0);
151 auto yaw = number(*values.value(), "yaw", 0.0);
152 auto pitch = number(*values.value(), "pitch", 0.0);
153 auto shooter = integer(*values.value(), "shooterId", 0);
154 if (!x) return Result<GameplayCommandReceipt>::failure(x.status());
155 if (!y) return Result<GameplayCommandReceipt>::failure(y.status());
156 if (!z) return Result<GameplayCommandReceipt>::failure(z.status());
157 if (!yaw) return Result<GameplayCommandReceipt>::failure(yaw.status());
159 if (!shooter) return Result<GameplayCommandReceipt>::failure(shooter.status());
160 AttackRequest attack;
161 attack.targetX = static_cast<float>(x.value());
162 attack.targetY = static_cast<float>(y.value());
163 attack.targetZ = static_cast<float>(z.value());
164 attack.hasTarget = values.value()->contains("targetX") || values.value()->contains("targetY") ||
165 values.value()->contains("targetZ");
166 attack.yaw = static_cast<float>(yaw.value());
167 attack.pitch = static_cast<float>(pitch.value());
168 attack.shooterId = shooter.value();
169 auto fired = WeaponActionAdapter::fire(definition_, attack, *account_, *effect_, tick_);
170 if (!fired) return Result<GameplayCommandReceipt>::failure(fired.status());
171 const auto transaction = std::move(fired).takeValue();
172 ++revision_;
173 GameplayEvent event;
174 event.sequence = nextEventSequence_++;
175 event.tick = tick_;
176 event.type = "weapon.fired";
177 event.subject = wielder_;
178 event.causationCommandId = command.id;
179 event.correlationId = command.id;
180 event.payload = Value(Value::Object{{"transactionId", Value(transaction.transactionId)},
181 {"weaponId", Value(definition_.id)}});
182 events_.push_back(std::move(event));
184 receipt.commandId = command.id;
185 receipt.executionId = transaction.transactionId;
186 receipt.acceptedTick = tick_;
187 receipt.resultingRevision = revision_;
188 receipt.details = Value(Value::Object{{"weaponId", Value(definition_.id)}});
189 return Result<GameplayCommandReceipt>::success(std::move(receipt),
191}
192
194 SubjectRef instance,
195 const SimulationStep& step) {
196 if (step.tick <= tick_)
198 Diagnostic::error(DiagnosticCode::Conflict, "weapon simulation tick must increase", "step.tick"));
199 auto observed = observeGameplay(session, instance);
200 if (!observed) return Result<GameplayObservation>::failure(observed.status());
201 std::move(observed).takeValue();
202 tick_ = step.tick;
203 return observeGameplay(session, instance);
204}
205
207 SubjectRef instance,
208 std::uint64_t afterSequence) const {
209 auto observed = observeGameplay(session, instance);
210 if (!observed) return Result<std::vector<GameplayEvent>>::failure(observed.status());
211 std::move(observed).takeValue();
212 std::vector<GameplayEvent> result;
213 for (const auto& event : events_)
214 if (event.sequence > afterSequence) result.push_back(event);
215 const bool empty = result.empty();
216 return Result<std::vector<GameplayEvent>>::success(
217 std::move(result), Status::success(empty ? StatusCode::NoOp : StatusCode::Applied));
218}
219
220} // namespace eve::weapon
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int subject
Definition AnimSmr.cpp:163
eve::resource::CostSpec cost
eve::resource::IResourceAccount * account
std::map< std::string, Var > values
std::int32_t second
std::string text
std::string name
std::unique_ptr< ParticleEffect > effect
std::string id
Definition PlayHost.cpp:108
Strongly typed, transaction-friendly resource cost protocol.
double number
bool found
std::optional< InteractionSession > session
Definition Tactics.cpp:39
Json object
float step
Definition TreeMesh.cpp:314
Weapon Attack to renderer-independent Action adapter.
Player-equivalent control adapter for one authoritative weapon action.
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
static std::optional< LogicalId > parse(std::string_view text)
Parses a scoped logical name.
Definition Identity.cpp:36
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
Provider-neutral resource account contract.
virtual eve::Result< Affordability > canAfford(const CostSpec &cost) const =0
Query whether a cost can be paid from currently available funds.
Synchronous participant contract for one coordinator transaction.
static eve::Result< std::optional< eve::resource::CostSpec > > resourceCost(const WeaponDefinition &weapon)
Convert the weapon trigger resource into a canonical cost.
static eve::Result< eve::transaction::TransactionReceipt > fire(const WeaponDefinition &weapon, eve::resource::IResourceAccount &account, eve::transaction::ITransactionParticipant &effect, std::string transactionId={})
Execute one already-built weapon effect and charge its resource atomically.
Result< std::vector< GameplayActionDescriptor > > availableGameplayActions(const GameplaySession &session, SubjectRef instance, SubjectRef subject) const override
Discover currently legal player-semantic actions for one subject.
Result< GameplayCommandReceipt > submitGameplay(const GameplaySession &session, SubjectRef instance, const GameplayCommand &command) override
Validate authority and atomically submit one domain command.
std::vector< SubjectRef > gameplayInstances() const override
Instances currently served, in publication order.
Result< std::vector< GameplayEvent > > gameplayEvents(const GameplaySession &session, SubjectRef instance, std::uint64_t afterSequence) const override
Return events strictly newer than an instance-local sequence cursor.
WeaponControl(SubjectRef instance, SubjectRef wielder, WeaponDefinition definition, resource::IResourceAccount &account, transaction::ITransactionParticipant &effect)
Construct a control adapter for one copied definition and borrowed effect authorities.
Result< GameplayObservation > advanceGameplay(const GameplaySession &session, SubjectRef instance, const SimulationStep &step) override
Advance one provider-owned instance using injected deterministic time.
~WeaponControl() override
Unpublish the capability without destroying borrowed authorities.
Result< GameplayObservation > observeGameplay(const GameplaySession &session, SubjectRef instance) const override
Observe one provider-owned gameplay instance without mutation.
std::string_view gameplayDomain() const noexcept override
Stable provider domain used for discovery and routing.
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
载具模块入口(eve.Vehicle):定义注册 / 实体工厂 / RTS 命令 / 帧调度。 设计文档:docs/dev/通用载具系统设计.md
Owning evidence that a gameplay authority accepted one command.
One player-semantic command submitted to a domain authority.
std::uint64_t expectedRevision
SimulationTick observedTick
Owning event projection safe across frames and process boundaries.
std::uint64_t sequence
Immutable owning observation of one authoritative gameplay instance.
Owning authorization context shared by player, script and automation adapters.
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158
一次攻击请求(C++ 系统 / 脚本发起)。角度均为度。 旧名 FireRequest 保留为别名,向后兼容。
int shooterId
游戏侧实体/玩家 id。
float yaw
发射方向(度)。
武器模板(registerWeaponsFromJson 注册,进程级注册表)。