15 const auto it =
object.find(std::string(
name));
16 return it ==
object.end() ? nullptr : &it->second;
21 const auto*
string =
value ==
nullptr ? nullptr :
value->getIf<std::string>();
22 return string ==
nullptr ? std::string{} : *
string;
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);
34 if (!std::isfinite(result) || result < 0.0)
36 "RTS upgrade factor must be finite and non-negative",
37 std::string(
name), {},
"rts.tech"));
41bool contains(
const std::vector<std::string>&
values, std::string_view
value) {
45bool insert(std::vector<std::string>&
values, std::string
value) {
52bool sameFaction(
const FactionLink& link,
const Faction& faction) {
return link.resolve() == &faction; }
54Result<Value::Object> upgrade(definitions::DefinitionRegistry& registry, std::string_view
id) {
55 auto resolved = registry.resolve(
"upgrade", std::string(
id));
59 auto value = std::move(parsed).takeValue();
61 if (
object ==
nullptr)
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);
74 auto value = std::move(parsed).takeValue();
76 if (
object ==
nullptr)
82Result<void> applyUnit(Unit&
unit, std::string_view upgradeId,
const Value::Object& definition) {
83 const std::string
target =
text(definition,
"targetUnit");
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");
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;
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;
116 unit.shield()->regenRate *=
static_cast<float>(shieldRegen.value());
117 insert(
unit.technology()->applied, std::string(upgradeId));
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");
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;
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;
146 building.shield()->regenRate *=
static_cast<float>(shieldRegen.value());
147 insert(building.technology()->applied, std::string(upgradeId));
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);
166 auto definition = upgrade(registry, task);
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);
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) {
187 const auto pinned = technology->unlockedDefinitions.find(upgradeId);
188 if (
pinned != technology->unlockedDefinitions.end())
190 return upgrade(registry, upgradeId);
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());
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());
212 for (
const auto& [buildingHandle,
taskId] : settlements) {
213 auto* building =
dynamic_cast<Building*
>(ecs::try_get(buildingHandle));
214 if (building ==
nullptr)
219 auto settled = building->production()->values.settle(
taskId, std::move(receipt));
std::map< std::string, Var > values
Research settlement over canonical Definitions and Production.
RTS domain roots and composition-only components.
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.
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
std::map< std::string, Value > Object
Deterministic registry of topic-neutral, versioned JSON definitions.
RTS building domain root with placement and production composition.
Faction domain root; membership is a set of typed runtime handles.
static Result< std::size_t > step(definitions::DefinitionRegistry ®istry)
Consume completed research tasks and apply their canonical upgrade definitions.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
const EditorValue * field(const EditorValue &value, const char *name)
EntityLink< FactionLinkTag > FactionLink
Owning proof that a domain consumer published one task exactly once.