载入中...
搜索中...
未找到
RTSArchetype.cpp
浏览该文件的文档.
1#include "rts/RTSArchetype.h"
2
3#include "common/Value.h"
5
6#include <algorithm>
7#include <cmath>
8#include <limits>
9#include <string>
10
11namespace eve::rts {
12namespace {
13
14const Value* field(const Value::Object& object, std::string_view name) {
15 const auto found = object.find(std::string(name));
16 return found == object.end() ? nullptr : &found->second;
17}
18
19Result<Value::Object> definitionObject(definitions::DefinitionRegistry& registry, std::string_view type,
20 const LogicalId& id) {
21 if (!id.isValid() || id.name().empty())
23 Diagnostic::error(DiagnosticCode::InvalidArgument, "RTS root requires a logical definition",
24 std::string(type) + ".definition", {}, "rts.archetype"));
25 auto resolved = registry.resolve(std::string(type), std::string(id.name()));
26 if (!resolved) return Result<Value::Object>::failure(resolved.status());
27 auto parsed = Value::fromJson(resolved.value().get().json);
28 if (!parsed) return Result<Value::Object>::failure(parsed.status());
29 auto value = std::move(parsed).takeValue();
30 const auto* object = value.getIf<Value::Object>();
31 if (object == nullptr)
33 "RTS definition must be a JSON object",
34 std::string(type) + ".json", {}, "rts.archetype"));
35 return Result<Value::Object>::success(*object);
36}
37
38Result<float> number(const Value::Object& object, std::string_view name, float fallback,
39 float minimum = -std::numeric_limits<float>::max()) {
40 const Value* source = field(object, name);
41 double value = fallback;
42 if (source != nullptr) {
43 if (const auto* real = source->getIf<double>()) value = *real;
44 else if (const auto* integer = source->getIf<std::int64_t>()) value = static_cast<double>(*integer);
45 else
47 "RTS archetype numeric field must be a number",
48 std::string(name), {}, "rts.archetype"));
49 }
50 if (!std::isfinite(value) || value < minimum || value > std::numeric_limits<float>::max())
52 "RTS archetype numeric field is outside its valid range",
53 std::string(name), {}, "rts.archetype"));
54 return Result<float>::success(static_cast<float>(value));
55}
56
57Result<int> integer(const Value::Object& object, std::string_view name, int fallback, int minimum = 0) {
58 const Value* source = field(object, name);
59 std::int64_t value = fallback;
60 if (source != nullptr) {
61 if (const auto* exact = source->getIf<std::int64_t>()) value = *exact;
62 else
64 "RTS archetype integer field must be an integer",
65 std::string(name), {}, "rts.archetype"));
66 }
67 if (value < minimum || value > std::numeric_limits<int>::max())
69 "RTS archetype integer field is outside its valid range",
70 std::string(name), {}, "rts.archetype"));
71 return Result<int>::success(static_cast<int>(value));
72}
73
74Result<bool> boolean(const Value::Object& object, std::string_view name, bool fallback) {
75 const Value* source = field(object, name);
76 if (source == nullptr) return Result<bool>::success(fallback);
77 const auto* value = source->getIf<bool>();
78 if (value == nullptr)
80 "RTS archetype boolean field must be boolean", std::string(name),
81 {}, "rts.archetype"));
83}
84
85Result<std::string> text(const Value::Object& object, std::string_view name, std::string fallback = {}) {
86 const Value* source = field(object, name);
87 if (source == nullptr) return Result<std::string>::success(std::move(fallback));
88 const auto* value = source->getIf<std::string>();
89 if (value == nullptr)
91 "RTS archetype text field must be a string",
92 std::string(name), {}, "rts.archetype"));
94}
95
96Result<TagSet> tags(const Value::Object& object, std::string_view name) {
97 TagSet result;
98 const Value* source = field(object, name);
99 if (source == nullptr) return Result<TagSet>::success(std::move(result));
100 const auto* array = source->getIf<Value::Array>();
101 if (array == nullptr)
103 "RTS archetype tags must be an array", std::string(name), {},
104 "rts.archetype"));
105 for (std::size_t index = 0; index < array->size(); ++index) {
106 const auto* value = (*array)[index].getIf<std::string>();
107 if (value == nullptr)
109 Diagnostic::error(DiagnosticCode::InvalidArgument, "RTS archetype tag must be a string",
110 std::string(name) + "[" + std::to_string(index) + "]", {}, "rts.archetype"));
111 auto added = result.add(*value);
112 if (!added) return Result<TagSet>::failure(added.status());
113 }
114 return Result<TagSet>::success(std::move(result));
115}
116
117} // namespace
118
120 const ArchetypeWeaponFactory& weaponFactory) {
121 auto objectResult = definitionObject(registry, "unit", unit.definition()->id);
122 if (!objectResult) return Result<void>::failure(objectResult.status());
123 const auto object = std::move(objectResult).takeValue();
124
125 auto health = number(object, "health", 1.0f, 0.0f);
126 auto speed = number(object, "speed", 1.0f, 0.0f);
127 auto radius = number(object, "radius", 0.5f, 0.0f);
128 auto sight = number(object, "sightRange", 0.0f, 0.0f);
129 auto detection = number(object, "detectionRange", 0.0f, 0.0f);
130 auto radar = number(object, "radarRange", 0.0f, 0.0f);
131 auto jamming = number(object, "jammingRange", 0.0f, 0.0f);
132 auto range = number(object, "attackRange", 0.0f, 0.0f);
133 auto role = text(object, "role");
134 auto weaponType = text(object, "weaponType");
135 auto targetTags = tags(object, "targetTags");
136 if (!health || !speed || !radius || !sight || !detection || !radar || !jamming || !range || !role ||
137 !weaponType || !targetTags)
139 "RTS unit definition contains an invalid core field", "unit", {},
140 "rts.archetype"));
141
142 auto capacity = number(object, "cargoCapacity", role.value() == "worker" ? 10.0f : 0.0f, 0.0f);
143 auto gather = number(object, "gatherRate", role.value() == "worker" ? 5.0f : 0.0f, 0.0f);
144 auto buildRate = number(object, "buildRate", 1.0f, 0.0f);
145 auto repairRate = number(object, "repairRate", 0.0f, 0.0f);
146 auto captureRate = number(object, "captureRate", 0.0f, 0.0f);
147 auto shield = number(object, "shieldCapacity", 0.0f, 0.0f);
148 auto shieldRegen = number(object, "shieldRegenRate", 0.0f, 0.0f);
149 auto shieldDelay = number(object, "shieldRegenDelay", 0.0f, 0.0f);
150 auto transport = integer(object, "transportCapacity", 0);
151 auto airborne = text(object, "targetDomain");
152 auto suppression = number(object, "suppressionCapacity", 0.0f, 0.0f);
153 auto suppressionRecovery = number(object, "suppressionRecoveryRate", 0.0f, 0.0f);
154 auto suppressedSpeed = number(object, "suppressedSpeedFactor", 1.0f, 0.0f);
155 auto suppressedDamage = number(object, "suppressedDamageFactor", 1.0f, 0.0f);
156 auto retreatEnabled = boolean(object, "suppressionRetreatEnabled", false);
157 auto retreatThreshold = number(object, "suppressionRetreatThreshold", 0.8f, 0.0f);
158 auto retreatDistance = number(object, "suppressionRetreatDistance", 6.0f, 0.0f);
159 auto moraleRange = number(object, "moraleAuraRange", 0.0f, 0.0f);
160 auto moraleFactor = number(object, "moraleAuraSuppressionFactor", 1.0f, 0.0f);
161 auto moraleRecovery = number(object, "moraleAuraRecoveryBonus", 0.0f, 0.0f);
162 auto veteranThreshold = number(object, "veteranThreshold", 0.0f, 0.0f);
163 auto eliteThreshold = number(object, "eliteThreshold", 0.0f, 0.0f);
164 auto veteranDamage = number(object, "veteranDamageFactor", 1.0f, 0.0f);
165 auto eliteDamage = number(object, "eliteDamageFactor", 1.0f, 0.0f);
166 auto veteranHealth = number(object, "veteranHealthFactor", 1.0f, 0.0f);
167 auto eliteHealth = number(object, "eliteHealthFactor", 1.0f, 0.0f);
168 auto commandRange = number(object, "commandRange", 0.0f, 0.0f);
169 auto commandCapacity = integer(object, "commandCapacity", 0);
170 auto commandCost = integer(object, "commandCost", 1);
171 auto commandPriority = integer(object, "commandPriority", 0);
172 auto commandJamming = number(object, "commandJammingRange", 0.0f, 0.0f);
173 auto commandRequired = boolean(object, "requiresCommand", false);
174 auto relayUplink = boolean(object, "commandRelayRequiresUplink", false);
175 auto outOfCommandSpeed = number(object, "outOfCommandSpeedFactor", 1.0f, 0.0f);
176 auto outOfCommandDamage = number(object, "outOfCommandDamageFactor", 1.0f, 0.0f);
177 auto ammoCapacity = number(object, "ammoSupplyCapacity", 0.0f, 0.0f);
178 auto ammoRange = number(object, "ammoSupplyRange", 0.0f, 0.0f);
179 auto ammoRate = number(object, "ammoSupplyRate", 0.0f, 0.0f);
180 auto ammoRelay = boolean(object, "ammoRelayEnabled", false);
181 auto autoResupply = boolean(object, "autoResupplyEnabled", false);
182 auto autoThreshold = number(object, "autoResupplyThreshold", 0.5f, 0.0f);
183 auto reserveAmmo = number(object, "reserveAmmoCapacity", 0.0f, 0.0f);
184 auto supplyPriority = number(object, "supplyPriority", 1.0f, 0.0f);
185 auto firingTurnRate = number(object, "turretTurnRate", 0.0f, 0.0f);
186 auto firingArc = number(object, "firingArc", 0.01f, 0.0f);
187 auto firingHeight = number(object, "firingHeight", 1.0f, 0.000001f);
188 auto targetHeight = number(object, "targetHeight", 1.0f, 0.000001f);
189 if (!capacity || !gather || !buildRate || !repairRate || !captureRate || !shield || !shieldRegen ||
190 !shieldDelay || !transport || !airborne || !suppression || !suppressionRecovery ||
191 !suppressedSpeed || !suppressedDamage || !retreatEnabled || !retreatThreshold || !retreatDistance ||
192 !moraleRange || !moraleFactor || !moraleRecovery || !veteranThreshold || !eliteThreshold ||
193 !veteranDamage || !eliteDamage || !veteranHealth || !eliteHealth || !commandRange ||
194 !commandCapacity || !commandCost || !commandPriority || !commandJamming || !commandRequired ||
195 !relayUplink || !outOfCommandSpeed || !outOfCommandDamage || !ammoCapacity || !ammoRange ||
196 !ammoRate || !ammoRelay || !autoResupply || !autoThreshold || !reserveAmmo || !supplyPriority ||
197 !firingTurnRate || !firingArc || !firingHeight || !targetHeight)
199 "RTS unit definition contains an invalid capability field",
200 "unit", {}, "rts.archetype"));
201
202 weapon::WeaponEntity* weapon = nullptr;
203 if (!weaponType.value().empty()) {
204 if (!weaponFactory)
206 "RTS unit definition requires a weapon factory",
207 "unit.weaponType", {}, "rts.archetype"));
208 auto created = weaponFactory(weaponType.value(), unit.identity()->subject.persistentId());
209 if (!created) return Result<void>::failure(created.status());
210 weapon = std::move(created).takeValue();
211 }
212
213 unit.motion()->speed = speed.value();
214 unit.motion()->airborne = airborne.value() == "air";
215 unit.crowd()->radius = radius.value();
216 unit.vision()->sightRange = sight.value();
217 unit.vision()->detectionRange = detection.value();
218 unit.vision()->radarRange = radar.value();
219 unit.vision()->jammingRange = jamming.value();
220 unit.combat()->acquisitionRange = std::max(sight.value(), range.value());
221 unit.combat()->engagementRange = range.value();
222 unit.combat()->leashRange = std::max(sight.value(), range.value()) * 1.5f;
223 unit.durability()->state.subject = unit.identity()->subject;
224 unit.durability()->state.health = health.value();
225 unit.durability()->state.maxHealth = health.value();
226 unit.durability()->alive = health.value() > 0.0f;
227 unit.worker()->capacity = capacity.value();
228 unit.worker()->gatherRate = gather.value();
229 unit.worker()->buildRate = buildRate.value();
230 unit.worker()->repairRate = repairRate.value();
231 unit.worker()->autoAssign = role.value() == "worker";
232 unit.capture()->rate = captureRate.value();
233 unit.shield()->capacity = shield.value();
234 unit.shield()->value = shield.value();
235 unit.shield()->regenRate = shieldRegen.value();
236 unit.shield()->regenDelay = shieldDelay.value();
237 unit.containment()->capacity = static_cast<std::size_t>(transport.value());
238 unit.morale()->capacity = suppression.value();
239 unit.morale()->recoveryRate = suppressionRecovery.value();
240 unit.morale()->suppressedSpeedFactor = suppressedSpeed.value();
241 unit.morale()->suppressedDamageFactor = suppressedDamage.value();
242 unit.morale()->retreatEnabled = retreatEnabled.value();
243 unit.morale()->retreatThreshold = retreatThreshold.value();
244 unit.morale()->retreatDistance = retreatDistance.value();
245 unit.morale()->auraRange = moraleRange.value();
246 unit.morale()->auraSuppressionFactor = moraleFactor.value();
247 unit.morale()->auraRecoveryBonus = moraleRecovery.value();
248 unit.veterancy()->veteranThreshold = veteranThreshold.value();
249 unit.veterancy()->eliteThreshold = eliteThreshold.value();
250 unit.veterancy()->veteranDamageFactor = veteranDamage.value();
251 unit.veterancy()->eliteDamageFactor = eliteDamage.value();
252 unit.veterancy()->veteranHealthFactor = veteranHealth.value();
253 unit.veterancy()->eliteHealthFactor = eliteHealth.value();
254 unit.command()->range = commandRange.value();
255 unit.command()->capacity = commandCapacity.value();
256 unit.command()->cost = commandCost.value();
257 unit.command()->priority = commandPriority.value();
258 unit.command()->jammingRange = commandJamming.value();
259 unit.command()->requiresCommand = commandRequired.value();
260 unit.command()->relayRequiresUplink = relayUplink.value();
261 unit.command()->outOfCommandSpeedFactor = outOfCommandSpeed.value();
262 unit.command()->outOfCommandDamageFactor = outOfCommandDamage.value();
263 unit.supply()->capacity = std::max(ammoCapacity.value(), reserveAmmo.value());
264 unit.supply()->stock = ammoCapacity.value();
265 unit.supply()->range = ammoRange.value();
266 unit.supply()->transferRate = ammoRate.value();
267 unit.supply()->relayEnabled = ammoRelay.value();
268 unit.supply()->autoDispatch = autoResupply.value();
269 unit.supply()->autoThreshold = autoThreshold.value();
270 unit.supply()->priority = supplyPriority.value();
271 unit.combat()->turnRateDegrees = firingTurnRate.value();
272 unit.combat()->aimToleranceDegrees = firingArc.value();
273 unit.combat()->firingHeight = firingHeight.value();
274 unit.combat()->targetHeight = targetHeight.value();
275 unit.tags()->values = std::move(targetTags).takeValue();
276 if (weapon != nullptr) {
277 auto link = WeaponLink::bind(ecs::handle_of(weapon));
278 if (!link) return Result<void>::failure(link.status());
279 unit.weapon()->link = std::move(link).takeValue();
280 }
282}
283
285 const ArchetypeWeaponFactory& weaponFactory) {
286 auto objectResult = definitionObject(registry, "building", building.definition()->id);
287 if (!objectResult) return Result<void>::failure(objectResult.status());
288 const auto object = std::move(objectResult).takeValue();
289 auto health = number(object, "health", 1.0f, 0.0f);
290 auto radius = number(object, "radius", 0.5f, 0.0f);
291 auto buildTime = number(object, "buildTime", 0.0f, 0.0f);
292 auto sight = number(object, "sightRange", 0.0f, 0.0f);
293 auto weaponType = text(object, "weaponType");
294 auto garrison = integer(object, "garrisonCapacity", 0);
295 auto capturable = boolean(object, "capturable", false);
296 auto captureTime = number(object, "captureTime", 5.0f, 0.000001f);
297 auto powerProduced = number(object, "powerProvided", 0.0f, 0.0f);
298 auto powerUsed = number(object, "powerUsed", 0.0f, 0.0f);
299 auto powerPriority = integer(object, "powerPriority", 0);
300 auto influence = number(object, "buildInfluenceRadius", 0.0f, 0.0f);
301 auto incomeResource = text(object, "incomeResource");
302 auto incomeRate = number(object, "incomeRate", 0.0f, 0.0f);
303 auto repairCost = number(object, "repairCostPerHealth", 0.0f, 0.0f);
304 auto repairResource = text(object, "repairResource", "minerals");
305 auto shield = number(object, "shieldCapacity", 0.0f, 0.0f);
306 auto shieldRegen = number(object, "shieldRegenRate", 0.0f, 0.0f);
307 auto shieldDelay = number(object, "shieldRegenDelay", 0.0f, 0.0f);
308 auto garrisonBonus = number(object, "garrisonDamageBonus", 0.0f, 0.0f);
309 auto ammoCapacity = number(object, "ammoSupplyCapacity", 0.0f, 0.0f);
310 auto ammoRange = number(object, "ammoSupplyRange", 0.0f, 0.0f);
311 auto ammoRate = number(object, "ammoSupplyRate", 0.0f, 0.0f);
312 auto ammoResource = text(object, "ammoProductionResource");
313 auto ammoCost = number(object, "ammoProductionCost", 0.0f, 0.0f);
314 auto ammoProduction = number(object, "ammoProductionRate", 0.0f, 0.0f);
315 auto commandRange = number(object, "commandRange", 0.0f, 0.0f);
316 auto commandCapacity = integer(object, "commandCapacity", 0);
317 auto commandJamming = number(object, "commandJammingRange", 0.0f, 0.0f);
318 auto turretTurn = number(object, "turretTurnRate", 0.0f, 0.0f);
319 auto firingArc = number(object, "firingArc", 0.01f, 0.0f);
320 auto firingHeight = number(object, "firingHeight", 2.0f, 0.000001f);
321 auto targetHeight = number(object, "targetHeight", 1.5f, 0.000001f);
322 if (!health || !radius || !buildTime || !sight || !weaponType || !garrison || !capturable || !captureTime ||
323 !powerProduced || !powerUsed || !powerPriority || !influence || !incomeResource || !incomeRate ||
324 !repairCost || !repairResource || !shield || !shieldRegen || !shieldDelay || !garrisonBonus ||
325 !ammoCapacity || !ammoRange || !ammoRate || !ammoResource || !ammoCost || !ammoProduction ||
326 !commandRange || !commandCapacity || !commandJamming || !turretTurn || !firingArc ||
327 !firingHeight || !targetHeight)
329 "RTS building definition contains an invalid field", "building",
330 {}, "rts.archetype"));
331
332 weapon::WeaponEntity* weapon = nullptr;
333 if (!weaponType.value().empty()) {
334 if (!weaponFactory)
336 "RTS building definition requires a weapon factory",
337 "building.weaponType", {}, "rts.archetype"));
338 auto created = weaponFactory(weaponType.value(), building.identity()->subject.persistentId());
339 if (!created) return Result<void>::failure(created.status());
340 weapon = std::move(created).takeValue();
341 }
342
343 building.placement()->placed = true;
344 building.construction()->buildTimeSeconds = buildTime.value();
345 building.integrity()->state.subject = building.identity()->subject;
346 building.integrity()->state.health = health.value();
347 building.integrity()->state.maxHealth = health.value();
348 building.integrity()->alive = health.value() > 0.0f;
349 building.integrity()->repairCostPerHealth = repairCost.value();
350 building.integrity()->repairResource = std::move(repairResource).takeValue();
351 building.shield()->capacity = shield.value();
352 building.shield()->value = shield.value();
353 building.shield()->regenRate = shieldRegen.value();
354 building.shield()->regenDelay = shieldDelay.value();
355 building.vision()->sightRange = sight.value();
356 building.garrison()->capacity = static_cast<std::size_t>(garrison.value());
357 building.garrison()->damageBonusPerOccupant = garrisonBonus.value();
358 building.capture()->capturable = capturable.value();
359 building.capture()->durationSeconds = captureTime.value();
360 building.infrastructure()->powerProduced = powerProduced.value();
361 building.infrastructure()->powerConsumed = powerUsed.value();
362 building.infrastructure()->powerPriority = powerPriority.value();
363 building.infrastructure()->buildInfluenceRadius = influence.value();
364 building.infrastructure()->incomeResource = std::move(incomeResource).takeValue();
365 building.infrastructure()->incomeRate = incomeRate.value();
366 building.supply()->capacity = ammoCapacity.value();
367 building.supply()->stock = ammoCapacity.value();
368 building.supply()->range = ammoRange.value();
369 building.supply()->transferRate = ammoRate.value();
370 building.supply()->productionResource = std::move(ammoResource).takeValue();
371 building.supply()->productionCostPerRound = static_cast<std::int64_t>(std::ceil(ammoCost.value()));
372 building.supply()->productionRate = ammoProduction.value();
373 building.command()->range = commandRange.value();
374 building.command()->capacity = commandCapacity.value();
375 building.command()->jammingRange = commandJamming.value();
376 building.command()->active = commandRange.value() > 0.0f;
377 building.combat()->firingHeight = firingHeight.value();
378 building.combat()->targetHeight = targetHeight.value();
379 building.dropoff()->radius = radius.value();
380 if (const auto dropoff = boolean(object, "dropoff", false); dropoff && dropoff.value())
381 building.dropoff()->acceptedResources.push_back("minerals");
382 if (weapon != nullptr) {
383 auto link = WeaponLink::bind(ecs::handle_of(weapon));
384 if (!link) return Result<void>::failure(link.status());
385 building.weapon()->link = std::move(link).takeValue();
386 building.combat()->acquisitionRange = sight.value();
387 building.combat()->engagementRange = sight.value();
388 building.combat()->turnRateDegrees = turretTurn.value();
389 building.combat()->aimToleranceDegrees = firingArc.value();
390 }
392}
393
394} // namespace eve::rts
double value
float minimum[3]
std::uint32_t capacity
std::string text
Range range
std::string name
float radius
Canonical definition-to-RTS-component materialization.
double number
bool found
int created
bool boolean
TacticalUnit * unit
uint32_t index
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.
Definition Diagnostic.h:125
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
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
Deterministic registry of topic-neutral, versioned JSON definitions.
Definition Definitions.h:58
RTS building domain root with placement and production composition.
Definition RTSTypes.h:840
static Result< void > apply(definitions::DefinitionRegistry &registry, Unit &unit, const ArchetypeWeaponFactory &weaponFactory={})
Apply the Unit root's logical definition atomically.
RTS unit domain root.
Definition RTSTypes.h:495
武器实体:数据全部在组件里,行为在 WeaponSystem。
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
const EditorValue * field(const EditorValue &value, const char *name)
std::function< Result< weapon::WeaponEntity * >(std::string_view definitionId, PersistentId instanceId)> ArchetypeWeaponFactory
Factory that creates one canonical weapon instance from a registry definition.
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.