载入中...
搜索中...
未找到
WeaponDefinitionRuntime.cpp
浏览该文件的文档.
2
3#include "common/Value.h"
4#include "weapon/Weapon.h"
5
6#include <algorithm>
7#include <cmath>
8#include <cstdint>
9#include <limits>
10#include <memory>
11#include <set>
12#include <stdexcept>
13#include <utility>
14
15namespace eve::weapon {
16namespace {
17
18const eve::Value* field(const eve::Value::Object& object, std::string_view name) {
19 const auto it = object.find(std::string(name));
20 return it == object.end() ? nullptr : &it->second;
21}
22
23bool readText(const eve::Value::Object& object, std::string_view name, std::string& output) {
24 const auto* value = field(object, name);
25 if (value == nullptr) return true;
26 const auto* text = value->getIf<std::string>();
27 if (text == nullptr) return false;
28 output = *text;
29 return true;
30}
31
32bool readNumber(const eve::Value::Object& object, std::string_view name, float& output) {
33 const auto* value = field(object, name);
34 if (value == nullptr) return true;
35 if (const auto* number = value->getIf<double>()) {
36 if (!std::isfinite(*number)) return false;
37 output = static_cast<float>(*number);
38 return std::isfinite(output);
39 }
40 if (const auto* integer = value->getIf<std::int64_t>()) {
41 output = static_cast<float>(*integer);
42 return std::isfinite(output);
43 }
44 return false;
45}
46
47bool readInteger(const eve::Value::Object& object, std::string_view name, int& output) {
48 const auto* value = field(object, name);
49 if (value == nullptr) return true;
50 const auto* integer = value->getIf<std::int64_t>();
51 if (integer == nullptr || *integer < std::numeric_limits<int>::min() || *integer > std::numeric_limits<int>::max())
52 return false;
53 output = static_cast<int>(*integer);
54 return true;
55}
56
57bool readBoolean(const eve::Value::Object& object, std::string_view name, bool& output) {
58 const auto* value = field(object, name);
59 if (value == nullptr) return true;
60 const auto* boolean = value->getIf<bool>();
61 if (boolean == nullptr) return false;
62 output = *boolean;
63 return true;
64}
65
66bool readStringArray(const eve::Value::Object& object, std::string_view name,
67 std::vector<std::string>& output) {
68 const auto* value = field(object, name);
69 if (value == nullptr) return true;
70 const auto* array = value->getIf<eve::Value::Array>();
71 if (array == nullptr) return false;
72 std::vector<std::string> candidate;
73 candidate.reserve(array->size());
74 for (const auto& entry : *array) {
75 const auto* text = entry.getIf<std::string>();
76 if (text == nullptr || text->empty()) return false;
77 candidate.push_back(*text);
78 }
79 std::sort(candidate.begin(), candidate.end());
80 if (std::adjacent_find(candidate.begin(), candidate.end()) != candidate.end()) return false;
81 output = std::move(candidate);
82 return true;
83}
84
85const char* resourceKindName(ResourceKind kind) noexcept {
86 switch (kind) {
87 case ResourceKind::None: return "none";
88 case ResourceKind::Ammo: return "ammo";
89 case ResourceKind::Mana: return "mana";
90 case ResourceKind::Charges: return "charges";
91 case ResourceKind::Stamina: return "stamina";
92 }
93 return "none";
94}
95
96ResourceKind resourceKindFromName(std::string_view name) noexcept {
97 if (name == "ammo") return ResourceKind::Ammo;
98 if (name == "mana") return ResourceKind::Mana;
99 if (name == "charges") return ResourceKind::Charges;
100 if (name == "stamina") return ResourceKind::Stamina;
101 return ResourceKind::None;
102}
103
104AttackStage attackStageFromName(std::string_view name) noexcept {
105 if (name == "windup") return AttackStage::Windup;
106 if (name == "active") return AttackStage::Active;
107 if (name == "recover") return AttackStage::Recover;
108 return AttackStage::Idle;
109}
110
113 auto parsed = eve::Value::fromJson(source.json);
114 if (!parsed) return eve::Result<WeaponDefinition>::failure(parsed.status());
115 auto value = std::move(parsed).takeValue();
116 const auto* object = value.getIf<eve::Value::Object>();
117 if (object == nullptr)
119 "weapon definition must be an object",
120 "json", {}, "weapon.definition_runtime"));
121
122 WeaponDefinition result;
123 result.id = std::string(reference.id().name());
124 std::string id = result.id;
125 if (!readText(*object, "id", id) || id != result.id)
127 eve::DiagnosticCode::InvalidArgument, "weapon definition id does not match its logical reference", "id", {},
128 "weapon.definition_runtime"));
129 std::string kind = weaponKindName(result.kind);
130 std::string logic = result.logic;
131 if (!readText(*object, "kind", kind) || !readText(*object, "logic", logic))
133 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon kind and logic must be strings",
134 "definition", {}, "weapon.definition_runtime"));
135 result.kind = weaponKindFromName(kind);
136 result.logic = std::move(logic);
137
138 if (!readNumber(*object, "damage", result.damage) || !readNumber(*object, "penetration", result.penetration) ||
139 !readNumber(*object, "range", result.range) || !readNumber(*object, "spread", result.spread) ||
140 !readNumber(*object, "falloffStart", result.falloffStart) ||
141 !readNumber(*object, "minimumDamageFactor", result.minimumDamageFactor) ||
142 !readNumber(*object, "splashMinimumDamageFactor", result.splashMinimumDamageFactor) ||
143 !readNumber(*object, "preferredTargetBonus", result.preferredTargetBonus) ||
144 !readNumber(*object, "accuracy", result.accuracy) ||
145 !readNumber(*object, "scatterRadius", result.scatterRadius) ||
146 !readNumber(*object, "spreadMin", result.spreadMin) || !readNumber(*object, "spreadMax", result.spreadMax) ||
147 !readNumber(*object, "spreadPerShot", result.spreadPerShot) ||
148 !readNumber(*object, "spreadRecover", result.spreadRecover) ||
149 !readNumber(*object, "recoilPitch", result.recoilPitch) ||
150 !readNumber(*object, "recoilYaw", result.recoilYaw) ||
151 !readNumber(*object, "recoilRecover", result.recoilRecover) ||
152 !readNumber(*object, "zoomFov", result.zoomFov) || !readNumber(*object, "cooldown", result.cooldown) ||
153 !readNumber(*object, "arc", result.arc))
155 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon numeric fields must be finite numbers",
156 "definition", {}, "weapon.definition_runtime"));
157 if (result.falloffStart < 0.0f || result.falloffStart > result.range ||
158 result.minimumDamageFactor < 0.0f || result.minimumDamageFactor > 1.0f ||
159 result.splashMinimumDamageFactor < 0.0f || result.splashMinimumDamageFactor > 1.0f ||
160 result.accuracy < 0.0f || result.accuracy > 1.0f || result.scatterRadius < 0.0f ||
161 result.preferredTargetBonus < 1.0f)
163 eve::DiagnosticCode::InvalidArgument, "weapon damage falloff fields are outside valid ranges", "definition",
164 {}, "weapon.definition_runtime"));
165
166 if (!readText(*object, "damageType", result.damageType) || !readText(*object, "element", result.element) ||
167 !readBoolean(*object, "targetsGround", result.targetsGround) ||
168 !readBoolean(*object, "targetsAir", result.targetsAir) ||
169 !readBoolean(*object, "friendlyFire", result.friendlyFire) ||
170 !readBoolean(*object, "blockedByObstacles", result.blockedByObstacles) ||
171 !readStringArray(*object, "requiredTargetTags", result.requiredTargetTags) ||
172 !readStringArray(*object, "excludedTargetTags", result.excludedTargetTags) ||
173 !readStringArray(*object, "preferredTargetTags", result.preferredTargetTags))
175 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon target policy fields are invalid",
176 "definition", {}, "weapon.definition_runtime"));
177 if (!result.targetsGround && !result.targetsAir)
179 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon must target ground, air, or both",
180 "definition", {}, "weapon.definition_runtime"));
181
182 if (const auto* modes = field(*object, "fireModes")) {
183 const auto* array = modes->getIf<eve::Value::Array>();
184 if (array == nullptr)
186 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "fireModes must be an array", "fireModes",
187 {}, "weapon.definition_runtime"));
188 for (const auto& entry : *array) {
189 const auto* text = entry.getIf<std::string>();
190 if (text == nullptr)
193 "fireModes", {}, "weapon.definition_runtime"));
194 result.selectableModes.push_back(fireModeFromName(*text));
195 }
196 }
197 std::string fireMode = fireModeName(result.fireMode);
198 if (!readText(*object, "fireMode", fireMode))
200 "fireMode must be a string", "fireMode",
201 {}, "weapon.definition_runtime"));
202 result.fireMode = fireModeFromName(fireMode);
203
204 if (const auto* resource = field(*object, "resource")) {
205 const auto* nested = resource->getIf<eve::Value::Object>();
206 if (nested == nullptr)
208 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "resource must be an object", "resource",
209 {}, "weapon.definition_runtime"));
210 std::string resourceName = resourceKindName(result.resource.kind);
211 if (!readText(*nested, "kind", resourceName) || !readNumber(*nested, "max", result.resource.max) ||
212 !readNumber(*nested, "regen", result.resource.regen) ||
213 !readNumber(*nested, "cost", result.resource.cost) ||
214 !readBoolean(*nested, "infinite", result.resource.infinite))
216 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon resource fields are invalid",
217 "resource", {}, "weapon.definition_runtime"));
218 result.resource.kind = resourceKindFromName(resourceName);
219 }
220 if (const auto* stages = field(*object, "stages")) {
221 const auto* nested = stages->getIf<eve::Value::Object>();
222 if (nested == nullptr || !readNumber(*nested, "windup", result.stages.windupTime) ||
223 !readNumber(*nested, "active", result.stages.activeTime) ||
224 !readNumber(*nested, "recover", result.stages.recoverTime))
226 "stages fields are invalid", "stages",
227 {}, "weapon.definition_runtime"));
228 }
229 if (const auto* burst = field(*object, "burst")) {
230 const auto* nested = burst->getIf<eve::Value::Object>();
231 if (nested == nullptr || !readInteger(*nested, "size", result.burstSize) ||
232 !readNumber(*nested, "interval", result.burstInterval))
234 "burst fields are invalid", "burst",
235 {}, "weapon.definition_runtime"));
236 }
237 if (const auto* ammo = field(*object, "ammo")) {
238 const auto* nested = ammo->getIf<eve::Value::Object>();
239 if (nested == nullptr || !readInteger(*nested, "mag", result.magSize) ||
240 !readInteger(*nested, "reserve", result.reserveSize) || !readNumber(*nested, "reload", result.reloadTime))
242 "ammo fields are invalid", "ammo", {},
243 "weapon.definition_runtime"));
244 }
245 if (const auto* projectile = field(*object, "projectile")) {
246 const auto* nested = projectile->getIf<eve::Value::Object>();
247 if (nested == nullptr || !readText(*nested, "type", result.projectile.type) ||
248 !readNumber(*nested, "speed", result.projectile.speed) ||
249 !readNumber(*nested, "gravity", result.projectile.gravity) ||
250 !readNumber(*nested, "aoe", result.projectile.aoe) ||
251 !readInteger(*nested, "pelletCount", result.projectile.pelletCount) ||
252 !readNumber(*nested, "pelletSpread", result.projectile.pelletSpread))
254 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "projectile fields are invalid",
255 "projectile", {}, "weapon.definition_runtime"));
256 }
257 if (const auto* effects = field(*object, "effects")) {
258 const auto* nested = effects->getIf<eve::Value::Object>();
259 if (nested == nullptr || !readText(*nested, "muzzle", result.effectMuzzle) ||
260 !readText(*nested, "sound", result.effectSound))
262 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effects fields are invalid", "effects",
263 {}, "weapon.definition_runtime"));
264 }
265 if (result.resource.kind == ResourceKind::None) result.resource.infinite = true;
266 return eve::Result<WeaponDefinition>::success(std::move(result));
267}
268
271 const auto& logical = reference.id();
272 return registry.handle(std::string(logical.namespaceName()), std::string(logical.name()));
273}
274
278 auto resolved = registry.resolveHandle(handle);
279 if (!resolved) return eve::Result<WeaponDefinition>::failure(resolved.status());
280 return parseDefinition(resolved.value().get(), reference);
281}
282
283eve::LogicalId runtimeSchema() {
284 static const eve::LogicalId value = [] {
285 auto parsed = eve::LogicalId::parse("weapon:runtime");
286 if (!parsed) throw std::logic_error("invalid built-in weapon snapshot schema");
287 return *parsed;
288 }();
289 return value;
290}
291
292WeaponRuntimeState initialState(const WeaponDefinition& definition) {
293 WeaponRuntimeState result;
294 result.selector = definition.fireMode;
295 result.currentSpread = definition.spreadMin;
296 result.resource.kind = definition.resource.kind;
297 result.resource.max = definition.resource.max;
298 result.resource.regen = definition.resource.regen;
299 result.resource.cost = definition.resource.cost;
300 result.resource.infinite = definition.resource.infinite;
301 if (definition.kind == WeaponKind::Ranged) {
302 result.resource.kind = ResourceKind::Ammo;
303 result.resource.max = static_cast<float>(definition.magSize);
304 result.resource.value = static_cast<float>(definition.magSize);
305 result.resource.cost = 1.f;
306 result.resource.reserve = definition.reserveSize < 0 ? 0 : definition.reserveSize;
307 result.resource.infinite = definition.reserveSize < 0;
308 } else {
309 result.resource.value = result.resource.infinite ? 0.f : result.resource.max;
310 }
311 return result;
312}
313
314eve::Value encodeState(const WeaponRuntimeState& state) {
315 const Resource& resource = state.resource;
316 eve::Value::Object resourceObject{
317 {"cost", eve::Value(resource.cost)},
318 {"infinite", eve::Value(resource.infinite)},
319 {"kind", eve::Value(resourceKindName(resource.kind))},
320 {"max", eve::Value(resource.max)},
321 {"regen", eve::Value(resource.regen)},
322 {"reloadProgress", eve::Value(resource.reloadProgress)},
323 {"reloading", eve::Value(resource.reloading)},
324 {"reserve", eve::Value(resource.reserve)},
325 {"value", eve::Value(resource.value)},
326 };
328 {"aiming", eve::Value(state.aiming)},
329 {"burstRemaining", eve::Value(state.burstRemaining)},
330 {"burstTimer", eve::Value(state.burstTimer)},
331 {"cooldown", eve::Value(state.cooldown)},
332 {"currentSpread", eve::Value(state.currentSpread)},
333 {"jammed", eve::Value(state.jammed)},
334 {"recoilPitch", eve::Value(state.recoilPitch)},
335 {"recoilYaw", eve::Value(state.recoilYaw)},
336 {"resource", eve::Value(std::move(resourceObject))},
337 {"selector", eve::Value(fireModeName(state.selector))},
338 {"stage", eve::Value(attackStageName(state.stage))},
339 {"stageTimer", eve::Value(state.stageTimer)},
340 });
341}
342
343bool readStateNumber(const eve::Value::Object& object, const char* name, float& output) {
344 return readNumber(object, name, output);
345}
346
348 const auto* object = value.getIf<eve::Value::Object>();
349 if (object == nullptr)
351 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon runtime state must be an object",
352 "state", {}, "weapon.definition_runtime"));
353 static const std::set<std::string> fields = {"aiming", "burstRemaining", "burstTimer", "cooldown",
354 "currentSpread", "jammed", "recoilPitch", "recoilYaw",
355 "resource", "selector", "stage", "stageTimer"};
356 for (const auto& [name, unused] : *object) {
357 (void)unused;
358 if (!fields.contains(name))
360 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "unknown weapon runtime state field",
361 "state." + name, {}, "weapon.definition_runtime"));
362 }
363 for (const auto& name : fields)
364 if (!object->contains(name))
365 return eve::Result<WeaponRuntimeState>::failure(
366 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon runtime state is missing a field",
367 "state." + name, {}, "weapon.definition_runtime"));
368
369 WeaponRuntimeState result;
370 std::string selector;
371 std::string stage;
372 if (!readBoolean(*object, "aiming", result.aiming) ||
373 !readInteger(*object, "burstRemaining", result.burstRemaining) ||
374 !readStateNumber(*object, "burstTimer", result.burstTimer) ||
375 !readStateNumber(*object, "cooldown", result.cooldown) ||
376 !readStateNumber(*object, "currentSpread", result.currentSpread) ||
377 !readBoolean(*object, "jammed", result.jammed) ||
378 !readStateNumber(*object, "recoilPitch", result.recoilPitch) ||
379 !readStateNumber(*object, "recoilYaw", result.recoilYaw) || !readText(*object, "selector", selector) ||
380 !readText(*object, "stage", stage) || !readStateNumber(*object, "stageTimer", result.stageTimer))
382 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon runtime scalar field is invalid",
383 "state", {}, "weapon.definition_runtime"));
384 result.selector = fireModeFromName(selector);
385 result.stage = attackStageFromName(stage);
386 const auto* resourceValue = object->at("resource").getIf<eve::Value::Object>();
387 if (resourceValue == nullptr)
389 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon runtime resource is invalid",
390 "state.resource", {}, "weapon.definition_runtime"));
391 std::string kind;
392 if (!readText(*resourceValue, "kind", kind) || !readStateNumber(*resourceValue, "value", result.resource.value) ||
393 !readStateNumber(*resourceValue, "max", result.resource.max) ||
394 !readStateNumber(*resourceValue, "regen", result.resource.regen) ||
395 !readStateNumber(*resourceValue, "cost", result.resource.cost) ||
396 !readBoolean(*resourceValue, "infinite", result.resource.infinite) ||
397 !readInteger(*resourceValue, "reserve", result.resource.reserve) ||
398 !readBoolean(*resourceValue, "reloading", result.resource.reloading) ||
399 !readStateNumber(*resourceValue, "reloadProgress", result.resource.reloadProgress))
401 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon runtime resource fields are invalid",
402 "state.resource", {}, "weapon.definition_runtime"));
403 result.resource.kind = resourceKindFromName(kind);
404 return eve::Result<WeaponRuntimeState>::success(std::move(result));
405}
406
407} // namespace
408
410 auto logical = eve::LogicalId::fromParts("weapon", source.id);
411 if (!logical)
413 "weapon definition id is invalid", "id",
414 {}, "weapon.definition_runtime"));
415 auto reference = eve::DefinitionRef::fromId(*logical);
417 if (source.type != "weapon")
419 "definition type must be weapon", "type",
420 {}, "weapon.definition_runtime"));
421 return parseDefinition(source, reference.value());
422}
423
425 eve::DefinitionRef definition,
426 eve::PersistentId instanceId,
428 if (!definition.id().isValid() || definition.id().namespaceName() != "weapon")
430 eve::DiagnosticCode::InvalidArgument, "weapon definition reference must use weapon namespace", "definition",
431 {}, "weapon.definition_runtime"));
432 auto handle = currentHandle(registry, definition);
434 auto typed = definitionFor(registry, handle.value(), definition);
435 if (!typed) return eve::Result<WeaponDefinitionRuntime>::failure(typed.status());
437 instanceId, std::move(definition), handle.value().generation, initialState(typed.value()));
438 if (!runtime) return eve::Result<WeaponDefinitionRuntime>::failure(runtime.status());
440 WeaponDefinitionRuntime(registry, std::move(runtime).takeValue(), policy));
441}
442
444 std::string_view definitionId,
445 eve::PersistentId instanceId,
447 auto logical = eve::LogicalId::fromParts("weapon", definitionId);
448 if (!logical)
450 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon id is not a valid logical name",
451 "definitionId", {}, "weapon.definition_runtime"));
452 auto reference = eve::DefinitionRef::fromId(*logical);
454 return create(registry, std::move(reference).takeValue(), instanceId, policy);
455}
456
458 eve::PersistentId instanceId,
460 return create(module.definitionRegistry(), definitionId, instanceId, policy);
461}
462
464 return runtime_.identity();
465}
466
467const WeaponRuntimeState& WeaponDefinitionRuntime::state() const noexcept { return runtime_.state(); }
468
469WeaponRuntimeState& WeaponDefinitionRuntime::state() noexcept { return runtime_.state(); }
470
472 return runtime_.identity().definitionHandle();
473}
474
476 if (registry_ == nullptr)
478 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon definition registry is not bound",
479 "registry", {}, "weapon.definition_runtime"));
480 return definitionFor(*registry_, definitionHandle(), identity().definition);
481}
482
483void WeaponDefinitionRuntime::setActive(bool active) noexcept { runtime_.setActive(active); }
484
485bool WeaponDefinitionRuntime::isActive() const noexcept { return runtime_.isActive(); }
486
488 if (entity == nullptr)
490 "weapon runtime cannot project to a null WeaponEntity",
491 "entity", {}, "weapon.definition_runtime"));
492 auto typed = definition();
493 if (!typed) return eve::Result<void>::failure(typed.status());
494 try {
495 auto definitionOwner = std::make_shared<const WeaponDefinition>(typed.value());
496 auto definitionComponent = *entity->definition();
497 auto binding = *entity->definitionBinding();
498 auto targetState = *entity->state();
499 definitionComponent.owned = std::move(definitionOwner);
500 definitionComponent.def = definitionComponent.owned.get();
501 binding.identity = identity();
502 binding.reloadPolicy = policy_;
503 binding.active = isActive();
504 const auto& source = state();
505 targetState.resource = source.resource;
506 targetState.cooldown = source.cooldown;
507 targetState.burstRemaining = source.burstRemaining;
508 targetState.burstTimer = source.burstTimer;
509 targetState.jammed = source.jammed;
510 targetState.stage = source.stage;
511 targetState.stageTimer = source.stageTimer;
512 targetState.stages = &definitionComponent.def->stages;
513 targetState.currentSpread = source.currentSpread;
514 targetState.recoilPitch = source.recoilPitch;
515 targetState.recoilYaw = source.recoilYaw;
516 targetState.selector = source.selector;
517 targetState.aiming = source.aiming;
518 using std::swap;
519 swap(*entity->definition(), definitionComponent);
520 swap(*entity->definitionBinding(), binding);
521 swap(*entity->state(), targetState);
523 } catch (const std::exception&) {
525 "weapon runtime projection allocation failed",
526 "entity", {}, "weapon.definition_runtime"));
527 }
528}
529
531 if (registry_ == nullptr)
533 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "weapon definition registry is not bound",
534 "registry", {}, "weapon.definition_runtime"));
535 auto next = currentHandle(*registry_, identity().definition);
536 if (!next) return eve::Result<eve::definition::ReloadOutcome>::failure(next.status());
537 auto typed = definitionFor(*registry_, next.value(), identity().definition);
538 if (!typed) return eve::Result<eve::definition::ReloadOutcome>::failure(typed.status());
539 const WeaponRuntimeState defaults = initialState(typed.value());
540 const float maxSpread = std::max(defaults.currentSpread, typed.value().spreadMax);
541 auto rebuild = [defaults, maxSpread](const WeaponRuntimeState& oldState, const eve::definition::InstanceIdentity&,
543 WeaponRuntimeState rebuilt = oldState;
544 if (oldState.resource.kind != defaults.resource.kind) {
545 rebuilt.resource = defaults.resource;
546 } else {
547 rebuilt.resource.kind = defaults.resource.kind;
548 rebuilt.resource.max = defaults.resource.max;
549 rebuilt.resource.regen = defaults.resource.regen;
550 rebuilt.resource.cost = defaults.resource.cost;
551 rebuilt.resource.infinite = defaults.resource.infinite;
552 if (rebuilt.resource.infinite) {
553 rebuilt.resource.value = 0.f;
554 } else {
555 rebuilt.resource.value = std::clamp(rebuilt.resource.value, 0.f, rebuilt.resource.max);
556 }
557 rebuilt.resource.reserve = defaults.resource.reserve < 0 ? 0 : rebuilt.resource.reserve;
558 }
559 rebuilt.selector = defaults.selector;
560 rebuilt.currentSpread = std::clamp(oldState.currentSpread, 0.f, maxSpread);
561 return eve::Result<WeaponRuntimeState>::success(std::move(rebuilt));
562 };
563 auto result = runtime_.reload(next.value(), policy, defaults, std::move(rebuild));
564 if (result.ok()) policy_ = policy;
565 return result;
566}
567
576
578 const eve::SnapshotHashProvider& hashProvider) const {
579 auto result = snapshot(revision, tick, hashProvider);
580 if (!result) return eve::Result<std::string>::failure(result.status());
581 return std::move(result).andThen(
583}
584
586 const eve::SnapshotHashProvider& hashProvider) {
587 if (registry_ == nullptr)
589 "weapon definition registry is not bound", "registry",
590 {}, "weapon.definition_runtime"));
591 auto current = currentHandle(*registry_, identity().definition);
592 if (!current) return eve::Result<void>::failure(current.status());
593 if (current.value() != definitionHandle())
595 eve::DiagnosticCode::StaleHandle, "weapon snapshot cannot restore against a replaced definition",
596 "definitionGeneration", {}, "weapon.definition_runtime"));
598 return eve::definition::restoreRuntimeInstance(runtime_, snapshotValue, "weapon.runtime", runtimeSchema(),
599 eve::SchemaVersion(1), hashProvider, decoder);
600}
601
602} // namespace eve::weapon
double value
bool & active
std::string output
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
std::string text
TokenKind kind
std::string name
std::string error
Definition Package.cpp:60
eve::action::ActionVfxBinding binding
std::uint64_t revision
std::vector< std::string > fields
Definition PlayHost.cpp:111
PrimitiveHandle handle
double number
double current
std::string resource
bool boolean
SimulationTick tick
Json object
const UnitySourceAsset & source
Owning, renderer-independent dynamic values.
Strongly typed Weapon definition/runtime adapter.
Stable definition reference backed by LogicalId.
Definition ResourceRef.h:85
static Result< DefinitionRef > fromId(LogicalId id)
Build a definition reference from a valid LogicalId.
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
Human-readable, scoped identifier in the form namespace:name.
Definition Identity.h:411
static std::optional< LogicalId > fromParts(std::string_view namespaceName, std::string_view name)
Builds a logical ID from separate namespace and name components.
Definition Identity.cpp:48
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
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
static eve::Result< RuntimeInstance > create(eve::PersistentId instanceId, eve::DefinitionRef definition, eve::Generation definitionGeneration, State state, bool active=true)
Construct a runtime instance after validating its identity.
Deterministic registry of topic-neutral, versioned JSON definitions.
Definition Definitions.h:58
eve::ResultRef< const Definition > resolveHandle(const DefinitionHandle &handle) const
Resolves a generation-qualified handle and reports stale identities.
eve::Result< DefinitionHandle > handle(const std::string &type, const std::string &id) const
Returns the current generation-qualified handle through Result.
Id128 public API.
Definition Identity.h:112
Binds a typed WeaponDefinition to one mutable runtime instance.
eve::Result< void > applyTo(WeaponEntity *entity) const
Atomically project this runtime into a borrowed WeaponEntity.
const WeaponRuntimeState & state() const noexcept
Borrow mutable typed firing/resource state.
eve::Result< eve::definition::ReloadOutcome > reload(eve::definition::ReloadPolicy policy)
Reload the registry's current generation using a typed policy.
eve::Result< eve::SnapshotEnvelope > snapshot(eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider &hashProvider) const
Capture stable typed runtime state in the common SnapshotEnvelope.
eve::Result< std::string > snapshotJson(eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider &hashProvider) const
Serialize snapshot() as canonical deterministic JSON.
eve::Result< void > restore(const eve::SnapshotEnvelope &snapshot, const eve::SnapshotHashProvider &hashProvider)
Verify and restore a same-generation snapshot transactionally.
eve::Result< WeaponDefinition > definition() const
Resolve the current strongly typed WeaponDefinition projection.
void setActive(bool active) noexcept
Set the active guard used by RejectWhileActive.
static eve::Result< WeaponDefinitionRuntime > create(eve::definitions::DefinitionRegistry &registry, eve::DefinitionRef definition, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy=eve::definition::ReloadPolicy::RebuildInstance)
Create from a common weapon:<name> definition reference.
eve::definition::DefinitionHandle definitionHandle() const noexcept
Return the exact generation-qualified definition handle.
bool isActive() const noexcept
Return whether the instance is active.
const eve::definition::InstanceIdentity & identity() const noexcept
Borrow the common instance identity and exact definition generation.
武器实体:数据全部在组件里,行为在 WeaponSystem。
武器模块(eve.Weapon)。
Definition Weapon.h:24
eve::definitions::DefinitionRegistry & definitionRegistry() noexcept
Return the common registry backing legacy weapon definitions.
Definition Weapon.h:54
ReloadPolicy
Policy used when a live definition is replaced.
eve::Result< eve::SnapshotEnvelope > snapshotRuntimeInstance(const RuntimeInstance< State > &runtime, std::string type, eve::LogicalId schema, eve::SchemaVersion schemaVersion, eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider &hashProvider, const RuntimeStateEncoder< State > &encode)
Capture a typed runtime instance in the common snapshot envelope.
eve::Result< void > restoreRuntimeInstance(RuntimeInstance< State > &runtime, const eve::SnapshotEnvelope &snapshot, std::string_view expectedType, const eve::LogicalId &expectedSchema, eve::SchemaVersion expectedVersion, const eve::SnapshotHashProvider &hashProvider, const RuntimeStateDecoder< State > &decode)
Verify and atomically restore a typed runtime instance.
std::function< eve::Result< State >(const eve::Value &)> RuntimeStateDecoder
Decodes one domain-owned runtime state from an owning Value.
std::function< eve::Result< eve::Value >(const State &)> RuntimeStateEncoder
Converts one domain-owned runtime state into an owning Value.
eve::Diagnostic Diagnostic
eve::Result< T > Result
const EditorValue * field(const EditorValue &value, const char *name)
bool readNumber(const EditorValue &value, const char *name, float &output)
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
载具模块入口(eve.Vehicle):定义注册 / 实体工厂 / RTS 命令 / 帧调度。 设计文档:docs/dev/通用载具系统设计.md
const char * weaponKindName(WeaponKind kind)
武器形态的字符串名("melee" | "ranged" | "magic" | "missile" | "custom")。
Definition Weapon.cpp:86
eve::Result< WeaponDefinition > parseWeaponDefinition(const eve::definitions::Definition &source)
Parse one canonical registry definition into an owning typed snapshot.
WeaponKind weaponKindFromName(const std::string &name)
从字符串解析武器形态;未知名字回退 Ranged。
Definition Weapon.cpp:97
FireMode fireModeFromName(const std::string &name)
从字符串解析开火模式;未知名字回退 Single。
Definition Weapon.cpp:80
AttackStage
攻击阶段。
Definition WeaponTypes.h:62
const char * fireModeName(FireMode mode)
开火模式的字符串名("single" | "burst" | "auto")。
Definition Weapon.cpp:71
const char * attackStageName(AttackStage stage)
攻击阶段的字符串名("idle" | "windup" | "active" | "recover")。
Definition Weapon.cpp:105
ResourceKind
触发资源的类型。
Definition WeaponTypes.h:50
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Result< std::string > serializeSnapshotEnvelope(const SnapshotEnvelope &snapshot)
Serialize an envelope as deterministic compact JSON.
Definition Snapshot.cpp:184
std::function< Result< ContentId >(std::string_view canonicalInput)> SnapshotHashProvider
Injected content-digest implementation used by snapshots.
Definition Snapshot.h:36
Stable outer format shared by persistence and cross-process snapshots.
Definition Snapshot.h:46
A generation-qualified reference to one definition incarnation.
Canonical identity carried by every typed runtime instance.
One immutable view of a registered versioned JSON definition.
Definition Definitions.h:31
int reserve
备用弹药(热武器;<0 = 无限)。
Mutable state owned by one typed weapon runtime instance.