载入中...
搜索中...
未找到
RTSTech.cpp
浏览该文件的文档.
1#include "rts/RTSTech.h"
2
3#include "common/Value.h"
4#include "rts/RTSTypes.h"
5
6#include <algorithm>
7#include <cmath>
8#include <string>
9#include <utility>
10
11namespace eve::rts {
12namespace {
13
14const Value* field(const Value::Object& object, std::string_view name) {
15 const auto it = object.find(std::string(name));
16 return it == object.end() ? nullptr : &it->second;
17}
18
19std::string text(const Value::Object& object, std::string_view name) {
20 const auto* value = field(object, name);
21 const auto* string = value == nullptr ? nullptr : value->getIf<std::string>();
22 return string == nullptr ? std::string{} : *string;
23}
24
25Result<double> factor(const Value::Object& object, std::string_view name) {
26 const auto* value = field(object, name);
28 double result = 0.0;
29 if (const auto* real = value->getIf<double>()) result = *real;
30 else if (const auto* integer = value->getIf<std::int64_t>()) result = static_cast<double>(*integer);
31 else
33 DiagnosticCode::InvalidArgument, "RTS upgrade factor must be numeric", std::string(name), {}, "rts.tech"));
34 if (!std::isfinite(result) || result < 0.0)
36 "RTS upgrade factor must be finite and non-negative",
37 std::string(name), {}, "rts.tech"));
38 return Result<double>::success(result);
39}
40
41bool contains(const std::vector<std::string>& values, std::string_view value) {
42 return std::binary_search(values.begin(), values.end(), value);
43}
44
45bool insert(std::vector<std::string>& values, std::string value) {
46 const auto at = std::lower_bound(values.begin(), values.end(), value);
47 if (at != values.end() && *at == value) return false;
48 values.insert(at, std::move(value));
49 return true;
50}
51
52bool sameFaction(const FactionLink& link, const Faction& faction) { return link.resolve() == &faction; }
53
54Result<Value::Object> upgrade(definitions::DefinitionRegistry& registry, std::string_view id) {
55 auto resolved = registry.resolve("upgrade", std::string(id));
56 if (!resolved) return Result<Value::Object>::failure(resolved.status());
57 auto parsed = Value::fromJson(resolved.value().get().json);
58 if (!parsed) return Result<Value::Object>::failure(parsed.status());
59 auto value = std::move(parsed).takeValue();
60 const auto* object = value.getIf<Value::Object>();
61 if (object == nullptr)
63 DiagnosticCode::InvalidArgument, "RTS upgrade definition must be an object", "upgrade", {}, "rts.tech"));
64 return Result<Value::Object>::success(*object);
65}
66
67Result<Value::Object> upgrade(definitions::DefinitionRegistry& registry,
68 const production::ProductionTask& task) {
69 if (!task.definition.isValid()) return upgrade(registry, task.product);
70 auto resolved = registry.resolveHandle(task.definition);
71 if (!resolved) return Result<Value::Object>::failure(resolved.status());
72 auto parsed = Value::fromJson(resolved.value().get().json);
73 if (!parsed) return Result<Value::Object>::failure(parsed.status());
74 auto value = std::move(parsed).takeValue();
75 const auto* object = value.getIf<Value::Object>();
76 if (object == nullptr)
78 DiagnosticCode::InvalidArgument, "RTS pinned upgrade definition must be an object", task.product));
79 return Result<Value::Object>::success(*object);
80}
81
82Result<void> applyUnit(Unit& unit, std::string_view upgradeId, const Value::Object& definition) {
83 const std::string target = text(definition, "targetUnit");
84 if (!target.empty() && unit.definition()->id.name() != target)
86 if (target.empty() && !text(definition, "targetBuilding").empty())
88 if (contains(unit.technology()->applied, upgradeId))
90 auto attack = factor(definition, "attackMultiplier");
91 auto health = factor(definition, "healthMultiplier");
92 auto speed = factor(definition, "speedMultiplier");
93 auto gather = factor(definition, "gatherMultiplier");
94 auto shield = factor(definition, "shieldMultiplier");
95 auto shieldRegen = factor(definition, "shieldRegenMultiplier");
96 if (!attack) return Result<void>::failure(attack.status());
97 if (!health) return Result<void>::failure(health.status());
98 if (!speed) return Result<void>::failure(speed.status());
99 if (!gather) return Result<void>::failure(gather.status());
100 if (!shield) return Result<void>::failure(shield.status());
101 if (!shieldRegen) return Result<void>::failure(shieldRegen.status());
102 unit.combat()->upgradeDamageFactor *= static_cast<float>(attack.value());
103 auto& durability = unit.durability()->state;
104 if (durability.maxHealth > 0.0) {
105 const double ratio = std::clamp(durability.health / durability.maxHealth, 0.0, 1.0);
106 durability.maxHealth *= health.value();
107 durability.health = durability.maxHealth * ratio;
108 }
109 unit.motion()->speed *= static_cast<float>(speed.value());
110 unit.worker()->gatherRate *= static_cast<float>(gather.value());
111 if (unit.shield()->capacity > 0.0f) {
112 const float ratio = std::clamp(unit.shield()->value / unit.shield()->capacity, 0.0f, 1.0f);
113 unit.shield()->capacity *= static_cast<float>(shield.value());
114 unit.shield()->value = unit.shield()->capacity * ratio;
115 }
116 unit.shield()->regenRate *= static_cast<float>(shieldRegen.value());
117 insert(unit.technology()->applied, std::string(upgradeId));
119}
120
121Result<void> applyBuilding(Building& building, std::string_view upgradeId, const Value::Object& definition) {
122 const std::string target = text(definition, "targetBuilding");
123 if (!target.empty() && building.definition()->id.name() != target)
125 if (target.empty() && !text(definition, "targetUnit").empty())
127 if (contains(building.technology()->applied, upgradeId))
129 auto health = factor(definition, "healthMultiplier");
130 auto shield = factor(definition, "shieldMultiplier");
131 auto shieldRegen = factor(definition, "shieldRegenMultiplier");
132 if (!health) return Result<void>::failure(health.status());
133 if (!shield) return Result<void>::failure(shield.status());
134 if (!shieldRegen) return Result<void>::failure(shieldRegen.status());
135 auto& integrity = building.integrity()->state;
136 if (integrity.maxHealth > 0.0) {
137 const double ratio = std::clamp(integrity.health / integrity.maxHealth, 0.0, 1.0);
138 integrity.maxHealth *= health.value();
139 integrity.health = integrity.maxHealth * ratio;
140 }
141 if (building.shield()->capacity > 0.0f) {
142 const float ratio = std::clamp(building.shield()->value / building.shield()->capacity, 0.0f, 1.0f);
143 building.shield()->capacity *= static_cast<float>(shield.value());
144 building.shield()->value = building.shield()->capacity * ratio;
145 }
146 building.shield()->regenRate *= static_cast<float>(shieldRegen.value());
147 insert(building.technology()->applied, std::string(upgradeId));
149}
150
151} // namespace
152
154 std::size_t processed = 0;
155 std::vector<std::pair<ecs::EntityHandle, std::string>> settlements;
156 auto buildings = ecs::View<Building, Building::Identity, Building::Faction, Building::Production>();
157 for (auto it = buildings.begin(); it != buildings.end(); ++it) {
158 auto [identity, factionLink, production] = *it;
159 auto* faction = dynamic_cast<Faction*>(factionLink->link.resolve());
160 if (faction == nullptr) continue;
161 for (const auto& task : production->values.readyToSettle("research")) {
162 if (contains(faction->technology()->consumedTasks, task.id)) {
163 settlements.emplace_back(identity->self, task.id);
164 continue;
165 }
166 auto definition = upgrade(registry, task);
167 if (!definition) return Result<std::size_t>::failure(definition.status());
168 const std::string prerequisite = text(definition.value(), "prerequisiteUpgrade");
169 if (!prerequisite.empty() && !contains(faction->technology()->unlocked, prerequisite))
171 "completed RTS research lacks its prerequisite",
172 task.product, {}, "rts.tech"));
173 faction->technology()->unlockedDefinitions.insert_or_assign(task.product, definition.value());
174 insert(faction->technology()->unlocked, task.product);
175 insert(faction->technology()->consumedTasks, task.id);
176 settlements.emplace_back(identity->self, task.id);
177 ++processed;
178 }
179 }
180 auto factions = ecs::View<Faction, Faction::Identity, Faction::Technology>();
181 for (auto fit = factions.begin(); fit != factions.end(); ++fit) {
182 auto [identity, technology] = *fit;
183 auto* faction = dynamic_cast<Faction*>(ecs::try_get(identity->self));
184 if (faction == nullptr) continue;
185 for (const auto& upgradeId : technology->unlocked) {
186 Result<Value::Object> definition = [&]() {
187 const auto pinned = technology->unlockedDefinitions.find(upgradeId);
188 if (pinned != technology->unlockedDefinitions.end())
190 return upgrade(registry, upgradeId);
191 }();
192 if (!definition) return Result<std::size_t>::failure(definition.status());
193 auto allUnits = ecs::View<Unit, Unit::Identity, Unit::Faction>();
194 for (auto it = allUnits.begin(); it != allUnits.end(); ++it) {
195 auto [unitIdentity, factionLink] = *it;
196 auto* unit = dynamic_cast<Unit*>(ecs::try_get(unitIdentity->self));
197 if (unit != nullptr && sameFaction(factionLink->link, *faction)) {
198 auto applied = applyUnit(*unit, upgradeId, definition.value());
199 if (!applied) return Result<std::size_t>::failure(applied.status());
200 }
201 }
202 for (auto it = buildings.begin(); it != buildings.end(); ++it) {
203 auto [buildingIdentity, factionLink, production] = *it;
204 auto* building = dynamic_cast<Building*>(ecs::try_get(buildingIdentity->self));
205 if (building != nullptr && sameFaction(factionLink->link, *faction)) {
206 auto applied = applyBuilding(*building, upgradeId, definition.value());
207 if (!applied) return Result<std::size_t>::failure(applied.status());
208 }
209 }
210 }
211 }
212 for (const auto& [buildingHandle, taskId] : settlements) {
213 auto* building = dynamic_cast<Building*>(ecs::try_get(buildingHandle));
214 if (building == nullptr)
216 DiagnosticCode::StaleHandle, "RTS research producer disappeared during settlement", taskId));
218 receipt.settlementId = "rts.research:" + taskId;
219 auto settled = building->production()->values.settle(taskId, std::move(receipt));
220 if (!settled) return Result<std::size_t>::failure(settled.status());
221 }
222 return Result<std::size_t>::success(processed,
224}
225
226} // namespace eve::rts
LogicalId target
double value
std::map< std::string, Var > values
eve::ResourcePin pinned
std::string text
std::string name
std::string taskId
Research settlement over canonical Definitions and Production.
RTS domain roots and composition-only components.
std::string string
TacticalUnit * unit
std::size_t at
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
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
Faction domain root; membership is a set of typed runtime handles.
Definition RTSTypes.h:1178
static Result< std::size_t > step(definitions::DefinitionRegistry &registry)
Consume completed research tasks and apply their canonical upgrade definitions.
Definition RTSTech.cpp:153
RTS unit domain root.
Definition RTSTypes.h:495
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)
EntityLink< FactionLinkTag > FactionLink
Definition RTSTypes.h:285
Owning proof that a domain consumer published one task exactly once.
Definition Production.h:46