15struct StagedDefinition {
22 const auto it =
object.find(std::string(
name));
23 return it ==
object.end() ? nullptr : &it->second;
28 const auto* result =
value ==
nullptr ? nullptr :
value->getIf<std::string>();
29 return result ==
nullptr ? std::string{} : *result;
34 if (
value ==
nullptr)
return fallback;
35 if (
const auto* real =
value->getIf<
double>())
return *real;
36 if (
const auto* integer =
value->getIf<std::int64_t>())
return static_cast<double>(*integer);
41 if (
field(
object,
"kind") ==
nullptr)
object.emplace(
"kind",
Value(
"ranged"));
42 const double speed =
number(
object,
"projectileSpeed", 0.0);
43 if (
field(
object,
"logic") ==
nullptr)
object.emplace(
"logic",
Value(speed > 0.0 ?
"projectile" :
"hitscan"));
44 if (
field(
object,
"ammo") ==
nullptr) {
45 const int magazine = std::max(1,
static_cast<int>(
number(
object,
"magazineSize", 0.0)));
46 const int reserve =
number(
object,
"magazineSize", 0.0) > 0.0 ? 0 : -1;
48 {
"reload",
Value(
number(
object,
"reloadTime", 1.0))},
49 {
"reserve",
Value(reserve)}}));
51 if (
field(
object,
"projectile") ==
nullptr && speed > 0.0) {
53 {
"pelletCount",
Value(1)},
54 {
"pelletSpread",
Value(0.0)},
55 {
"speed",
Value(speed)},
56 {
"type",
Value(
"rts")}}));
58 const std::string targets =
text(
object,
"targets");
59 if (
field(
object,
"targetsGround") ==
nullptr)
60 object.emplace(
"targetsGround",
Value(targets !=
"air"));
61 if (
field(
object,
"targetsAir") ==
nullptr)
62 object.emplace(
"targetsAir",
Value(targets !=
"ground"));
65bool available(
const definitions::DefinitionRegistry& registry,
const std::vector<StagedDefinition>& staged,
66 std::string_view
type, std::string_view
id) {
67 if (
id.empty())
return true;
68 if (std::any_of(staged.begin(), staged.end(), [&](
const auto&
value) {
69 return value.type == type && value.id == id;
71 auto resolved = registry.resolve(std::string(
type), std::string(
id));
72 if (resolved)
return true;
73 resolved.ignore(
"optional RTS content cross-reference lookup");
77Result<void> requireReference(
const definitions::DefinitionRegistry& registry,
78 const std::vector<StagedDefinition>& staged,
const StagedDefinition& owner,
82 const auto*
id =
source->getIf<std::string>();
86 owner.type +
"." + owner.id +
"." + std::string(
member), {},
"rts.content"));
87 if (!available(registry, staged, targetType, *
id))
90 "RTS content references an unknown " + std::string(targetType) +
" '" + *
id +
"'",
91 owner.type +
"." + owner.id +
"." + std::string(
member), {},
"rts.content"));
98 std::string_view json) {
101 Value root = std::move(parsed).takeValue();
103 if (
object ==
nullptr)
106 static constexpr std::array<std::pair<std::string_view, std::string_view>, 7> arrays{{
107 {
"weapons",
"weapon"}, {
"units",
"unit"}, {
"buildings",
"building"}, {
"upgrades",
"upgrade"},
108 {
"statusEffects",
"effect"}, {
"abilities",
"ability"}, {
"damageMultipliers",
"damage_multiplier"},
110 std::vector<StagedDefinition> staged;
111 std::set<std::pair<std::string, std::string>> keys;
114 if (
value ==
nullptr)
continue;
118 "RTS content collection must be an array",
119 std::string(
member), {},
"rts.content"));
125 std::string(
member) +
"[" + std::to_string(
index) +
"]", {},
"rts.content"));
127 std::string
id =
text(normalized,
"id");
128 if (
type ==
"damage_multiplier" &&
id.empty()) {
129 const std::string damage =
text(normalized,
"damageType");
130 const std::string armor =
text(normalized,
"armorType");
131 if (!damage.empty() && !armor.empty()) {
132 id = damage +
"." + armor;
133 normalized.emplace(
"id",
Value(
id));
139 std::string(
member) +
"[" + std::to_string(
index) +
"].id", {},
"rts.content"));
140 if (!keys.emplace(std::string(
type),
id).second)
143 std::string(
type) +
":" +
id, {},
"rts.content"));
144 if (
type ==
"weapon") normalizeWeapon(normalized);
145 staged.push_back({std::string(
type), std::move(
id),
Value(std::move(normalized))});
148 for (
const auto& definition : staged) {
150 if (definition.type ==
"unit") {
151 for (
const auto& [
member,
target] : {std::pair{
"weaponType",
"weapon"},
152 {
"producer",
"building"}, {
"prerequisite",
"building"}}) {
153 auto checked = requireReference(registry, staged, definition, *item,
member,
target);
156 }
else if (definition.type ==
"building") {
157 for (
const auto& [
member,
target] : {std::pair{
"weaponType",
"weapon"},
158 {
"prerequisite",
"building"}}) {
159 auto checked = requireReference(registry, staged, definition, *item,
member,
target);
162 }
else if (definition.type ==
"upgrade") {
163 for (
const auto& [
member,
target] : {std::pair{
"producer",
"building"},
164 {
"prerequisiteBuilding",
"building"},
165 {
"prerequisiteUpgrade",
"upgrade"},
166 {
"targetUnit",
"unit"}, {
"targetBuilding",
"building"}}) {
167 auto checked = requireReference(registry, staged, definition, *item,
member,
target);
170 }
else if (definition.type ==
"ability") {
171 for (
const auto& [
member,
target] : {std::pair{
"casterUnit",
"unit"},
172 {
"statusEffect",
"effect"}}) {
173 auto checked = requireReference(registry, staged, definition, *item,
member,
target);
181 for (
const auto& definition : staged) {
182 auto encoded = definition.value.toJson();
188 auto existing = registry.
handle(definition.type, definition.id);
189 const bool replace = existing.ok();
190 if (!replace) existing.ignore(
"RTS content insert lookup");
191 auto published = replace ? registry.
replace(definition.type, definition.id, 1, encoded.value())
192 : registry.
insert(definition.type, definition.id, 1, encoded.value());
194 const Status failure = published.status();
std::unordered_map< std::string, QuestRuntime > entries
Atomic RTS content-pack import into the canonical definition registry.
float(ui::Theme::* member)[4]
const UnitySourceAsset & source
Owning, renderer-independent dynamic values.
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.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
Structured status and zero or more diagnostics for an operation.
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
The canonical owning dynamic value used by data-facing protocols.
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
std::map< std::string, Value > Object
std::vector< Value > Array
const T * getIf() const noexcept
Return a typed pointer, or nullptr when the kind differs.
Deterministic registry of topic-neutral, versioned JSON definitions.
eve::Result< DefinitionHandle > insert(const std::string &type, const std::string &id, int version, const std::string &json)
Inserts a definition using the common generation-qualified registry.
std::string snapshotJson() const
Exports all registry state as deterministic canonical JSON.
eve::Result< DefinitionHandle > replace(const std::string &type, const std::string &id, int version, const std::string &json)
Replaces a live definition and invalidates its previous handle.
eve::Result< DefinitionHandle > handle(const std::string &type, const std::string &id) const
Returns the current generation-qualified handle through Result.
eve::Result< void > restoreJson(const std::string &json)
Transactionally restores registry state from snapshotJson() output.
static Result< ContentImportReceipt > load(definitions::DefinitionRegistry ®istry, std::string_view json)
Parse, cross-validate, normalize weapons, and atomically import one JSON pack.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
const EditorValue * field(const EditorValue &value, const char *name)
Receipt for one atomically imported RTS content pack.