1 #include "Conditions.h"
2
3 #include "../util/Logger.h"
4 #include "../util/Random.h"
5 #include "../util/i18n.h"
6 #include "../util/ScopedTimer.h"
7 #include "UniverseObject.h"
8 #include "Pathfinder.h"
9 #include "Universe.h"
10 #include "Building.h"
11 #include "BuildingType.h"
12 #include "Fighter.h"
13 #include "Fleet.h"
14 #include "Ship.h"
15 #include "ShipDesign.h"
16 #include "ShipPart.h"
17 #include "ShipHull.h"
18 #include "ObjectMap.h"
19 #include "Planet.h"
20 #include "System.h"
21 #include "Species.h"
22 #include "Special.h"
23 #include "Meter.h"
24 #include "ValueRefs.h"
25 #include "Enums.h"
26 #include "../Empire/Empire.h"
27 #include "../Empire/EmpireManager.h"
28 #include "../Empire/Supply.h"
29
30 #include <boost/algorithm/cxx11/all_of.hpp>
31 #include <boost/algorithm/string/case_conv.hpp>
32 #include <boost/graph/adjacency_list.hpp>
33 #include <boost/graph/st_connected.hpp>
34
35 #include <cfloat>
36
37 using boost::io::str;
38
39 FO_COMMON_API extern const int INVALID_DESIGN_ID;
40
41 bool UserStringExists(const std::string& str);
42
43 namespace {
44 const std::string EMPTY_STRING;
45
46 DeclareThreadSafeLogger(conditions);
47
48 #if BOOST_VERSION >= 106000
49 using boost::placeholders::_1;
50 #endif
51
AddAllObjectsSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)52 void AddAllObjectsSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
53 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingObjects().size());
54 std::transform(objects.ExistingObjects().begin(), objects.ExistingObjects().end(),
55 std::back_inserter(condition_non_targets),
56 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
57 }
58
AddBuildingSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)59 void AddBuildingSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
60 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingBuildings().size());
61 std::transform(objects.ExistingBuildings().begin(), objects.ExistingBuildings().end(),
62 std::back_inserter(condition_non_targets),
63 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
64 }
65
AddFieldSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)66 void AddFieldSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
67 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingFields().size());
68 std::transform(objects.ExistingFields().begin(), objects.ExistingFields().end(),
69 std::back_inserter(condition_non_targets),
70 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
71 }
72
AddFleetSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)73 void AddFleetSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
74 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingFleets().size());
75 std::transform(objects.ExistingFleets().begin(), objects.ExistingFleets().end(),
76 std::back_inserter(condition_non_targets),
77 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
78 }
79
AddPlanetSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)80 void AddPlanetSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
81 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingPlanets().size());
82 std::transform(objects.ExistingPlanets().begin(), objects.ExistingPlanets().end(),
83 std::back_inserter(condition_non_targets),
84 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
85 }
86
AddPopCenterSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)87 void AddPopCenterSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
88 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingPopCenters().size());
89 std::transform(objects.ExistingPopCenters().begin(), objects.ExistingPopCenters().end(),
90 std::back_inserter(condition_non_targets),
91 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
92 }
93
AddResCenterSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)94 void AddResCenterSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
95 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingResourceCenters().size());
96 std::transform(objects.ExistingResourceCenters().begin(), objects.ExistingResourceCenters().end(),
97 std::back_inserter(condition_non_targets),
98 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
99 }
100
AddShipSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)101 void AddShipSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
102 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingShips().size());
103 std::transform(objects.ExistingShips().begin(), objects.ExistingShips().end(),
104 std::back_inserter(condition_non_targets),
105 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
106 }
107
AddSystemSet(const ObjectMap & objects,Condition::ObjectSet & condition_non_targets)108 void AddSystemSet(const ObjectMap& objects, Condition::ObjectSet& condition_non_targets) {
109 condition_non_targets.reserve(condition_non_targets.size() + objects.ExistingSystems().size());
110 std::transform(objects.ExistingSystems().begin(), objects.ExistingSystems().end(),
111 std::back_inserter(condition_non_targets),
112 boost::bind(&std::map<int, std::shared_ptr<const UniverseObject>>::value_type::second, _1));
113 }
114
115 /** Used by 4-parameter Condition::Eval function, and some of its
116 * overrides, to scan through \a matches or \a non_matches set and apply
117 * \a pred to each object, to test if it should remain in its current set
118 * or be transferred from the \a search_domain specified set into the
119 * other. */
120 template <typename Pred>
EvalImpl(Condition::ObjectSet & matches,Condition::ObjectSet & non_matches,Condition::SearchDomain search_domain,const Pred & pred)121 void EvalImpl(Condition::ObjectSet& matches, Condition::ObjectSet& non_matches,
122 Condition::SearchDomain search_domain, const Pred& pred)
123 {
124 auto& from_set = search_domain == Condition::MATCHES ? matches : non_matches;
125 auto& to_set = search_domain == Condition::MATCHES ? non_matches : matches;
126 for (auto it = from_set.begin(); it != from_set.end(); ) {
127 bool match = pred(*it);
128 if ((search_domain == Condition::MATCHES && !match) ||
129 (search_domain == Condition::NON_MATCHES && match))
130 {
131 to_set.push_back(*it);
132 *it = from_set.back();
133 from_set.pop_back();
134 } else {
135 ++it;
136 }
137 }
138 }
139
FlattenAndNestedConditions(const std::vector<Condition::Condition * > & input_conditions)140 std::vector<Condition::Condition*> FlattenAndNestedConditions(
141 const std::vector<Condition::Condition*>& input_conditions)
142 {
143 std::vector<Condition::Condition*> retval;
144 for (Condition::Condition* condition : input_conditions) {
145 if (Condition::And* and_condition = dynamic_cast<Condition::And*>(condition)) {
146 std::vector<Condition::Condition*> flattened_operands =
147 FlattenAndNestedConditions(and_condition->Operands());
148 std::copy(flattened_operands.begin(), flattened_operands.end(), std::back_inserter(retval));
149 } else {
150 if (condition)
151 retval.push_back(condition);
152 }
153 }
154 return retval;
155 }
156
ConditionDescriptionAndTest(const std::vector<Condition::Condition * > & conditions,const ScriptingContext & parent_context,std::shared_ptr<const UniverseObject> candidate_object)157 std::map<std::string, bool> ConditionDescriptionAndTest(
158 const std::vector<Condition::Condition*>& conditions,
159 const ScriptingContext& parent_context,
160 std::shared_ptr<const UniverseObject> candidate_object/* = nullptr*/)
161 {
162 std::map<std::string, bool> retval;
163
164 std::vector<Condition::Condition*> flattened_conditions;
165 if (conditions.empty())
166 return retval;
167 else if (conditions.size() > 1 || dynamic_cast<Condition::And*>(*conditions.begin()))
168 flattened_conditions = FlattenAndNestedConditions(conditions);
169 //else if (dynamic_cast<const Condition::Or*>(*conditions.begin()))
170 // flattened_conditions = FlattenOrNestedConditions(conditions);
171 else
172 flattened_conditions = conditions;
173
174 for (Condition::Condition* condition : flattened_conditions) {
175 retval[condition->Description()] = condition->Eval(parent_context, candidate_object);
176 }
177 return retval;
178 }
179 }
180
181 namespace Condition {
ConditionFailedDescription(const std::vector<Condition * > & conditions,std::shared_ptr<const UniverseObject> candidate_object,std::shared_ptr<const UniverseObject> source_object)182 std::string ConditionFailedDescription(const std::vector<Condition*>& conditions,
183 std::shared_ptr<const UniverseObject> candidate_object/* = nullptr*/,
184 std::shared_ptr<const UniverseObject> source_object/* = nullptr*/)
185 {
186 if (conditions.empty())
187 return UserString("NONE");
188
189 std::string retval;
190
191 // test candidate against all input conditions, and store descriptions of each
192 for (const auto& result : ConditionDescriptionAndTest(conditions, ScriptingContext(source_object), candidate_object)) {
193 if (!result.second)
194 retval += UserString("FAILED") + " <rgba 255 0 0 255>" + result.first +"</rgba>\n";
195 }
196
197 // remove empty line from the end of the string
198 retval = retval.substr(0, retval.length() - 1);
199
200 return retval;
201 }
202
ConditionDescription(const std::vector<Condition * > & conditions,std::shared_ptr<const UniverseObject> candidate_object,std::shared_ptr<const UniverseObject> source_object)203 std::string ConditionDescription(const std::vector<Condition*>& conditions,
204 std::shared_ptr<const UniverseObject> candidate_object/* = nullptr*/,
205 std::shared_ptr<const UniverseObject> source_object/* = nullptr*/)
206 {
207 if (conditions.empty())
208 return UserString("NONE");
209
210 // test candidate against all input conditions, and store descriptions of each
211 auto condition_description_and_test_results =
212 ConditionDescriptionAndTest(conditions, ScriptingContext(source_object), candidate_object);
213 bool all_conditions_match_candidate = true, at_least_one_condition_matches_candidate = false;
214 for (const auto& result : condition_description_and_test_results) {
215 all_conditions_match_candidate = all_conditions_match_candidate && result.second;
216 at_least_one_condition_matches_candidate = at_least_one_condition_matches_candidate || result.second;
217 }
218
219 // concatenate (non-duplicated) single-description results
220 std::string retval;
221 if (conditions.size() > 1 || dynamic_cast<And*>(*conditions.begin())) {
222 retval += UserString("ALL_OF") + " ";
223 retval += (all_conditions_match_candidate ? UserString("PASSED") : UserString("FAILED")) + "\n";
224 } else if (dynamic_cast<Or*>(*conditions.begin())) {
225 retval += UserString("ANY_OF") + " ";
226 retval += (at_least_one_condition_matches_candidate ? UserString("PASSED") : UserString("FAILED")) + "\n";
227 }
228 // else just output single condition description and PASS/FAIL text
229
230 for (const auto& result : condition_description_and_test_results) {
231 retval += (result.second ? UserString("PASSED") : UserString("FAILED"));
232 retval += " " + result.first + "\n";
233 }
234 return retval;
235 }
236
237 #define CHECK_COND_VREF_MEMBER(m_ptr) { if (m_ptr == rhs_.m_ptr) { \
238 /* check next member */ \
239 } else if (!m_ptr || !rhs_.m_ptr) { \
240 return false; \
241 } else { \
242 if (*m_ptr != *(rhs_.m_ptr)) \
243 return false; \
244 } }
245
246 ///////////////////////////////////////////////////////////
247 // Condition //
248 ///////////////////////////////////////////////////////////
249 struct Condition::MatchHelper {
MatchHelperCondition::Condition::MatchHelper250 MatchHelper(const Condition* this_, const ScriptingContext& parent_context) :
251 m_this(this_),
252 m_parent_context(parent_context)
253 {}
254
operator ()Condition::Condition::MatchHelper255 bool operator()(std::shared_ptr<const UniverseObject> candidate) const
256 { return m_this->Match(ScriptingContext(m_parent_context, candidate)); }
257
258 const Condition* m_this;
259 const ScriptingContext& m_parent_context;
260 };
261
262 Condition::~Condition() = default;
263
operator ==(const Condition & rhs) const264 bool Condition::operator==(const Condition& rhs) const {
265 if (this == &rhs)
266 return true;
267
268 if (typeid(*this) != typeid(rhs))
269 return false;
270
271 return true;
272 }
273
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const274 void Condition::Eval(const ScriptingContext& parent_context,
275 ObjectSet& matches, ObjectSet& non_matches,
276 SearchDomain search_domain/* = NON_MATCHES*/) const
277 { EvalImpl(matches, non_matches, search_domain, MatchHelper(this, parent_context)); }
278
Eval(const ScriptingContext & parent_context,Effect::TargetSet & matches,Effect::TargetSet & non_matches,SearchDomain search_domain) const279 void Condition::Eval(const ScriptingContext& parent_context,
280 Effect::TargetSet& matches, Effect::TargetSet& non_matches,
281 SearchDomain search_domain/* = NON_MATCHES*/) const
282 {
283 // reinterpret sets of mutable objects as sets of non-mutable objects.
284 auto& matches_as_objectset = reinterpret_cast<ObjectSet&>(matches);
285 auto& non_matches_as_objectset = reinterpret_cast<ObjectSet&>(non_matches);
286 this->Eval(parent_context, matches_as_objectset, non_matches_as_objectset, search_domain);
287 }
288
Eval(const ScriptingContext & parent_context,ObjectSet & matches) const289 void Condition::Eval(const ScriptingContext& parent_context,
290 ObjectSet& matches) const
291 {
292 matches.clear();
293 ObjectSet condition_initial_candidates;
294
295 // evaluate condition only on objects that could potentially be matched by the condition
296 GetDefaultInitialCandidateObjects(parent_context, condition_initial_candidates);
297
298 matches.reserve(condition_initial_candidates.size());
299 Eval(parent_context, matches, condition_initial_candidates);
300 }
301
Eval(const ScriptingContext & parent_context,Effect::TargetSet & matches) const302 void Condition::Eval(const ScriptingContext& parent_context,
303 Effect::TargetSet& matches) const
304 {
305 // reinterpret sets of mutable objects as sets of non-mutable objects.
306 auto& matches_as_objectset = reinterpret_cast<ObjectSet&>(matches);
307 this->Eval(parent_context, matches_as_objectset);
308 }
309
Eval(const ScriptingContext & parent_context,std::shared_ptr<const UniverseObject> candidate) const310 bool Condition::Eval(const ScriptingContext& parent_context,
311 std::shared_ptr<const UniverseObject> candidate) const
312 {
313 if (!candidate)
314 return false;
315 ObjectSet non_matches, matches;
316 non_matches.push_back(candidate);
317 Eval(parent_context, matches, non_matches);
318 return non_matches.empty(); // if candidate has been matched, non_matches will now be empty
319 }
320
Eval(std::shared_ptr<const UniverseObject> candidate) const321 bool Condition::Eval(std::shared_ptr<const UniverseObject> candidate) const {
322 if (!candidate)
323 return false;
324 ObjectSet non_matches, matches;
325 non_matches.push_back(candidate);
326 Eval(ScriptingContext(), matches, non_matches);
327 return non_matches.empty(); // if candidate has been matched, non_matches will now be empty
328 }
329
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const330 void Condition::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
331 ObjectSet& condition_non_targets) const
332 { AddAllObjectsSet(parent_context.ContextObjects(), condition_non_targets); }
333
Description(bool negated) const334 std::string Condition::Description(bool negated/* = false*/) const
335 { return ""; }
336
Dump(unsigned short ntabs) const337 std::string Condition::Dump(unsigned short ntabs) const
338 { return ""; }
339
Match(const ScriptingContext & local_context) const340 bool Condition::Match(const ScriptingContext& local_context) const
341 { return false; }
342
343 ///////////////////////////////////////////////////////////
344 // Number //
345 ///////////////////////////////////////////////////////////
Number(std::unique_ptr<ValueRef::ValueRef<int>> && low,std::unique_ptr<ValueRef::ValueRef<int>> && high,std::unique_ptr<Condition> && condition)346 Number::Number(std::unique_ptr<ValueRef::ValueRef<int>>&& low,
347 std::unique_ptr<ValueRef::ValueRef<int>>&& high,
348 std::unique_ptr<Condition>&& condition) :
349 m_low(std::move(low)),
350 m_high(std::move(high)),
351 m_condition(std::move(condition))
352 {
353 auto operands = {m_low.get(), m_high.get()};
354 m_root_candidate_invariant =
355 m_condition->RootCandidateInvariant() &&
356 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
357 m_target_invariant =
358 m_condition->TargetInvariant() &&
359 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
360 m_source_invariant =
361 m_condition->SourceInvariant() &&
362 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
363 }
364
operator ==(const Condition & rhs) const365 bool Number::operator==(const Condition& rhs) const {
366 if (this == &rhs)
367 return true;
368 if (typeid(*this) != typeid(rhs))
369 return false;
370
371 const Number& rhs_ = static_cast<const Number&>(rhs);
372
373 CHECK_COND_VREF_MEMBER(m_low)
374 CHECK_COND_VREF_MEMBER(m_high)
375 CHECK_COND_VREF_MEMBER(m_condition)
376
377 return true;
378 }
379
Description(bool negated) const380 std::string Number::Description(bool negated/* = false*/) const {
381 std::string low_str = (m_low ? (m_low->ConstantExpr() ?
382 std::to_string(m_low->Eval()) :
383 m_low->Description())
384 : "0");
385 std::string high_str = (m_high ? (m_high->ConstantExpr() ?
386 std::to_string(m_high->Eval()) :
387 m_high->Description())
388 : std::to_string(INT_MAX));
389
390 const std::string& description_str = (!negated)
391 ? UserString("DESC_NUMBER")
392 : UserString("DESC_NUMBER_NOT");
393 return str(FlexibleFormat(description_str)
394 % low_str
395 % high_str
396 % m_condition->Description());
397 }
398
Dump(unsigned short ntabs) const399 std::string Number::Dump(unsigned short ntabs) const {
400 std::string retval = DumpIndent(ntabs) + "Number";
401 if (m_low)
402 retval += " low = " + m_low->Dump(ntabs);
403 if (m_high)
404 retval += " high = " + m_high->Dump(ntabs);
405 retval += " condition =\n";
406 retval += m_condition->Dump(ntabs+1);
407 return retval;
408 }
409
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const410 void Number::Eval(const ScriptingContext& parent_context,
411 ObjectSet& matches, ObjectSet& non_matches,
412 SearchDomain search_domain/* = NON_MATCHES*/) const
413 {
414 // Number does not have a single valid local candidate to be matched, as it
415 // will match anything if the proper number of objects match the subcondition.
416
417 if (!(
418 (!m_low || m_low->LocalCandidateInvariant())
419 && (!m_high || m_high->LocalCandidateInvariant())
420 )
421 )
422 {
423 ErrorLogger() << "Condition::Number::Eval has local candidate-dependent ValueRefs, but no valid local candidate!";
424 } else if (
425 !parent_context.condition_root_candidate
426 && !(
427 (!m_low || m_low->RootCandidateInvariant())
428 && (!m_high || m_high->RootCandidateInvariant())
429 )
430 )
431 {
432 ErrorLogger() << "Condition::Number::Eval has root candidate-dependent ValueRefs, but expects local candidate to be the root candidate, and has no valid local candidate!";
433 }
434
435 if (!parent_context.condition_root_candidate && !this->RootCandidateInvariant()) {
436 // no externally-defined root candidate, so each object matched must
437 // separately act as a root candidate, and sub-condition must be re-
438 // evaluated for each tested object and the number of objects matched
439 // checked for each object being tested
440 Condition::Eval(parent_context, matches, non_matches, search_domain);
441
442 } else {
443 // Matching for this condition doesn't need to check each candidate object against
444 // the number of subcondition matches, so don't need to use EvalImpl
445 bool in_range = Match(parent_context);
446
447 // transfer objects to or from candidate set, according to whether
448 // number of matches was within the requested range.
449 if (search_domain == MATCHES && !in_range) {
450 // move all objects from matches to non_matches
451 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
452 matches.clear();
453 } else if (search_domain == NON_MATCHES && in_range) {
454 // move all objects from non_matches to matches
455 matches.insert(matches.end(), non_matches.begin(), non_matches.end());
456 non_matches.clear();
457 }
458 }
459 }
460
Match(const ScriptingContext & local_context) const461 bool Number::Match(const ScriptingContext& local_context) const {
462 // get acceptable range of subcondition matches for candidate
463 int low = (m_low ? std::max(0, m_low->Eval(local_context)) : 0);
464 int high = (m_high ? std::min(m_high->Eval(local_context), INT_MAX) : INT_MAX);
465
466 // get set of all UniverseObjects that satisfy m_condition
467 ObjectSet condition_matches;
468 m_condition->Eval(local_context, condition_matches);
469
470 // compare number of objects that satisfy m_condition to the acceptable range of such objects
471 int matched = condition_matches.size();
472 bool in_range = (low <= matched && matched <= high);
473 return in_range;
474 }
475
SetTopLevelContent(const std::string & content_name)476 void Number::SetTopLevelContent(const std::string& content_name) {
477 if (m_low)
478 m_low->SetTopLevelContent(content_name);
479 if (m_high)
480 m_high->SetTopLevelContent(content_name);
481 if (m_condition)
482 m_condition->SetTopLevelContent(content_name);
483 }
484
GetCheckSum() const485 unsigned int Number::GetCheckSum() const {
486 unsigned int retval{0};
487
488 CheckSums::CheckSumCombine(retval, "Condition::Number");
489 CheckSums::CheckSumCombine(retval, m_low);
490 CheckSums::CheckSumCombine(retval, m_high);
491 CheckSums::CheckSumCombine(retval, m_condition);
492
493 TraceLogger() << "GetCheckSum(Number): retval: " << retval;
494 return retval;
495 }
496
497 ///////////////////////////////////////////////////////////
498 // Turn //
499 ///////////////////////////////////////////////////////////
Turn(std::unique_ptr<ValueRef::ValueRef<int>> && low,std::unique_ptr<ValueRef::ValueRef<int>> && high)500 Turn::Turn(std::unique_ptr<ValueRef::ValueRef<int>>&& low,
501 std::unique_ptr<ValueRef::ValueRef<int>>&& high) :
502 m_low(std::move(low)),
503 m_high(std::move(high))
504 {
505 auto operands = {m_low.get(), m_high.get()};
506 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
507 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
508 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
509 }
510
operator ==(const Condition & rhs) const511 bool Turn::operator==(const Condition& rhs) const {
512 if (this == &rhs)
513 return true;
514 if (typeid(*this) != typeid(rhs))
515 return false;
516
517 const Turn& rhs_ = static_cast<const Turn&>(rhs);
518
519 CHECK_COND_VREF_MEMBER(m_low)
520 CHECK_COND_VREF_MEMBER(m_high)
521
522 return true;
523 }
524
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const525 void Turn::Eval(const ScriptingContext& parent_context,
526 ObjectSet& matches, ObjectSet& non_matches,
527 SearchDomain search_domain/* = NON_MATCHES*/) const
528 {
529 // if ValueRef for low or high range limits depend on local candidate, then
530 // they must be evaluated per-candidate.
531 // if there already is a root candidate, then this condition's parameters
532 // can be evaluated assuming it will not change.
533 // if there is no root candidate in the parent context, then this
534 // condition's candidates will be the root candidates, and this condition's
535 // parameters must be root candidate invariant or else must be evaluated
536 // per-candidate
537 bool simple_eval_safe = ((!m_low || m_low->LocalCandidateInvariant()) &&
538 (!m_high || m_high->LocalCandidateInvariant()) &&
539 (parent_context.condition_root_candidate || RootCandidateInvariant()));
540 if (simple_eval_safe) {
541 // Matching for this condition doesn't need to check each candidate object against
542 // the turn number separately, so don't need to use EvalImpl
543 bool match = Match(parent_context);
544
545 // transfer objects to or from candidate set, according to whether the
546 // current turn was within the requested range.
547 if (match && search_domain == NON_MATCHES) {
548 // move all objects from non_matches to matches
549 matches.insert(matches.end(), non_matches.begin(), non_matches.end());
550 non_matches.clear();
551 } else if (!match && search_domain == MATCHES) {
552 // move all objects from matches to non_matches
553 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
554 matches.clear();
555 }
556 } else {
557 // re-evaluate allowed turn range for each candidate object
558 Condition::Eval(parent_context, matches, non_matches, search_domain);
559 }
560 }
561
Description(bool negated) const562 std::string Turn::Description(bool negated/* = false*/) const {
563 std::string low_str;
564 if (m_low)
565 low_str = (m_low->ConstantExpr() ?
566 std::to_string(m_low->Eval()) :
567 m_low->Description());
568 std::string high_str;
569 if (m_high)
570 high_str = (m_high->ConstantExpr() ?
571 std::to_string(m_high->Eval()) :
572 m_high->Description());
573 std::string description_str;
574
575 if (m_low && m_high) {
576 description_str = (!negated)
577 ? UserString("DESC_TURN")
578 : UserString("DESC_TURN_NOT");
579 return str(FlexibleFormat(description_str)
580 % low_str
581 % high_str);
582
583 } else if (m_low) {
584 description_str = (!negated)
585 ? UserString("DESC_TURN_MIN_ONLY")
586 : UserString("DESC_TURN_MIN_ONLY_NOT");
587 return str(FlexibleFormat(description_str)
588 % low_str);
589
590 } else if (m_high) {
591 description_str = (!negated)
592 ? UserString("DESC_TURN_MAX_ONLY")
593 : UserString("DESC_TURN_MAX_ONLY_NOT");
594 return str(FlexibleFormat(description_str)
595 % high_str);
596
597 } else {
598 return (!negated)
599 ? UserString("DESC_TURN_ANY")
600 : UserString("DESC_TURN_ANY_NOT");
601 }
602 }
603
Dump(unsigned short ntabs) const604 std::string Turn::Dump(unsigned short ntabs) const {
605 std::string retval = DumpIndent(ntabs) + "Turn";
606 if (m_low)
607 retval += " low = " + m_low->Dump(ntabs);
608 if (m_high)
609 retval += " high = " + m_high->Dump(ntabs);
610 retval += "\n";
611 return retval;
612 }
613
Match(const ScriptingContext & local_context) const614 bool Turn::Match(const ScriptingContext& local_context) const {
615 int low = (m_low ? std::max(BEFORE_FIRST_TURN, m_low->Eval(local_context)) : BEFORE_FIRST_TURN);
616 int high = (m_high ? std::min(m_high->Eval(local_context), IMPOSSIBLY_LARGE_TURN) : IMPOSSIBLY_LARGE_TURN);
617 int turn = CurrentTurn();
618 return (low <= turn && turn <= high);
619 }
620
SetTopLevelContent(const std::string & content_name)621 void Turn::SetTopLevelContent(const std::string& content_name) {
622 if (m_low)
623 m_low->SetTopLevelContent(content_name);
624 if (m_high)
625 m_high->SetTopLevelContent(content_name);
626 }
627
GetCheckSum() const628 unsigned int Turn::GetCheckSum() const {
629 unsigned int retval{0};
630
631 CheckSums::CheckSumCombine(retval, "Condition::Turn");
632 CheckSums::CheckSumCombine(retval, m_low);
633 CheckSums::CheckSumCombine(retval, m_high);
634
635 TraceLogger() << "GetCheckSum(Turn): retval: " << retval;
636 return retval;
637 }
638
639 ///////////////////////////////////////////////////////////
640 // SortedNumberOf //
641 ///////////////////////////////////////////////////////////
SortedNumberOf(std::unique_ptr<ValueRef::ValueRef<int>> && number,std::unique_ptr<Condition> && condition)642 SortedNumberOf::SortedNumberOf(std::unique_ptr<ValueRef::ValueRef<int>>&& number,
643 std::unique_ptr<Condition>&& condition) :
644 SortedNumberOf(std::move(number), nullptr, SORT_RANDOM, std::move(condition))
645 {}
646
SortedNumberOf(std::unique_ptr<ValueRef::ValueRef<int>> && number,std::unique_ptr<ValueRef::ValueRef<double>> && sort_key_ref,SortingMethod sorting_method,std::unique_ptr<Condition> && condition)647 SortedNumberOf::SortedNumberOf(std::unique_ptr<ValueRef::ValueRef<int>>&& number,
648 std::unique_ptr<ValueRef::ValueRef<double>>&& sort_key_ref,
649 SortingMethod sorting_method,
650 std::unique_ptr<Condition>&& condition) :
651 m_number(std::move(number)),
652 m_sort_key(std::move(sort_key_ref)),
653 m_sorting_method(sorting_method),
654 m_condition(std::move(condition))
655 {
656 m_root_candidate_invariant =
657 (!m_number || m_number->RootCandidateInvariant()) &&
658 (!m_sort_key || m_sort_key->RootCandidateInvariant()) &&
659 (!m_condition || m_condition->RootCandidateInvariant());
660 m_target_invariant =
661 (!m_number || m_number->TargetInvariant()) &&
662 (!m_sort_key || m_sort_key->TargetInvariant()) &&
663 (!m_condition || m_condition->TargetInvariant());
664 m_source_invariant =
665 (!m_number || m_number->SourceInvariant()) &&
666 (!m_sort_key || m_sort_key->SourceInvariant()) &&
667 (!m_condition || m_condition->SourceInvariant());
668
669 }
670
operator ==(const Condition & rhs) const671 bool SortedNumberOf::operator==(const Condition& rhs) const {
672 if (this == &rhs)
673 return true;
674 if (typeid(*this) != typeid(rhs))
675 return false;
676
677 const SortedNumberOf& rhs_ = static_cast<const SortedNumberOf&>(rhs);
678
679 if (m_sorting_method != rhs_.m_sorting_method)
680 return false;
681
682 CHECK_COND_VREF_MEMBER(m_number)
683 CHECK_COND_VREF_MEMBER(m_sort_key)
684 CHECK_COND_VREF_MEMBER(m_condition)
685
686 return true;
687 }
688
689 namespace {
690 /** Random number genrator function to use with random_shuffle */
CustomRandInt(int max_plus_one)691 int CustomRandInt(int max_plus_one)
692 { return RandSmallInt(0, max_plus_one - 1); }
693 int (*CRI)(int) = CustomRandInt;
694
695 /** Transfers the indicated \a number of objects, randomly selected from from_set to to_set */
TransferRandomObjects(unsigned int number,ObjectSet & from_set,ObjectSet & to_set)696 void TransferRandomObjects(unsigned int number, ObjectSet& from_set, ObjectSet& to_set) {
697 // ensure number of objects to be moved is within reasonable range
698 number = std::min<unsigned int>(number, from_set.size());
699 if (number == 0)
700 return;
701
702 // create list of bool flags to indicate whether each item in from_set
703 // with corresponding place in iteration order should be transfered
704 std::vector<bool> transfer_flags(from_set.size(), false); // initialized to all false
705
706 // set first number flags to true
707 std::fill_n(transfer_flags.begin(), number, true);
708
709 // shuffle flags to randomize which flags are set
710 std::random_shuffle(transfer_flags.begin(), transfer_flags.end(), CRI);
711
712 // transfer objects that have been flagged
713 int i = 0;
714 for (auto it = from_set.begin(); it != from_set.end(); ++i) {
715 if (transfer_flags[i]) {
716 to_set.push_back(*it);
717 *it = from_set.back();
718 from_set.pop_back();
719 } else {
720 ++it;
721 }
722 }
723 }
724
725 /** Transfers the indicated \a number of objects, selected from \a from_set
726 * into \a to_set. The objects transferred are selected based on the value
727 * of \a sort_key evaluated on them, with the largest / smallest / most
728 * common sort keys chosen, or a random selection chosen, depending on the
729 * specified \a sorting_method */
TransferSortedObjects(unsigned int number,ValueRef::ValueRef<double> * sort_key,const ScriptingContext & context,SortingMethod sorting_method,ObjectSet & from_set,ObjectSet & to_set)730 void TransferSortedObjects(unsigned int number, ValueRef::ValueRef<double>* sort_key,
731 const ScriptingContext& context, SortingMethod sorting_method,
732 ObjectSet& from_set, ObjectSet& to_set)
733 {
734 // handle random case, which doesn't need sorting key
735 if (sorting_method == SORT_RANDOM) {
736 TransferRandomObjects(number, from_set, to_set);
737 return;
738 }
739
740 // for other SoringMethods, need sort key values
741 if (!sort_key) {
742 ErrorLogger() << "TransferSortedObjects given null sort_key";
743 return;
744 }
745
746 // get sort key values for all objects in from_set, and sort by inserting into map
747 std::multimap<float, std::shared_ptr<const UniverseObject>> sort_key_objects;
748 for (auto& from : from_set) {
749 float sort_value = sort_key->Eval(ScriptingContext(context, from));
750 sort_key_objects.insert({sort_value, from});
751 }
752
753 // how many objects to select?
754 number = std::min<unsigned int>(number, sort_key_objects.size());
755 if (number == 0)
756 return;
757 unsigned int number_transferred(0);
758
759 // pick max / min / most common values
760 if (sorting_method == SORT_MIN) {
761 // move (number) objects with smallest sort key (at start of map)
762 // from the from_set into the to_set.
763 for (const auto& entry : sort_key_objects) {
764 auto object_to_transfer = entry.second;
765 auto from_it = std::find(from_set.begin(), from_set.end(), object_to_transfer);
766 if (from_it != from_set.end()) {
767 *from_it = from_set.back();
768 from_set.pop_back();
769 to_set.push_back(object_to_transfer);
770 number_transferred++;
771 if (number_transferred >= number)
772 return;
773 }
774 }
775
776 } else if (sorting_method == SORT_MAX) {
777 // move (number) objects with largest sort key (at end of map)
778 // from the from_set into the to_set.
779 for (auto sorted_it = sort_key_objects.rbegin(); // would use const_reverse_iterator but this causes a compile error in some compilers
780 sorted_it != sort_key_objects.rend(); ++sorted_it)
781 {
782 auto object_to_transfer = sorted_it->second;
783 auto from_it = std::find(from_set.begin(), from_set.end(), object_to_transfer);
784 if (from_it != from_set.end()) {
785 *from_it = from_set.back();
786 from_set.pop_back();
787 to_set.push_back(object_to_transfer);
788 number_transferred++;
789 if (number_transferred >= number)
790 return;
791 }
792 }
793
794 } else if (sorting_method == SORT_MODE) {
795 // compile histogram of of number of times each sort key occurs
796 std::map<float, unsigned int> histogram;
797 for (const auto& entry : sort_key_objects) {
798 histogram[entry.first]++;
799 }
800
801 // invert histogram to index by number of occurances
802 std::multimap<unsigned int, float> inv_histogram;
803 for (const auto& entry : histogram)
804 inv_histogram.insert({entry.second, entry.first});
805
806 // reverse-loop through inverted histogram to find which sort keys
807 // occurred most frequently, and transfer objects with those sort
808 // keys from from_set to to_set.
809 for (auto inv_hist_it = inv_histogram.rbegin(); // would use const_reverse_iterator but this causes a compile error in some compilers
810 inv_hist_it != inv_histogram.rend(); ++inv_hist_it)
811 {
812 float cur_sort_key = inv_hist_it->second;
813
814 // get range of objects with the current sort key
815 auto key_range = sort_key_objects.equal_range(cur_sort_key);
816
817 // loop over range, selecting objects to transfer from from_set to to_set
818 for (auto sorted_it = key_range.first;
819 sorted_it != key_range.second; ++sorted_it)
820 {
821 auto object_to_transfer = sorted_it->second;
822 auto from_it = std::find(from_set.begin(), from_set.end(), object_to_transfer);
823 if (from_it != from_set.end()) {
824 *from_it = from_set.back();
825 from_set.pop_back();
826 to_set.push_back(object_to_transfer);
827 number_transferred++;
828 if (number_transferred >= number)
829 return;
830 }
831 }
832 }
833
834 } else {
835 ErrorLogger() << "TransferSortedObjects given unknown sort method";
836 }
837 }
838 }
839
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const840 void SortedNumberOf::Eval(const ScriptingContext& parent_context,
841 ObjectSet& matches, ObjectSet& non_matches,
842 SearchDomain search_domain/* = NON_MATCHES*/) const
843 {
844 // Most conditions match objects independently of the other objects being
845 // tested, but the number parameter for NumberOf conditions makes things
846 // more complicated. In order to match some number of the potential
847 // matches property, both the matches and non_matches need to be checked
848 // against the subcondition, and the total number of subcondition matches
849 // counted.
850 // Then, when searching NON_MATCHES, non_matches may be moved into matches
851 // so that the number of subcondition matches in matches is equal to the
852 // requested number. There may also be subcondition non-matches in
853 // matches, but these are not counted or affected by this condition.
854 // Or, when searching MATCHES, matches may be moved into non_matches so
855 // that the number of subcondition matches in matches is equal to the
856 // requested number. There again may be subcondition non-matches in
857 // matches, but these are also not counted or affected by this condition.
858
859 // SortedNumberOf does not have a valid local candidate to be matched
860 // before the subcondition is evaluated, so the local context that is
861 // passed to the subcondition should have a null local candidate.
862 std::shared_ptr<const UniverseObject> no_object;
863 ScriptingContext local_context(parent_context, no_object);
864
865 // which input matches match the subcondition?
866 ObjectSet subcondition_matching_matches;
867 subcondition_matching_matches.reserve(matches.size());
868 m_condition->Eval(local_context, subcondition_matching_matches, matches, NON_MATCHES);
869
870 // remaining input matches don't match the subcondition...
871 ObjectSet subcondition_non_matching_matches = matches;
872 matches.clear(); // to be refilled later
873
874 // which input non_matches match the subcondition?
875 ObjectSet subcondition_matching_non_matches;
876 subcondition_matching_non_matches.reserve(non_matches.size());
877 m_condition->Eval(local_context, subcondition_matching_non_matches, non_matches, NON_MATCHES);
878
879 // remaining input non_matches don't match the subcondition...
880 ObjectSet subcondition_non_matching_non_matches = non_matches;
881 non_matches.clear(); // to be refilled later
882
883 // assemble single set of subcondition matching objects
884 ObjectSet all_subcondition_matches;
885 all_subcondition_matches.reserve(subcondition_matching_matches.size() + subcondition_matching_non_matches.size());
886 all_subcondition_matches.insert(all_subcondition_matches.end(), subcondition_matching_matches.begin(), subcondition_matching_matches.end());
887 all_subcondition_matches.insert(all_subcondition_matches.end(), subcondition_matching_non_matches.begin(), subcondition_matching_non_matches.end());
888
889 // how many subcondition matches to select as matches to this condition
890 int number = m_number->Eval(local_context);
891
892 // compile single set of all objects that are matched by this condition.
893 // these are the objects that should be transferred from non_matches into
894 // matches, or those left in matches while the rest are moved into non_matches
895 ObjectSet matched_objects;
896 matched_objects.reserve(number);
897 TransferSortedObjects(number, m_sort_key.get(), parent_context, m_sorting_method, all_subcondition_matches, matched_objects);
898
899 // put objects back into matches and non_target sets as output...
900
901 if (search_domain == NON_MATCHES) {
902 // put matched objects that are in subcondition_matching_non_matches into matches
903 for (auto& matched_object : matched_objects) {
904 // is this matched object in subcondition_matching_non_matches?
905 auto smnt_it = std::find(subcondition_matching_non_matches.begin(),
906 subcondition_matching_non_matches.end(), matched_object);
907 if (smnt_it != subcondition_matching_non_matches.end()) {
908 // yes; move object to matches
909 *smnt_it = subcondition_matching_non_matches.back();
910 subcondition_matching_non_matches.pop_back();
911 matches.push_back(matched_object);
912 }
913 }
914
915 // put remaining (non-matched) objects in subcondition_matching_non_matches back into non_matches
916 non_matches.insert( non_matches.end(), subcondition_matching_non_matches.begin(), subcondition_matching_non_matches.end());
917 // put objects in subcondition_non_matching_non_matches back into non_matches
918 non_matches.insert( non_matches.end(), subcondition_non_matching_non_matches.begin(), subcondition_non_matching_non_matches.end());
919 // put objects in subcondition_matching_matches and subcondition_non_matching_matches back into matches
920 matches.insert( matches.end(), subcondition_matching_matches.begin(), subcondition_matching_matches.end());
921 matches.insert( matches.end(), subcondition_non_matching_matches.begin(), subcondition_non_matching_matches.end());
922 // this leaves the original contents of matches unchanged, other than
923 // possibly having transferred some objects into matches from non_matches
924
925 } else { /*(search_domain == MATCHES)*/
926 // put matched objecs that are in subcondition_matching_matches back into matches
927 for (auto& matched_object : matched_objects) {
928 // is this matched object in subcondition_matching_matches?
929 auto smt_it = std::find(subcondition_matching_matches.begin(),
930 subcondition_matching_matches.end(), matched_object);
931 if (smt_it != subcondition_matching_matches.end()) {
932 // yes; move back into matches
933 *smt_it = subcondition_matching_matches.back();
934 subcondition_matching_matches.pop_back();
935 matches.push_back(matched_object);
936 }
937 }
938
939 // put remaining (non-matched) objects in subcondition_matching_matches) into non_matches
940 non_matches.insert( non_matches.end(), subcondition_matching_matches.begin(), subcondition_matching_matches.end());
941 // put objects in subcondition_non_matching_matches into non_matches
942 non_matches.insert( non_matches.end(), subcondition_non_matching_matches.begin(), subcondition_non_matching_matches.end());
943 // put objects in subcondition_matching_non_matches and subcondition_non_matching_non_matches back into non_matches
944 non_matches.insert( non_matches.end(), subcondition_matching_non_matches.begin(), subcondition_matching_non_matches.end());
945 non_matches.insert( non_matches.end(), subcondition_non_matching_non_matches.begin(), subcondition_non_matching_non_matches.end());
946 // this leaves the original contents of non_matches unchanged, other than
947 // possibly having transferred some objects into non_matches from matches
948 }
949 }
950
Description(bool negated) const951 std::string SortedNumberOf::Description(bool negated/* = false*/) const {
952 std::string number_str = m_number->ConstantExpr() ? m_number->Dump() : m_number->Description();
953
954 if (m_sorting_method == SORT_RANDOM) {
955 return str(FlexibleFormat((!negated)
956 ? UserString("DESC_NUMBER_OF")
957 : UserString("DESC_NUMBER_OF_NOT")
958 )
959 % number_str
960 % m_condition->Description());
961 } else {
962 std::string sort_key_str = m_sort_key->ConstantExpr() ? m_sort_key->Dump() : m_sort_key->Description();
963
964 std::string description_str;
965 switch (m_sorting_method) {
966 case SORT_MAX:
967 description_str = (!negated)
968 ? UserString("DESC_MAX_NUMBER_OF")
969 : UserString("DESC_MAX_NUMBER_OF_NOT");
970 break;
971
972 case SORT_MIN:
973 description_str = (!negated)
974 ? UserString("DESC_MIN_NUMBER_OF")
975 : UserString("DESC_MIN_NUMBER_OF_NOT");
976 break;
977
978 case SORT_MODE:
979 description_str = (!negated)
980 ? UserString("DESC_MODE_NUMBER_OF")
981 : UserString("DESC_MODE_NUMBER_OF_NOT");
982 break;
983 default:
984 break;
985 }
986
987 return str(FlexibleFormat(description_str)
988 % number_str
989 % sort_key_str
990 % m_condition->Description());
991 }
992 }
993
Dump(unsigned short ntabs) const994 std::string SortedNumberOf::Dump(unsigned short ntabs) const {
995 std::string retval = DumpIndent(ntabs);
996 switch (m_sorting_method) {
997 case SORT_RANDOM:
998 retval += "NumberOf"; break;
999 case SORT_MAX:
1000 retval += "MaximumNumberOf"; break;
1001 case SORT_MIN:
1002 retval += "MinimumNumberOf"; break;
1003 case SORT_MODE:
1004 retval += "ModeNumberOf"; break;
1005 default:
1006 retval += "??NumberOf??"; break;
1007 }
1008
1009 retval += " number = " + m_number->Dump(ntabs);
1010
1011 if (m_sort_key)
1012 retval += " sortby = " + m_sort_key->Dump(ntabs);
1013
1014 retval += " condition =\n";
1015 retval += m_condition->Dump(ntabs+1);
1016
1017 return retval;
1018 }
1019
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1020 void SortedNumberOf::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1021 ObjectSet& condition_non_targets) const
1022 {
1023 if (m_condition) {
1024 m_condition->GetDefaultInitialCandidateObjects(parent_context, condition_non_targets);
1025 } else {
1026 Condition::GetDefaultInitialCandidateObjects(parent_context, condition_non_targets);
1027 }
1028 }
1029
SetTopLevelContent(const std::string & content_name)1030 void SortedNumberOf::SetTopLevelContent(const std::string& content_name) {
1031 if (m_number)
1032 m_number->SetTopLevelContent(content_name);
1033 if (m_sort_key)
1034 m_sort_key->SetTopLevelContent(content_name);
1035 if (m_condition)
1036 m_condition->SetTopLevelContent(content_name);
1037 }
1038
GetCheckSum() const1039 unsigned int SortedNumberOf::GetCheckSum() const {
1040 unsigned int retval{0};
1041
1042 CheckSums::CheckSumCombine(retval, "Condition::SortedNumberOf");
1043 CheckSums::CheckSumCombine(retval, m_number);
1044 CheckSums::CheckSumCombine(retval, m_sort_key);
1045 CheckSums::CheckSumCombine(retval, m_sorting_method);
1046 CheckSums::CheckSumCombine(retval, m_condition);
1047
1048 TraceLogger() << "GetCheckSum(SortedNumberOf): retval: " << retval;
1049 return retval;
1050 }
1051
1052 ///////////////////////////////////////////////////////////
1053 // All //
1054 ///////////////////////////////////////////////////////////
All()1055 All::All() :
1056 Condition()
1057 {
1058 m_root_candidate_invariant = true;
1059 m_target_invariant = true;
1060 m_source_invariant = true;
1061 }
1062
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const1063 void All::Eval(const ScriptingContext& parent_context,
1064 ObjectSet& matches, ObjectSet& non_matches,
1065 SearchDomain search_domain/* = NON_MATCHES*/) const
1066 {
1067 if (search_domain == NON_MATCHES) {
1068 // move all objects from non_matches to matches
1069 matches.insert(matches.end(), non_matches.begin(), non_matches.end());
1070 non_matches.clear();
1071 }
1072 // if search_comain is MATCHES, do nothing: all objects in matches set
1073 // match this condition, so should remain in matches set
1074 }
1075
operator ==(const Condition & rhs) const1076 bool All::operator==(const Condition& rhs) const
1077 { return Condition::operator==(rhs); }
1078
Description(bool negated) const1079 std::string All::Description(bool negated/* = false*/) const {
1080 return (!negated)
1081 ? UserString("DESC_ALL")
1082 : UserString("DESC_ALL_NOT");
1083 }
1084
Dump(unsigned short ntabs) const1085 std::string All::Dump(unsigned short ntabs) const
1086 { return DumpIndent(ntabs) + "All\n"; }
1087
GetCheckSum() const1088 unsigned int All::GetCheckSum() const {
1089 unsigned int retval{0};
1090
1091 CheckSums::CheckSumCombine(retval, "Condition::All");
1092
1093 TraceLogger() << "GetCheckSum(All): retval: " << retval;
1094 return retval;
1095 }
1096
1097 ///////////////////////////////////////////////////////////
1098 // None //
1099 ///////////////////////////////////////////////////////////
None()1100 None::None() :
1101 Condition()
1102 {
1103 m_root_candidate_invariant = true;
1104 m_target_invariant = true;
1105 m_source_invariant = true;
1106 }
1107
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const1108 void None::Eval(const ScriptingContext& parent_context,
1109 ObjectSet& matches, ObjectSet& non_matches,
1110 SearchDomain search_domain/* = NON_MATCHES*/) const
1111 {
1112 if (search_domain == MATCHES) {
1113 // move all objects from matches to non_matches
1114 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
1115 matches.clear();
1116 }
1117 // if search domain is non_matches, no need to do anything since none of them match None.
1118 }
1119
operator ==(const Condition & rhs) const1120 bool None::operator==(const Condition& rhs) const
1121 { return Condition::operator==(rhs); }
1122
Description(bool negated) const1123 std::string None::Description(bool negated/* = false*/) const {
1124 return (!negated)
1125 ? UserString("DESC_NONE")
1126 : UserString("DESC_NONE_NOT");
1127 }
1128
Dump(unsigned short ntabs) const1129 std::string None::Dump(unsigned short ntabs) const
1130 { return DumpIndent(ntabs) + "None\n"; }
1131
GetCheckSum() const1132 unsigned int None::GetCheckSum() const {
1133 unsigned int retval{0};
1134
1135 CheckSums::CheckSumCombine(retval, "Condition::None");
1136
1137 TraceLogger() << "GetCheckSum(None): retval: " << retval;
1138 return retval;
1139 }
1140
1141 ///////////////////////////////////////////////////////////
1142 // EmpireAffiliation //
1143 ///////////////////////////////////////////////////////////
EmpireAffiliation(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,EmpireAffiliationType affiliation)1144 EmpireAffiliation::EmpireAffiliation(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
1145 EmpireAffiliationType affiliation) :
1146 m_empire_id(std::move(empire_id)),
1147 m_affiliation(affiliation)
1148 {
1149 m_root_candidate_invariant = !m_empire_id || m_empire_id->RootCandidateInvariant();
1150 m_target_invariant = !m_empire_id || m_empire_id->TargetInvariant();
1151 m_source_invariant = !m_empire_id || m_empire_id->SourceInvariant();
1152 }
1153
EmpireAffiliation(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)1154 EmpireAffiliation::EmpireAffiliation(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
1155 EmpireAffiliation(std::move(empire_id), AFFIL_SELF)
1156 {}
1157
EmpireAffiliation(EmpireAffiliationType affiliation)1158 EmpireAffiliation::EmpireAffiliation(EmpireAffiliationType affiliation) :
1159 EmpireAffiliation(nullptr, affiliation)
1160 {}
1161
operator ==(const Condition & rhs) const1162 bool EmpireAffiliation::operator==(const Condition& rhs) const {
1163 if (this == &rhs)
1164 return true;
1165 if (typeid(*this) != typeid(rhs))
1166 return false;
1167
1168 const EmpireAffiliation& rhs_ = static_cast<const EmpireAffiliation&>(rhs);
1169
1170 if (m_affiliation != rhs_.m_affiliation)
1171 return false;
1172
1173 CHECK_COND_VREF_MEMBER(m_empire_id)
1174
1175 return true;
1176 }
1177
1178 namespace {
1179 struct EmpireAffiliationSimpleMatch {
EmpireAffiliationSimpleMatchCondition::__anone9a56faf0911::EmpireAffiliationSimpleMatch1180 EmpireAffiliationSimpleMatch(int empire_id, EmpireAffiliationType affiliation) :
1181 m_empire_id(empire_id),
1182 m_affiliation(affiliation)
1183 {}
1184
operator ()Condition::__anone9a56faf0911::EmpireAffiliationSimpleMatch1185 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
1186 if (!candidate)
1187 return false;
1188
1189 switch (m_affiliation) {
1190 case AFFIL_SELF:
1191 return m_empire_id != ALL_EMPIRES && candidate->OwnedBy(m_empire_id);
1192 break;
1193
1194 case AFFIL_ENEMY: {
1195 if (m_empire_id == ALL_EMPIRES)
1196 return true;
1197 if (m_empire_id == candidate->Owner())
1198 return false;
1199 DiplomaticStatus status = Empires().GetDiplomaticStatus(m_empire_id, candidate->Owner());
1200 return (status == DIPLO_WAR);
1201 break;
1202 }
1203
1204 case AFFIL_PEACE: {
1205 if (m_empire_id == ALL_EMPIRES)
1206 return false;
1207 if (m_empire_id == candidate->Owner())
1208 return false;
1209 DiplomaticStatus status = Empires().GetDiplomaticStatus(m_empire_id, candidate->Owner());
1210 return (status == DIPLO_PEACE);
1211 break;
1212 }
1213
1214 case AFFIL_ALLY: {
1215 if (m_empire_id == ALL_EMPIRES)
1216 return false;
1217 if (m_empire_id == candidate->Owner())
1218 return false;
1219 DiplomaticStatus status = Empires().GetDiplomaticStatus(m_empire_id, candidate->Owner());
1220 return (status >= DIPLO_ALLIED);
1221 break;
1222 }
1223
1224 case AFFIL_ANY:
1225 return !candidate->Unowned();
1226 break;
1227
1228 case AFFIL_NONE:
1229 return candidate->Unowned();
1230 break;
1231
1232 case AFFIL_CAN_SEE: {
1233 // todo...
1234 return false;
1235 break;
1236 }
1237
1238 case AFFIL_HUMAN: {
1239 if (candidate->Unowned())
1240 return false;
1241 if (GetEmpireClientType(candidate->Owner()) == Networking::CLIENT_TYPE_HUMAN_PLAYER)
1242 return true;
1243 return false;
1244 break;
1245 }
1246
1247 default:
1248 return false;
1249 break;
1250 }
1251 }
1252
1253 int m_empire_id;
1254 EmpireAffiliationType m_affiliation;
1255 };
1256 }
1257
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const1258 void EmpireAffiliation::Eval(const ScriptingContext& parent_context,
1259 ObjectSet& matches, ObjectSet& non_matches,
1260 SearchDomain search_domain/* = NON_MATCHES*/) const
1261 {
1262 bool simple_eval_safe = (!m_empire_id || m_empire_id->ConstantExpr()) ||
1263 ((!m_empire_id || m_empire_id->LocalCandidateInvariant()) &&
1264 (parent_context.condition_root_candidate || RootCandidateInvariant()));
1265 if (simple_eval_safe) {
1266 // evaluate empire id once, and use to check all candidate objects
1267 int empire_id = m_empire_id ? m_empire_id->Eval(parent_context) : ALL_EMPIRES;
1268 EvalImpl(matches, non_matches, search_domain, EmpireAffiliationSimpleMatch(empire_id, m_affiliation));
1269 } else {
1270 // re-evaluate empire id for each candidate object
1271 Condition::Eval(parent_context, matches, non_matches, search_domain);
1272 }
1273 }
1274
Description(bool negated) const1275 std::string EmpireAffiliation::Description(bool negated/* = false*/) const {
1276 std::string empire_str;
1277 if (m_empire_id) {
1278 int empire_id = ALL_EMPIRES;
1279 if (m_empire_id->ConstantExpr())
1280 empire_id = m_empire_id->Eval();
1281 if (const Empire* empire = GetEmpire(empire_id))
1282 empire_str = empire->Name();
1283 else
1284 empire_str = m_empire_id->Description();
1285 }
1286
1287 if (m_affiliation == AFFIL_SELF) {
1288 return str(FlexibleFormat((!negated)
1289 ? UserString("DESC_EMPIRE_AFFILIATION_SELF")
1290 : UserString("DESC_EMPIRE_AFFILIATION_SELF_NOT")) % empire_str);
1291 } else if (m_affiliation == AFFIL_ANY) {
1292 return (!negated)
1293 ? UserString("DESC_EMPIRE_AFFILIATION_ANY")
1294 : UserString("DESC_EMPIRE_AFFILIATION_ANY_NOT");
1295 } else if (m_affiliation == AFFIL_NONE) {
1296 return (!negated)
1297 ? UserString("DESC_EMPIRE_AFFILIATION_ANY_NOT")
1298 : UserString("DESC_EMPIRE_AFFILIATION_ANY");
1299 } else {
1300 return str(FlexibleFormat((!negated)
1301 ? UserString("DESC_EMPIRE_AFFILIATION")
1302 : UserString("DESC_EMPIRE_AFFILIATION_NOT"))
1303 % UserString(boost::lexical_cast<std::string>(m_affiliation))
1304 % empire_str);
1305 }
1306 }
1307
Dump(unsigned short ntabs) const1308 std::string EmpireAffiliation::Dump(unsigned short ntabs) const {
1309 std::string retval = DumpIndent(ntabs);
1310 if (m_affiliation == AFFIL_SELF) {
1311 retval += "OwnedBy";
1312 if (m_empire_id)
1313 retval += " empire = " + m_empire_id->Dump(ntabs);
1314
1315 } else if (m_affiliation == AFFIL_ANY) {
1316 retval += "OwnedBy affiliation = AnyEmpire";
1317
1318 } else if (m_affiliation == AFFIL_NONE) {
1319 retval += "Unowned";
1320
1321 } else if (m_affiliation == AFFIL_ENEMY) {
1322 retval += "OwnedBy affiliation = EnemyOf";
1323 if (m_empire_id)
1324 retval += " empire = " + m_empire_id->Dump(ntabs);
1325
1326 } else if (m_affiliation == AFFIL_PEACE) {
1327 retval += "OwnedBy affiliation = PeaceWith";
1328 if (m_empire_id)
1329 retval += " empire = " + m_empire_id->Dump(ntabs);
1330
1331 } else if (m_affiliation == AFFIL_ALLY) {
1332 retval += "OwnedBy affiliation = AllyOf";
1333 if (m_empire_id)
1334 retval += " empire = " + m_empire_id->Dump(ntabs);
1335
1336 } else if (m_affiliation == AFFIL_CAN_SEE) {
1337 retval += "OwnedBy affiliation = CanSee";
1338
1339 } else if (m_affiliation == AFFIL_HUMAN) {
1340 retval += "OwnedBy affiliation = Human";
1341
1342 } else {
1343 retval += "OwnedBy ??";
1344 }
1345
1346 retval += "\n";
1347 return retval;
1348 }
1349
Match(const ScriptingContext & local_context) const1350 bool EmpireAffiliation::Match(const ScriptingContext& local_context) const {
1351 auto candidate = local_context.condition_local_candidate;
1352 if (!candidate) {
1353 ErrorLogger() << "EmpireAffiliation::Match passed no candidate object";
1354 return false;
1355 }
1356
1357 int empire_id = m_empire_id ? m_empire_id->Eval(local_context) : ALL_EMPIRES;
1358
1359 return EmpireAffiliationSimpleMatch(empire_id, m_affiliation)(candidate);
1360 }
1361
SetTopLevelContent(const std::string & content_name)1362 void EmpireAffiliation::SetTopLevelContent(const std::string& content_name) {
1363 if (m_empire_id)
1364 m_empire_id->SetTopLevelContent(content_name);
1365 }
1366
GetCheckSum() const1367 unsigned int EmpireAffiliation::GetCheckSum() const {
1368 unsigned int retval{0};
1369
1370 CheckSums::CheckSumCombine(retval, "Condition::EmpireAffiliation");
1371 CheckSums::CheckSumCombine(retval, m_empire_id);
1372 CheckSums::CheckSumCombine(retval, m_affiliation);
1373
1374 TraceLogger() << "GetCheckSum(EmpireAffiliation): retval: " << retval;
1375 return retval;
1376 }
1377
1378 ///////////////////////////////////////////////////////////
1379 // Source //
1380 ///////////////////////////////////////////////////////////
Source()1381 Source::Source() :
1382 Condition()
1383 {
1384 m_root_candidate_invariant = true;
1385 m_target_invariant = true;
1386 m_source_invariant = false;
1387 }
1388
operator ==(const Condition & rhs) const1389 bool Source::operator==(const Condition& rhs) const
1390 { return Condition::operator==(rhs); }
1391
Description(bool negated) const1392 std::string Source::Description(bool negated/* = false*/) const {
1393 return (!negated)
1394 ? UserString("DESC_SOURCE")
1395 : UserString("DESC_SOURCE_NOT");
1396 }
1397
Dump(unsigned short ntabs) const1398 std::string Source::Dump(unsigned short ntabs) const
1399 { return DumpIndent(ntabs) + "Source\n"; }
1400
Match(const ScriptingContext & local_context) const1401 bool Source::Match(const ScriptingContext& local_context) const {
1402 if (!local_context.source)
1403 return false;
1404 return local_context.source == local_context.condition_local_candidate;
1405 }
1406
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1407 void Source::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1408 ObjectSet& condition_non_targets) const
1409 {
1410 if (parent_context.source)
1411 condition_non_targets.push_back(parent_context.source);
1412 }
1413
GetCheckSum() const1414 unsigned int Source::GetCheckSum() const {
1415 unsigned int retval{0};
1416
1417 CheckSums::CheckSumCombine(retval, "Condition::Source");
1418
1419 TraceLogger() << "GetCheckSum(Source): retval: " << retval;
1420 return retval;
1421 }
1422
1423 ///////////////////////////////////////////////////////////
1424 // RootCandidate //
1425 ///////////////////////////////////////////////////////////
RootCandidate()1426 RootCandidate::RootCandidate() :
1427 Condition()
1428 {
1429 m_root_candidate_invariant = false;
1430 m_target_invariant = true;
1431 m_source_invariant = true;
1432 }
1433
operator ==(const Condition & rhs) const1434 bool RootCandidate::operator==(const Condition& rhs) const
1435 { return Condition::operator==(rhs); }
1436
Description(bool negated) const1437 std::string RootCandidate::Description(bool negated/* = false*/) const {
1438 return (!negated)
1439 ? UserString("DESC_ROOT_CANDIDATE")
1440 : UserString("DESC_ROOT_CANDIDATE_NOT");
1441 }
1442
Dump(unsigned short ntabs) const1443 std::string RootCandidate::Dump(unsigned short ntabs) const
1444 { return DumpIndent(ntabs) + "RootCandidate\n"; }
1445
Match(const ScriptingContext & local_context) const1446 bool RootCandidate::Match(const ScriptingContext& local_context) const {
1447 if (!local_context.condition_root_candidate)
1448 return false;
1449 return local_context.condition_root_candidate == local_context.condition_local_candidate;
1450 }
1451
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1452 void RootCandidate::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1453 ObjectSet& condition_non_targets) const
1454 {
1455 if (parent_context.condition_root_candidate)
1456 condition_non_targets.push_back(parent_context.condition_root_candidate);
1457 }
1458
GetCheckSum() const1459 unsigned int RootCandidate::GetCheckSum() const {
1460 unsigned int retval{0};
1461
1462 CheckSums::CheckSumCombine(retval, "Condition::RootCandidate");
1463
1464 TraceLogger() << "GetCheckSum(RootCandidate): retval: " << retval;
1465 return retval;
1466 }
1467
1468 ///////////////////////////////////////////////////////////
1469 // Target //
1470 ///////////////////////////////////////////////////////////
Target()1471 Target::Target() :
1472 Condition()
1473 {
1474 m_root_candidate_invariant = true;
1475 m_target_invariant = false;
1476 m_source_invariant = true;
1477 }
1478
operator ==(const Condition & rhs) const1479 bool Target::operator==(const Condition& rhs) const
1480 { return Condition::operator==(rhs); }
1481
Description(bool negated) const1482 std::string Target::Description(bool negated/* = false*/) const {
1483 return (!negated)
1484 ? UserString("DESC_TARGET")
1485 : UserString("DESC_TARGET_NOT");
1486 }
1487
Dump(unsigned short ntabs) const1488 std::string Target::Dump(unsigned short ntabs) const
1489 { return DumpIndent(ntabs) + "Target\n"; }
1490
Match(const ScriptingContext & local_context) const1491 bool Target::Match(const ScriptingContext& local_context) const {
1492 if (!local_context.effect_target)
1493 return false;
1494 return local_context.effect_target == local_context.condition_local_candidate;
1495 }
1496
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1497 void Target::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1498 ObjectSet& condition_non_targets) const
1499 {
1500 if (parent_context.effect_target)
1501 condition_non_targets.push_back(parent_context.effect_target);
1502 }
1503
GetCheckSum() const1504 unsigned int Target::GetCheckSum() const {
1505 unsigned int retval{0};
1506
1507 CheckSums::CheckSumCombine(retval, "Condition::Target");
1508
1509 TraceLogger() << "GetCheckSum(Target): retval: " << retval;
1510 return retval;
1511 }
1512
1513 ///////////////////////////////////////////////////////////
1514 // Homeworld //
1515 ///////////////////////////////////////////////////////////
Homeworld()1516 Homeworld::Homeworld() :
1517 Homeworld(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>>{})
1518 {}
1519
Homeworld(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>> && names)1520 Homeworld::Homeworld(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>>&& names) :
1521 Condition(),
1522 m_names(std::move(names))
1523 {
1524 m_root_candidate_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->RootCandidateInvariant(); });
1525 m_target_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->TargetInvariant(); });
1526 m_source_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->SourceInvariant(); });
1527 }
1528
operator ==(const Condition & rhs) const1529 bool Homeworld::operator==(const Condition& rhs) const {
1530 if (this == &rhs)
1531 return true;
1532 if (typeid(*this) != typeid(rhs))
1533 return false;
1534
1535 const Homeworld& rhs_ = static_cast<const Homeworld&>(rhs);
1536
1537 if (m_names.size() != rhs_.m_names.size())
1538 return false;
1539 for (unsigned int i = 0; i < m_names.size(); ++i) {
1540 CHECK_COND_VREF_MEMBER(m_names.at(i))
1541 }
1542
1543 return true;
1544 }
1545
1546 namespace {
1547 struct HomeworldSimpleMatch {
HomeworldSimpleMatchCondition::__anone9a56faf0d11::HomeworldSimpleMatch1548 HomeworldSimpleMatch(const std::vector<std::string>& names, const ObjectMap& objects) :
1549 m_names(names),
1550 m_objects(objects)
1551 {}
1552
operator ()Condition::__anone9a56faf0d11::HomeworldSimpleMatch1553 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
1554 if (!candidate)
1555 return false;
1556
1557 // is it a planet or a building on a planet?
1558 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
1559 std::shared_ptr<const ::Building> building;
1560 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
1561 planet = m_objects.get<Planet>(building->PlanetID());
1562 if (!planet)
1563 return false;
1564
1565 int planet_id = planet->ID();
1566 const SpeciesManager& manager = GetSpeciesManager();
1567
1568 if (m_names.empty()) {
1569 // match homeworlds for any species
1570 for (auto species_it = manager.begin(); species_it != manager.end(); ++species_it) {
1571 if (const auto& species = species_it->second) {
1572 const auto& homeworld_ids = species->Homeworlds();
1573 if (homeworld_ids.count(planet_id))
1574 return true;
1575 }
1576 }
1577
1578 } else {
1579 // match any of the species specified
1580 for (const std::string& name : m_names) {
1581 if (auto species = GetSpecies(name)) {
1582 const auto& homeworld_ids = species->Homeworlds();
1583 if (homeworld_ids.count(planet_id))
1584 return true;
1585 }
1586 }
1587 }
1588
1589 return false;
1590 }
1591
1592 const std::vector<std::string>& m_names;
1593 const ObjectMap& m_objects;
1594 };
1595 }
1596
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const1597 void Homeworld::Eval(const ScriptingContext& parent_context,
1598 ObjectSet& matches, ObjectSet& non_matches,
1599 SearchDomain search_domain/* = NON_MATCHES*/) const
1600 {
1601 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
1602 if (simple_eval_safe) {
1603 // check each valueref for invariance to local candidate
1604 for (auto& name : m_names) {
1605 if (!name->LocalCandidateInvariant()) {
1606 simple_eval_safe = false;
1607 break;
1608 }
1609 }
1610 }
1611 if (simple_eval_safe) {
1612 // evaluate names once, and use to check all candidate objects
1613 std::vector<std::string> names;
1614 names.reserve(m_names.size());
1615 // get all names from valuerefs
1616 for (auto& name : m_names)
1617 names.push_back(name->Eval(parent_context));
1618 EvalImpl(matches, non_matches, search_domain, HomeworldSimpleMatch(names, parent_context.ContextObjects()));
1619 } else {
1620 // re-evaluate allowed names for each candidate object
1621 Condition::Eval(parent_context, matches, non_matches, search_domain);
1622 }
1623 }
1624
Description(bool negated) const1625 std::string Homeworld::Description(bool negated/* = false*/) const {
1626 std::string values_str;
1627 for (unsigned int i = 0; i < m_names.size(); ++i) {
1628 values_str += m_names[i]->ConstantExpr() ?
1629 UserString(m_names[i]->Eval()) :
1630 m_names[i]->Description();
1631 if (2 <= m_names.size() && i < m_names.size() - 2) {
1632 values_str += ", ";
1633 } else if (i == m_names.size() - 2) {
1634 values_str += m_names.size() < 3 ? " " : ", ";
1635 values_str += UserString("OR");
1636 values_str += " ";
1637 }
1638 }
1639 return str(FlexibleFormat((!negated)
1640 ? UserString("DESC_HOMEWORLD")
1641 : UserString("DESC_HOMEWORLD_NOT"))
1642 % values_str);
1643 }
1644
Dump(unsigned short ntabs) const1645 std::string Homeworld::Dump(unsigned short ntabs) const {
1646 std::string retval = DumpIndent(ntabs) + "HomeWorld";
1647 if (m_names.size() == 1) {
1648 retval += " name = " + m_names[0]->Dump(ntabs);
1649 } else if (!m_names.empty()) {
1650 retval += " name = [ ";
1651 for (auto& name : m_names) {
1652 retval += name->Dump(ntabs) + " ";
1653 }
1654 retval += "]";
1655 }
1656 return retval;
1657 }
1658
Match(const ScriptingContext & local_context) const1659 bool Homeworld::Match(const ScriptingContext& local_context) const {
1660 auto candidate = local_context.condition_local_candidate;
1661 if (!candidate) {
1662 ErrorLogger() << "Homeworld::Match passed no candidate object";
1663 return false;
1664 }
1665
1666 // is it a planet or a building on a planet?
1667 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
1668 std::shared_ptr<const ::Building> building;
1669 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
1670 planet = local_context.ContextObjects().get<Planet>(building->PlanetID());
1671 if (!planet)
1672 return false;
1673
1674 int planet_id = planet->ID();
1675 const SpeciesManager& manager = GetSpeciesManager();
1676
1677 if (m_names.empty()) {
1678 // match homeworlds for any species
1679 for (const auto& entry : manager) {
1680 if (const auto& species = entry.second) {
1681 const auto& homeworld_ids = species->Homeworlds();
1682 if (homeworld_ids.count(planet_id))
1683 return true;
1684 }
1685 }
1686
1687 } else {
1688 // match any of the species specified
1689 for (auto& name : m_names) {
1690 const auto& species_name = name->Eval(local_context);
1691 if (const auto species = manager.GetSpecies(species_name)) {
1692 const auto& homeworld_ids = species->Homeworlds();
1693 if (homeworld_ids.count(planet_id))
1694 return true;
1695 }
1696 }
1697 }
1698
1699 return false;
1700 }
1701
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1702 void Homeworld::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1703 ObjectSet& condition_non_targets) const
1704 { AddPlanetSet(parent_context.ContextObjects(), condition_non_targets); }
1705
SetTopLevelContent(const std::string & content_name)1706 void Homeworld::SetTopLevelContent(const std::string& content_name) {
1707 for (auto& name : m_names) {
1708 if (name)
1709 name->SetTopLevelContent(content_name);
1710 }
1711 }
1712
GetCheckSum() const1713 unsigned int Homeworld::GetCheckSum() const {
1714 unsigned int retval{0};
1715
1716 CheckSums::CheckSumCombine(retval, "Condition::Homeworld");
1717 CheckSums::CheckSumCombine(retval, m_names);
1718
1719 TraceLogger() << "GetCheckSum(Homeworld): retval: " << retval;
1720 return retval;
1721 }
1722
1723 ///////////////////////////////////////////////////////////
1724 // Capital //
1725 ///////////////////////////////////////////////////////////
Capital()1726 Capital::Capital() :
1727 Condition()
1728 {
1729 m_root_candidate_invariant = true;
1730 m_target_invariant = true;
1731 m_source_invariant = true;
1732 }
1733
operator ==(const Condition & rhs) const1734 bool Capital::operator==(const Condition& rhs) const
1735 { return Condition::operator==(rhs); }
1736
Description(bool negated) const1737 std::string Capital::Description(bool negated/* = false*/) const {
1738 return (!negated)
1739 ? UserString("DESC_CAPITAL")
1740 : UserString("DESC_CAPITAL_NOT");
1741 }
1742
Dump(unsigned short ntabs) const1743 std::string Capital::Dump(unsigned short ntabs) const
1744 { return DumpIndent(ntabs) + "Capital\n"; }
1745
Match(const ScriptingContext & local_context) const1746 bool Capital::Match(const ScriptingContext& local_context) const {
1747 auto candidate = local_context.condition_local_candidate;
1748 if (!candidate) {
1749 ErrorLogger() << "Capital::Match passed no candidate object";
1750 return false;
1751 }
1752 int candidate_id = candidate->ID();
1753
1754 // check if any empire's capital's ID is that candidate object's id.
1755 // if it is, the candidate object is a capital.
1756 for (const auto& entry : Empires())
1757 if (entry.second->CapitalID() == candidate_id)
1758 return true;
1759 return false;
1760 }
1761
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1762 void Capital::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1763 ObjectSet& condition_non_targets) const
1764 { AddPlanetSet(parent_context.ContextObjects(), condition_non_targets); }
1765
GetCheckSum() const1766 unsigned int Capital::GetCheckSum() const {
1767 unsigned int retval{0};
1768
1769 CheckSums::CheckSumCombine(retval, "Condition::Capital");
1770
1771 TraceLogger() << "GetCheckSum(Capital): retval: " << retval;
1772 return retval;
1773 }
1774
1775 ///////////////////////////////////////////////////////////
1776 // Monster //
1777 ///////////////////////////////////////////////////////////
Monster()1778 Monster::Monster() :
1779 Condition()
1780 {
1781 m_root_candidate_invariant = true;
1782 m_target_invariant = true;
1783 m_source_invariant = true;
1784 }
1785
operator ==(const Condition & rhs) const1786 bool Monster::operator==(const Condition& rhs) const
1787 { return Condition::operator==(rhs); }
1788
Description(bool negated) const1789 std::string Monster::Description(bool negated/* = false*/) const {
1790 return (!negated)
1791 ? UserString("DESC_MONSTER")
1792 : UserString("DESC_MONSTER_NOT");
1793 }
1794
Dump(unsigned short ntabs) const1795 std::string Monster::Dump(unsigned short ntabs) const
1796 { return DumpIndent(ntabs) + "Monster\n"; }
1797
Match(const ScriptingContext & local_context) const1798 bool Monster::Match(const ScriptingContext& local_context) const {
1799 auto candidate = local_context.condition_local_candidate;
1800 if (!candidate) {
1801 ErrorLogger() << "Monster::Match passed no candidate object";
1802 return false;
1803 }
1804
1805 if (auto ship = std::dynamic_pointer_cast<const Ship>(candidate))
1806 if (ship->IsMonster())
1807 return true;
1808
1809 return false;
1810 }
1811
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1812 void Monster::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1813 ObjectSet& condition_non_targets) const
1814 { AddShipSet(parent_context.ContextObjects(), condition_non_targets); }
1815
GetCheckSum() const1816 unsigned int Monster::GetCheckSum() const {
1817 unsigned int retval{0};
1818
1819 CheckSums::CheckSumCombine(retval, "Condition::Monster");
1820
1821 TraceLogger() << "GetCheckSum(Monster): retval: " << retval;
1822 return retval;
1823 }
1824
1825 ///////////////////////////////////////////////////////////
1826 // Armed //
1827 ///////////////////////////////////////////////////////////
Armed()1828 Armed::Armed() :
1829 Condition()
1830 {
1831 m_root_candidate_invariant = true;
1832 m_target_invariant = true;
1833 m_source_invariant = true;
1834 }
1835
operator ==(const Condition & rhs) const1836 bool Armed::operator==(const Condition& rhs) const
1837 { return Condition::operator==(rhs); }
1838
Description(bool negated) const1839 std::string Armed::Description(bool negated/* = false*/) const {
1840 return (!negated)
1841 ? UserString("DESC_ARMED")
1842 : UserString("DESC_ARMED_NOT");
1843 }
1844
Dump(unsigned short ntabs) const1845 std::string Armed::Dump(unsigned short ntabs) const
1846 { return DumpIndent(ntabs) + "Armed\n"; }
1847
Match(const ScriptingContext & local_context) const1848 bool Armed::Match(const ScriptingContext& local_context) const {
1849 auto candidate = local_context.condition_local_candidate;
1850 if (!candidate) {
1851 ErrorLogger() << "Armed::Match passed no candidate object";
1852 return false;
1853 }
1854
1855 if (auto ship = std::dynamic_pointer_cast<const Ship>(candidate))
1856 if (ship->IsArmed())
1857 return true;
1858
1859 return false;
1860 }
1861
GetCheckSum() const1862 unsigned int Armed::GetCheckSum() const {
1863 unsigned int retval{0};
1864
1865 CheckSums::CheckSumCombine(retval, "Condition::Armed");
1866
1867 TraceLogger() << "GetCheckSum(Armed): retval: " << retval;
1868 return retval;
1869 }
1870
1871 ///////////////////////////////////////////////////////////
1872 // Type //
1873 ///////////////////////////////////////////////////////////
Type(std::unique_ptr<ValueRef::ValueRef<UniverseObjectType>> && type)1874 Type::Type(std::unique_ptr<ValueRef::ValueRef<UniverseObjectType>>&& type) :
1875 Condition(),
1876 m_type(std::move(type))
1877 {
1878 m_root_candidate_invariant = m_type->RootCandidateInvariant();
1879 m_target_invariant = m_type->TargetInvariant();
1880 m_source_invariant = m_type->SourceInvariant();
1881 }
1882
Type(UniverseObjectType type)1883 Type::Type(UniverseObjectType type) :
1884 Type(std::make_unique<ValueRef::Constant<UniverseObjectType>>(type))
1885 {}
1886
operator ==(const Condition & rhs) const1887 bool Type::operator==(const Condition& rhs) const {
1888 if (this == &rhs)
1889 return true;
1890 if (typeid(*this) != typeid(rhs))
1891 return false;
1892
1893 const Type& rhs_ = static_cast<const Type&>(rhs);
1894
1895 CHECK_COND_VREF_MEMBER(m_type)
1896
1897 return true;
1898 }
1899
1900 namespace {
1901 struct TypeSimpleMatch {
TypeSimpleMatchCondition::__anone9a56faf0e11::TypeSimpleMatch1902 TypeSimpleMatch(UniverseObjectType type) :
1903 m_type(type)
1904 {}
1905
operator ()Condition::__anone9a56faf0e11::TypeSimpleMatch1906 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
1907 if (!candidate)
1908 return false;
1909
1910 switch (m_type) {
1911 case OBJ_BUILDING:
1912 case OBJ_SHIP:
1913 case OBJ_FLEET:
1914 case OBJ_PLANET:
1915 case OBJ_SYSTEM:
1916 case OBJ_FIELD:
1917 case OBJ_FIGHTER:
1918 return candidate->ObjectType() == m_type;
1919 break;
1920 case OBJ_POP_CENTER:
1921 return (bool)std::dynamic_pointer_cast<const PopCenter>(candidate);
1922 break;
1923 case OBJ_PROD_CENTER:
1924 return (bool)std::dynamic_pointer_cast<const ResourceCenter>(candidate);
1925 break;
1926 default:
1927 break;
1928 }
1929 return false;
1930 }
1931
1932 UniverseObjectType m_type;
1933 };
1934 }
1935
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const1936 void Type::Eval(const ScriptingContext& parent_context,
1937 ObjectSet& matches, ObjectSet& non_matches,
1938 SearchDomain search_domain/* = NON_MATCHES*/) const
1939 {
1940 bool simple_eval_safe = m_type->ConstantExpr() ||
1941 (m_type->LocalCandidateInvariant() &&
1942 (parent_context.condition_root_candidate || RootCandidateInvariant()));
1943 if (simple_eval_safe) {
1944 UniverseObjectType type = m_type->Eval(parent_context);
1945 EvalImpl(matches, non_matches, search_domain, TypeSimpleMatch(type));
1946 } else {
1947 // re-evaluate allowed turn range for each candidate object
1948 Condition::Eval(parent_context, matches, non_matches, search_domain);
1949 }
1950 }
1951
Description(bool negated) const1952 std::string Type::Description(bool negated/* = false*/) const {
1953 std::string value_str = m_type->ConstantExpr() ?
1954 UserString(boost::lexical_cast<std::string>(m_type->Eval())) :
1955 m_type->Description();
1956 return str(FlexibleFormat((!negated)
1957 ? UserString("DESC_TYPE")
1958 : UserString("DESC_TYPE_NOT"))
1959 % value_str);
1960 }
1961
Dump(unsigned short ntabs) const1962 std::string Type::Dump(unsigned short ntabs) const {
1963 std::string retval = DumpIndent(ntabs);
1964 if (dynamic_cast<ValueRef::Constant<UniverseObjectType>*>(m_type.get())) {
1965 switch (m_type->Eval()) {
1966 case OBJ_BUILDING: retval += "Building\n"; break;
1967 case OBJ_SHIP: retval += "Ship\n"; break;
1968 case OBJ_FLEET: retval += "Fleet\n"; break;
1969 case OBJ_PLANET: retval += "Planet\n"; break;
1970 case OBJ_POP_CENTER: retval += "PopulationCenter\n"; break;
1971 case OBJ_PROD_CENTER: retval += "ProductionCenter\n"; break;
1972 case OBJ_SYSTEM: retval += "System\n"; break;
1973 case OBJ_FIELD: retval += "Field\n"; break;
1974 case OBJ_FIGHTER: retval += "Fighter\n"; break;
1975 default: retval += "?\n"; break;
1976 }
1977 } else {
1978 retval += "ObjectType type = " + m_type->Dump(ntabs) + "\n";
1979 }
1980 return retval;
1981 }
1982
Match(const ScriptingContext & local_context) const1983 bool Type::Match(const ScriptingContext& local_context) const {
1984 auto candidate = local_context.condition_local_candidate;
1985 if (!candidate) {
1986 ErrorLogger() << "Type::Match passed no candidate object";
1987 return false;
1988 }
1989
1990 return TypeSimpleMatch(m_type->Eval(local_context))(candidate);
1991 }
1992
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const1993 void Type::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
1994 ObjectSet& condition_non_targets) const
1995 {
1996 // Ships, Fleets and default checks for current objects only
1997 bool found_type = false;
1998 if (m_type) {
1999 found_type = true;
2000 //std::vector<std::shared_ptr<const T>> this_base;
2001 switch (m_type->Eval()) {
2002 case OBJ_BUILDING:
2003 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
2004 break;
2005 case OBJ_FIELD:
2006 AddFieldSet(parent_context.ContextObjects(), condition_non_targets);
2007 break;
2008 case OBJ_FLEET:
2009 AddFleetSet(parent_context.ContextObjects(), condition_non_targets);
2010 break;
2011 case OBJ_PLANET:
2012 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
2013 break;
2014 case OBJ_POP_CENTER:
2015 AddPopCenterSet(parent_context.ContextObjects(), condition_non_targets);
2016 break;
2017 case OBJ_PROD_CENTER:
2018 AddResCenterSet(parent_context.ContextObjects(), condition_non_targets);
2019 break;
2020 case OBJ_SHIP:
2021 AddShipSet(parent_context.ContextObjects(), condition_non_targets);
2022 break;
2023 case OBJ_SYSTEM:
2024 AddSystemSet(parent_context.ContextObjects(), condition_non_targets);
2025 break;
2026 case OBJ_FIGHTER: // shouldn't exist outside of combat as a separate object
2027 default:
2028 found_type = false;
2029 break;
2030 }
2031 }
2032 if (found_type) {
2033 //if (int(condition_non_targets.size()) < parent_context.ContextObjects().size()) {
2034 // DebugLogger() << "Type::GetBaseNonMatches will provide " << condition_non_targets.size()
2035 // << " objects of type " << GetType()->Eval() << " rather than "
2036 // << parent_context.ContextObjects().size() << " total objects";
2037 //}
2038 } else {
2039 Condition::GetDefaultInitialCandidateObjects(parent_context, condition_non_targets);
2040 }
2041 }
2042
SetTopLevelContent(const std::string & content_name)2043 void Type::SetTopLevelContent(const std::string& content_name) {
2044 if (m_type)
2045 m_type->SetTopLevelContent(content_name);
2046 }
2047
GetCheckSum() const2048 unsigned int Type::GetCheckSum() const {
2049 unsigned int retval{0};
2050
2051 CheckSums::CheckSumCombine(retval, "Condition::Type");
2052 CheckSums::CheckSumCombine(retval, m_type);
2053
2054 TraceLogger() << "GetCheckSum(Type): retval: " << retval;
2055 return retval;
2056 }
2057
2058 ///////////////////////////////////////////////////////////
2059 // Building //
2060 ///////////////////////////////////////////////////////////
Building(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>> && names)2061 Building::Building(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>>&& names) :
2062 Condition(),
2063 m_names(std::move(names))
2064 {
2065 m_root_candidate_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->RootCandidateInvariant(); });
2066 m_target_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->TargetInvariant(); });
2067 m_source_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->SourceInvariant(); });
2068 }
2069
operator ==(const Condition & rhs) const2070 bool Building::operator==(const Condition& rhs) const {
2071 if (this == &rhs)
2072 return true;
2073 if (typeid(*this) != typeid(rhs))
2074 return false;
2075
2076 const Building& rhs_ = static_cast<const Building&>(rhs);
2077
2078 if (m_names.size() != rhs_.m_names.size())
2079 return false;
2080 for (unsigned int i = 0; i < m_names.size(); ++i) {
2081 CHECK_COND_VREF_MEMBER(m_names.at(i))
2082 }
2083
2084 return true;
2085 }
2086
2087 namespace {
2088 struct BuildingSimpleMatch {
BuildingSimpleMatchCondition::__anone9a56faf1211::BuildingSimpleMatch2089 BuildingSimpleMatch(const std::vector<std::string>& names) :
2090 m_names(names)
2091 {}
2092
operator ()Condition::__anone9a56faf1211::BuildingSimpleMatch2093 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
2094 if (!candidate)
2095 return false;
2096
2097 // is it a building?
2098 auto building = std::dynamic_pointer_cast<const ::Building>(candidate);
2099 if (!building)
2100 return false;
2101
2102 // if no name supplied, match any building
2103 if (m_names.empty())
2104 return true;
2105
2106 // is it one of the specified building types?
2107 return std::count(m_names.begin(), m_names.end(), building->BuildingTypeName());
2108 }
2109
2110 const std::vector<std::string>& m_names;
2111 };
2112 }
2113
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const2114 void Building::Eval(const ScriptingContext& parent_context,
2115 ObjectSet& matches, ObjectSet& non_matches,
2116 SearchDomain search_domain/* = NON_MATCHES*/) const
2117 {
2118 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
2119 if (simple_eval_safe) {
2120 // check each valueref for invariance to local candidate
2121 for (auto& name : m_names) {
2122 if (!name->LocalCandidateInvariant()) {
2123 simple_eval_safe = false;
2124 break;
2125 }
2126 }
2127 }
2128 if (simple_eval_safe) {
2129 // evaluate names once, and use to check all candidate objects
2130 std::vector<std::string> names;
2131 // get all names from valuerefs
2132 for (auto& name : m_names) {
2133 names.push_back(name->Eval(parent_context));
2134 }
2135 EvalImpl(matches, non_matches, search_domain, BuildingSimpleMatch(names));
2136 } else {
2137 // re-evaluate allowed building types range for each candidate object
2138 Condition::Eval(parent_context, matches, non_matches, search_domain);
2139 }
2140 }
2141
Description(bool negated) const2142 std::string Building::Description(bool negated/* = false*/) const {
2143 std::string values_str;
2144 for (unsigned int i = 0; i < m_names.size(); ++i) {
2145 values_str += m_names[i]->ConstantExpr() ?
2146 UserString(m_names[i]->Eval()) :
2147 m_names[i]->Description();
2148 if (2 <= m_names.size() && i < m_names.size() - 2) {
2149 values_str += ", ";
2150 } else if (i == m_names.size() - 2) {
2151 values_str += m_names.size() < 3 ? " " : ", ";
2152 values_str += UserString("OR");
2153 values_str += " ";
2154 }
2155 }
2156 return str(FlexibleFormat((!negated)
2157 ? UserString("DESC_BUILDING")
2158 : UserString("DESC_BUILDING_NOT"))
2159 % values_str);
2160 }
2161
Dump(unsigned short ntabs) const2162 std::string Building::Dump(unsigned short ntabs) const {
2163 std::string retval = DumpIndent(ntabs) + "Building name = ";
2164 if (m_names.size() == 1) {
2165 retval += m_names[0]->Dump(ntabs) + "\n";
2166 } else {
2167 retval += "[ ";
2168 for (auto& name : m_names) {
2169 retval += name->Dump(ntabs) + " ";
2170 }
2171 retval += "]\n";
2172 }
2173 return retval;
2174 }
2175
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const2176 void Building::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
2177 ObjectSet& condition_non_targets) const
2178 { AddBuildingSet(parent_context.ContextObjects(), condition_non_targets); }
2179
Match(const ScriptingContext & local_context) const2180 bool Building::Match(const ScriptingContext& local_context) const {
2181 auto candidate = local_context.condition_local_candidate;
2182 if (!candidate) {
2183 ErrorLogger() << "Building::Match passed no candidate object";
2184 return false;
2185 }
2186
2187 // is it a building?
2188 auto building = std::dynamic_pointer_cast<const ::Building>(candidate);
2189 if (building) {
2190 // match any building type?
2191 if (m_names.empty())
2192 return true;
2193
2194 // match one of the specified building types
2195 for (auto& name : m_names) {
2196 if (name->Eval(local_context) == building->BuildingTypeName())
2197 return true;
2198 }
2199 }
2200
2201 return false;
2202 }
2203
SetTopLevelContent(const std::string & content_name)2204 void Building::SetTopLevelContent(const std::string& content_name) {
2205 for (auto& name : m_names) {
2206 if (name)
2207 name->SetTopLevelContent(content_name);
2208 }
2209 }
2210
GetCheckSum() const2211 unsigned int Building::GetCheckSum() const {
2212 unsigned int retval{0};
2213
2214 CheckSums::CheckSumCombine(retval, "Condition::Building");
2215 CheckSums::CheckSumCombine(retval, m_names);
2216
2217 TraceLogger() << "GetCheckSum(Building): retval: " << retval;
2218 return retval;
2219 }
2220
2221 ///////////////////////////////////////////////////////////
2222 // HasSpecial //
2223 ///////////////////////////////////////////////////////////
HasSpecial()2224 HasSpecial::HasSpecial() :
2225 HasSpecial(nullptr,
2226 std::unique_ptr<ValueRef::ValueRef<int>>{},
2227 std::unique_ptr<ValueRef::ValueRef<int>>{})
2228 {}
2229
HasSpecial(std::string name)2230 HasSpecial::HasSpecial(std::string name) :
2231 HasSpecial(std::make_unique<ValueRef::Constant<std::string>>(std::move(name)),
2232 std::unique_ptr<ValueRef::ValueRef<int>>{},
2233 std::unique_ptr<ValueRef::ValueRef<int>>{})
2234 {}
2235
HasSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)2236 HasSpecial::HasSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
2237 HasSpecial(std::move(name),
2238 std::unique_ptr<ValueRef::ValueRef<int>>{},
2239 std::unique_ptr<ValueRef::ValueRef<int>>{})
2240 {}
2241
HasSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::unique_ptr<ValueRef::ValueRef<int>> && since_turn_low,std::unique_ptr<ValueRef::ValueRef<int>> && since_turn_high)2242 HasSpecial::HasSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
2243 std::unique_ptr<ValueRef::ValueRef<int>>&& since_turn_low,
2244 std::unique_ptr<ValueRef::ValueRef<int>>&& since_turn_high) :
2245 Condition(),
2246 m_name(std::move(name)),
2247 m_since_turn_low(std::move(since_turn_low)),
2248 m_since_turn_high(std::move(since_turn_high))
2249 {
2250 auto operands = {m_since_turn_low.get(), m_since_turn_high.get()};
2251 m_root_candidate_invariant =
2252 (!m_name || m_name->RootCandidateInvariant()) &&
2253 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
2254 m_target_invariant =
2255 (!m_name || m_name->TargetInvariant()) &&
2256 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
2257 m_source_invariant =
2258 (!m_name || m_name->SourceInvariant()) &&
2259 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
2260 }
2261
HasSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::unique_ptr<ValueRef::ValueRef<double>> && capacity_low,std::unique_ptr<ValueRef::ValueRef<double>> && capacity_high)2262 HasSpecial::HasSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
2263 std::unique_ptr<ValueRef::ValueRef<double>>&& capacity_low,
2264 std::unique_ptr<ValueRef::ValueRef<double>>&& capacity_high) :
2265 Condition(),
2266 m_name(std::move(name)),
2267 m_capacity_low(std::move(capacity_low)),
2268 m_capacity_high(std::move(capacity_high))
2269 {
2270 auto operands = {m_capacity_low.get(), m_capacity_high.get()};
2271 m_root_candidate_invariant =
2272 (!m_name || m_name->RootCandidateInvariant()) &&
2273 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
2274 m_target_invariant =
2275 (!m_name || m_name->TargetInvariant()) &&
2276 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
2277 m_source_invariant =
2278 (!m_name || m_name->SourceInvariant()) &&
2279 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
2280 }
2281
operator ==(const Condition & rhs) const2282 bool HasSpecial::operator==(const Condition& rhs) const {
2283 if (this == &rhs)
2284 return true;
2285 if (typeid(*this) != typeid(rhs))
2286 return false;
2287
2288 const HasSpecial& rhs_ = static_cast<const HasSpecial&>(rhs);
2289
2290 CHECK_COND_VREF_MEMBER(m_name)
2291 CHECK_COND_VREF_MEMBER(m_capacity_low)
2292 CHECK_COND_VREF_MEMBER(m_capacity_high)
2293 CHECK_COND_VREF_MEMBER(m_since_turn_low)
2294 CHECK_COND_VREF_MEMBER(m_since_turn_high)
2295
2296 return true;
2297 }
2298
2299 namespace {
2300 struct HasSpecialSimpleMatch {
HasSpecialSimpleMatchCondition::__anone9a56faf1911::HasSpecialSimpleMatch2301 HasSpecialSimpleMatch(const std::string& name, float low_cap, float high_cap, int low_turn, int high_turn) :
2302 m_name(name),
2303 m_low_cap(low_cap),
2304 m_high_cap(high_cap),
2305 m_low_turn(low_turn),
2306 m_high_turn(high_turn)
2307 {}
2308
operator ()Condition::__anone9a56faf1911::HasSpecialSimpleMatch2309 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
2310 if (!candidate)
2311 return false;
2312
2313 if (m_name.empty())
2314 return !candidate->Specials().empty();
2315
2316 auto it = candidate->Specials().find(m_name);
2317 if (it == candidate->Specials().end())
2318 return false;
2319
2320 int special_since_turn = it->second.first;
2321 float special_capacity = it->second.second;
2322 return m_low_turn <= special_since_turn
2323 && special_since_turn <= m_high_turn
2324 && m_low_cap <= special_capacity
2325 && special_capacity <= m_high_cap;
2326 }
2327
2328 const std::string& m_name;
2329 float m_low_cap;
2330 float m_high_cap;
2331 int m_low_turn;
2332 int m_high_turn;
2333 };
2334 }
2335
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const2336 void HasSpecial::Eval(const ScriptingContext& parent_context,
2337 ObjectSet& matches, ObjectSet& non_matches,
2338 SearchDomain search_domain/* = NON_MATCHES*/) const
2339 {
2340 bool simple_eval_safe = ((!m_name || m_name->LocalCandidateInvariant()) &&
2341 (!m_capacity_low || m_capacity_low->LocalCandidateInvariant()) &&
2342 (!m_capacity_high || m_capacity_high->LocalCandidateInvariant()) &&
2343 (!m_since_turn_low || m_since_turn_low->LocalCandidateInvariant()) &&
2344 (!m_since_turn_high || m_since_turn_high->LocalCandidateInvariant()) &&
2345 (parent_context.condition_root_candidate || RootCandidateInvariant()));
2346 if (simple_eval_safe) {
2347 // evaluate turn limits and capacities once, pass to simple match for all candidates
2348 std::string name = (m_name ? m_name->Eval(parent_context) : "");
2349 float low_cap = (m_capacity_low ? m_capacity_low->Eval(parent_context) : -FLT_MAX);
2350 float high_cap = (m_capacity_high ? m_capacity_high->Eval(parent_context) : FLT_MAX);
2351 int low_turn = (m_since_turn_low ? m_since_turn_low->Eval(parent_context) : BEFORE_FIRST_TURN);
2352 int high_turn = (m_since_turn_high ? m_since_turn_high->Eval(parent_context) : IMPOSSIBLY_LARGE_TURN);
2353 EvalImpl(matches, non_matches, search_domain, HasSpecialSimpleMatch(name, low_cap, high_cap, low_turn, high_turn));
2354 } else {
2355 // re-evaluate allowed turn range for each candidate object
2356 Condition::Eval(parent_context, matches, non_matches, search_domain);
2357 }
2358 }
2359
Description(bool negated) const2360 std::string HasSpecial::Description(bool negated/* = false*/) const {
2361 std::string name_str;
2362 if (m_name) {
2363 name_str = m_name->Description();
2364 if (m_name->ConstantExpr() && UserStringExists(name_str))
2365 name_str = UserString(name_str);
2366 }
2367
2368 if (m_since_turn_low || m_since_turn_high) {
2369 // turn range has been specified; must indicate in description
2370 std::string low_str = std::to_string(BEFORE_FIRST_TURN);
2371 if (m_since_turn_low)
2372 low_str = m_since_turn_low->Description();
2373
2374 std::string high_str = std::to_string(IMPOSSIBLY_LARGE_TURN);
2375 if (m_since_turn_high)
2376 high_str = m_since_turn_high->Description();
2377
2378 return str(FlexibleFormat((!negated)
2379 ? UserString("DESC_SPECIAL_TURN_RANGE")
2380 : UserString("DESC_SPECIAL_TURN_RANGE_NOT"))
2381 % name_str
2382 % low_str
2383 % high_str);
2384 }
2385
2386 if (m_capacity_low || m_capacity_high) {
2387 // capacity range has been specified; must indicate in description
2388 std::string low_str = std::to_string(-FLT_MAX);
2389 if (m_capacity_low)
2390 low_str = m_capacity_low->Description();
2391
2392 std::string high_str = std::to_string(FLT_MAX);
2393 if (m_capacity_high)
2394 high_str = m_capacity_high->Description();
2395
2396 return str(FlexibleFormat((!negated)
2397 ? UserString("DESC_SPECIAL_CAPACITY_RANGE")
2398 : UserString("DESC_SPECIAL_CAPACITY_RANGE_NOT"))
2399 % name_str
2400 % low_str
2401 % high_str);
2402 }
2403
2404 return str(FlexibleFormat((!negated)
2405 ? UserString("DESC_SPECIAL")
2406 : UserString("DESC_SPECIAL_NOT"))
2407 % name_str);
2408 }
2409
Dump(unsigned short ntabs) const2410 std::string HasSpecial::Dump(unsigned short ntabs) const {
2411 std::string name_str = (m_name ? m_name->Dump(ntabs) : "");
2412
2413 if (m_since_turn_low || m_since_turn_high) {
2414 std::string low_dump = (m_since_turn_low ? m_since_turn_low->Dump(ntabs) : std::to_string(BEFORE_FIRST_TURN));
2415 std::string high_dump = (m_since_turn_high ? m_since_turn_high->Dump(ntabs) : std::to_string(IMPOSSIBLY_LARGE_TURN));
2416 return DumpIndent(ntabs) + "HasSpecialSinceTurn name = \"" + name_str + "\" low = " + low_dump + " high = " + high_dump;
2417 }
2418
2419 if (m_capacity_low || m_capacity_high) {
2420 std::string low_dump = (m_capacity_low ? m_capacity_low->Dump(ntabs) : std::to_string(-FLT_MAX));
2421 std::string high_dump = (m_capacity_high ? m_capacity_high->Dump(ntabs) : std::to_string(FLT_MAX));
2422 return DumpIndent(ntabs) + "HasSpecialCapacity name = \"" + name_str + "\" low = " + low_dump + " high = " + high_dump;
2423 }
2424
2425 return DumpIndent(ntabs) + "HasSpecial name = \"" + name_str + "\"\n";
2426 }
2427
Match(const ScriptingContext & local_context) const2428 bool HasSpecial::Match(const ScriptingContext& local_context) const {
2429 auto candidate = local_context.condition_local_candidate;
2430 if (!candidate) {
2431 ErrorLogger() << "HasSpecial::Match passed no candidate object";
2432 return false;
2433 }
2434 std::string name = (m_name ? m_name->Eval(local_context) : "");
2435 float low_cap = (m_capacity_low ? m_capacity_low->Eval(local_context) : -FLT_MAX);
2436 float high_cap = (m_capacity_high ? m_capacity_high->Eval(local_context) : FLT_MAX);
2437 int low_turn = (m_since_turn_low ? m_since_turn_low->Eval(local_context) : BEFORE_FIRST_TURN);
2438 int high_turn = (m_since_turn_high ? m_since_turn_high->Eval(local_context) : IMPOSSIBLY_LARGE_TURN);
2439
2440 return HasSpecialSimpleMatch(name, low_cap, high_cap, low_turn, high_turn)(candidate);
2441 }
2442
SetTopLevelContent(const std::string & content_name)2443 void HasSpecial::SetTopLevelContent(const std::string& content_name) {
2444 if (m_name)
2445 m_name->SetTopLevelContent(content_name);
2446 if (m_capacity_low)
2447 m_capacity_low->SetTopLevelContent(content_name);
2448 if (m_capacity_high)
2449 m_capacity_high->SetTopLevelContent(content_name);
2450 if (m_since_turn_low)
2451 m_since_turn_low->SetTopLevelContent(content_name);
2452 if (m_since_turn_high)
2453 m_since_turn_high->SetTopLevelContent(content_name);
2454 }
2455
GetCheckSum() const2456 unsigned int HasSpecial::GetCheckSum() const {
2457 unsigned int retval{0};
2458
2459 CheckSums::CheckSumCombine(retval, "Condition::HasSpecial");
2460 CheckSums::CheckSumCombine(retval, m_name);
2461 CheckSums::CheckSumCombine(retval, m_capacity_low);
2462 CheckSums::CheckSumCombine(retval, m_capacity_high);
2463 CheckSums::CheckSumCombine(retval, m_since_turn_low);
2464 CheckSums::CheckSumCombine(retval, m_since_turn_high);
2465
2466 TraceLogger() << "GetCheckSum(HasSpecial): retval: " << retval;
2467 return retval;
2468 }
2469
2470 ///////////////////////////////////////////////////////////
2471 // HasTag //
2472 ///////////////////////////////////////////////////////////
HasTag()2473 HasTag::HasTag() :
2474 HasTag(std::unique_ptr<ValueRef::ValueRef<std::string>>{})
2475 {}
2476
HasTag(const std::string & name)2477 HasTag::HasTag(const std::string& name) :
2478 HasTag(std::make_unique<ValueRef::Constant<std::string>>(name))
2479 {}
2480
HasTag(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)2481 HasTag::HasTag(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
2482 Condition(),
2483 m_name(std::move(name))
2484 {
2485 m_root_candidate_invariant = !m_name || m_name->RootCandidateInvariant();
2486 m_target_invariant = !m_name || m_name->TargetInvariant();
2487 m_source_invariant = !m_name || m_name->SourceInvariant();
2488 }
2489
operator ==(const Condition & rhs) const2490 bool HasTag::operator==(const Condition& rhs) const {
2491 if (this == &rhs)
2492 return true;
2493 if (typeid(*this) != typeid(rhs))
2494 return false;
2495
2496 const HasTag& rhs_ = static_cast<const HasTag&>(rhs);
2497
2498 CHECK_COND_VREF_MEMBER(m_name)
2499
2500 return true;
2501 }
2502
2503 namespace {
2504 struct HasTagSimpleMatch {
HasTagSimpleMatchCondition::__anone9a56faf1a11::HasTagSimpleMatch2505 HasTagSimpleMatch() :
2506 m_any_tag_ok(true),
2507 m_name()
2508 {}
2509
HasTagSimpleMatchCondition::__anone9a56faf1a11::HasTagSimpleMatch2510 HasTagSimpleMatch(const std::string& name) :
2511 m_any_tag_ok(false),
2512 m_name(name)
2513 {}
2514
operator ()Condition::__anone9a56faf1a11::HasTagSimpleMatch2515 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
2516 if (!candidate)
2517 return false;
2518
2519 if (m_any_tag_ok && !candidate->Tags().empty())
2520 return true;
2521
2522 return candidate->HasTag(m_name);
2523 }
2524
2525 bool m_any_tag_ok;
2526 std::string m_name;
2527 };
2528 }
2529
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const2530 void HasTag::Eval(const ScriptingContext& parent_context,
2531 ObjectSet& matches, ObjectSet& non_matches,
2532 SearchDomain search_domain/* = NON_MATCHES*/) const
2533 {
2534 bool simple_eval_safe = (!m_name || m_name->LocalCandidateInvariant()) &&
2535 (parent_context.condition_root_candidate || RootCandidateInvariant());
2536 if (simple_eval_safe) {
2537 // evaluate number limits once, use to match all candidates
2538 if (!m_name) {
2539 EvalImpl(matches, non_matches, search_domain, HasTagSimpleMatch());
2540 } else {
2541 std::string name = boost::to_upper_copy<std::string>(m_name->Eval(parent_context));
2542 EvalImpl(matches, non_matches, search_domain, HasTagSimpleMatch(name));
2543 }
2544 } else {
2545 // re-evaluate allowed turn range for each candidate object
2546 Condition::Eval(parent_context, matches, non_matches, search_domain);
2547 }
2548 }
2549
Description(bool negated) const2550 std::string HasTag::Description(bool negated/* = false*/) const {
2551 std::string name_str;
2552 if (m_name) {
2553 name_str = m_name->Description();
2554 if (m_name->ConstantExpr() && UserStringExists(name_str))
2555 name_str = UserString(name_str);
2556 }
2557 return str(FlexibleFormat((!negated)
2558 ? UserString("DESC_HAS_TAG")
2559 : UserString("DESC_HAS_TAG_NOT"))
2560 % name_str);
2561 }
2562
Dump(unsigned short ntabs) const2563 std::string HasTag::Dump(unsigned short ntabs) const {
2564 std::string retval = DumpIndent(ntabs) + "HasTag";
2565 if (m_name)
2566 retval += " name = " + m_name->Dump(ntabs);
2567 retval += "\n";
2568 return retval;
2569 }
2570
Match(const ScriptingContext & local_context) const2571 bool HasTag::Match(const ScriptingContext& local_context) const {
2572 auto candidate = local_context.condition_local_candidate;
2573 if (!candidate) {
2574 ErrorLogger() << "HasTag::Match passed no candidate object";
2575 return false;
2576 }
2577
2578 if (!m_name)
2579 return HasTagSimpleMatch()(candidate);
2580
2581 std::string name = boost::to_upper_copy<std::string>(m_name->Eval(local_context));
2582 return HasTagSimpleMatch(name)(candidate);
2583 }
2584
SetTopLevelContent(const std::string & content_name)2585 void HasTag::SetTopLevelContent(const std::string& content_name) {
2586 if (m_name)
2587 m_name->SetTopLevelContent(content_name);
2588 }
2589
GetCheckSum() const2590 unsigned int HasTag::GetCheckSum() const {
2591 unsigned int retval{0};
2592
2593 CheckSums::CheckSumCombine(retval, "Condition::HasTag");
2594 CheckSums::CheckSumCombine(retval, m_name);
2595
2596 TraceLogger() << "GetCheckSum(HasTag): retval: " << retval;
2597 return retval;
2598 }
2599
2600 ///////////////////////////////////////////////////////////
2601 // CreatedOnTurn //
2602 ///////////////////////////////////////////////////////////
CreatedOnTurn(std::unique_ptr<ValueRef::ValueRef<int>> && low,std::unique_ptr<ValueRef::ValueRef<int>> && high)2603 CreatedOnTurn::CreatedOnTurn(std::unique_ptr<ValueRef::ValueRef<int>>&& low,
2604 std::unique_ptr<ValueRef::ValueRef<int>>&& high) :
2605 Condition(),
2606 m_low(std::move(low)),
2607 m_high(std::move(high))
2608 {
2609 auto operands = {m_low.get(), m_high.get()};
2610 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
2611 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
2612 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
2613 }
2614
operator ==(const Condition & rhs) const2615 bool CreatedOnTurn::operator==(const Condition& rhs) const {
2616 if (this == &rhs)
2617 return true;
2618 if (typeid(*this) != typeid(rhs))
2619 return false;
2620
2621 const CreatedOnTurn& rhs_ = static_cast<const CreatedOnTurn&>(rhs);
2622
2623 CHECK_COND_VREF_MEMBER(m_low)
2624 CHECK_COND_VREF_MEMBER(m_high)
2625
2626 return true;
2627 }
2628
2629 namespace {
2630 struct CreatedOnTurnSimpleMatch {
CreatedOnTurnSimpleMatchCondition::__anone9a56faf1e11::CreatedOnTurnSimpleMatch2631 CreatedOnTurnSimpleMatch(int low, int high) :
2632 m_low(low),
2633 m_high(high)
2634 {}
2635
operator ()Condition::__anone9a56faf1e11::CreatedOnTurnSimpleMatch2636 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
2637 if (!candidate)
2638 return false;
2639 int turn = candidate->CreationTurn();
2640 return m_low <= turn && turn <= m_high;
2641 }
2642
2643 int m_low;
2644 int m_high;
2645 };
2646 }
2647
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const2648 void CreatedOnTurn::Eval(const ScriptingContext& parent_context,
2649 ObjectSet& matches, ObjectSet& non_matches,
2650 SearchDomain search_domain/* = NON_MATCHES*/) const
2651 {
2652 bool simple_eval_safe = ((!m_low || m_low->LocalCandidateInvariant()) &&
2653 (!m_high || m_high->LocalCandidateInvariant()) &&
2654 (parent_context.condition_root_candidate || RootCandidateInvariant()));
2655 if (simple_eval_safe) {
2656 int low = (m_low ? m_low->Eval(parent_context) : BEFORE_FIRST_TURN);
2657 int high = (m_high ? m_high->Eval(parent_context) : IMPOSSIBLY_LARGE_TURN);
2658 EvalImpl(matches, non_matches, search_domain, CreatedOnTurnSimpleMatch(low, high));
2659 } else {
2660 // re-evaluate allowed turn range for each candidate object
2661 Condition::Eval(parent_context, matches, non_matches, search_domain);
2662 }
2663 }
2664
Description(bool negated) const2665 std::string CreatedOnTurn::Description(bool negated/* = false*/) const {
2666 std::string low_str = (m_low ? (m_low->ConstantExpr() ?
2667 std::to_string(m_low->Eval()) :
2668 m_low->Description())
2669 : std::to_string(BEFORE_FIRST_TURN));
2670 std::string high_str = (m_high ? (m_high->ConstantExpr() ?
2671 std::to_string(m_high->Eval()) :
2672 m_high->Description())
2673 : std::to_string(IMPOSSIBLY_LARGE_TURN));
2674 return str(FlexibleFormat((!negated)
2675 ? UserString("DESC_CREATED_ON_TURN")
2676 : UserString("DESC_CREATED_ON_TURN_NOT"))
2677 % low_str
2678 % high_str);
2679 }
2680
Dump(unsigned short ntabs) const2681 std::string CreatedOnTurn::Dump(unsigned short ntabs) const {
2682 std::string retval = DumpIndent(ntabs) + "CreatedOnTurn";
2683 if (m_low)
2684 retval += " low = " + m_low->Dump(ntabs);
2685 if (m_high)
2686 retval += " high = " + m_high->Dump(ntabs);
2687 retval += "\n";
2688 return retval;
2689 }
2690
Match(const ScriptingContext & local_context) const2691 bool CreatedOnTurn::Match(const ScriptingContext& local_context) const {
2692 auto candidate = local_context.condition_local_candidate;
2693 if (!candidate) {
2694 ErrorLogger() << "CreatedOnTurn::Match passed no candidate object";
2695 return false;
2696 }
2697 int low = (m_low ? std::max(0, m_low->Eval(local_context)) : BEFORE_FIRST_TURN);
2698 int high = (m_high ? std::min(m_high->Eval(local_context), IMPOSSIBLY_LARGE_TURN) : IMPOSSIBLY_LARGE_TURN);
2699 return CreatedOnTurnSimpleMatch(low, high)(candidate);
2700 }
2701
SetTopLevelContent(const std::string & content_name)2702 void CreatedOnTurn::SetTopLevelContent(const std::string& content_name) {
2703 if (m_low)
2704 m_low->SetTopLevelContent(content_name);
2705 if (m_high)
2706 m_high->SetTopLevelContent(content_name);
2707 }
2708
GetCheckSum() const2709 unsigned int CreatedOnTurn::GetCheckSum() const {
2710 unsigned int retval{0};
2711
2712 CheckSums::CheckSumCombine(retval, "Condition::CreatedOnTurn");
2713 CheckSums::CheckSumCombine(retval, m_low);
2714 CheckSums::CheckSumCombine(retval, m_high);
2715
2716 TraceLogger() << "GetCheckSum(CreatedOnTurn): retval: " << retval;
2717 return retval;
2718 }
2719
2720 ///////////////////////////////////////////////////////////
2721 // Contains //
2722 ///////////////////////////////////////////////////////////
Contains(std::unique_ptr<Condition> && condition)2723 Contains::Contains(std::unique_ptr<Condition>&& condition) :
2724 Condition(),
2725 m_condition(std::move(condition))
2726 {
2727 m_root_candidate_invariant = m_condition->RootCandidateInvariant();
2728 m_target_invariant = m_condition->TargetInvariant();
2729 m_source_invariant = m_condition->SourceInvariant();
2730 }
2731
operator ==(const Condition & rhs) const2732 bool Contains::operator==(const Condition& rhs) const {
2733 if (this == &rhs)
2734 return true;
2735 if (typeid(*this) != typeid(rhs))
2736 return false;
2737
2738 const Contains& rhs_ = static_cast<const Contains&>(rhs);
2739
2740 CHECK_COND_VREF_MEMBER(m_condition)
2741
2742 return true;
2743 }
2744
2745 namespace {
2746 struct ContainsSimpleMatch {
ContainsSimpleMatchCondition::__anone9a56faf1f11::ContainsSimpleMatch2747 ContainsSimpleMatch(const ObjectSet& subcondition_matches) :
2748 m_subcondition_matches_ids()
2749 {
2750 // We need a sorted container for efficiently intersecting
2751 // subcondition_matches with the set of objects contained in some
2752 // candidate object.
2753 // We only need ids, not objects, so we can do that conversion
2754 // here as well, simplifying later code.
2755 // Note that this constructor is called only once per
2756 // Contains::Eval(), its work cannot help performance when executed
2757 // for each candidate.
2758 m_subcondition_matches_ids.reserve(subcondition_matches.size());
2759 // gather the ids
2760 for (auto& obj : subcondition_matches) {
2761 if (obj)
2762 m_subcondition_matches_ids.push_back(obj->ID());
2763 }
2764 // sort them
2765 std::sort(m_subcondition_matches_ids.begin(), m_subcondition_matches_ids.end());
2766 }
2767
operator ()Condition::__anone9a56faf1f11::ContainsSimpleMatch2768 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
2769 if (!candidate)
2770 return false;
2771
2772 bool match = false;
2773 const auto& candidate_elements = candidate->ContainedObjectIDs(); // guaranteed O(1)
2774
2775 // We need to test whether candidate_elements and m_subcondition_matches_ids have a common element.
2776 // We choose the strategy that is more efficient by comparing the sizes of both sets.
2777 if (candidate_elements.size() < m_subcondition_matches_ids.size()) {
2778 // candidate_elements is smaller, so we iterate it and look up each candidate element in m_subcondition_matches_ids
2779 for (int id : candidate_elements) {
2780 // std::lower_bound requires m_subcondition_matches_ids to be sorted
2781 auto matching_it = std::lower_bound(m_subcondition_matches_ids.begin(), m_subcondition_matches_ids.end(), id);
2782
2783 if (matching_it != m_subcondition_matches_ids.end() && *matching_it == id) {
2784 match = true;
2785 break;
2786 }
2787 }
2788 } else {
2789 // m_subcondition_matches_ids is smaller, so we iterate it and look up each subcondition match in the set of candidate's elements
2790 for (int id : m_subcondition_matches_ids) {
2791 // candidate->Contains() may have a faster implementation than candidate_elements->find()
2792 if (candidate->Contains(id)) {
2793 match = true;
2794 break;
2795 }
2796 }
2797 }
2798
2799 return match;
2800 }
2801
2802 std::vector<int> m_subcondition_matches_ids;
2803 };
2804 }
2805
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const2806 void Contains::Eval(const ScriptingContext& parent_context,
2807 ObjectSet& matches, ObjectSet& non_matches,
2808 SearchDomain search_domain/* = NON_MATCHES*/) const
2809 {
2810 unsigned int search_domain_size = (search_domain == MATCHES ? matches.size() : non_matches.size());
2811 bool simple_eval_safe = parent_context.condition_root_candidate ||
2812 RootCandidateInvariant() ||
2813 search_domain_size < 2;
2814 if (!simple_eval_safe) {
2815 // re-evaluate contained objects for each candidate object
2816 Condition::Eval(parent_context, matches, non_matches, search_domain);
2817 return;
2818 }
2819
2820 // how complicated is this containment test?
2821 if (((search_domain == MATCHES) && matches.empty()) ||
2822 ((search_domain == NON_MATCHES) && non_matches.empty()))
2823 {
2824 // don't need to evaluate anything...
2825
2826 } else if (search_domain_size == 1) {
2827 // evaluate subcondition on objects contained by the candidate
2828 ScriptingContext local_context(parent_context, search_domain == MATCHES ? *matches.begin() : *non_matches.begin());
2829
2830 // initialize subcondition candidates from local candidate's contents
2831 const ObjectMap& objects = parent_context.ContextObjects();
2832 ObjectSet subcondition_matches = objects.find(local_context.condition_local_candidate->ContainedObjectIDs());
2833
2834 // apply subcondition to candidates
2835 if (!subcondition_matches.empty()) {
2836 ObjectSet dummy;
2837 m_condition->Eval(local_context, subcondition_matches, dummy, MATCHES);
2838 }
2839
2840 // move single local candidate as appropriate...
2841 if (search_domain == MATCHES && subcondition_matches.empty()) {
2842 // move to non_matches
2843 matches.clear();
2844 non_matches.push_back(local_context.condition_local_candidate);
2845 } else if (search_domain == NON_MATCHES && !subcondition_matches.empty()) {
2846 // move to matches
2847 non_matches.clear();
2848 matches.push_back(local_context.condition_local_candidate);
2849 }
2850
2851 } else {
2852 // evaluate contained objects once using default initial candidates
2853 // of subcondition to find all subcondition matches in the Universe
2854 std::shared_ptr<const UniverseObject> no_object;
2855 ScriptingContext local_context(parent_context, no_object);
2856 ObjectSet subcondition_matches;
2857 m_condition->Eval(local_context, subcondition_matches);
2858
2859 // check all candidates to see if they contain any subcondition matches
2860 EvalImpl(matches, non_matches, search_domain, ContainsSimpleMatch(subcondition_matches));
2861 }
2862 }
2863
Description(bool negated) const2864 std::string Contains::Description(bool negated/* = false*/) const {
2865 return str(FlexibleFormat((!negated)
2866 ? UserString("DESC_CONTAINS")
2867 : UserString("DESC_CONTAINS_NOT"))
2868 % m_condition->Description());
2869 }
2870
Dump(unsigned short ntabs) const2871 std::string Contains::Dump(unsigned short ntabs) const {
2872 std::string retval = DumpIndent(ntabs) + "Contains condition =\n";
2873 retval += m_condition->Dump(ntabs+1);
2874 return retval;
2875 }
2876
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const2877 void Contains::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
2878 ObjectSet& condition_non_targets) const
2879 {
2880 // objects that can contain other objects: systems, fleets, planets
2881 AddSystemSet(parent_context.ContextObjects(), condition_non_targets);
2882 AddFleetSet(parent_context.ContextObjects(), condition_non_targets);
2883 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
2884 }
2885
Match(const ScriptingContext & local_context) const2886 bool Contains::Match(const ScriptingContext& local_context) const {
2887 auto candidate = local_context.condition_local_candidate;
2888 if (!candidate) {
2889 ErrorLogger() << "Contains::Match passed no candidate object";
2890 return false;
2891 }
2892
2893 // get subcondition matches
2894 ObjectSet subcondition_matches;
2895 m_condition->Eval(local_context, subcondition_matches);
2896
2897 // does candidate object contain any subcondition matches?
2898 for (auto& obj : subcondition_matches)
2899 if (candidate->Contains(obj->ID()))
2900 return true;
2901
2902 return false;
2903 }
2904
SetTopLevelContent(const std::string & content_name)2905 void Contains::SetTopLevelContent(const std::string& content_name) {
2906 if (m_condition)
2907 m_condition->SetTopLevelContent(content_name);
2908 }
2909
GetCheckSum() const2910 unsigned int Contains::GetCheckSum() const {
2911 unsigned int retval{0};
2912
2913 CheckSums::CheckSumCombine(retval, "Condition::Contains");
2914 CheckSums::CheckSumCombine(retval, m_condition);
2915
2916 TraceLogger() << "GetCheckSum(Contains): retval: " << retval;
2917 return retval;
2918 }
2919
2920 ///////////////////////////////////////////////////////////
2921 // ContainedBy //
2922 ///////////////////////////////////////////////////////////
ContainedBy(std::unique_ptr<Condition> && condition)2923 ContainedBy::ContainedBy(std::unique_ptr<Condition>&& condition) :
2924 Condition(),
2925 m_condition(std::move(condition))
2926 {
2927 m_root_candidate_invariant = m_condition->RootCandidateInvariant();
2928 m_target_invariant = m_condition->TargetInvariant();
2929 m_source_invariant = m_condition->SourceInvariant();
2930 }
2931
operator ==(const Condition & rhs) const2932 bool ContainedBy::operator==(const Condition& rhs) const {
2933 if (this == &rhs)
2934 return true;
2935 if (typeid(*this) != typeid(rhs))
2936 return false;
2937
2938 const ContainedBy& rhs_ = static_cast<const ContainedBy&>(rhs);
2939
2940 CHECK_COND_VREF_MEMBER(m_condition)
2941
2942 return true;
2943 }
2944
2945 namespace {
2946 struct ContainedBySimpleMatch {
ContainedBySimpleMatchCondition::__anone9a56faf2011::ContainedBySimpleMatch2947 ContainedBySimpleMatch(const ObjectSet& subcondition_matches) :
2948 m_subcondition_matches_ids()
2949 {
2950 // We need a sorted container for efficiently intersecting
2951 // subcondition_matches with the set of objects containing some
2952 // candidate object.
2953 // We only need ids, not objects, so we can do that conversion
2954 // here as well, simplifying later code.
2955 // Note that this constructor is called only once per
2956 // ContainedBy::Eval(), its work cannot help performance when
2957 // executed for each candidate.
2958 m_subcondition_matches_ids.reserve(subcondition_matches.size());
2959 // gather the ids
2960 for (auto& obj : subcondition_matches) {
2961 if (obj)
2962 { m_subcondition_matches_ids.push_back(obj->ID()); }
2963 }
2964 // sort them
2965 std::sort(m_subcondition_matches_ids.begin(), m_subcondition_matches_ids.end());
2966 }
2967
operator ()Condition::__anone9a56faf2011::ContainedBySimpleMatch2968 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
2969 if (!candidate)
2970 return false;
2971
2972 bool match = false;
2973 // gather the objects containing candidate
2974 std::vector<int> candidate_containers;
2975 const int candidate_id = candidate->ID();
2976 const int system_id = candidate->SystemID();
2977 const int container_id = candidate->ContainerObjectID();
2978 if ( system_id != INVALID_OBJECT_ID && system_id != candidate_id) candidate_containers.push_back( system_id);
2979 if (container_id != INVALID_OBJECT_ID && container_id != system_id) candidate_containers.push_back(container_id);
2980 // FIXME: currently, direct container and system will do. In the future, we might need a way to retrieve containers of containers
2981
2982 // We need to test whether candidate_containers and m_subcondition_matches_ids have a common element.
2983 // We choose the strategy that is more efficient by comparing the sizes of both sets.
2984 if (candidate_containers.size() < m_subcondition_matches_ids.size()) {
2985 // candidate_containers is smaller, so we iterate it and look up each candidate container in m_subcondition_matches_ids
2986 for (int id : candidate_containers) {
2987 // std::lower_bound requires m_subcondition_matches_ids to be sorted
2988 auto matching_it = std::lower_bound(m_subcondition_matches_ids.begin(), m_subcondition_matches_ids.end(), id);
2989
2990 if (matching_it != m_subcondition_matches_ids.end() && *matching_it == id) {
2991 match = true;
2992 break;
2993 }
2994 }
2995 } else {
2996 // m_subcondition_matches_ids is smaller, so we iterate it and look up each subcondition match in the set of candidate's containers
2997 for (int id : m_subcondition_matches_ids) {
2998 // candidate->ContainedBy() may have a faster implementation than candidate_containers->find()
2999 if (candidate->ContainedBy(id)) {
3000 match = true;
3001 break;
3002 }
3003 }
3004 }
3005
3006 return match;
3007 }
3008
3009 std::vector<int> m_subcondition_matches_ids;
3010 };
3011 }
3012
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const3013 void ContainedBy::Eval(const ScriptingContext& parent_context,
3014 ObjectSet& matches, ObjectSet& non_matches,
3015 SearchDomain search_domain/* = NON_MATCHES*/) const
3016 {
3017 unsigned int search_domain_size = (search_domain == MATCHES ? matches.size() : non_matches.size());
3018 bool simple_eval_safe = parent_context.condition_root_candidate ||
3019 RootCandidateInvariant() ||
3020 search_domain_size < 2;
3021
3022 if (!simple_eval_safe) {
3023 // re-evaluate container objects for each candidate object
3024 Condition::Eval(parent_context, matches, non_matches, search_domain);
3025 return;
3026 }
3027
3028 // how complicated is this containment test?
3029 if (((search_domain == MATCHES) && matches.empty()) ||
3030 ((search_domain == NON_MATCHES) && non_matches.empty()))
3031 {
3032 // don't need to evaluate anything...
3033
3034 } else if (search_domain_size == 1) {
3035 // evaluate subcondition on objects that contain the candidate
3036 ScriptingContext local_context(parent_context, search_domain == MATCHES ? *matches.begin() : *non_matches.begin());
3037
3038 // initialize subcondition candidates from local candidate's containers
3039 const ObjectMap& objects = parent_context.ContextObjects();
3040 std::set<int> container_object_ids;
3041 if (local_context.condition_local_candidate->ContainerObjectID() != INVALID_OBJECT_ID)
3042 container_object_ids.insert(local_context.condition_local_candidate->ContainerObjectID());
3043 if (local_context.condition_local_candidate->SystemID() != INVALID_OBJECT_ID)
3044 container_object_ids.insert(local_context.condition_local_candidate->SystemID());
3045
3046 ObjectSet subcondition_matches = objects.find(container_object_ids);
3047
3048 // apply subcondition to candidates
3049 if (!subcondition_matches.empty()) {
3050 ObjectSet dummy;
3051 m_condition->Eval(local_context, subcondition_matches, dummy, MATCHES);
3052 }
3053
3054 // move single local candidate as appropriate...
3055 if (search_domain == MATCHES && subcondition_matches.empty()) {
3056 // move to non_matches
3057 matches.clear();
3058 non_matches.push_back(local_context.condition_local_candidate);
3059 } else if (search_domain == NON_MATCHES && !subcondition_matches.empty()) {
3060 // move to matches
3061 non_matches.clear();
3062 matches.push_back(local_context.condition_local_candidate);
3063 }
3064
3065 } else {
3066 // evaluate container objects once using default initial candidates
3067 // of subcondition to find all subcondition matches in the Universe
3068 std::shared_ptr<const UniverseObject> no_object;
3069 ScriptingContext local_context(parent_context, no_object);
3070 ObjectSet subcondition_matches;
3071 m_condition->Eval(local_context, subcondition_matches);
3072
3073 // check all candidates to see if they contain any subcondition matches
3074 EvalImpl(matches, non_matches, search_domain, ContainedBySimpleMatch(subcondition_matches));
3075 }
3076 }
3077
Description(bool negated) const3078 std::string ContainedBy::Description(bool negated/* = false*/) const {
3079 return str(FlexibleFormat((!negated)
3080 ? UserString("DESC_CONTAINED_BY")
3081 : UserString("DESC_CONTAINED_BY_NOT"))
3082 % m_condition->Description());
3083 }
3084
Dump(unsigned short ntabs) const3085 std::string ContainedBy::Dump(unsigned short ntabs) const {
3086 std::string retval = DumpIndent(ntabs) + "ContainedBy condition =\n";
3087 retval += m_condition->Dump(ntabs+1);
3088 return retval;
3089 }
3090
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const3091 void ContainedBy::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
3092 ObjectSet& condition_non_targets) const
3093 {
3094 // objects that can be contained by other objects: fleets, planets, ships, buildings
3095 AddFleetSet(parent_context.ContextObjects(), condition_non_targets);
3096 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
3097 AddShipSet(parent_context.ContextObjects(), condition_non_targets);
3098 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
3099 }
3100
Match(const ScriptingContext & local_context) const3101 bool ContainedBy::Match(const ScriptingContext& local_context) const {
3102 auto candidate = local_context.condition_local_candidate;
3103 if (!candidate) {
3104 ErrorLogger() << "ContainedBy::Match passed no candidate object";
3105 return false;
3106 }
3107
3108 // get containing objects
3109 std::set<int> containers;
3110 if (candidate->SystemID() != INVALID_OBJECT_ID)
3111 containers.insert(candidate->SystemID());
3112 if (candidate->ContainerObjectID() != INVALID_OBJECT_ID && candidate->ContainerObjectID() != candidate->SystemID())
3113 containers.insert(candidate->ContainerObjectID());
3114
3115 ObjectSet container_objects = local_context.ContextObjects().find<const UniverseObject>(containers);
3116 if (container_objects.empty())
3117 return false;
3118
3119 m_condition->Eval(local_context, container_objects);
3120
3121 return container_objects.empty();
3122 }
3123
SetTopLevelContent(const std::string & content_name)3124 void ContainedBy::SetTopLevelContent(const std::string& content_name) {
3125 if (m_condition)
3126 m_condition->SetTopLevelContent(content_name);
3127 }
3128
GetCheckSum() const3129 unsigned int ContainedBy::GetCheckSum() const {
3130 unsigned int retval{0};
3131
3132 CheckSums::CheckSumCombine(retval, "Condition::ContainedBy");
3133 CheckSums::CheckSumCombine(retval, m_condition);
3134
3135 TraceLogger() << "GetCheckSum(ContainedBy): retval: " << retval;
3136 return retval;
3137 }
3138
3139 ///////////////////////////////////////////////////////////
3140 // InOrIsSystem //
3141 ///////////////////////////////////////////////////////////
InOrIsSystem(std::unique_ptr<ValueRef::ValueRef<int>> && system_id)3142 InOrIsSystem::InOrIsSystem(std::unique_ptr<ValueRef::ValueRef<int>>&& system_id) :
3143 Condition(),
3144 m_system_id(std::move(system_id))
3145 {
3146 m_root_candidate_invariant = !m_system_id || m_system_id->RootCandidateInvariant();
3147 m_target_invariant = !m_system_id || m_system_id->TargetInvariant();
3148 m_source_invariant = !m_system_id || m_system_id->SourceInvariant();
3149 }
3150
operator ==(const Condition & rhs) const3151 bool InOrIsSystem::operator==(const Condition& rhs) const {
3152 if (this == &rhs)
3153 return true;
3154 if (typeid(*this) != typeid(rhs))
3155 return false;
3156
3157 const InOrIsSystem& rhs_ = static_cast<const InOrIsSystem&>(rhs);
3158
3159 CHECK_COND_VREF_MEMBER(m_system_id)
3160
3161 return true;
3162 }
3163
3164 namespace {
3165 struct InSystemSimpleMatch {
InSystemSimpleMatchCondition::__anone9a56faf2111::InSystemSimpleMatch3166 InSystemSimpleMatch(int system_id) :
3167 m_system_id(system_id)
3168 {}
3169
operator ()Condition::__anone9a56faf2111::InSystemSimpleMatch3170 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
3171 if (!candidate)
3172 return false;
3173 if (m_system_id == INVALID_OBJECT_ID)
3174 return candidate->SystemID() != INVALID_OBJECT_ID; // match objects in any system (including any system itself)
3175 else
3176 return candidate->SystemID() == m_system_id; // match objects in specified system (including that system itself)
3177 }
3178
3179 int m_system_id;
3180 };
3181 }
3182
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const3183 void InOrIsSystem::Eval(const ScriptingContext& parent_context,
3184 ObjectSet& matches, ObjectSet& non_matches,
3185 SearchDomain search_domain/* = NON_MATCHES*/) const
3186 {
3187 bool simple_eval_safe = !m_system_id || m_system_id->ConstantExpr() ||
3188 (m_system_id->LocalCandidateInvariant() &&
3189 (parent_context.condition_root_candidate || RootCandidateInvariant()));
3190 if (simple_eval_safe) {
3191 // evaluate system id once, and use to check all candidate objects
3192 int system_id = (m_system_id ? m_system_id->Eval(parent_context) : INVALID_OBJECT_ID);
3193 EvalImpl(matches, non_matches, search_domain, InSystemSimpleMatch(system_id));
3194 } else {
3195 // re-evaluate empire id for each candidate object
3196 Condition::Eval(parent_context, matches, non_matches, search_domain);
3197 }
3198 }
3199
Description(bool negated) const3200 std::string InOrIsSystem::Description(bool negated/* = false*/) const {
3201 std::string system_str;
3202 int system_id = INVALID_OBJECT_ID;
3203 if (m_system_id && m_system_id->ConstantExpr())
3204 system_id = m_system_id->Eval();
3205 if (auto system = Objects().get<System>(system_id))
3206 system_str = system->Name();
3207 else if (m_system_id)
3208 system_str = m_system_id->Description();
3209
3210 std::string description_str;
3211 if (!system_str.empty())
3212 description_str = (!negated)
3213 ? UserString("DESC_IN_SYSTEM")
3214 : UserString("DESC_IN_SYSTEM_NOT");
3215 else
3216 description_str = (!negated)
3217 ? UserString("DESC_IN_SYSTEM_SIMPLE")
3218 : UserString("DESC_IN_SYSTEM_SIMPLE_NOT");
3219
3220 return str(FlexibleFormat(description_str) % system_str);
3221 }
3222
Dump(unsigned short ntabs) const3223 std::string InOrIsSystem::Dump(unsigned short ntabs) const {
3224 std::string retval = DumpIndent(ntabs) + "InSystem";
3225 if (m_system_id)
3226 retval += " id = " + m_system_id->Dump(ntabs);
3227 retval += "\n";
3228 return retval;
3229 }
3230
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const3231 void InOrIsSystem::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
3232 ObjectSet& condition_non_targets) const
3233 {
3234 if (!m_system_id) {
3235 // can match objects in any system, or any system
3236 AddAllObjectsSet(parent_context.ContextObjects(), condition_non_targets);
3237 return;
3238 }
3239
3240 bool simple_eval_safe = m_system_id->ConstantExpr() ||
3241 (m_system_id->LocalCandidateInvariant() &&
3242 (parent_context.condition_root_candidate || RootCandidateInvariant()));
3243
3244 if (!simple_eval_safe) {
3245 // almost anything can be in a system, and can also match the system itself
3246 AddAllObjectsSet(parent_context.ContextObjects(), condition_non_targets);
3247 return;
3248 }
3249
3250 // simple case of a single specified system id; can add just objects in that system
3251 int system_id = m_system_id->Eval(parent_context);
3252 auto system = parent_context.ContextObjects().get<System>(system_id);
3253 if (!system)
3254 return;
3255
3256 const std::set<int>& system_object_ids = system->ObjectIDs();
3257 auto sys_objs = parent_context.ContextObjects().find(system_object_ids);
3258
3259 // insert all objects that have the specified system id
3260 condition_non_targets.reserve(sys_objs.size() + 1);
3261 std::copy(sys_objs.begin(), sys_objs.end(), std::back_inserter(condition_non_targets));
3262 // also insert system itself
3263 condition_non_targets.push_back(system);
3264 }
3265
Match(const ScriptingContext & local_context) const3266 bool InOrIsSystem::Match(const ScriptingContext& local_context) const {
3267 auto candidate = local_context.condition_local_candidate;
3268 if (!candidate) {
3269 ErrorLogger() << "InOrIsSystem::Match passed no candidate object";
3270 return false;
3271 }
3272 int system_id = (m_system_id ? m_system_id->Eval(local_context) : INVALID_OBJECT_ID);
3273 return InSystemSimpleMatch(system_id)(candidate);
3274 }
3275
SetTopLevelContent(const std::string & content_name)3276 void InOrIsSystem::SetTopLevelContent(const std::string& content_name) {
3277 if (m_system_id)
3278 m_system_id->SetTopLevelContent(content_name);
3279 }
3280
GetCheckSum() const3281 unsigned int InOrIsSystem::GetCheckSum() const {
3282 unsigned int retval{0};
3283
3284 CheckSums::CheckSumCombine(retval, "Condition::InOrIsSystem");
3285 CheckSums::CheckSumCombine(retval, m_system_id);
3286
3287 TraceLogger() << "GetCheckSum(InOrIsSystem): retval: " << retval;
3288 return retval;
3289 }
3290
3291 ///////////////////////////////////////////////////////////
3292 // OnPlanet //
3293 ///////////////////////////////////////////////////////////
OnPlanet(std::unique_ptr<ValueRef::ValueRef<int>> && planet_id)3294 OnPlanet::OnPlanet(std::unique_ptr<ValueRef::ValueRef<int>>&& planet_id) :
3295 Condition(),
3296 m_planet_id(std::move(planet_id))
3297 {
3298 m_root_candidate_invariant = !m_planet_id || m_planet_id->RootCandidateInvariant();
3299 m_target_invariant = !m_planet_id || m_planet_id->TargetInvariant();
3300 m_source_invariant = !m_planet_id || m_planet_id->SourceInvariant();
3301 }
3302
operator ==(const Condition & rhs) const3303 bool OnPlanet::operator==(const Condition& rhs) const {
3304 if (this == &rhs)
3305 return true;
3306 if (typeid(*this) != typeid(rhs))
3307 return false;
3308
3309 const OnPlanet& rhs_ = static_cast<const OnPlanet&>(rhs);
3310
3311 CHECK_COND_VREF_MEMBER(m_planet_id)
3312
3313 return true;
3314 }
3315
3316 namespace {
3317 struct OnPlanetSimpleMatch {
OnPlanetSimpleMatchCondition::__anone9a56faf2211::OnPlanetSimpleMatch3318 OnPlanetSimpleMatch(int planet_id) :
3319 m_planet_id(planet_id)
3320 {}
3321
operator ()Condition::__anone9a56faf2211::OnPlanetSimpleMatch3322 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
3323 if (!candidate)
3324 return false;
3325 auto building = std::dynamic_pointer_cast<const ::Building>(candidate);
3326 if (!building)
3327 return false;
3328
3329 if (m_planet_id == INVALID_OBJECT_ID)
3330 return building->PlanetID() != INVALID_OBJECT_ID; // match objects on any planet
3331 else
3332 return building->PlanetID() == m_planet_id; // match objects on specified planet
3333 }
3334
3335 int m_planet_id;
3336 };
3337 }
3338
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const3339 void OnPlanet::Eval(const ScriptingContext& parent_context,
3340 ObjectSet& matches, ObjectSet& non_matches,
3341 SearchDomain search_domain/* = NON_MATCHES*/) const
3342 {
3343 bool simple_eval_safe = !m_planet_id || m_planet_id->ConstantExpr() ||
3344 (m_planet_id->LocalCandidateInvariant() &&
3345 (parent_context.condition_root_candidate || RootCandidateInvariant()));
3346 if (simple_eval_safe) {
3347 // evaluate planet id once, and use to check all candidate objects
3348 int planet_id = (m_planet_id ? m_planet_id->Eval(parent_context) : INVALID_OBJECT_ID);
3349 EvalImpl(matches, non_matches, search_domain, OnPlanetSimpleMatch(planet_id));
3350 } else {
3351 // re-evaluate empire id for each candidate object
3352 Condition::Eval(parent_context, matches, non_matches, search_domain);
3353 }
3354 }
3355
Description(bool negated) const3356 std::string OnPlanet::Description(bool negated/* = false*/) const {
3357 std::string planet_str;
3358 int planet_id = INVALID_OBJECT_ID;
3359 if (m_planet_id && m_planet_id->ConstantExpr())
3360 planet_id = m_planet_id->Eval();
3361 if (auto planet = Objects().get<Planet>(planet_id))
3362 planet_str = planet->Name();
3363 else if (m_planet_id)
3364 planet_str = m_planet_id->Description();
3365
3366 std::string description_str;
3367 if (!planet_str.empty())
3368 description_str = (!negated)
3369 ? UserString("DESC_ON_PLANET")
3370 : UserString("DESC_ON_PLANET_NOT");
3371 else
3372 description_str = (!negated)
3373 ? UserString("DESC_ON_PLANET_SIMPLE")
3374 : UserString("DESC_ON_PLANET_SIMPLE_NOT");
3375
3376 return str(FlexibleFormat(description_str) % planet_str);
3377 }
3378
Dump(unsigned short ntabs) const3379 std::string OnPlanet::Dump(unsigned short ntabs) const {
3380 std::string retval = DumpIndent(ntabs) + "OnPlanet";
3381 if (m_planet_id)
3382 retval += " id = " + m_planet_id->Dump(ntabs);
3383 retval += "\n";
3384 return retval;
3385 }
3386
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const3387 void OnPlanet::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
3388 ObjectSet& condition_non_targets) const
3389 {
3390 if (!m_planet_id) {
3391 // only buildings can be on planets
3392 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
3393 return;
3394 }
3395
3396 bool simple_eval_safe = m_planet_id->ConstantExpr() ||
3397 (m_planet_id->LocalCandidateInvariant() &&
3398 (parent_context.condition_root_candidate || RootCandidateInvariant()));
3399
3400 if (!simple_eval_safe) {
3401 // only buildings can be on planets
3402 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
3403 return;
3404 }
3405
3406 // simple case of a single specified system id; can add just objects in that system
3407 int planet_id = m_planet_id->Eval(parent_context);
3408 auto planet = parent_context.ContextObjects().get<Planet>(planet_id);
3409 if (!planet)
3410 return;
3411
3412 // insert all objects that have the specified planet id
3413 condition_non_targets = parent_context.ContextObjects().find(planet->BuildingIDs());
3414 }
3415
Match(const ScriptingContext & local_context) const3416 bool OnPlanet::Match(const ScriptingContext& local_context) const {
3417 auto candidate = local_context.condition_local_candidate;
3418 if (!candidate) {
3419 ErrorLogger() << "OnPlanet::Match passed no candidate object";
3420 return false;
3421 }
3422 int planet_id = (m_planet_id ? m_planet_id->Eval(local_context) : INVALID_OBJECT_ID);
3423 return OnPlanetSimpleMatch(planet_id)(candidate);
3424 }
3425
SetTopLevelContent(const std::string & content_name)3426 void OnPlanet::SetTopLevelContent(const std::string& content_name) {
3427 if (m_planet_id)
3428 m_planet_id->SetTopLevelContent(content_name);
3429 }
3430
GetCheckSum() const3431 unsigned int OnPlanet::GetCheckSum() const {
3432 unsigned int retval{0};
3433
3434 CheckSums::CheckSumCombine(retval, "Condition::OnPlanet");
3435 CheckSums::CheckSumCombine(retval, m_planet_id);
3436
3437 TraceLogger() << "GetCheckSum(OnPlanet): retval: " << retval;
3438 return retval;
3439 }
3440
3441 ///////////////////////////////////////////////////////////
3442 // ObjectID //
3443 ///////////////////////////////////////////////////////////
ObjectID(std::unique_ptr<ValueRef::ValueRef<int>> && object_id)3444 ObjectID::ObjectID(std::unique_ptr<ValueRef::ValueRef<int>>&& object_id) :
3445 Condition(),
3446 m_object_id(std::move(object_id))
3447 {
3448 m_root_candidate_invariant = !m_object_id || m_object_id->RootCandidateInvariant();
3449 m_target_invariant = !m_object_id || m_object_id->TargetInvariant();
3450 m_source_invariant = !m_object_id || m_object_id->SourceInvariant();
3451 }
3452
operator ==(const Condition & rhs) const3453 bool ObjectID::operator==(const Condition& rhs) const {
3454 if (this == &rhs)
3455 return true;
3456 if (typeid(*this) != typeid(rhs))
3457 return false;
3458
3459 const ObjectID& rhs_ = static_cast<const ObjectID&>(rhs);
3460
3461 CHECK_COND_VREF_MEMBER(m_object_id)
3462
3463 return true;
3464 }
3465
3466 namespace {
3467 struct ObjectIDSimpleMatch {
ObjectIDSimpleMatchCondition::__anone9a56faf2311::ObjectIDSimpleMatch3468 ObjectIDSimpleMatch(int object_id) :
3469 m_object_id(object_id)
3470 {}
3471
operator ()Condition::__anone9a56faf2311::ObjectIDSimpleMatch3472 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
3473 return candidate &&
3474 m_object_id != INVALID_OBJECT_ID &&
3475 candidate->ID() == m_object_id;
3476 }
3477
3478 int m_object_id;
3479 };
3480 }
3481
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const3482 void ObjectID::Eval(const ScriptingContext& parent_context,
3483 ObjectSet& matches, ObjectSet& non_matches,
3484 SearchDomain search_domain/* = NON_MATCHES*/) const
3485 {
3486 bool simple_eval_safe = !m_object_id || m_object_id->ConstantExpr() ||
3487 (m_object_id->LocalCandidateInvariant() &&
3488 (parent_context.condition_root_candidate || RootCandidateInvariant()));
3489 if (simple_eval_safe) {
3490 // evaluate empire id once, and use to check all candidate objects
3491 int object_id = (m_object_id ? m_object_id->Eval(parent_context) : INVALID_OBJECT_ID);
3492 EvalImpl(matches, non_matches, search_domain, ObjectIDSimpleMatch(object_id));
3493 } else {
3494 // re-evaluate empire id for each candidate object
3495 Condition::Eval(parent_context, matches, non_matches, search_domain);
3496 }
3497 }
3498
Description(bool negated) const3499 std::string ObjectID::Description(bool negated/* = false*/) const {
3500 std::string object_str;
3501 int object_id = INVALID_OBJECT_ID;
3502 if (m_object_id && m_object_id->ConstantExpr())
3503 object_id = m_object_id->Eval();
3504 if (auto system = Objects().get<System>(object_id))
3505 object_str = system->Name();
3506 else if (m_object_id)
3507 object_str = m_object_id->Description();
3508 else
3509 object_str = UserString("ERROR"); // should always have a valid ID for this condition
3510
3511 return str(FlexibleFormat((!negated)
3512 ? UserString("DESC_OBJECT_ID")
3513 : UserString("DESC_OBJECT_ID_NOT"))
3514 % object_str);
3515 }
3516
Dump(unsigned short ntabs) const3517 std::string ObjectID::Dump(unsigned short ntabs) const
3518 { return DumpIndent(ntabs) + "Object id = " + m_object_id->Dump(ntabs) + "\n"; }
3519
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const3520 void ObjectID::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
3521 ObjectSet& condition_non_targets) const
3522 {
3523 if (!m_object_id)
3524 return;
3525
3526 bool simple_eval_safe = m_object_id->ConstantExpr() ||
3527 (m_object_id->LocalCandidateInvariant() &&
3528 (parent_context.condition_root_candidate || RootCandidateInvariant()));
3529
3530 if (!simple_eval_safe) {
3531 AddAllObjectsSet(parent_context.ContextObjects(), condition_non_targets);
3532 return;
3533 }
3534
3535 // simple case of a single specified id; can add just that object
3536 int object_id = m_object_id->Eval(parent_context);
3537 if (object_id == INVALID_OBJECT_ID)
3538 return;
3539
3540 if (auto obj = parent_context.ContextObjects().ExistingObject(object_id))
3541 condition_non_targets.push_back(obj);
3542 }
3543
Match(const ScriptingContext & local_context) const3544 bool ObjectID::Match(const ScriptingContext& local_context) const {
3545 auto candidate = local_context.condition_local_candidate;
3546 if (!candidate) {
3547 ErrorLogger() << "ObjectID::Match passed no candidate object";
3548 return false;
3549 }
3550
3551 return ObjectIDSimpleMatch(m_object_id->Eval(local_context))(candidate);
3552 }
3553
SetTopLevelContent(const std::string & content_name)3554 void ObjectID::SetTopLevelContent(const std::string& content_name) {
3555 if (m_object_id)
3556 m_object_id->SetTopLevelContent(content_name);
3557 }
3558
GetCheckSum() const3559 unsigned int ObjectID::GetCheckSum() const {
3560 unsigned int retval{0};
3561
3562 CheckSums::CheckSumCombine(retval, "Condition::ObjectID");
3563 CheckSums::CheckSumCombine(retval, m_object_id);
3564
3565 TraceLogger() << "GetCheckSum(ObjectID): retval: " << retval;
3566 return retval;
3567 }
3568
3569 ///////////////////////////////////////////////////////////
3570 // PlanetType //
3571 ///////////////////////////////////////////////////////////
PlanetType(std::vector<std::unique_ptr<ValueRef::ValueRef<::PlanetType>>> && types)3572 PlanetType::PlanetType(std::vector<std::unique_ptr<ValueRef::ValueRef< ::PlanetType>>>&& types) :
3573 Condition(),
3574 m_types(std::move(types))
3575 {
3576 m_root_candidate_invariant = boost::algorithm::all_of(m_types, [](auto& e){ return e->RootCandidateInvariant(); });
3577 m_target_invariant = boost::algorithm::all_of(m_types, [](auto& e){ return e->TargetInvariant(); });
3578 m_source_invariant = boost::algorithm::all_of(m_types, [](auto& e){ return e->SourceInvariant(); });
3579 }
3580
operator ==(const Condition & rhs) const3581 bool PlanetType::operator==(const Condition& rhs) const {
3582 if (this == &rhs)
3583 return true;
3584 if (typeid(*this) != typeid(rhs))
3585 return false;
3586
3587 const PlanetType& rhs_ = static_cast<const PlanetType&>(rhs);
3588
3589 if (m_types.size() != rhs_.m_types.size())
3590 return false;
3591 for (unsigned int i = 0; i < m_types.size(); ++i) {
3592 CHECK_COND_VREF_MEMBER(m_types.at(i))
3593 }
3594
3595 return true;
3596 }
3597
3598 namespace {
3599 struct PlanetTypeSimpleMatch {
PlanetTypeSimpleMatchCondition::__anone9a56faf2711::PlanetTypeSimpleMatch3600 PlanetTypeSimpleMatch(const std::vector< ::PlanetType>& types, const ObjectMap& objects) :
3601 m_types(types),
3602 m_objects(objects)
3603 {}
3604
operator ()Condition::__anone9a56faf2711::PlanetTypeSimpleMatch3605 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
3606 if (!candidate)
3607 return false;
3608
3609 // is it a planet or on a planet?
3610 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
3611 std::shared_ptr<const ::Building> building;
3612 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
3613 planet = m_objects.get<Planet>(building->PlanetID());
3614 if (planet) {
3615 // is it one of the specified building types?
3616 return std::count(m_types.begin(), m_types.end(), planet->Type());
3617 }
3618
3619 return false;
3620 }
3621
3622 const std::vector< ::PlanetType>& m_types;
3623 const ObjectMap& m_objects;
3624 };
3625 }
3626
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const3627 void PlanetType::Eval(const ScriptingContext& parent_context,
3628 ObjectSet& matches, ObjectSet& non_matches,
3629 SearchDomain search_domain/* = NON_MATCHES*/) const
3630 {
3631 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
3632 if (simple_eval_safe) {
3633 // check each valueref for invariance to local candidate
3634 for (auto& type : m_types) {
3635 if (!type->LocalCandidateInvariant()) {
3636 simple_eval_safe = false;
3637 break;
3638 }
3639 }
3640 }
3641 if (simple_eval_safe) {
3642 // evaluate types once, and use to check all candidate objects
3643 std::vector< ::PlanetType> types;
3644 // get all types from valuerefs
3645 for (auto& type : m_types) {
3646 types.push_back(type->Eval(parent_context));
3647 }
3648 EvalImpl(matches, non_matches, search_domain, PlanetTypeSimpleMatch(types, parent_context.ContextObjects()));
3649 } else {
3650 // re-evaluate contained objects for each candidate object
3651 Condition::Eval(parent_context, matches, non_matches, search_domain);
3652 }
3653 }
3654
Description(bool negated) const3655 std::string PlanetType::Description(bool negated/* = false*/) const {
3656 std::string values_str;
3657 for (unsigned int i = 0; i < m_types.size(); ++i) {
3658 values_str += m_types[i]->ConstantExpr() ?
3659 UserString(boost::lexical_cast<std::string>(m_types[i]->Eval())) :
3660 m_types[i]->Description();
3661 if (2 <= m_types.size() && i < m_types.size() - 2) {
3662 values_str += ", ";
3663 } else if (i == m_types.size() - 2) {
3664 values_str += m_types.size() < 3 ? " " : ", ";
3665 values_str += UserString("OR");
3666 values_str += " ";
3667 }
3668 }
3669 return str(FlexibleFormat((!negated)
3670 ? UserString("DESC_PLANET_TYPE")
3671 : UserString("DESC_PLANET_TYPE_NOT"))
3672 % values_str);
3673 }
3674
Dump(unsigned short ntabs) const3675 std::string PlanetType::Dump(unsigned short ntabs) const {
3676 std::string retval = DumpIndent(ntabs) + "Planet type = ";
3677 if (m_types.size() == 1) {
3678 retval += m_types[0]->Dump(ntabs) + "\n";
3679 } else {
3680 retval += "[ ";
3681 for (auto& type : m_types) {
3682 retval += type->Dump(ntabs) + " ";
3683 }
3684 retval += "]\n";
3685 }
3686 return retval;
3687 }
3688
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const3689 void PlanetType::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
3690 ObjectSet& condition_non_targets) const
3691 {
3692 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
3693 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
3694 }
3695
Match(const ScriptingContext & local_context) const3696 bool PlanetType::Match(const ScriptingContext& local_context) const {
3697 auto candidate = local_context.condition_local_candidate;
3698 if (!candidate) {
3699 ErrorLogger() << "PlanetType::Match passed no candidate object";
3700 return false;
3701 }
3702
3703 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
3704 std::shared_ptr<const ::Building> building;
3705 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
3706 planet = local_context.ContextObjects().get<Planet>(building->PlanetID());
3707
3708 if (planet) {
3709 for (auto& type : m_types) {
3710 if (type->Eval(local_context) == planet->Type())
3711 return true;
3712 }
3713 }
3714 return false;
3715 }
3716
SetTopLevelContent(const std::string & content_name)3717 void PlanetType::SetTopLevelContent(const std::string& content_name) {
3718 for (auto& type : m_types) {
3719 if (type)
3720 type->SetTopLevelContent(content_name);
3721 }
3722 }
3723
GetCheckSum() const3724 unsigned int PlanetType::GetCheckSum() const {
3725 unsigned int retval{0};
3726
3727 CheckSums::CheckSumCombine(retval, "Condition::PlanetType");
3728 CheckSums::CheckSumCombine(retval, m_types);
3729
3730 TraceLogger() << "GetCheckSum(PlanetType): retval: " << retval;
3731 return retval;
3732 }
3733
3734 ///////////////////////////////////////////////////////////
3735 // PlanetSize //
3736 ///////////////////////////////////////////////////////////
PlanetSize(std::vector<std::unique_ptr<ValueRef::ValueRef<::PlanetSize>>> && sizes)3737 PlanetSize::PlanetSize(std::vector<std::unique_ptr<ValueRef::ValueRef< ::PlanetSize>>>&& sizes) :
3738 Condition(),
3739 m_sizes(std::move(sizes))
3740 {
3741 m_root_candidate_invariant = boost::algorithm::all_of(m_sizes, [](auto& e){ return e->RootCandidateInvariant(); });
3742 m_target_invariant = boost::algorithm::all_of(m_sizes, [](auto& e){ return e->TargetInvariant(); });
3743 m_source_invariant = boost::algorithm::all_of(m_sizes, [](auto& e){ return e->SourceInvariant(); });
3744 }
3745
operator ==(const Condition & rhs) const3746 bool PlanetSize::operator==(const Condition& rhs) const {
3747 if (this == &rhs)
3748 return true;
3749 if (typeid(*this) != typeid(rhs))
3750 return false;
3751
3752 const PlanetSize& rhs_ = static_cast<const PlanetSize&>(rhs);
3753
3754 if (m_sizes.size() != rhs_.m_sizes.size())
3755 return false;
3756 for (unsigned int i = 0; i < m_sizes.size(); ++i) {
3757 CHECK_COND_VREF_MEMBER(m_sizes.at(i))
3758 }
3759
3760 return true;
3761 }
3762
3763 namespace {
3764 struct PlanetSizeSimpleMatch {
PlanetSizeSimpleMatchCondition::__anone9a56faf2b11::PlanetSizeSimpleMatch3765 PlanetSizeSimpleMatch(const std::vector< ::PlanetSize>& sizes, const ObjectMap& objects) :
3766 m_sizes(sizes),
3767 m_objects(objects)
3768 {}
3769
operator ()Condition::__anone9a56faf2b11::PlanetSizeSimpleMatch3770 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
3771 if (!candidate)
3772 return false;
3773
3774 // is it a planet or on a planet? TODO: This concept should be generalized and factored out.
3775 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
3776 std::shared_ptr<const ::Building> building;
3777 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
3778 planet = m_objects.get<Planet>(building->PlanetID());
3779 if (planet) {
3780 // is it one of the specified building types?
3781 for (auto size : m_sizes) {
3782 if (planet->Size() == size)
3783 return true;
3784 }
3785 }
3786
3787 return false;
3788 }
3789
3790 const std::vector< ::PlanetSize>& m_sizes;
3791 const ObjectMap& m_objects;
3792 };
3793 }
3794
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const3795 void PlanetSize::Eval(const ScriptingContext& parent_context,
3796 ObjectSet& matches, ObjectSet& non_matches,
3797 SearchDomain search_domain/* = NON_MATCHES*/) const
3798 {
3799 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
3800 if (simple_eval_safe) {
3801 // check each valueref for invariance to local candidate
3802 for (auto& size : m_sizes) {
3803 if (!size->LocalCandidateInvariant()) {
3804 simple_eval_safe = false;
3805 break;
3806 }
3807 }
3808 }
3809 if (simple_eval_safe) {
3810 // evaluate types once, and use to check all candidate objects
3811 std::vector< ::PlanetSize> sizes;
3812 // get all types from valuerefs
3813 for (auto& size : m_sizes) {
3814 sizes.push_back(size->Eval(parent_context));
3815 }
3816 EvalImpl(matches, non_matches, search_domain, PlanetSizeSimpleMatch(sizes, parent_context.ContextObjects()));
3817 } else {
3818 // re-evaluate contained objects for each candidate object
3819 Condition::Eval(parent_context, matches, non_matches, search_domain);
3820 }
3821 }
3822
Description(bool negated) const3823 std::string PlanetSize::Description(bool negated/* = false*/) const {
3824 std::string values_str;
3825 for (unsigned int i = 0; i < m_sizes.size(); ++i) {
3826 values_str += m_sizes[i]->ConstantExpr() ?
3827 UserString(boost::lexical_cast<std::string>(m_sizes[i]->Eval())) :
3828 m_sizes[i]->Description();
3829 if (2 <= m_sizes.size() && i < m_sizes.size() - 2) {
3830 values_str += ", ";
3831 } else if (i == m_sizes.size() - 2) {
3832 values_str += m_sizes.size() < 3 ? " " : ", ";
3833 values_str += UserString("OR");
3834 values_str += " ";
3835 }
3836 }
3837 return str(FlexibleFormat((!negated)
3838 ? UserString("DESC_PLANET_SIZE")
3839 : UserString("DESC_PLANET_SIZE_NOT"))
3840 % values_str);
3841 }
3842
Dump(unsigned short ntabs) const3843 std::string PlanetSize::Dump(unsigned short ntabs) const {
3844 std::string retval = DumpIndent(ntabs) + "Planet size = ";
3845 if (m_sizes.size() == 1) {
3846 retval += m_sizes[0]->Dump(ntabs) + "\n";
3847 } else {
3848 retval += "[ ";
3849 for (auto& size : m_sizes) {
3850 retval += size->Dump(ntabs) + " ";
3851 }
3852 retval += "]\n";
3853 }
3854 return retval;
3855 }
3856
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const3857 void PlanetSize::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
3858 ObjectSet& condition_non_targets) const
3859 {
3860 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
3861 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
3862 }
3863
Match(const ScriptingContext & local_context) const3864 bool PlanetSize::Match(const ScriptingContext& local_context) const {
3865 auto candidate = local_context.condition_local_candidate;
3866 if (!candidate) {
3867 ErrorLogger() << "PlanetSize::Match passed no candidate object";
3868 return false;
3869 }
3870
3871 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
3872 std::shared_ptr<const ::Building> building;
3873 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
3874 planet = local_context.ContextObjects().get<Planet>(building->PlanetID());
3875
3876 if (planet) {
3877 for (auto& size : m_sizes) {
3878 if (size->Eval(local_context) == planet->Size())
3879 return true;
3880 }
3881 }
3882 return false;
3883 }
3884
SetTopLevelContent(const std::string & content_name)3885 void PlanetSize::SetTopLevelContent(const std::string& content_name) {
3886 for (auto& size : m_sizes) {
3887 if (size)
3888 size->SetTopLevelContent(content_name);
3889 }
3890 }
3891
GetCheckSum() const3892 unsigned int PlanetSize::GetCheckSum() const {
3893 unsigned int retval{0};
3894
3895 CheckSums::CheckSumCombine(retval, "Condition::PlanetSize");
3896 CheckSums::CheckSumCombine(retval, m_sizes);
3897
3898 TraceLogger() << "GetCheckSum(PlanetSize): retval: " << retval;
3899 return retval;
3900 }
3901
3902 ///////////////////////////////////////////////////////////
3903 // PlanetEnvironment //
3904 ///////////////////////////////////////////////////////////
PlanetEnvironment(std::vector<std::unique_ptr<ValueRef::ValueRef<::PlanetEnvironment>>> && environments,std::unique_ptr<ValueRef::ValueRef<std::string>> && species_name_ref)3905 PlanetEnvironment::PlanetEnvironment(std::vector<std::unique_ptr<ValueRef::ValueRef< ::PlanetEnvironment>>>&& environments,
3906 std::unique_ptr<ValueRef::ValueRef<std::string>>&& species_name_ref) :
3907 Condition(),
3908 m_environments(std::move(environments)),
3909 m_species_name(std::move(species_name_ref))
3910 {
3911 m_root_candidate_invariant =
3912 (!m_species_name || m_species_name->RootCandidateInvariant()) &&
3913 boost::algorithm::all_of(m_environments, [](auto& e){ return !e || e->RootCandidateInvariant(); });
3914 m_target_invariant =
3915 (!m_species_name || m_species_name->TargetInvariant()) &&
3916 boost::algorithm::all_of(m_environments, [](auto& e){ return !e || e->TargetInvariant(); });
3917 m_source_invariant =
3918 (!m_species_name || m_species_name->SourceInvariant()) &&
3919 boost::algorithm::all_of(m_environments, [](auto& e){ return !e || e->SourceInvariant(); });
3920 }
3921
operator ==(const Condition & rhs) const3922 bool PlanetEnvironment::operator==(const Condition& rhs) const {
3923 if (this == &rhs)
3924 return true;
3925 if (typeid(*this) != typeid(rhs))
3926 return false;
3927
3928 const PlanetEnvironment& rhs_ = static_cast<const PlanetEnvironment&>(rhs);
3929
3930 CHECK_COND_VREF_MEMBER(m_species_name)
3931
3932 if (m_environments.size() != rhs_.m_environments.size())
3933 return false;
3934 for (unsigned int i = 0; i < m_environments.size(); ++i) {
3935 CHECK_COND_VREF_MEMBER(m_environments.at(i))
3936 }
3937
3938 return true;
3939 }
3940
3941 namespace {
3942 struct PlanetEnvironmentSimpleMatch {
PlanetEnvironmentSimpleMatchCondition::__anone9a56faf2f11::PlanetEnvironmentSimpleMatch3943 PlanetEnvironmentSimpleMatch(const std::vector< ::PlanetEnvironment>& environments,
3944 const ObjectMap& objects,
3945 const std::string& species = "") :
3946 m_environments(environments),
3947 m_species(species),
3948 m_objects(objects)
3949 {}
3950
operator ()Condition::__anone9a56faf2f11::PlanetEnvironmentSimpleMatch3951 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
3952 if (!candidate)
3953 return false;
3954
3955 // is it a planet or on a planet? TODO: factor out
3956 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
3957 std::shared_ptr<const ::Building> building;
3958 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
3959 planet = m_objects.get<Planet>(building->PlanetID());
3960 if (planet) {
3961 // is it one of the specified building types?
3962 for (auto environment : m_environments) {
3963 if (planet->EnvironmentForSpecies(m_species) == environment)
3964 return true;
3965 }
3966 }
3967
3968 return false;
3969 }
3970
3971 const std::vector< ::PlanetEnvironment>& m_environments;
3972 const std::string& m_species;
3973 const ObjectMap& m_objects;
3974 };
3975 }
3976
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const3977 void PlanetEnvironment::Eval(const ScriptingContext& parent_context,
3978 ObjectSet& matches, ObjectSet& non_matches,
3979 SearchDomain search_domain/* = NON_MATCHES*/) const
3980 {
3981 bool simple_eval_safe = ((!m_species_name || m_species_name->LocalCandidateInvariant()) &&
3982 (parent_context.condition_root_candidate || RootCandidateInvariant()));
3983 if (simple_eval_safe) {
3984 // check each valueref for invariance to local candidate
3985 for (auto& environment : m_environments) {
3986 if (!environment->LocalCandidateInvariant()) {
3987 simple_eval_safe = false;
3988 break;
3989 }
3990 }
3991 }
3992 if (simple_eval_safe) {
3993 // evaluate types once, and use to check all candidate objects
3994 std::vector< ::PlanetEnvironment> environments;
3995 // get all types from valuerefs
3996 for (auto& environment : m_environments) {
3997 environments.push_back(environment->Eval(parent_context));
3998 }
3999 std::string species_name;
4000 if (m_species_name)
4001 species_name = m_species_name->Eval(parent_context);
4002 EvalImpl(matches, non_matches, search_domain, PlanetEnvironmentSimpleMatch(environments, parent_context.ContextObjects(), species_name));
4003 } else {
4004 // re-evaluate contained objects for each candidate object
4005 Condition::Eval(parent_context, matches, non_matches, search_domain);
4006 }
4007 }
4008
Description(bool negated) const4009 std::string PlanetEnvironment::Description(bool negated/* = false*/) const {
4010 std::string values_str;
4011 for (unsigned int i = 0; i < m_environments.size(); ++i) {
4012 values_str += m_environments[i]->ConstantExpr() ?
4013 UserString(boost::lexical_cast<std::string>(m_environments[i]->Eval())) :
4014 m_environments[i]->Description();
4015 if (2 <= m_environments.size() && i < m_environments.size() - 2) {
4016 values_str += ", ";
4017 } else if (i == m_environments.size() - 2) {
4018 values_str += m_environments.size() < 3 ? " " : ", ";
4019 values_str += UserString("OR");
4020 values_str += " ";
4021 }
4022 }
4023 std::string species_str;
4024 if (m_species_name) {
4025 species_str = m_species_name->Description();
4026 if (m_species_name->ConstantExpr() && UserStringExists(species_str))
4027 species_str = UserString(species_str);
4028 }
4029 if (species_str.empty())
4030 species_str = UserString("DESC_PLANET_ENVIRONMENT_CUR_SPECIES");
4031 return str(FlexibleFormat((!negated)
4032 ? UserString("DESC_PLANET_ENVIRONMENT")
4033 : UserString("DESC_PLANET_ENVIRONMENT_NOT"))
4034 % values_str
4035 % species_str);
4036 }
4037
Dump(unsigned short ntabs) const4038 std::string PlanetEnvironment::Dump(unsigned short ntabs) const {
4039 std::string retval = DumpIndent(ntabs) + "Planet environment = ";
4040 if (m_environments.size() == 1) {
4041 retval += m_environments[0]->Dump(ntabs);
4042 } else {
4043 retval += "[ ";
4044 for (auto& environment : m_environments) {
4045 retval += environment->Dump(ntabs) + " ";
4046 }
4047 retval += "]";
4048 }
4049 if (m_species_name)
4050 retval += " species = " + m_species_name->Dump(ntabs);
4051 retval += "\n";
4052 return retval;
4053 }
4054
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const4055 void PlanetEnvironment::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
4056 ObjectSet& condition_non_targets) const
4057 {
4058 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
4059 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
4060 }
4061
Match(const ScriptingContext & local_context) const4062 bool PlanetEnvironment::Match(const ScriptingContext& local_context) const {
4063 auto candidate = local_context.condition_local_candidate;
4064 if (!candidate) {
4065 ErrorLogger() << "PlanetEnvironment::Match passed no candidate object";
4066 return false;
4067 }
4068
4069 // is it a planet or on a planet? TODO: factor out
4070 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
4071 std::shared_ptr<const ::Building> building;
4072 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
4073 planet = local_context.ContextObjects().get<Planet>(building->PlanetID());
4074 if (!planet)
4075 return false;
4076
4077 std::string species_name;
4078 if (m_species_name)
4079 species_name = m_species_name->Eval(local_context);
4080
4081 auto env_for_planets_species = planet->EnvironmentForSpecies(species_name);
4082 for (auto& environment : m_environments) {
4083 if (environment->Eval(local_context) == env_for_planets_species)
4084 return true;
4085 }
4086 return false;
4087 }
4088
SetTopLevelContent(const std::string & content_name)4089 void PlanetEnvironment::SetTopLevelContent(const std::string& content_name) {
4090 if (m_species_name)
4091 m_species_name->SetTopLevelContent(content_name);
4092 for (auto& environment : m_environments) {
4093 if (environment)
4094 environment->SetTopLevelContent(content_name);
4095 }
4096 }
4097
GetCheckSum() const4098 unsigned int PlanetEnvironment::GetCheckSum() const {
4099 unsigned int retval{0};
4100
4101 CheckSums::CheckSumCombine(retval, "Condition::PlanetEnvironment");
4102 CheckSums::CheckSumCombine(retval, m_environments);
4103 CheckSums::CheckSumCombine(retval, m_species_name);
4104
4105 TraceLogger() << "GetCheckSum(PlanetEnvironment): retval: " << retval;
4106 return retval;
4107 }
4108
4109 ///////////////////////////////////////////////////////////
4110 // Species //
4111 ///////////////////////////////////////////////////////////
Species(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>> && names)4112 Species::Species(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>>&& names) :
4113 Condition(),
4114 m_names(std::move(names))
4115 {
4116 m_root_candidate_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->RootCandidateInvariant(); });
4117 m_target_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->TargetInvariant(); });
4118 m_source_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->SourceInvariant(); });
4119 }
4120
Species()4121 Species::Species() :
4122 Species(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>>{})
4123 {}
4124
operator ==(const Condition & rhs) const4125 bool Species::operator==(const Condition& rhs) const {
4126 if (this == &rhs)
4127 return true;
4128 if (typeid(*this) != typeid(rhs))
4129 return false;
4130
4131 const Species& rhs_ = static_cast<const Species&>(rhs);
4132
4133 if (m_names.size() != rhs_.m_names.size())
4134 return false;
4135 for (unsigned int i = 0; i < m_names.size(); ++i) {
4136 CHECK_COND_VREF_MEMBER(m_names.at(i))
4137 }
4138
4139 return true;
4140 }
4141
4142 namespace {
4143 struct SpeciesSimpleMatch {
SpeciesSimpleMatchCondition::__anone9a56faf3311::SpeciesSimpleMatch4144 SpeciesSimpleMatch(const std::vector<std::string>& names, const ObjectMap& objects) :
4145 m_names(names),
4146 m_objects(objects)
4147 {}
4148
operator ()Condition::__anone9a56faf3311::SpeciesSimpleMatch4149 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
4150 if (!candidate)
4151 return false;
4152
4153 // is it a population centre?
4154 if (auto pop = std::dynamic_pointer_cast<const ::PopCenter>(candidate)) {
4155 const std::string& species_name = pop->SpeciesName();
4156 // if the popcenter has a species and that species is one of those specified...
4157 return !species_name.empty() && (m_names.empty() || std::count(m_names.begin(), m_names.end(), species_name));
4158 }
4159 // is it a ship?
4160 if (auto ship = std::dynamic_pointer_cast<const Ship>(candidate)) {
4161 // if the ship has a species and that species is one of those specified...
4162 const std::string& species_name = ship->SpeciesName();
4163 return !species_name.empty() && (m_names.empty() || std::count(m_names.begin(), m_names.end(), species_name));
4164 }
4165 // is it a building on a planet?
4166 if (auto building = std::dynamic_pointer_cast<const ::Building>(candidate)) {
4167 auto planet = m_objects.get<Planet>(building->PlanetID());
4168 const std::string& species_name = planet->SpeciesName();
4169 // if the planet (which IS a popcenter) has a species and that species is one of those specified...
4170 return !species_name.empty() && (m_names.empty() || std::count(m_names.begin(), m_names.end(), species_name));
4171 }
4172
4173 return false;
4174 }
4175
4176 const std::vector<std::string>& m_names;
4177 const ObjectMap& m_objects;
4178 };
4179 }
4180
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const4181 void Species::Eval(const ScriptingContext& parent_context,
4182 ObjectSet& matches, ObjectSet& non_matches,
4183 SearchDomain search_domain/* = NON_MATCHES*/) const
4184 {
4185 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
4186 if (simple_eval_safe) {
4187 // check each valueref for invariance to local candidate
4188 for (auto& name : m_names) {
4189 if (!name->LocalCandidateInvariant()) {
4190 simple_eval_safe = false;
4191 break;
4192 }
4193 }
4194 }
4195 if (simple_eval_safe) {
4196 // evaluate names once, and use to check all candidate objects
4197 std::vector<std::string> names;
4198 // get all names from valuerefs
4199 for (auto& name : m_names) {
4200 names.push_back(name->Eval(parent_context));
4201 }
4202 EvalImpl(matches, non_matches, search_domain, SpeciesSimpleMatch(names, parent_context.ContextObjects()));
4203 } else {
4204 // re-evaluate allowed building types range for each candidate object
4205 Condition::Eval(parent_context, matches, non_matches, search_domain);
4206 }
4207 }
4208
Description(bool negated) const4209 std::string Species::Description(bool negated/* = false*/) const {
4210 std::string values_str;
4211 if (m_names.empty())
4212 values_str = "(" + UserString("CONDITION_ANY") +")";
4213 for (unsigned int i = 0; i < m_names.size(); ++i) {
4214 values_str += m_names[i]->ConstantExpr() ?
4215 UserString(m_names[i]->Eval()) :
4216 m_names[i]->Description();
4217 if (2 <= m_names.size() && i < m_names.size() - 2) {
4218 values_str += ", ";
4219 } else if (i == m_names.size() - 2) {
4220 values_str += m_names.size() < 3 ? " " : ", ";
4221 values_str += UserString("OR");
4222 values_str += " ";
4223 }
4224 }
4225 return str(FlexibleFormat((!negated)
4226 ? UserString("DESC_SPECIES")
4227 : UserString("DESC_SPECIES_NOT"))
4228 % values_str);
4229 }
4230
Dump(unsigned short ntabs) const4231 std::string Species::Dump(unsigned short ntabs) const {
4232 std::string retval = DumpIndent(ntabs) + "Species";
4233 if (m_names.empty()) {
4234 // do nothing else
4235 } else if (m_names.size() == 1) {
4236 retval += " name = " + m_names[0]->Dump(ntabs) + "\n";
4237 } else {
4238 retval += " name = [ ";
4239 for (auto& name : m_names) {
4240 retval += name->Dump(ntabs) + " ";
4241 }
4242 retval += "]\n";
4243 }
4244 return retval;
4245 }
4246
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const4247 void Species::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
4248 ObjectSet& condition_non_targets) const
4249 {
4250 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
4251 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
4252 AddShipSet(parent_context.ContextObjects(), condition_non_targets);
4253 }
4254
Match(const ScriptingContext & local_context) const4255 bool Species::Match(const ScriptingContext& local_context) const {
4256 auto candidate = local_context.condition_local_candidate;
4257 if (!candidate) {
4258 ErrorLogger() << "Species::Match passed no candidate object";
4259 return false;
4260 }
4261
4262 // is it a planet or a building on a planet? TODO: factor out
4263 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
4264 std::shared_ptr<const ::Building> building;
4265 if (!planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
4266 planet = local_context.ContextObjects().get<Planet>(building->PlanetID());
4267 if (planet) {
4268 if (m_names.empty()) {
4269 return !planet->SpeciesName().empty(); // match any species name
4270 } else {
4271 // match only specified species names
4272 for (auto& name : m_names) {
4273 if (name->Eval(local_context) == planet->SpeciesName())
4274 return true;
4275 }
4276 }
4277 }
4278 // is it a ship?
4279 auto ship = std::dynamic_pointer_cast<const Ship>(candidate);
4280 if (ship) {
4281 if (m_names.empty()) {
4282 return !ship->SpeciesName().empty(); // match any species name
4283 } else {
4284 // match only specified species names
4285 for (auto& name : m_names) {
4286 if (name->Eval(local_context) == ship->SpeciesName())
4287 return true;
4288 }
4289 }
4290 }
4291 return false;
4292 }
4293
SetTopLevelContent(const std::string & content_name)4294 void Species::SetTopLevelContent(const std::string& content_name) {
4295 for (auto& name : m_names) {
4296 if (name)
4297 name->SetTopLevelContent(content_name);
4298 }
4299 }
4300
GetCheckSum() const4301 unsigned int Species::GetCheckSum() const {
4302 unsigned int retval{0};
4303
4304 CheckSums::CheckSumCombine(retval, "Condition::Species");
4305 CheckSums::CheckSumCombine(retval, m_names);
4306
4307 TraceLogger() << "GetCheckSum(Species): retval: " << retval;
4308 return retval;
4309 }
4310
4311 ///////////////////////////////////////////////////////////
4312 // Enqueued //
4313 ///////////////////////////////////////////////////////////
Enqueued(std::unique_ptr<ValueRef::ValueRef<int>> && design_id,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<int>> && low,std::unique_ptr<ValueRef::ValueRef<int>> && high)4314 Enqueued::Enqueued(std::unique_ptr<ValueRef::ValueRef<int>>&& design_id,
4315 std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
4316 std::unique_ptr<ValueRef::ValueRef<int>>&& low,
4317 std::unique_ptr<ValueRef::ValueRef<int>>&& high) :
4318 Condition(),
4319 m_build_type(BT_SHIP),
4320 m_design_id(std::move(design_id)),
4321 m_empire_id(std::move(empire_id)),
4322 m_low(std::move(low)),
4323 m_high(std::move(high))
4324 {
4325 auto operands = {m_design_id.get(), m_empire_id.get(), m_low.get(), m_high.get()};
4326 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
4327 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
4328 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
4329 }
4330
Enqueued()4331 Enqueued::Enqueued() :
4332 Enqueued(BT_NOT_BUILDING, nullptr, nullptr, nullptr)
4333 {}
4334
Enqueued(BuildType build_type,std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<int>> && low,std::unique_ptr<ValueRef::ValueRef<int>> && high)4335 Enqueued::Enqueued(BuildType build_type,
4336 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
4337 std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
4338 std::unique_ptr<ValueRef::ValueRef<int>>&& low,
4339 std::unique_ptr<ValueRef::ValueRef<int>>&& high) :
4340 Condition(),
4341 m_build_type(build_type),
4342 m_name(std::move(name)),
4343 m_empire_id(std::move(empire_id)),
4344 m_low(std::move(low)),
4345 m_high(std::move(high))
4346 {
4347 auto operands = {m_empire_id.get(), m_low.get(), m_high.get()};
4348 m_root_candidate_invariant =
4349 (!m_name || m_name->RootCandidateInvariant()) &&
4350 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
4351 m_target_invariant =
4352 (!m_name || m_name->TargetInvariant()) &&
4353 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
4354 m_source_invariant =
4355 (!m_name || m_name->SourceInvariant()) &&
4356 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
4357 }
4358
operator ==(const Condition & rhs) const4359 bool Enqueued::operator==(const Condition& rhs) const {
4360 if (this == &rhs)
4361 return true;
4362 if (typeid(*this) != typeid(rhs))
4363 return false;
4364
4365 const Enqueued& rhs_ = static_cast<const Enqueued&>(rhs);
4366
4367 if (m_build_type != rhs_.m_build_type)
4368 return false;
4369
4370 CHECK_COND_VREF_MEMBER(m_name)
4371 CHECK_COND_VREF_MEMBER(m_design_id)
4372 CHECK_COND_VREF_MEMBER(m_empire_id)
4373 CHECK_COND_VREF_MEMBER(m_low)
4374 CHECK_COND_VREF_MEMBER(m_high)
4375
4376 return true;
4377 }
4378
4379 namespace {
NumberOnQueue(const ProductionQueue & queue,BuildType build_type,const int location_id,const std::string & name="",int design_id=INVALID_DESIGN_ID)4380 int NumberOnQueue(const ProductionQueue& queue, BuildType build_type, const int location_id,
4381 const std::string& name = "", int design_id = INVALID_DESIGN_ID)
4382 {
4383 int retval = 0;
4384 for (const auto& element : queue) {
4385 if (!(build_type == INVALID_BUILD_TYPE || build_type == element.item.build_type))
4386 continue;
4387 if (location_id != element.location)
4388 continue;
4389 if (build_type == BT_BUILDING) {
4390 // if looking for buildings, accept specifically named building
4391 // or any building if no name specified
4392 if (!name.empty() && element.item.name != name)
4393 continue;
4394 } else if (build_type == BT_SHIP) {
4395 if (design_id != INVALID_DESIGN_ID) {
4396 // if looking for ships, accept design by id number...
4397 if (design_id != element.item.design_id)
4398 continue;
4399 } else if (!name.empty()) {
4400 // ... or accept design by predefined name
4401 const ShipDesign* design = GetShipDesign(element.item.design_id);
4402 if (!design || name != design->Name(false))
4403 continue;
4404 }
4405 } // else: looking for any production item
4406
4407 retval += element.blocksize;
4408 }
4409 return retval;
4410 }
4411
4412 struct EnqueuedSimpleMatch {
EnqueuedSimpleMatchCondition::__anone9a56faf3a11::EnqueuedSimpleMatch4413 EnqueuedSimpleMatch(BuildType build_type, const std::string& name, int design_id,
4414 int empire_id, int low, int high) :
4415 m_build_type(build_type),
4416 m_name(name),
4417 m_design_id(design_id),
4418 m_empire_id(empire_id),
4419 m_low(low),
4420 m_high(high)
4421 {}
operator ()Condition::__anone9a56faf3a11::EnqueuedSimpleMatch4422 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
4423 if (!candidate)
4424 return false;
4425
4426 int count = 0;
4427
4428 if (m_empire_id == ALL_EMPIRES) {
4429 for (auto& item : Empires()) {
4430 const Empire* empire = item.second;
4431 count += NumberOnQueue(empire->GetProductionQueue(), m_build_type,
4432 candidate->ID(), m_name, m_design_id);
4433 }
4434
4435 } else {
4436 const Empire* empire = GetEmpire(m_empire_id);
4437 if (!empire) return false;
4438 count = NumberOnQueue(empire->GetProductionQueue(), m_build_type,
4439 candidate->ID(), m_name, m_design_id);
4440 }
4441
4442 return (m_low <= count && count <= m_high);
4443 }
4444
4445 BuildType m_build_type;
4446 std::string m_name;
4447 int m_design_id;
4448 int m_empire_id;
4449 int m_low;
4450 int m_high;
4451 };
4452 }
4453
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const4454 void Enqueued::Eval(const ScriptingContext& parent_context,
4455 ObjectSet& matches, ObjectSet& non_matches,
4456 SearchDomain search_domain/* = NON_MATCHES*/) const
4457 {
4458 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
4459 if (simple_eval_safe) {
4460 // check each valueref for invariance to local candidate
4461 if ((m_name && !m_name->LocalCandidateInvariant()) ||
4462 (m_design_id && !m_design_id->LocalCandidateInvariant()) ||
4463 (m_empire_id && !m_empire_id->LocalCandidateInvariant()) ||
4464 (m_low && !m_low->LocalCandidateInvariant()) ||
4465 (m_high && !m_high->LocalCandidateInvariant()))
4466 { simple_eval_safe = false; }
4467 }
4468
4469 if (simple_eval_safe) {
4470 // evaluate valuerefs once, and use to check all candidate objects
4471 std::string name = (m_name ? m_name->Eval(parent_context) : "");
4472 int design_id = (m_design_id ? m_design_id->Eval(parent_context) : INVALID_DESIGN_ID);
4473 int empire_id = (m_empire_id ? m_empire_id->Eval(parent_context) : ALL_EMPIRES);
4474 int low = (m_low ? m_low->Eval(parent_context) : 0);
4475 int high = (m_high ? m_high->Eval(parent_context) : INT_MAX);
4476 // special case: if neither low nor high is specified, default to a
4477 // minimum of 1, so that just matching "Enqueued (type) (name/id)" will
4478 // match places where at least one of the specified item is enqueued.
4479 // if a max or other minimum are specified, then default to 0 low, so
4480 // that just specifying a max will include anything below that max,
4481 // including 0.
4482 if (!m_low && !m_high)
4483 low = 1;
4484
4485 // need to test each candidate separately using EvalImpl and EnqueuedSimpleMatch
4486 // because the test checks that something is enqueued at the candidate location
4487 EvalImpl(matches, non_matches, search_domain, EnqueuedSimpleMatch(m_build_type, name, design_id,
4488 empire_id, low, high));
4489 } else {
4490 // re-evaluate allowed building types range for each candidate object
4491 Condition::Eval(parent_context, matches, non_matches, search_domain);
4492 }
4493 }
4494
Description(bool negated) const4495 std::string Enqueued::Description(bool negated/* = false*/) const {
4496 std::string empire_str;
4497 if (m_empire_id) {
4498 int empire_id = ALL_EMPIRES;
4499 if (m_empire_id->ConstantExpr())
4500 empire_id = m_empire_id->Eval();
4501 if (const Empire* empire = GetEmpire(empire_id))
4502 empire_str = empire->Name();
4503 else
4504 empire_str = m_empire_id->Description();
4505 }
4506 std::string low_str = "1";
4507 if (m_low) {
4508 low_str = m_low->ConstantExpr() ?
4509 std::to_string(m_low->Eval()) :
4510 m_low->Description();
4511 }
4512 std::string high_str = std::to_string(INT_MAX);
4513 if (m_high) {
4514 high_str = m_high->ConstantExpr() ?
4515 std::to_string(m_high->Eval()) :
4516 m_high->Description();
4517 }
4518 std::string what_str;
4519 if (m_name) {
4520 what_str = m_name->Description();
4521 if (m_name->ConstantExpr() && UserStringExists(what_str))
4522 what_str = UserString(what_str);
4523 } else if (m_design_id) {
4524 what_str = m_design_id->ConstantExpr() ?
4525 std::to_string(m_design_id->Eval()) :
4526 m_design_id->Description();
4527 }
4528 std::string description_str;
4529 switch (m_build_type) {
4530 case BT_BUILDING: description_str = (!negated)
4531 ? UserString("DESC_ENQUEUED_BUILDING")
4532 : UserString("DESC_ENQUEUED_BUILDING_NOT");
4533 break;
4534 case BT_SHIP: description_str = (!negated)
4535 ? UserString("DESC_ENQUEUED_DESIGN")
4536 : UserString("DESC_ENQUEUED_DESIGN_NOT");
4537 break;
4538 default: description_str = (!negated)
4539 ? UserString("DESC_ENQUEUED")
4540 : UserString("DESC_ENQUEUED_NOT");
4541 break;
4542 }
4543 return str(FlexibleFormat(description_str)
4544 % empire_str
4545 % low_str
4546 % high_str
4547 % what_str);
4548 }
4549
Dump(unsigned short ntabs) const4550 std::string Enqueued::Dump(unsigned short ntabs) const {
4551 std::string retval = DumpIndent(ntabs) + "Enqueued";
4552
4553 if (m_build_type == BT_BUILDING) {
4554 retval += " type = Building";
4555 if (m_name)
4556 retval += " name = " + m_name->Dump(ntabs);
4557 } else if (m_build_type == BT_SHIP) {
4558 retval += " type = Ship";
4559 if (m_name)
4560 retval += " design = " + m_name->Dump(ntabs);
4561 else if (m_design_id)
4562 retval += " design = " + m_design_id->Dump(ntabs);
4563 }
4564 if (m_empire_id)
4565 retval += " empire = " + m_empire_id->Dump(ntabs);
4566 if (m_low)
4567 retval += " low = " + m_low->Dump(ntabs);
4568 if (m_high)
4569 retval += " high = " + m_high->Dump(ntabs);
4570 retval += "\n";
4571 return retval;
4572 }
4573
Match(const ScriptingContext & local_context) const4574 bool Enqueued::Match(const ScriptingContext& local_context) const {
4575 auto candidate = local_context.condition_local_candidate;
4576 if (!candidate) {
4577 ErrorLogger() << "Enqueued::Match passed no candidate object";
4578 return false;
4579 }
4580 std::string name = (m_name ? m_name->Eval(local_context) : "");
4581 int empire_id = (m_empire_id ? m_empire_id->Eval(local_context) : ALL_EMPIRES);
4582 int design_id = (m_design_id ? m_design_id->Eval(local_context) : INVALID_DESIGN_ID);
4583 int low = (m_low ? m_low->Eval(local_context) : 0);
4584 int high = (m_high ? m_high->Eval(local_context) : INT_MAX);
4585 return EnqueuedSimpleMatch(m_build_type, name, design_id, empire_id, low, high)(candidate);
4586 }
4587
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const4588 void Enqueued::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
4589 ObjectSet& condition_non_targets) const
4590 {
4591 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
4592 }
4593
SetTopLevelContent(const std::string & content_name)4594 void Enqueued::SetTopLevelContent(const std::string& content_name) {
4595 if (m_name)
4596 m_name->SetTopLevelContent(content_name);
4597 if (m_design_id)
4598 m_design_id->SetTopLevelContent(content_name);
4599 if (m_empire_id)
4600 m_empire_id->SetTopLevelContent(content_name);
4601 if (m_low)
4602 m_low->SetTopLevelContent(content_name);
4603 if (m_high)
4604 m_high->SetTopLevelContent(content_name);
4605 }
4606
GetCheckSum() const4607 unsigned int Enqueued::GetCheckSum() const {
4608 unsigned int retval{0};
4609
4610 CheckSums::CheckSumCombine(retval, "Condition::Enqueued");
4611 CheckSums::CheckSumCombine(retval, m_name);
4612 CheckSums::CheckSumCombine(retval, m_design_id);
4613 CheckSums::CheckSumCombine(retval, m_empire_id);
4614 CheckSums::CheckSumCombine(retval, m_low);
4615 CheckSums::CheckSumCombine(retval, m_high);
4616
4617 TraceLogger() << "GetCheckSum(Enqueued): retval: " << retval;
4618 return retval;
4619 }
4620
4621 ///////////////////////////////////////////////////////////
4622 // FocusType //
4623 ///////////////////////////////////////////////////////////
FocusType(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>> && names)4624 FocusType::FocusType(std::vector<std::unique_ptr<ValueRef::ValueRef<std::string>>>&& names) :
4625 Condition(),
4626 m_names(std::move(names))
4627 {
4628 m_root_candidate_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->RootCandidateInvariant(); });
4629 m_target_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->TargetInvariant(); });
4630 m_source_invariant = boost::algorithm::all_of(m_names, [](auto& e){ return e->SourceInvariant(); });
4631 }
4632
operator ==(const Condition & rhs) const4633 bool FocusType::operator==(const Condition& rhs) const {
4634 if (this == &rhs)
4635 return true;
4636 if (typeid(*this) != typeid(rhs))
4637 return false;
4638
4639 const FocusType& rhs_ = static_cast<const FocusType&>(rhs);
4640
4641 if (m_names.size() != rhs_.m_names.size())
4642 return false;
4643 for (unsigned int i = 0; i < m_names.size(); ++i) {
4644 CHECK_COND_VREF_MEMBER(m_names.at(i))
4645 }
4646
4647 return true;
4648 }
4649
4650 namespace {
4651 struct FocusTypeSimpleMatch {
FocusTypeSimpleMatchCondition::__anone9a56faf3e11::FocusTypeSimpleMatch4652 FocusTypeSimpleMatch(const std::vector<std::string>& names, const ObjectMap& objects) :
4653 m_names(names),
4654 m_objects(objects)
4655 {}
4656
operator ()Condition::__anone9a56faf3e11::FocusTypeSimpleMatch4657 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
4658 if (!candidate)
4659 return false;
4660
4661 // is it a ResourceCenter or a Building on a Planet (that is a ResourceCenter)
4662 auto res_center = std::dynamic_pointer_cast<const ResourceCenter>(candidate);
4663 std::shared_ptr<const ::Building> building;
4664 if (!res_center && (building = std::dynamic_pointer_cast<const ::Building>(candidate))) {
4665 if (auto planet = m_objects.get<Planet>(building->PlanetID()))
4666 res_center = std::dynamic_pointer_cast<const ResourceCenter>(planet);
4667 }
4668 if (res_center) {
4669 return !res_center->Focus().empty() &&
4670 std::count(m_names.begin(), m_names.end(), res_center->Focus());
4671 }
4672
4673 return false;
4674 }
4675
4676 const std::vector<std::string>& m_names;
4677 const ObjectMap& m_objects;
4678 };
4679 }
4680
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const4681 void FocusType::Eval(const ScriptingContext& parent_context,
4682 ObjectSet& matches, ObjectSet& non_matches,
4683 SearchDomain search_domain/* = NON_MATCHES*/) const
4684 {
4685 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
4686 if (simple_eval_safe) {
4687 // check each valueref for invariance to local candidate
4688 for (auto& name : m_names) {
4689 if (!name->LocalCandidateInvariant()) {
4690 simple_eval_safe = false;
4691 break;
4692 }
4693 }
4694 }
4695 if (simple_eval_safe) {
4696 // evaluate names once, and use to check all candidate objects
4697 std::vector<std::string> names;
4698 // get all names from valuerefs
4699 for (auto& name : m_names) {
4700 names.push_back(name->Eval(parent_context));
4701 }
4702 EvalImpl(matches, non_matches, search_domain, FocusTypeSimpleMatch(names, parent_context.ContextObjects()));
4703 } else {
4704 // re-evaluate allowed building types range for each candidate object
4705 Condition::Eval(parent_context, matches, non_matches, search_domain);
4706 }
4707 }
4708
Description(bool negated) const4709 std::string FocusType::Description(bool negated/* = false*/) const {
4710 std::string values_str;
4711 for (unsigned int i = 0; i < m_names.size(); ++i) {
4712 values_str += m_names[i]->ConstantExpr() ?
4713 UserString(m_names[i]->Eval()) :
4714 m_names[i]->Description();
4715 if (2 <= m_names.size() && i < m_names.size() - 2) {
4716 values_str += ", ";
4717 } else if (i == m_names.size() - 2) {
4718 values_str += m_names.size() < 3 ? " " : ", ";
4719 values_str += UserString("OR");
4720 values_str += " ";
4721 }
4722 }
4723 return str(FlexibleFormat((!negated)
4724 ? UserString("DESC_FOCUS_TYPE")
4725 : UserString("DESC_FOCUS_TYPE_NOT"))
4726 % values_str);
4727 }
4728
Dump(unsigned short ntabs) const4729 std::string FocusType::Dump(unsigned short ntabs) const {
4730 std::string retval = DumpIndent(ntabs) + "Focus name = ";
4731 if (m_names.size() == 1) {
4732 retval += m_names[0]->Dump(ntabs) + "\n";
4733 } else {
4734 retval += "[ ";
4735 for (auto& name : m_names) {
4736 retval += name->Dump(ntabs) + " ";
4737 }
4738 retval += "]\n";
4739 }
4740 return retval;
4741 }
4742
Match(const ScriptingContext & local_context) const4743 bool FocusType::Match(const ScriptingContext& local_context) const {
4744 auto candidate = local_context.condition_local_candidate;
4745 if (!candidate) {
4746 ErrorLogger() << "FocusType::Match passed no candidate object";
4747 return false;
4748 }
4749
4750 // is it a ResourceCenter or a Building on a Planet (that is a ResourceCenter)
4751 auto res_center = std::dynamic_pointer_cast<const ResourceCenter>(candidate);
4752 std::shared_ptr<const ::Building> building;
4753 if (!res_center && (building = std::dynamic_pointer_cast<const ::Building>(candidate))) {
4754 if (auto planet = local_context.ContextObjects().get<Planet>(building->PlanetID()))
4755 res_center = std::dynamic_pointer_cast<const ResourceCenter>(planet);
4756 }
4757 if (res_center) {
4758 for (auto& name : m_names) {
4759 if (name->Eval(local_context) == res_center->Focus())
4760 return true;
4761 }
4762 }
4763 return false;
4764 }
4765
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const4766 void FocusType::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
4767 ObjectSet& condition_non_targets) const
4768 {
4769 AddPlanetSet(parent_context.ContextObjects(), condition_non_targets);
4770 AddBuildingSet(parent_context.ContextObjects(), condition_non_targets);
4771 }
4772
SetTopLevelContent(const std::string & content_name)4773 void FocusType::SetTopLevelContent(const std::string& content_name) {
4774 for (auto& name : m_names) {
4775 if (name)
4776 name->SetTopLevelContent(content_name);
4777 }
4778 }
4779
GetCheckSum() const4780 unsigned int FocusType::GetCheckSum() const {
4781 unsigned int retval{0};
4782
4783 CheckSums::CheckSumCombine(retval, "Condition::FocusType");
4784 CheckSums::CheckSumCombine(retval, m_names);
4785
4786 TraceLogger() << "GetCheckSum(FocusType): retval: " << retval;
4787 return retval;
4788 }
4789
4790 ///////////////////////////////////////////////////////////
4791 // StarType //
4792 ///////////////////////////////////////////////////////////
StarType(std::vector<std::unique_ptr<ValueRef::ValueRef<::StarType>>> && types)4793 StarType::StarType(std::vector<std::unique_ptr<ValueRef::ValueRef< ::StarType>>>&& types) :
4794 Condition(),
4795 m_types(std::move(types))
4796 {
4797 m_root_candidate_invariant = boost::algorithm::all_of(m_types, [](auto& e){ return e->RootCandidateInvariant(); });
4798 m_target_invariant = boost::algorithm::all_of(m_types, [](auto& e){ return e->TargetInvariant(); });
4799 m_source_invariant = boost::algorithm::all_of(m_types, [](auto& e){ return e->SourceInvariant(); });
4800 }
4801
operator ==(const Condition & rhs) const4802 bool StarType::operator==(const Condition& rhs) const {
4803 if (this == &rhs)
4804 return true;
4805 if (typeid(*this) != typeid(rhs))
4806 return false;
4807
4808 const StarType& rhs_ = static_cast<const StarType&>(rhs);
4809
4810 if (m_types.size() != rhs_.m_types.size())
4811 return false;
4812 for (unsigned int i = 0; i < m_types.size(); ++i) {
4813 CHECK_COND_VREF_MEMBER(m_types.at(i))
4814 }
4815
4816 return true;
4817 }
4818
4819 namespace {
4820 struct StarTypeSimpleMatch {
StarTypeSimpleMatchCondition::__anone9a56faf4211::StarTypeSimpleMatch4821 StarTypeSimpleMatch(const std::vector< ::StarType>& types, const ObjectMap& objects) :
4822 m_types(types),
4823 m_objects(objects)
4824 {}
4825
operator ()Condition::__anone9a56faf4211::StarTypeSimpleMatch4826 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
4827 if (!candidate)
4828 return false;
4829
4830 std::shared_ptr<const System> system = m_objects.get<System>(candidate->SystemID());
4831 if (system || (system = std::dynamic_pointer_cast<const System>(candidate)))
4832 return !m_types.empty() && std::count(m_types.begin(), m_types.end(), system->GetStarType());
4833
4834 return false;
4835 }
4836
4837 const std::vector< ::StarType>& m_types;
4838 const ObjectMap& m_objects;
4839 };
4840 }
4841
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const4842 void StarType::Eval(const ScriptingContext& parent_context,
4843 ObjectSet& matches, ObjectSet& non_matches,
4844 SearchDomain search_domain/* = NON_MATCHES*/) const
4845 {
4846 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
4847 if (simple_eval_safe) {
4848 // check each valueref for invariance to local candidate
4849 for (auto& type : m_types) {
4850 if (!type->LocalCandidateInvariant()) {
4851 simple_eval_safe = false;
4852 break;
4853 }
4854 }
4855 }
4856 if (simple_eval_safe) {
4857 // evaluate types once, and use to check all candidate objects
4858 std::vector< ::StarType> types;
4859 // get all types from valuerefs
4860 for (auto& type : m_types) {
4861 types.push_back(type->Eval(parent_context));
4862 }
4863 EvalImpl(matches, non_matches, search_domain, StarTypeSimpleMatch(types, parent_context.ContextObjects()));
4864 } else {
4865 // re-evaluate contained objects for each candidate object
4866 Condition::Eval(parent_context, matches, non_matches, search_domain);
4867 }
4868 }
4869
Description(bool negated) const4870 std::string StarType::Description(bool negated/* = false*/) const {
4871 std::string values_str;
4872 for (unsigned int i = 0; i < m_types.size(); ++i) {
4873 values_str += m_types[i]->ConstantExpr() ?
4874 UserString(boost::lexical_cast<std::string>(m_types[i]->Eval())) :
4875 m_types[i]->Description();
4876 if (2 <= m_types.size() && i < m_types.size() - 2) {
4877 values_str += ", ";
4878 } else if (i == m_types.size() - 2) {
4879 values_str += m_types.size() < 3 ? " " : ", ";
4880 values_str += UserString("OR");
4881 values_str += " ";
4882 }
4883 }
4884 return str(FlexibleFormat((!negated)
4885 ? UserString("DESC_STAR_TYPE")
4886 : UserString("DESC_STAR_TYPE_NOT"))
4887 % values_str);
4888 }
4889
Dump(unsigned short ntabs) const4890 std::string StarType::Dump(unsigned short ntabs) const {
4891 std::string retval = DumpIndent(ntabs) + "Star type = ";
4892 if (m_types.size() == 1) {
4893 retval += m_types[0]->Dump(ntabs) + "\n";
4894 } else {
4895 retval += "[ ";
4896 for (auto& type : m_types) {
4897 retval += type->Dump(ntabs) + " ";
4898 }
4899 retval += "]\n";
4900 }
4901 return retval;
4902 }
4903
Match(const ScriptingContext & local_context) const4904 bool StarType::Match(const ScriptingContext& local_context) const {
4905 auto candidate = local_context.condition_local_candidate;
4906 if (!candidate) {
4907 ErrorLogger() << "StarType::Match passed no candidate object";
4908 return false;
4909 }
4910
4911 std::shared_ptr<const System> system = local_context.ContextObjects().get<System>(candidate->SystemID());
4912 if (system || (system = std::dynamic_pointer_cast<const System>(candidate))) {
4913 for (auto& type : m_types) {
4914 if (type->Eval(local_context) == system->GetStarType())
4915 return true;
4916 }
4917 }
4918 return false;
4919 }
4920
SetTopLevelContent(const std::string & content_name)4921 void StarType::SetTopLevelContent(const std::string& content_name) {
4922 for (auto& type : m_types) {
4923 if (type)
4924 (type)->SetTopLevelContent(content_name);
4925 }
4926 }
4927
GetCheckSum() const4928 unsigned int StarType::GetCheckSum() const {
4929 unsigned int retval{0};
4930
4931 CheckSums::CheckSumCombine(retval, "Condition::StarType");
4932 CheckSums::CheckSumCombine(retval, m_types);
4933
4934 TraceLogger() << "GetCheckSum(StarType): retval: " << retval;
4935 return retval;
4936 }
4937
4938 ///////////////////////////////////////////////////////////
4939 // DesignHasHull //
4940 ///////////////////////////////////////////////////////////
DesignHasHull(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)4941 DesignHasHull::DesignHasHull(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
4942 Condition(),
4943 m_name(std::move(name))
4944 {
4945 m_root_candidate_invariant = !m_name || m_name->RootCandidateInvariant();
4946 m_target_invariant = !m_name || m_name->TargetInvariant();
4947 m_source_invariant = !m_name || m_name->SourceInvariant();
4948 }
4949
operator ==(const Condition & rhs) const4950 bool DesignHasHull::operator==(const Condition& rhs) const {
4951 if (this == &rhs)
4952 return true;
4953 if (typeid(*this) != typeid(rhs))
4954 return false;
4955
4956 const DesignHasHull& rhs_ = static_cast<const DesignHasHull&>(rhs);
4957
4958 CHECK_COND_VREF_MEMBER(m_name);
4959
4960 return true;
4961 }
4962
4963 namespace {
4964 struct DesignHasHullSimpleMatch {
DesignHasHullSimpleMatchCondition::__anone9a56faf4311::DesignHasHullSimpleMatch4965 explicit DesignHasHullSimpleMatch(const std::string& name) :
4966 m_name(name)
4967 {}
4968
operator ()Condition::__anone9a56faf4311::DesignHasHullSimpleMatch4969 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
4970 if (!candidate)
4971 return false;
4972
4973 // is it a ship?
4974 auto ship = std::dynamic_pointer_cast<const ::Ship>(candidate);
4975 if (!ship)
4976 return false;
4977 // with a valid design?
4978 const ShipDesign* design = ship->Design();
4979 if (!design)
4980 return false;
4981
4982 return design->Hull() == m_name;
4983 }
4984
4985 const std::string& m_name;
4986 };
4987 }
4988
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const4989 void DesignHasHull::Eval(const ScriptingContext& parent_context,
4990 ObjectSet& matches, ObjectSet& non_matches,
4991 SearchDomain search_domain/* = NON_MATCHES*/) const
4992 {
4993 bool simple_eval_safe = (!m_name || m_name->LocalCandidateInvariant()) &&
4994 (parent_context.condition_root_candidate || RootCandidateInvariant());
4995 if (simple_eval_safe) {
4996 // evaluate number limits once, use to match all candidates
4997 std::string name = (m_name ? m_name->Eval(parent_context) : "");
4998
4999 // need to test each candidate separately using EvalImpl and because the
5000 // design of the candidate object is tested
5001 EvalImpl(matches, non_matches, search_domain, DesignHasHullSimpleMatch(name));
5002 } else {
5003 // re-evaluate allowed turn range for each candidate object
5004 Condition::Eval(parent_context, matches, non_matches, search_domain);
5005 }
5006 }
5007
Description(bool negated) const5008 std::string DesignHasHull::Description(bool negated/* = false*/) const {
5009 std::string name_str;
5010 if (m_name) {
5011 name_str = m_name->Description();
5012 if (m_name->ConstantExpr() && UserStringExists(name_str))
5013 name_str = UserString(name_str);
5014 }
5015 return str(FlexibleFormat((!negated)
5016 ? UserString("DESC_DESIGN_HAS_HULL")
5017 : UserString("DESC_DESIGN_HAS_HULL_NOT"))
5018 % name_str);
5019 }
5020
Dump(unsigned short ntabs) const5021 std::string DesignHasHull::Dump(unsigned short ntabs) const {
5022 std::string retval = DumpIndent(ntabs) + "DesignHasHull";
5023 if (m_name)
5024 retval += " name = " + m_name->Dump(ntabs);
5025 retval += "\n";
5026 return retval;
5027 }
5028
Match(const ScriptingContext & local_context) const5029 bool DesignHasHull::Match(const ScriptingContext& local_context) const {
5030 auto candidate = local_context.condition_local_candidate;
5031 if (!candidate) {
5032 ErrorLogger() << "DesignHasHull::Match passed no candidate object";
5033 return false;
5034 }
5035
5036 std::string name = (m_name ? m_name->Eval(local_context) : "");
5037
5038 return DesignHasHullSimpleMatch(name)(candidate);
5039 }
5040
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const5041 void DesignHasHull::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
5042 ObjectSet& condition_non_targets) const
5043 {
5044 AddShipSet(parent_context.ContextObjects(), condition_non_targets);
5045 }
5046
SetTopLevelContent(const std::string & content_name)5047 void DesignHasHull::SetTopLevelContent(const std::string& content_name) {
5048 if (m_name)
5049 m_name->SetTopLevelContent(content_name);
5050 }
5051
GetCheckSum() const5052 unsigned int DesignHasHull::GetCheckSum() const {
5053 unsigned int retval{0};
5054
5055 CheckSums::CheckSumCombine(retval, "Condition::DesignHasHull");
5056 CheckSums::CheckSumCombine(retval, m_name);
5057
5058 TraceLogger() << "GetCheckSum(DesignHasHull): retval: " << retval;
5059 return retval;
5060 }
5061
5062 ///////////////////////////////////////////////////////////
5063 // DesignHasPart //
5064 ///////////////////////////////////////////////////////////
DesignHasPart(std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::unique_ptr<ValueRef::ValueRef<int>> && low,std::unique_ptr<ValueRef::ValueRef<int>> && high)5065 DesignHasPart::DesignHasPart(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
5066 std::unique_ptr<ValueRef::ValueRef<int>>&& low,
5067 std::unique_ptr<ValueRef::ValueRef<int>>&& high) :
5068 Condition(),
5069 m_low(std::move(low)),
5070 m_high(std::move(high)),
5071 m_name(std::move(name))
5072 {
5073 auto operands = {m_low.get(), m_high.get()};
5074 m_root_candidate_invariant =
5075 (!m_name || m_name->RootCandidateInvariant()) &&
5076 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
5077 m_target_invariant =
5078 (!m_name || m_name->TargetInvariant()) &&
5079 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
5080 m_source_invariant =
5081 (!m_name || m_name->SourceInvariant()) &&
5082 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
5083 }
5084
operator ==(const Condition & rhs) const5085 bool DesignHasPart::operator==(const Condition& rhs) const {
5086 if (this == &rhs)
5087 return true;
5088 if (typeid(*this) != typeid(rhs))
5089 return false;
5090
5091 const DesignHasPart& rhs_ = static_cast<const DesignHasPart&>(rhs);
5092
5093 CHECK_COND_VREF_MEMBER(m_name);
5094 CHECK_COND_VREF_MEMBER(m_low)
5095 CHECK_COND_VREF_MEMBER(m_high)
5096
5097 return true;
5098 }
5099
5100 namespace {
5101 struct DesignHasPartSimpleMatch {
DesignHasPartSimpleMatchCondition::__anone9a56faf4711::DesignHasPartSimpleMatch5102 DesignHasPartSimpleMatch(int low, int high, const std::string& name, const ObjectMap& objects) :
5103 m_low(low),
5104 m_high(high),
5105 m_name(name),
5106 m_objects(objects)
5107 {}
5108
operator ()Condition::__anone9a56faf4711::DesignHasPartSimpleMatch5109 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
5110 if (!candidate)
5111 return false;
5112
5113 std::shared_ptr<const Ship> ship = nullptr;
5114 if (auto fighter = std::dynamic_pointer_cast<const ::Fighter>(candidate)) {
5115 // it is a fighter
5116 ship = m_objects.get<Ship>(fighter->LaunchedFrom());
5117 } else {
5118 ship = std::dynamic_pointer_cast<const ::Ship>(candidate);
5119 }
5120
5121 // is it a ship
5122 if (!ship)
5123 return false;
5124
5125 // with a valid design?
5126 const ShipDesign* design = ship->Design();
5127 if (!design)
5128 return false;
5129
5130 int count = 0;
5131 for (const std::string& name : design->Parts()) {
5132 if (name == m_name || (m_name.empty() && !name.empty()))
5133 // number of copies of specified part,
5134 // or total number of parts if no part name specified
5135 ++count;
5136 }
5137 return (m_low <= count && count <= m_high);
5138 }
5139
5140 int m_low;
5141 int m_high;
5142 const std::string& m_name;
5143 const ObjectMap& m_objects;
5144 };
5145 }
5146
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const5147 void DesignHasPart::Eval(const ScriptingContext& parent_context,
5148 ObjectSet& matches, ObjectSet& non_matches,
5149 SearchDomain search_domain/* = NON_MATCHES*/) const
5150 {
5151 bool simple_eval_safe = (!m_low || m_low->LocalCandidateInvariant()) &&
5152 (!m_high || m_high->LocalCandidateInvariant()) &&
5153 (!m_name || m_name->LocalCandidateInvariant()) &&
5154 (parent_context.condition_root_candidate || RootCandidateInvariant());
5155 if (simple_eval_safe) {
5156 // evaluate number limits once, use to match all candidates
5157 std::string name = (m_name ? m_name->Eval(parent_context) : "");
5158 int low = (m_low ? std::max(0, m_low->Eval(parent_context)) : 1);
5159 int high = (m_high ? std::min(m_high->Eval(parent_context), INT_MAX) : INT_MAX);
5160
5161 // need to test each candidate separately using EvalImpl and because the
5162 // design of the candidate object is tested
5163 EvalImpl(matches, non_matches, search_domain, DesignHasPartSimpleMatch(low, high, name, parent_context.ContextObjects()));
5164 } else {
5165 // re-evaluate allowed turn range for each candidate object
5166 Condition::Eval(parent_context, matches, non_matches, search_domain);
5167 }
5168 }
5169
Description(bool negated) const5170 std::string DesignHasPart::Description(bool negated/* = false*/) const {
5171 std::string low_str = "1";
5172 if (m_low) {
5173 low_str = m_low->ConstantExpr() ?
5174 std::to_string(m_low->Eval()) :
5175 m_low->Description();
5176 }
5177 std::string high_str = std::to_string(INT_MAX);
5178 if (m_high) {
5179 high_str = m_high->ConstantExpr() ?
5180 std::to_string(m_high->Eval()) :
5181 m_high->Description();
5182 };
5183 std::string name_str;
5184 if (m_name) {
5185 name_str = m_name->Description();
5186 if (m_name->ConstantExpr() && UserStringExists(name_str))
5187 name_str = UserString(name_str);
5188 }
5189 return str(FlexibleFormat((!negated)
5190 ? UserString("DESC_DESIGN_HAS_PART")
5191 : UserString("DESC_DESIGN_HAS_PART_NOT"))
5192 % low_str
5193 % high_str
5194 % name_str);
5195 }
5196
Dump(unsigned short ntabs) const5197 std::string DesignHasPart::Dump(unsigned short ntabs) const {
5198 std::string retval = DumpIndent(ntabs) + "DesignHasPart";
5199 if (m_low)
5200 retval += "low = " + m_low->Dump(ntabs);
5201 if (m_high)
5202 retval += " high = " + m_high->Dump(ntabs);
5203 if (m_name)
5204 retval += " name = " + m_name->Dump(ntabs);
5205 retval += "\n";
5206 return retval;
5207 }
5208
Match(const ScriptingContext & local_context) const5209 bool DesignHasPart::Match(const ScriptingContext& local_context) const {
5210 auto candidate = local_context.condition_local_candidate;
5211 if (!candidate) {
5212 ErrorLogger() << "DesignHasPart::Match passed no candidate object";
5213 return false;
5214 }
5215
5216 int low = (m_low ? std::max(0, m_low->Eval(local_context)) : 0);
5217 int high = (m_high ? std::min(m_high->Eval(local_context), IMPOSSIBLY_LARGE_TURN) : IMPOSSIBLY_LARGE_TURN);
5218 std::string name = (m_name ? m_name->Eval(local_context) : "");
5219
5220 return DesignHasPartSimpleMatch(low, high, name, local_context.ContextObjects())(candidate);
5221 }
5222
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const5223 void DesignHasPart::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
5224 ObjectSet& condition_non_targets) const
5225 {
5226 AddShipSet(parent_context.ContextObjects(), condition_non_targets);
5227 }
5228
SetTopLevelContent(const std::string & content_name)5229 void DesignHasPart::SetTopLevelContent(const std::string& content_name) {
5230 if (m_low)
5231 m_low->SetTopLevelContent(content_name);
5232 if (m_high)
5233 m_high->SetTopLevelContent(content_name);
5234 if (m_name)
5235 m_name->SetTopLevelContent(content_name);
5236 }
5237
GetCheckSum() const5238 unsigned int DesignHasPart::GetCheckSum() const {
5239 unsigned int retval{0};
5240
5241 CheckSums::CheckSumCombine(retval, "Condition::DesignHasPart");
5242 CheckSums::CheckSumCombine(retval, m_low);
5243 CheckSums::CheckSumCombine(retval, m_high);
5244 CheckSums::CheckSumCombine(retval, m_name);
5245
5246 TraceLogger() << "GetCheckSum(DesignHasPart): retval: " << retval;
5247 return retval;
5248 }
5249
5250 ///////////////////////////////////////////////////////////
5251 // DesignHasPartClass //
5252 ///////////////////////////////////////////////////////////
DesignHasPartClass(ShipPartClass part_class,std::unique_ptr<ValueRef::ValueRef<int>> && low,std::unique_ptr<ValueRef::ValueRef<int>> && high)5253 DesignHasPartClass::DesignHasPartClass(ShipPartClass part_class,
5254 std::unique_ptr<ValueRef::ValueRef<int>>&& low,
5255 std::unique_ptr<ValueRef::ValueRef<int>>&& high) :
5256 Condition(),
5257 m_low(std::move(low)),
5258 m_high(std::move(high)),
5259 m_class(std::move(part_class))
5260 {
5261 auto operands = {m_low.get(), m_high.get()};
5262 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
5263 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
5264 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
5265 }
5266
operator ==(const Condition & rhs) const5267 bool DesignHasPartClass::operator==(const Condition& rhs) const {
5268 if (this == &rhs)
5269 return true;
5270 if (typeid(*this) != typeid(rhs))
5271 return false;
5272
5273 const DesignHasPartClass& rhs_ = static_cast<const DesignHasPartClass&>(rhs);
5274
5275 if (m_class != rhs_.m_class)
5276 return false;
5277
5278 CHECK_COND_VREF_MEMBER(m_low)
5279 CHECK_COND_VREF_MEMBER(m_high)
5280
5281 return true;
5282 }
5283
5284 namespace {
5285 struct DesignHasPartClassSimpleMatch {
DesignHasPartClassSimpleMatchCondition::__anone9a56faf4b11::DesignHasPartClassSimpleMatch5286 DesignHasPartClassSimpleMatch(int low, int high, ShipPartClass part_class) :
5287 m_low(low),
5288 m_high(high),
5289 m_part_class(part_class)
5290 {}
5291
operator ()Condition::__anone9a56faf4b11::DesignHasPartClassSimpleMatch5292 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
5293 if (!candidate)
5294 return false;
5295
5296 // is it a ship?
5297 auto ship = std::dynamic_pointer_cast<const ::Ship>(candidate);
5298 if (!ship)
5299 return false;
5300 // with a valid design?
5301 const ShipDesign* design = ship->Design();
5302 if (!design)
5303 return false;
5304
5305
5306 int count = 0;
5307 for (const std::string& name : design->Parts()) {
5308 if (const ShipPart* ship_part = GetShipPart(name)) {
5309 if (ship_part->Class() == m_part_class)
5310 ++count;
5311 }
5312 }
5313 return (m_low <= count && count <= m_high);
5314 }
5315
5316 int m_low;
5317 int m_high;
5318 ShipPartClass m_part_class;
5319 };
5320 }
5321
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const5322 void DesignHasPartClass::Eval(const ScriptingContext& parent_context,
5323 ObjectSet& matches, ObjectSet& non_matches,
5324 SearchDomain search_domain/* = NON_MATCHES*/) const
5325 {
5326 bool simple_eval_safe = (!m_low || m_low->LocalCandidateInvariant()) &&
5327 (!m_high || m_high->LocalCandidateInvariant()) &&
5328 (parent_context.condition_root_candidate || RootCandidateInvariant());
5329 if (simple_eval_safe) {
5330 // evaluate number limits once, use to match all candidates
5331 int low = (m_low ? std::max(0, m_low->Eval(parent_context)) : 1);
5332 int high = (m_high ? std::min(m_high->Eval(parent_context), INT_MAX) : INT_MAX);
5333
5334 // need to test each candidate separately using EvalImpl and because the
5335 // design of the candidate object is tested
5336 EvalImpl(matches, non_matches, search_domain, DesignHasPartClassSimpleMatch(low, high, m_class));
5337 } else {
5338 // re-evaluate allowed turn range for each candidate object
5339 Condition::Eval(parent_context, matches, non_matches, search_domain);
5340 }
5341 }
5342
Description(bool negated) const5343 std::string DesignHasPartClass::Description(bool negated/* = false*/) const {
5344 std::string low_str = "1";
5345 if (m_low) {
5346 low_str = m_low->ConstantExpr() ?
5347 std::to_string(m_low->Eval()) :
5348 m_low->Description();
5349 }
5350 std::string high_str = std::to_string(INT_MAX);
5351 if (m_high) {
5352 high_str = m_high->ConstantExpr() ?
5353 std::to_string(m_high->Eval()) :
5354 m_high->Description();
5355 }
5356 return str(FlexibleFormat((!negated)
5357 ? UserString("DESC_DESIGN_HAS_PART_CLASS")
5358 : UserString("DESC_DESIGN_HAS_PART_CLASS_NOT"))
5359 % low_str
5360 % high_str
5361 % UserString(boost::lexical_cast<std::string>(m_class)));
5362 }
5363
Dump(unsigned short ntabs) const5364 std::string DesignHasPartClass::Dump(unsigned short ntabs) const {
5365 std::string retval = DumpIndent(ntabs) + "DesignHasPartClass";
5366 if (m_low)
5367 retval += " low = " + m_low->Dump(ntabs);
5368 if (m_high)
5369 retval += " high = " + m_high->Dump(ntabs);
5370 retval += " class = " + UserString(boost::lexical_cast<std::string>(m_class));
5371 retval += "\n";
5372 return retval;
5373 }
5374
Match(const ScriptingContext & local_context) const5375 bool DesignHasPartClass::Match(const ScriptingContext& local_context) const {
5376 auto candidate = local_context.condition_local_candidate;
5377 if (!candidate) {
5378 ErrorLogger() << "DesignHasPartClass::Match passed no candidate object";
5379 return false;
5380 }
5381
5382 int low = (m_low ? m_low->Eval(local_context) : 0);
5383 int high = (m_high ? m_high->Eval(local_context) : INT_MAX);
5384
5385 return DesignHasPartClassSimpleMatch(low, high, m_class)(candidate);
5386 }
5387
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const5388 void DesignHasPartClass::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
5389 ObjectSet& condition_non_targets) const
5390 {
5391 AddShipSet(parent_context.ContextObjects(), condition_non_targets);
5392 }
5393
SetTopLevelContent(const std::string & content_name)5394 void DesignHasPartClass::SetTopLevelContent(const std::string& content_name) {
5395 if (m_low)
5396 m_low->SetTopLevelContent(content_name);
5397 if (m_high)
5398 m_high->SetTopLevelContent(content_name);
5399 }
5400
GetCheckSum() const5401 unsigned int DesignHasPartClass::GetCheckSum() const {
5402 unsigned int retval{0};
5403
5404 CheckSums::CheckSumCombine(retval, "Condition::DesignHasPartClass");
5405 CheckSums::CheckSumCombine(retval, m_low);
5406 CheckSums::CheckSumCombine(retval, m_high);
5407 CheckSums::CheckSumCombine(retval, m_class);
5408
5409 TraceLogger() << "GetCheckSum(DesignHasPartClass): retval: " << retval;
5410 return retval;
5411 }
5412
5413 ///////////////////////////////////////////////////////////
5414 // PredefinedShipDesign //
5415 ///////////////////////////////////////////////////////////
PredefinedShipDesign(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)5416 PredefinedShipDesign::PredefinedShipDesign(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
5417 Condition(),
5418 m_name(std::move(name))
5419 {
5420 m_root_candidate_invariant = !m_name || m_name->RootCandidateInvariant();
5421 m_target_invariant = !m_name || m_name->TargetInvariant();
5422 m_source_invariant = !m_name || m_name->SourceInvariant();
5423 }
5424
operator ==(const Condition & rhs) const5425 bool PredefinedShipDesign::operator==(const Condition& rhs) const {
5426 if (this == &rhs)
5427 return true;
5428 if (typeid(*this) != typeid(rhs))
5429 return false;
5430
5431 const PredefinedShipDesign& rhs_ = static_cast<const PredefinedShipDesign&>(rhs);
5432
5433 CHECK_COND_VREF_MEMBER(m_name)
5434
5435 return true;
5436 }
5437
5438 namespace {
5439 struct PredefinedShipDesignSimpleMatch {
PredefinedShipDesignSimpleMatchCondition::__anone9a56faf4c11::PredefinedShipDesignSimpleMatch5440 PredefinedShipDesignSimpleMatch() :
5441 m_any_predef_design_ok(true),
5442 m_name()
5443 {}
5444
PredefinedShipDesignSimpleMatchCondition::__anone9a56faf4c11::PredefinedShipDesignSimpleMatch5445 PredefinedShipDesignSimpleMatch(const std::string& name) :
5446 m_any_predef_design_ok(false),
5447 m_name(name)
5448 {}
5449
operator ()Condition::__anone9a56faf4c11::PredefinedShipDesignSimpleMatch5450 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
5451 auto ship = std::dynamic_pointer_cast<const Ship>(candidate);
5452 if (!ship)
5453 return false;
5454 const ShipDesign* candidate_design = ship->Design();
5455 if (!candidate_design)
5456 return false;
5457
5458 // ship has a valid design. see if it is / could be a predefined ship design...
5459
5460 // all predefined named designs are hard-coded in parsing to have a designed on turn 0 (before first turn)
5461 if (candidate_design->DesignedOnTurn() != 0)
5462 return false;
5463
5464 if (m_any_predef_design_ok)
5465 return true; // any predefined design is OK; don't need to check name.
5466
5467 return (m_name == candidate_design->Name(false)); // don't look up in stringtable; predefined designs are stored by stringtable entry key
5468 }
5469
5470 bool m_any_predef_design_ok;
5471 std::string m_name;
5472 };
5473 }
5474
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const5475 void PredefinedShipDesign::Eval(const ScriptingContext& parent_context,
5476 ObjectSet& matches, ObjectSet& non_matches,
5477 SearchDomain search_domain/* = NON_MATCHES*/) const
5478 {
5479 bool simple_eval_safe = (!m_name || m_name->LocalCandidateInvariant()) &&
5480 (parent_context.condition_root_candidate || RootCandidateInvariant());
5481 if (simple_eval_safe) {
5482 // testing each candidate to see if its design is predefined or is a
5483 // particular named predefined design
5484 if (!m_name) {
5485 EvalImpl(matches, non_matches, search_domain, PredefinedShipDesignSimpleMatch());
5486 } else {
5487 std::string name = m_name->Eval(parent_context);
5488 EvalImpl(matches, non_matches, search_domain, PredefinedShipDesignSimpleMatch(name));
5489 }
5490 } else {
5491 // re-evaluate allowed turn range for each candidate object
5492 Condition::Eval(parent_context, matches, non_matches, search_domain);
5493 }
5494 }
5495
Description(bool negated) const5496 std::string PredefinedShipDesign::Description(bool negated/* = false*/) const {
5497 std::string name_str;
5498 if (m_name) {
5499 name_str = m_name->Description();
5500 if (m_name->ConstantExpr() && UserStringExists(name_str))
5501 name_str = UserString(name_str);
5502 }
5503 return str(FlexibleFormat((!negated)
5504 ? UserString("DESC_PREDEFINED_SHIP_DESIGN")
5505 : UserString("DESC_PREDEFINED_SHIP_DESIGN_NOT"))
5506 % name_str);
5507 }
5508
Dump(unsigned short ntabs) const5509 std::string PredefinedShipDesign::Dump(unsigned short ntabs) const {
5510 std::string retval = DumpIndent(ntabs) + "PredefinedShipDesign";
5511 if (m_name)
5512 retval += " name = " + m_name->Dump(ntabs);
5513 retval += "\n";
5514 return retval;
5515 }
5516
Match(const ScriptingContext & local_context) const5517 bool PredefinedShipDesign::Match(const ScriptingContext& local_context) const {
5518 auto candidate = local_context.condition_local_candidate;
5519 if (!candidate) {
5520 ErrorLogger() << "PredefinedShipDesign::Match passed no candidate object";
5521 return false;
5522 }
5523
5524 if (!m_name)
5525 return PredefinedShipDesignSimpleMatch()(candidate);
5526
5527 std::string name = m_name->Eval(local_context);
5528 return PredefinedShipDesignSimpleMatch(name)(candidate);
5529 }
5530
SetTopLevelContent(const std::string & content_name)5531 void PredefinedShipDesign::SetTopLevelContent(const std::string& content_name) {
5532 if (m_name)
5533 m_name->SetTopLevelContent(content_name);
5534 }
5535
GetCheckSum() const5536 unsigned int PredefinedShipDesign::GetCheckSum() const {
5537 unsigned int retval{0};
5538
5539 CheckSums::CheckSumCombine(retval, "Condition::PredefinedShipDesign");
5540 CheckSums::CheckSumCombine(retval, m_name);
5541
5542 TraceLogger() << "GetCheckSum(PredefinedShipDesign): retval: " << retval;
5543 return retval;
5544 }
5545
5546 ///////////////////////////////////////////////////////////
5547 // NumberedShipDesign //
5548 ///////////////////////////////////////////////////////////
NumberedShipDesign(std::unique_ptr<ValueRef::ValueRef<int>> && design_id)5549 NumberedShipDesign::NumberedShipDesign(std::unique_ptr<ValueRef::ValueRef<int>>&& design_id) :
5550 Condition(),
5551 m_design_id(std::move(design_id))
5552 {
5553 m_root_candidate_invariant = !m_design_id || m_design_id->RootCandidateInvariant();
5554 m_target_invariant = !m_design_id || m_design_id->TargetInvariant();
5555 m_source_invariant = !m_design_id || m_design_id->SourceInvariant();
5556 }
5557
operator ==(const Condition & rhs) const5558 bool NumberedShipDesign::operator==(const Condition& rhs) const {
5559 if (this == &rhs)
5560 return true;
5561 if (typeid(*this) != typeid(rhs))
5562 return false;
5563
5564 const NumberedShipDesign& rhs_ = static_cast<const NumberedShipDesign&>(rhs);
5565
5566 CHECK_COND_VREF_MEMBER(m_design_id)
5567
5568 return true;
5569 }
5570
5571 namespace {
5572 struct NumberedShipDesignSimpleMatch {
NumberedShipDesignSimpleMatchCondition::__anone9a56faf4d11::NumberedShipDesignSimpleMatch5573 NumberedShipDesignSimpleMatch(int design_id) :
5574 m_design_id(design_id)
5575 {}
5576
operator ()Condition::__anone9a56faf4d11::NumberedShipDesignSimpleMatch5577 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
5578 if (!candidate)
5579 return false;
5580 if (m_design_id == INVALID_DESIGN_ID)
5581 return false;
5582 if (auto ship = std::dynamic_pointer_cast<const Ship>(candidate))
5583 if (ship->DesignID() == m_design_id)
5584 return true;
5585 return false;
5586 }
5587
5588 int m_design_id;
5589 };
5590 }
5591
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const5592 void NumberedShipDesign::Eval(const ScriptingContext& parent_context,
5593 ObjectSet& matches, ObjectSet& non_matches,
5594 SearchDomain search_domain/* = NON_MATCHES*/) const
5595 {
5596 bool simple_eval_safe = m_design_id->ConstantExpr() ||
5597 (m_design_id->LocalCandidateInvariant() &&
5598 (parent_context.condition_root_candidate || RootCandidateInvariant()));
5599 if (simple_eval_safe) {
5600 // evaluate design id once, and use to check all candidate objects
5601 int design_id = m_design_id->Eval(parent_context);
5602
5603 // design of the candidate objects is tested, so need to check each separately
5604 EvalImpl(matches, non_matches, search_domain, NumberedShipDesignSimpleMatch(design_id));
5605 } else {
5606 // re-evaluate design id for each candidate object
5607 Condition::Eval(parent_context, matches, non_matches, search_domain);
5608 }
5609 }
5610
Description(bool negated) const5611 std::string NumberedShipDesign::Description(bool negated/* = false*/) const {
5612 std::string id_str = m_design_id->ConstantExpr() ?
5613 std::to_string(m_design_id->Eval()) :
5614 m_design_id->Description();
5615
5616 return str(FlexibleFormat((!negated)
5617 ? UserString("DESC_NUMBERED_SHIP_DESIGN")
5618 : UserString("DESC_NUMBERED_SHIP_DESIGN_NOT"))
5619 % id_str);
5620 }
5621
Dump(unsigned short ntabs) const5622 std::string NumberedShipDesign::Dump(unsigned short ntabs) const
5623 { return DumpIndent(ntabs) + "NumberedShipDesign design_id = " + m_design_id->Dump(ntabs); }
5624
Match(const ScriptingContext & local_context) const5625 bool NumberedShipDesign::Match(const ScriptingContext& local_context) const {
5626 auto candidate = local_context.condition_local_candidate;
5627 if (!candidate) {
5628 ErrorLogger() << "NumberedShipDesign::Match passed no candidate object";
5629 return false;
5630 }
5631 return NumberedShipDesignSimpleMatch(m_design_id->Eval(local_context))(candidate);
5632 }
5633
SetTopLevelContent(const std::string & content_name)5634 void NumberedShipDesign::SetTopLevelContent(const std::string& content_name) {
5635 if (m_design_id)
5636 m_design_id->SetTopLevelContent(content_name);
5637 }
5638
GetCheckSum() const5639 unsigned int NumberedShipDesign::GetCheckSum() const {
5640 unsigned int retval{0};
5641
5642 CheckSums::CheckSumCombine(retval, "Condition::NumberedShipDesign");
5643 CheckSums::CheckSumCombine(retval, m_design_id);
5644
5645 TraceLogger() << "GetCheckSum(NumberedShipDesign): retval: " << retval;
5646 return retval;
5647 }
5648
5649 ///////////////////////////////////////////////////////////
5650 // ProducedByEmpire //
5651 ///////////////////////////////////////////////////////////
ProducedByEmpire(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)5652 ProducedByEmpire::ProducedByEmpire(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
5653 Condition(),
5654 m_empire_id(std::move(empire_id))
5655 {
5656 m_root_candidate_invariant = !m_empire_id || m_empire_id->RootCandidateInvariant();
5657 m_target_invariant = !m_empire_id || m_empire_id->TargetInvariant();
5658 m_source_invariant = !m_empire_id || m_empire_id->SourceInvariant();
5659 }
5660
operator ==(const Condition & rhs) const5661 bool ProducedByEmpire::operator==(const Condition& rhs) const {
5662 if (this == &rhs)
5663 return true;
5664 if (typeid(*this) != typeid(rhs))
5665 return false;
5666
5667 const ProducedByEmpire& rhs_ = static_cast<const ProducedByEmpire&>(rhs);
5668
5669 CHECK_COND_VREF_MEMBER(m_empire_id)
5670
5671 return true;
5672 }
5673
5674 namespace {
5675 struct ProducedByEmpireSimpleMatch {
ProducedByEmpireSimpleMatchCondition::__anone9a56faf4e11::ProducedByEmpireSimpleMatch5676 ProducedByEmpireSimpleMatch(int empire_id) :
5677 m_empire_id(empire_id)
5678 {}
5679
operator ()Condition::__anone9a56faf4e11::ProducedByEmpireSimpleMatch5680 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
5681 if (!candidate)
5682 return false;
5683
5684 if (auto ship = std::dynamic_pointer_cast<const ::Ship>(candidate))
5685 return ship->ProducedByEmpireID() == m_empire_id;
5686 else if (auto building = std::dynamic_pointer_cast<const ::Building>(candidate))
5687 return building->ProducedByEmpireID() == m_empire_id;
5688 return false;
5689 }
5690
5691 int m_empire_id;
5692 };
5693 }
5694
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const5695 void ProducedByEmpire::Eval(const ScriptingContext& parent_context,
5696 ObjectSet& matches, ObjectSet& non_matches,
5697 SearchDomain search_domain/* = NON_MATCHES*/) const
5698 {
5699 bool simple_eval_safe = m_empire_id->ConstantExpr() ||
5700 (m_empire_id->LocalCandidateInvariant() &&
5701 (parent_context.condition_root_candidate || RootCandidateInvariant()));
5702 if (simple_eval_safe) {
5703 // evaluate empire id once, and use to check all candidate objects
5704 int empire_id = m_empire_id->Eval(parent_context);
5705 EvalImpl(matches, non_matches, search_domain, ProducedByEmpireSimpleMatch(empire_id));
5706 } else {
5707 // re-evaluate empire id for each candidate object
5708 Condition::Eval(parent_context, matches, non_matches, search_domain);
5709 }
5710 }
5711
Description(bool negated) const5712 std::string ProducedByEmpire::Description(bool negated/* = false*/) const {
5713 std::string empire_str;
5714 if (m_empire_id) {
5715 int empire_id = ALL_EMPIRES;
5716 if (m_empire_id->ConstantExpr())
5717 empire_id = m_empire_id->Eval();
5718 if (const Empire* empire = GetEmpire(empire_id))
5719 empire_str = empire->Name();
5720 else
5721 empire_str = m_empire_id->Description();
5722 }
5723
5724 return str(FlexibleFormat((!negated)
5725 ? UserString("DESC_PRODUCED_BY_EMPIRE")
5726 : UserString("DESC_PRODUCED_BY_EMPIRE_NOT"))
5727 % empire_str);
5728 }
5729
Dump(unsigned short ntabs) const5730 std::string ProducedByEmpire::Dump(unsigned short ntabs) const
5731 { return DumpIndent(ntabs) + "ProducedByEmpire empire_id = " + m_empire_id->Dump(ntabs); }
5732
Match(const ScriptingContext & local_context) const5733 bool ProducedByEmpire::Match(const ScriptingContext& local_context) const {
5734 auto candidate = local_context.condition_local_candidate;
5735 if (!candidate) {
5736 ErrorLogger() << "ProducedByEmpire::Match passed no candidate object";
5737 return false;
5738 }
5739
5740 return ProducedByEmpireSimpleMatch(m_empire_id->Eval(local_context))(candidate);
5741 }
5742
SetTopLevelContent(const std::string & content_name)5743 void ProducedByEmpire::SetTopLevelContent(const std::string& content_name) {
5744 if (m_empire_id)
5745 m_empire_id->SetTopLevelContent(content_name);
5746 }
5747
GetCheckSum() const5748 unsigned int ProducedByEmpire::GetCheckSum() const {
5749 unsigned int retval{0};
5750
5751 CheckSums::CheckSumCombine(retval, "Condition::ProducedByEmpire");
5752 CheckSums::CheckSumCombine(retval, m_empire_id);
5753
5754 TraceLogger() << "GetCheckSum(ProducedByEmpire): retval: " << retval;
5755 return retval;
5756 }
5757
5758 ///////////////////////////////////////////////////////////
5759 // Chance //
5760 ///////////////////////////////////////////////////////////
Chance(std::unique_ptr<ValueRef::ValueRef<double>> && chance)5761 Chance::Chance(std::unique_ptr<ValueRef::ValueRef<double>>&& chance) :
5762 Condition(),
5763 m_chance(std::move(chance))
5764 {
5765 m_root_candidate_invariant = !m_chance || m_chance->RootCandidateInvariant();
5766 m_target_invariant = !m_chance || m_chance->TargetInvariant();
5767 m_source_invariant = !m_chance || m_chance->SourceInvariant();
5768 }
5769
operator ==(const Condition & rhs) const5770 bool Chance::operator==(const Condition& rhs) const {
5771 if (this == &rhs)
5772 return true;
5773 if (typeid(*this) != typeid(rhs))
5774 return false;
5775
5776 const Chance& rhs_ = static_cast<const Chance&>(rhs);
5777
5778 CHECK_COND_VREF_MEMBER(m_chance)
5779
5780 return true;
5781 }
5782
5783 namespace {
5784 struct ChanceSimpleMatch {
ChanceSimpleMatchCondition::__anone9a56faf4f11::ChanceSimpleMatch5785 ChanceSimpleMatch(float chance) :
5786 m_chance(chance)
5787 {}
5788
operator ()Condition::__anone9a56faf4f11::ChanceSimpleMatch5789 bool operator()(std::shared_ptr<const UniverseObject> candidate) const
5790 { return RandZeroToOne() <= m_chance; }
5791
5792 float m_chance;
5793 };
5794 }
5795
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const5796 void Chance::Eval(const ScriptingContext& parent_context,
5797 ObjectSet& matches, ObjectSet& non_matches,
5798 SearchDomain search_domain/* = NON_MATCHES*/) const
5799 {
5800 bool simple_eval_safe = m_chance->ConstantExpr() ||
5801 (m_chance->LocalCandidateInvariant() &&
5802 (parent_context.condition_root_candidate || RootCandidateInvariant()));
5803 if (simple_eval_safe) {
5804 // evaluate empire id once, and use to check all candidate objects
5805 float chance = std::max(0.0, std::min(1.0, m_chance->Eval(parent_context)));
5806 // chance is tested independently for each candidate object
5807 EvalImpl(matches, non_matches, search_domain, ChanceSimpleMatch(chance));
5808 } else {
5809 // re-evaluate empire id for each candidate object
5810 Condition::Eval(parent_context, matches, non_matches, search_domain);
5811 }
5812 }
5813
Description(bool negated) const5814 std::string Chance::Description(bool negated/* = false*/) const {
5815 if (m_chance->ConstantExpr()) {
5816 return str(FlexibleFormat((!negated)
5817 ? UserString("DESC_CHANCE_PERCENTAGE")
5818 : UserString("DESC_CHANCE_PERCENTAGE_NOT"))
5819 % std::to_string(std::max(0.0, std::min(m_chance->Eval(), 1.0)) * 100));
5820 } else {
5821 return str(FlexibleFormat((!negated)
5822 ? UserString("DESC_CHANCE")
5823 : UserString("DESC_CHANCE_NOT"))
5824 % m_chance->Description());
5825 }
5826 }
5827
Dump(unsigned short ntabs) const5828 std::string Chance::Dump(unsigned short ntabs) const
5829 { return DumpIndent(ntabs) + "Random probability = " + m_chance->Dump(ntabs) + "\n"; }
5830
Match(const ScriptingContext & local_context) const5831 bool Chance::Match(const ScriptingContext& local_context) const {
5832 float chance = std::max(0.0, std::min(m_chance->Eval(local_context), 1.0));
5833 return RandZeroToOne() <= chance;
5834 }
5835
SetTopLevelContent(const std::string & content_name)5836 void Chance::SetTopLevelContent(const std::string& content_name) {
5837 if (m_chance)
5838 m_chance->SetTopLevelContent(content_name);
5839 }
5840
GetCheckSum() const5841 unsigned int Chance::GetCheckSum() const {
5842 unsigned int retval{0};
5843
5844 CheckSums::CheckSumCombine(retval, "Condition::Chance");
5845 CheckSums::CheckSumCombine(retval, m_chance);
5846
5847 TraceLogger() << "GetCheckSum(Chance): retval: " << retval;
5848 return retval;
5849 }
5850
5851 ///////////////////////////////////////////////////////////
5852 // MeterValue //
5853 ///////////////////////////////////////////////////////////
MeterValue(MeterType meter,std::unique_ptr<ValueRef::ValueRef<double>> && low,std::unique_ptr<ValueRef::ValueRef<double>> && high)5854 MeterValue::MeterValue(MeterType meter,
5855 std::unique_ptr<ValueRef::ValueRef<double>>&& low,
5856 std::unique_ptr<ValueRef::ValueRef<double>>&& high) :
5857 Condition(),
5858 m_meter(meter),
5859 m_low(std::move(low)),
5860 m_high(std::move(high))
5861 {
5862 auto operands = {m_low.get(), m_high.get()};
5863 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
5864 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
5865 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
5866 }
5867
operator ==(const Condition & rhs) const5868 bool MeterValue::operator==(const Condition& rhs) const {
5869 if (this == &rhs)
5870 return true;
5871 if (typeid(*this) != typeid(rhs))
5872 return false;
5873
5874 const MeterValue& rhs_ = static_cast<const MeterValue&>(rhs);
5875
5876 if (m_meter != rhs_.m_meter)
5877 return false;
5878
5879 CHECK_COND_VREF_MEMBER(m_low)
5880 CHECK_COND_VREF_MEMBER(m_high)
5881
5882 return true;
5883 }
5884
5885 namespace {
5886 struct MeterValueSimpleMatch {
MeterValueSimpleMatchCondition::__anone9a56faf5311::MeterValueSimpleMatch5887 MeterValueSimpleMatch(float low, float high, MeterType meter_type) :
5888 m_low(low),
5889 m_high(high),
5890 m_meter_type(meter_type)
5891 {}
5892
operator ()Condition::__anone9a56faf5311::MeterValueSimpleMatch5893 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
5894 if (!candidate)
5895 return false;
5896
5897 if (const Meter* meter = candidate->GetMeter(m_meter_type)) {
5898 float value = meter->Initial(); // match Initial rather than Current to make results reproducible in a given turn, until back propagation happens
5899 return m_low <= value && value <= m_high;
5900 }
5901
5902 return false;
5903 }
5904
5905 float m_low;
5906 float m_high;
5907 MeterType m_meter_type;
5908 };
5909
MeterTypeDumpString(MeterType meter)5910 std::string MeterTypeDumpString(MeterType meter) {
5911 switch (meter) {
5912 case INVALID_METER_TYPE: return "INVALID_METER_TYPE"; break;
5913 case METER_TARGET_POPULATION: return "TargetPopulation"; break;
5914 case METER_TARGET_INDUSTRY: return "TargetIndustry"; break;
5915 case METER_TARGET_RESEARCH: return "TargetResearch"; break;
5916 case METER_TARGET_TRADE: return "TargetTrade"; break;
5917 case METER_TARGET_CONSTRUCTION: return "TargetConstruction"; break;
5918 case METER_TARGET_HAPPINESS: return "TargetHappiness"; break;
5919 case METER_MAX_CAPACITY: return "MaxCapacity"; break;
5920 case METER_MAX_SECONDARY_STAT: return "MaxSecondaryStat"; break;
5921 case METER_MAX_FUEL: return "MaxFuel"; break;
5922 case METER_MAX_SHIELD: return "MaxShield"; break;
5923 case METER_MAX_STRUCTURE: return "MaxStructure"; break;
5924 case METER_MAX_DEFENSE: return "MaxDefense"; break;
5925 case METER_MAX_SUPPLY: return "MaxSupply"; break;
5926 case METER_MAX_STOCKPILE: return "MaxStockpile"; break;
5927 case METER_MAX_TROOPS: return "MaxTroops"; break;
5928 case METER_POPULATION: return "Population"; break;
5929 case METER_INDUSTRY: return "Industry"; break;
5930 case METER_RESEARCH: return "Research"; break;
5931 case METER_TRADE: return "Trade"; break;
5932 case METER_CONSTRUCTION: return "Construction"; break;
5933 case METER_HAPPINESS: return "Happiness"; break;
5934 case METER_CAPACITY: return "Capacity"; break;
5935 case METER_SECONDARY_STAT: return "SecondaryStat"; break;
5936 case METER_FUEL: return "Fuel"; break;
5937 case METER_SHIELD: return "Shield"; break;
5938 case METER_STRUCTURE: return "Structure"; break;
5939 case METER_DEFENSE: return "Defense"; break;
5940 case METER_SUPPLY: return "Supply"; break;
5941 case METER_STOCKPILE: return "Stockpile"; break;
5942 case METER_TROOPS: return "Troops"; break;
5943 case METER_REBEL_TROOPS: return "RebelTroops"; break;
5944 case METER_SIZE: return "Size"; break;
5945 case METER_STEALTH: return "Stealth"; break;
5946 case METER_DETECTION: return "Detection"; break;
5947 case METER_SPEED: return "Speed"; break;
5948 default: return "?Meter?"; break;
5949 }
5950 }
5951 }
5952
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const5953 void MeterValue::Eval(const ScriptingContext& parent_context,
5954 ObjectSet& matches, ObjectSet& non_matches,
5955 SearchDomain search_domain/* = NON_MATCHES*/) const
5956 {
5957 bool simple_eval_safe = ((!m_low || m_low->LocalCandidateInvariant()) &&
5958 (!m_high || m_high->LocalCandidateInvariant()) &&
5959 (parent_context.condition_root_candidate || RootCandidateInvariant()));
5960 if (simple_eval_safe) {
5961 // evaluate number limits once, use to match all candidates
5962 float low = (m_low ? m_low->Eval(parent_context) : -Meter::LARGE_VALUE);
5963 float high = (m_high ? m_high->Eval(parent_context) : Meter::LARGE_VALUE);
5964 EvalImpl(matches, non_matches, search_domain, MeterValueSimpleMatch(low, high, m_meter));
5965 } else {
5966 // re-evaluate allowed turn range for each candidate object
5967 Condition::Eval(parent_context, matches, non_matches, search_domain);
5968 }
5969 }
5970
Description(bool negated) const5971 std::string MeterValue::Description(bool negated/* = false*/) const {
5972 std::string low_str = (m_low ? (m_low->ConstantExpr() ?
5973 std::to_string(m_low->Eval()) :
5974 m_low->Description())
5975 : std::to_string(-Meter::LARGE_VALUE));
5976 std::string high_str = (m_high ? (m_high->ConstantExpr() ?
5977 std::to_string(m_high->Eval()) :
5978 m_high->Description())
5979 : std::to_string(Meter::LARGE_VALUE));
5980
5981 if (m_low && !m_high) {
5982 return str(FlexibleFormat((!negated) ?
5983 UserString("DESC_METER_VALUE_CURRENT_MIN") :
5984 UserString("DESC_METER_VALUE_CURRENT_MIN_NOT"))
5985 % UserString(boost::lexical_cast<std::string>(m_meter))
5986 % low_str);
5987 } else if (m_high && !m_low) {
5988 return str(FlexibleFormat((!negated) ?
5989 UserString("DESC_METER_VALUE_CURRENT_MAX") :
5990 UserString("DESC_METER_VALUE_CURRENT_MAX_NOT"))
5991 % UserString(boost::lexical_cast<std::string>(m_meter))
5992 % high_str);
5993 } else {
5994 return str(FlexibleFormat((!negated) ?
5995 UserString("DESC_METER_VALUE_CURRENT") :
5996 UserString("DESC_METER_VALUE_CURRENT_NOT"))
5997 % UserString(boost::lexical_cast<std::string>(m_meter))
5998 % low_str
5999 % high_str);
6000 }
6001 }
6002
Dump(unsigned short ntabs) const6003 std::string MeterValue::Dump(unsigned short ntabs) const {
6004 std::string retval = DumpIndent(ntabs);
6005 retval += MeterTypeDumpString(m_meter);
6006 if (m_low)
6007 retval += " low = " + m_low->Dump(ntabs);
6008 if (m_high)
6009 retval += " high = " + m_high->Dump(ntabs);
6010 retval += "\n";
6011 return retval;
6012 }
6013
Match(const ScriptingContext & local_context) const6014 bool MeterValue::Match(const ScriptingContext& local_context) const {
6015 auto candidate = local_context.condition_local_candidate;
6016 if (!candidate) {
6017 ErrorLogger() << "MeterValue::Match passed no candidate object";
6018 return false;
6019 }
6020 float low = (m_low ? m_low->Eval(local_context) : -Meter::LARGE_VALUE);
6021 float high = (m_high ? m_high->Eval(local_context) : Meter::LARGE_VALUE);
6022 return MeterValueSimpleMatch(low, high, m_meter)(candidate);
6023 }
6024
SetTopLevelContent(const std::string & content_name)6025 void MeterValue::SetTopLevelContent(const std::string& content_name) {
6026 if (m_low)
6027 m_low->SetTopLevelContent(content_name);
6028 if (m_high)
6029 m_high->SetTopLevelContent(content_name);
6030 }
6031
GetCheckSum() const6032 unsigned int MeterValue::GetCheckSum() const {
6033 unsigned int retval{0};
6034
6035 CheckSums::CheckSumCombine(retval, "Condition::MeterValue");
6036 CheckSums::CheckSumCombine(retval, m_meter);
6037 CheckSums::CheckSumCombine(retval, m_low);
6038 CheckSums::CheckSumCombine(retval, m_high);
6039
6040 TraceLogger() << "GetCheckSum(MeterValue): retval: " << retval;
6041 return retval;
6042 }
6043
6044 ///////////////////////////////////////////////////////////
6045 // ShipPartMeterValue //
6046 ///////////////////////////////////////////////////////////
ShipPartMeterValue(std::unique_ptr<ValueRef::ValueRef<std::string>> && ship_part_name,MeterType meter,std::unique_ptr<ValueRef::ValueRef<double>> && low,std::unique_ptr<ValueRef::ValueRef<double>> && high)6047 ShipPartMeterValue::ShipPartMeterValue(std::unique_ptr<ValueRef::ValueRef<std::string>>&& ship_part_name,
6048 MeterType meter,
6049 std::unique_ptr<ValueRef::ValueRef<double>>&& low,
6050 std::unique_ptr<ValueRef::ValueRef<double>>&& high) :
6051 Condition(),
6052 m_part_name(std::move(ship_part_name)),
6053 m_meter(meter),
6054 m_low(std::move(low)),
6055 m_high(std::move(high))
6056 {
6057 auto operands = {m_low.get(), m_high.get()};
6058 m_root_candidate_invariant =
6059 (!m_part_name || m_part_name->RootCandidateInvariant()) &&
6060 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
6061 m_target_invariant =
6062 (!m_part_name || m_part_name->TargetInvariant()) &&
6063 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
6064 m_source_invariant =
6065 (!m_part_name || m_part_name->SourceInvariant()) &&
6066 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
6067 }
6068
operator ==(const Condition & rhs) const6069 bool ShipPartMeterValue::operator==(const Condition& rhs) const {
6070 if (this == &rhs)
6071 return true;
6072 if (typeid(*this) != typeid(rhs))
6073 return false;
6074
6075 const ShipPartMeterValue& rhs_ = static_cast<const ShipPartMeterValue&>(rhs);
6076
6077 if (m_meter != rhs_.m_meter)
6078 return false;
6079
6080 CHECK_COND_VREF_MEMBER(m_part_name)
6081 CHECK_COND_VREF_MEMBER(m_low)
6082 CHECK_COND_VREF_MEMBER(m_high)
6083
6084 return true;
6085 }
6086
6087 namespace {
6088 struct ShipPartMeterValueSimpleMatch {
ShipPartMeterValueSimpleMatchCondition::__anone9a56faf5711::ShipPartMeterValueSimpleMatch6089 ShipPartMeterValueSimpleMatch(const std::string& ship_part_name,
6090 MeterType meter, float low, float high) :
6091 m_part_name(ship_part_name),
6092 m_low(low),
6093 m_high(high),
6094 m_meter(meter)
6095 {}
6096
operator ()Condition::__anone9a56faf5711::ShipPartMeterValueSimpleMatch6097 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
6098 if (!candidate)
6099 return false;
6100 auto ship = std::dynamic_pointer_cast<const Ship>(candidate);
6101 if (!ship)
6102 return false;
6103 const Meter* meter = ship->GetPartMeter(m_meter, m_part_name);
6104 if (!meter)
6105 return false;
6106 float meter_current = meter->Current();
6107 return (m_low <= meter_current && meter_current <= m_high);
6108 }
6109
6110 std::string m_part_name;
6111 float m_low;
6112 float m_high;
6113 MeterType m_meter;
6114 };
6115 }
6116
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const6117 void ShipPartMeterValue::Eval(const ScriptingContext& parent_context,
6118 ObjectSet& matches, ObjectSet& non_matches,
6119 SearchDomain search_domain/* = NON_MATCHES*/) const
6120 {
6121 bool simple_eval_safe = ((!m_part_name || m_part_name->LocalCandidateInvariant()) &&
6122 (!m_low || m_low->LocalCandidateInvariant()) &&
6123 (!m_high || m_high->LocalCandidateInvariant()) &&
6124 (parent_context.condition_root_candidate || RootCandidateInvariant()));
6125 if (simple_eval_safe) {
6126 // evaluate number limits once, use to match all candidates
6127 float low = (m_low ? m_low->Eval(parent_context) : -Meter::LARGE_VALUE);
6128 float high = (m_high ? m_high->Eval(parent_context) : Meter::LARGE_VALUE);
6129 std::string part_name = (m_part_name ? m_part_name->Eval(parent_context) : "");
6130 EvalImpl(matches, non_matches, search_domain, ShipPartMeterValueSimpleMatch(part_name, m_meter, low, high));
6131 } else {
6132 // re-evaluate allowed turn range for each candidate object
6133 Condition::Eval(parent_context, matches, non_matches, search_domain);
6134 }
6135 }
6136
Description(bool negated) const6137 std::string ShipPartMeterValue::Description(bool negated/* = false*/) const {
6138 std::string low_str;
6139 if (m_low)
6140 low_str = m_low->Description();
6141 else
6142 low_str = std::to_string(-Meter::LARGE_VALUE);
6143
6144 std::string high_str;
6145 if (m_high)
6146 high_str = m_high->Description();
6147 else
6148 high_str = std::to_string(Meter::LARGE_VALUE);
6149
6150 std::string part_str;
6151 if (m_part_name) {
6152 part_str = m_part_name->Description();
6153 if (m_part_name->ConstantExpr() && UserStringExists(part_str))
6154 part_str = UserString(part_str);
6155 }
6156
6157 return str(FlexibleFormat((!negated)
6158 ? UserString("DESC_SHIP_PART_METER_VALUE_CURRENT")
6159 : UserString("DESC_SHIP_PART_METER_VALUE_CURRENT_NOT"))
6160 % UserString(boost::lexical_cast<std::string>(m_meter))
6161 % part_str
6162 % low_str
6163 % high_str);
6164 }
6165
Dump(unsigned short ntabs) const6166 std::string ShipPartMeterValue::Dump(unsigned short ntabs) const {
6167 std::string retval = DumpIndent(ntabs);
6168 retval += MeterTypeDumpString(m_meter);
6169 if (m_part_name)
6170 retval += " part = " + m_part_name->Dump(ntabs);
6171 if (m_low)
6172 retval += " low = " + m_low->Dump(ntabs);
6173 if (m_high)
6174 retval += " high = " + m_high->Dump(ntabs);
6175 retval += "\n";
6176 return retval;
6177 }
6178
Match(const ScriptingContext & local_context) const6179 bool ShipPartMeterValue::Match(const ScriptingContext& local_context) const {
6180 auto candidate = local_context.condition_local_candidate;
6181 if (!candidate) {
6182 ErrorLogger() << "ShipPartMeterValue::Match passed no candidate object";
6183 return false;
6184 }
6185 float low = (m_low ? m_low->Eval(local_context) : -Meter::LARGE_VALUE);
6186 float high = (m_high ? m_high->Eval(local_context) : Meter::LARGE_VALUE);
6187 std::string part_name = (m_part_name ? m_part_name->Eval(local_context) : "");
6188 return ShipPartMeterValueSimpleMatch(part_name, m_meter, low, high)(candidate);
6189 }
6190
SetTopLevelContent(const std::string & content_name)6191 void ShipPartMeterValue::SetTopLevelContent(const std::string& content_name) {
6192 if (m_part_name)
6193 m_part_name->SetTopLevelContent(content_name);
6194 if (m_low)
6195 m_low->SetTopLevelContent(content_name);
6196 if (m_high)
6197 m_high->SetTopLevelContent(content_name);
6198 }
6199
GetCheckSum() const6200 unsigned int ShipPartMeterValue::GetCheckSum() const {
6201 unsigned int retval{0};
6202
6203 CheckSums::CheckSumCombine(retval, "Condition::ShipPartMeterValue");
6204 CheckSums::CheckSumCombine(retval, m_part_name);
6205 CheckSums::CheckSumCombine(retval, m_meter);
6206 CheckSums::CheckSumCombine(retval, m_low);
6207 CheckSums::CheckSumCombine(retval, m_high);
6208
6209 TraceLogger() << "GetCheckSum(ShipPartMeterValue): retval: " << retval;
6210 return retval;
6211 }
6212
6213 ///////////////////////////////////////////////////////////
6214 // EmpireMeterValue //
6215 ///////////////////////////////////////////////////////////
EmpireMeterValue(const std::string & meter,std::unique_ptr<ValueRef::ValueRef<double>> && low,std::unique_ptr<ValueRef::ValueRef<double>> && high)6216 EmpireMeterValue::EmpireMeterValue(const std::string& meter,
6217 std::unique_ptr<ValueRef::ValueRef<double>>&& low,
6218 std::unique_ptr<ValueRef::ValueRef<double>>&& high) :
6219 EmpireMeterValue(nullptr, meter, std::move(low), std::move(high))
6220 {}
6221
EmpireMeterValue(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,const std::string & meter,std::unique_ptr<ValueRef::ValueRef<double>> && low,std::unique_ptr<ValueRef::ValueRef<double>> && high)6222 EmpireMeterValue::EmpireMeterValue(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
6223 const std::string& meter,
6224 std::unique_ptr<ValueRef::ValueRef<double>>&& low,
6225 std::unique_ptr<ValueRef::ValueRef<double>>&& high) :
6226 Condition(),
6227 m_empire_id(std::move(empire_id)),
6228 m_meter(meter),
6229 m_low(std::move(low)),
6230 m_high(std::move(high))
6231 {
6232 auto operands = {m_low.get(), m_high.get()};
6233 m_root_candidate_invariant =
6234 (!m_empire_id || m_empire_id->RootCandidateInvariant()) &&
6235 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
6236 m_target_invariant =
6237 (!m_empire_id || m_empire_id->TargetInvariant()) &&
6238 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
6239 m_source_invariant =
6240 (!m_empire_id || m_empire_id->SourceInvariant()) &&
6241 boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
6242 }
6243
operator ==(const Condition & rhs) const6244 bool EmpireMeterValue::operator==(const Condition& rhs) const {
6245 if (this == &rhs)
6246 return true;
6247 if (typeid(*this) != typeid(rhs))
6248 return false;
6249
6250 const EmpireMeterValue& rhs_ = static_cast<const EmpireMeterValue&>(rhs);
6251
6252 if (m_empire_id != rhs_.m_empire_id)
6253 return false;
6254
6255 if (m_meter != rhs_.m_meter)
6256 return false;
6257
6258 CHECK_COND_VREF_MEMBER(m_low)
6259 CHECK_COND_VREF_MEMBER(m_high)
6260
6261 return true;
6262 }
6263
6264
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const6265 void EmpireMeterValue::Eval(const ScriptingContext& parent_context,
6266 ObjectSet& matches, ObjectSet& non_matches,
6267 SearchDomain search_domain/* = NON_MATCHES*/) const
6268 {
6269 bool simple_eval_safe = ((m_empire_id && m_empire_id->LocalCandidateInvariant()) &&
6270 (!m_low || m_low->LocalCandidateInvariant()) &&
6271 (!m_high || m_high->LocalCandidateInvariant()) &&
6272 (parent_context.condition_root_candidate || RootCandidateInvariant()));
6273 if (simple_eval_safe) {
6274 // If m_empire_id is specified (not null), and all parameters are
6275 // local-candidate-invariant, then matching for this condition doesn't
6276 // need to check each candidate object separately for matching, so
6277 // don't need to use EvalImpl and can instead do a simpler transfer
6278 bool match = Match(parent_context);
6279
6280 // transfer objects to or from candidate set, according to whether the
6281 // specified empire meter was in the requested range
6282 if (match && search_domain == NON_MATCHES) {
6283 // move all objects from non_matches to matches
6284 matches.insert(matches.end(), non_matches.begin(), non_matches.end());
6285 non_matches.clear();
6286 } else if (!match && search_domain == MATCHES) {
6287 // move all objects from matches to non_matches
6288 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
6289 matches.clear();
6290 }
6291
6292 } else {
6293 // re-evaluate allowed turn range for each candidate object
6294 Condition::Eval(parent_context, matches, non_matches, search_domain);
6295 }
6296 }
6297
Description(bool negated) const6298 std::string EmpireMeterValue::Description(bool negated/* = false*/) const {
6299 std::string empire_str;
6300 if (m_empire_id) {
6301 int empire_id = ALL_EMPIRES;
6302 if (m_empire_id->ConstantExpr())
6303 empire_id = m_empire_id->Eval();
6304 if (const Empire* empire = GetEmpire(empire_id))
6305 empire_str = empire->Name();
6306 else
6307 empire_str = m_empire_id->Description();
6308 }
6309 std::string low_str = (m_low ? (m_low->ConstantExpr() ?
6310 std::to_string(m_low->Eval()) :
6311 m_low->Description())
6312 : std::to_string(-Meter::LARGE_VALUE));
6313 std::string high_str = (m_high ? (m_high->ConstantExpr() ?
6314 std::to_string(m_high->Eval()) :
6315 m_high->Description())
6316 : std::to_string(Meter::LARGE_VALUE));
6317 return str(FlexibleFormat((!negated)
6318 ? UserString("DESC_EMPIRE_METER_VALUE_CURRENT")
6319 : UserString("DESC_EMPIRE_METER_VALUE_CURRENT_NOT"))
6320 % UserString(m_meter)
6321 % low_str
6322 % high_str
6323 % empire_str);
6324 }
6325
Dump(unsigned short ntabs) const6326 std::string EmpireMeterValue::Dump(unsigned short ntabs) const {
6327 std::string retval = DumpIndent(ntabs) + "EmpireMeterValue";
6328 if (m_empire_id)
6329 retval += " empire = " + m_empire_id->Dump(ntabs);
6330 retval += " meter = " + m_meter;
6331 if (m_low)
6332 retval += " low = " + m_low->Dump(ntabs);
6333 if (m_high)
6334 retval += " high = " + m_high->Dump(ntabs);
6335 retval += "\n";
6336 return retval;
6337 }
6338
Match(const ScriptingContext & local_context) const6339 bool EmpireMeterValue::Match(const ScriptingContext& local_context) const {
6340 int empire_id = ALL_EMPIRES;
6341 auto candidate = local_context.condition_local_candidate;
6342 // if m_empire_id not set, default to candidate object's owner
6343 if (!m_empire_id && !candidate) {
6344 ErrorLogger() << "EmpireMeterValue::Match passed no candidate object but expects one due to having no empire id valueref specified and thus wanting to use the local candidate's owner as the empire id";
6345 return false;
6346
6347 } else if (m_empire_id && !candidate && !m_empire_id->LocalCandidateInvariant()) {
6348 ErrorLogger() << "EmpireMeterValue::Match passed no candidate object but but empire id valueref references the local candidate";
6349 return false;
6350
6351 } else if (!m_empire_id && candidate) {
6352 // default to candidate's owner if no empire id valueref is specified
6353 empire_id = candidate->Owner();
6354
6355 } else if (m_empire_id) {
6356 // either candidate exists or m_empire_id is local-candidate-invariant (or both)
6357 empire_id = m_empire_id->Eval(local_context);
6358
6359 } else {
6360 ErrorLogger() << "EmpireMeterValue::Match reached unexpected default case for candidate and empire id valueref existance";
6361 return false;
6362 }
6363
6364 const Empire* empire = GetEmpire(empire_id);
6365 if (!empire)
6366 return false;
6367 const Meter* meter = empire->GetMeter(m_meter);
6368 if (!meter)
6369 return false;
6370
6371 float meter_current = meter->Current();
6372 float low = (m_low ? m_low->Eval(local_context) : -Meter::LARGE_VALUE);
6373 float high = (m_high ? m_high->Eval(local_context) : Meter::LARGE_VALUE);
6374
6375 return (low <= meter_current && meter_current <= high);
6376 }
6377
SetTopLevelContent(const std::string & content_name)6378 void EmpireMeterValue::SetTopLevelContent(const std::string& content_name) {
6379 if (m_empire_id)
6380 m_empire_id->SetTopLevelContent(content_name);
6381 if (m_low)
6382 m_low->SetTopLevelContent(content_name);
6383 if (m_high)
6384 m_high->SetTopLevelContent(content_name);
6385 }
6386
GetCheckSum() const6387 unsigned int EmpireMeterValue::GetCheckSum() const {
6388 unsigned int retval{0};
6389
6390 CheckSums::CheckSumCombine(retval, "Condition::EmpireMeterValue");
6391 CheckSums::CheckSumCombine(retval, m_empire_id);
6392 CheckSums::CheckSumCombine(retval, m_meter);
6393 CheckSums::CheckSumCombine(retval, m_low);
6394 CheckSums::CheckSumCombine(retval, m_high);
6395
6396 TraceLogger() << "GetCheckSum(EmpireMeterValue): retval: " << retval;
6397 return retval;
6398 }
6399
6400 ///////////////////////////////////////////////////////////
6401 // EmpireStockpileValue //
6402 ///////////////////////////////////////////////////////////
EmpireStockpileValue(ResourceType stockpile,std::unique_ptr<ValueRef::ValueRef<double>> && low,std::unique_ptr<ValueRef::ValueRef<double>> && high)6403 EmpireStockpileValue::EmpireStockpileValue(ResourceType stockpile,
6404 std::unique_ptr<ValueRef::ValueRef<double>>&& low,
6405 std::unique_ptr<ValueRef::ValueRef<double>>&& high) :
6406 Condition(),
6407 m_stockpile(stockpile),
6408 m_low(std::move(low)),
6409 m_high(std::move(high))
6410 {
6411 auto operands = {m_low.get(), m_high.get()};
6412 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
6413 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
6414 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
6415 }
6416
operator ==(const Condition & rhs) const6417 bool EmpireStockpileValue::operator==(const Condition& rhs) const {
6418 if (this == &rhs)
6419 return true;
6420 if (typeid(*this) != typeid(rhs))
6421 return false;
6422
6423 const EmpireStockpileValue& rhs_ = static_cast<const EmpireStockpileValue&>(rhs);
6424
6425 if (m_stockpile != rhs_.m_stockpile)
6426 return false;
6427
6428 CHECK_COND_VREF_MEMBER(m_low)
6429 CHECK_COND_VREF_MEMBER(m_high)
6430
6431 return true;
6432 }
6433
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const6434 void EmpireStockpileValue::Eval(const ScriptingContext& parent_context,
6435 ObjectSet& matches, ObjectSet& non_matches,
6436 SearchDomain search_domain/* = NON_MATCHES*/) const
6437 {
6438 // if m_empire_id not set, the local candidate's owner is used, which is not target invariant
6439 bool simple_eval_safe = ((m_empire_id && m_empire_id->LocalCandidateInvariant()) &&
6440 (!m_low || m_low->LocalCandidateInvariant()) &&
6441 (!m_high || m_high->LocalCandidateInvariant()) &&
6442 (parent_context.condition_root_candidate || RootCandidateInvariant()));
6443 if (simple_eval_safe) {
6444 // If m_empire_id is specified (not null), and all parameters are
6445 // local-candidate-invariant, then matching for this condition doesn't
6446 // need to check each candidate object separately for matching, so
6447 // don't need to use EvalImpl and can instead do a simpler transfer
6448 bool match = Match(parent_context);
6449
6450 // transfer objects to or from candidate set, according to whether the
6451 // specified empire meter was in the requested range
6452 if (match && search_domain == NON_MATCHES) {
6453 // move all objects from non_matches to matches
6454 matches.insert(matches.end(), non_matches.begin(), non_matches.end());
6455 non_matches.clear();
6456 } else if (!match && search_domain == MATCHES) {
6457 // move all objects from matches to non_matches
6458 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
6459 matches.clear();
6460 }
6461
6462 } else {
6463 // re-evaluate all parameters for each candidate object.
6464 // could optimize further by only re-evaluating the local-candidate
6465 // variants.
6466 Condition::Eval(parent_context, matches, non_matches, search_domain);
6467 }
6468 }
6469
Description(bool negated) const6470 std::string EmpireStockpileValue::Description(bool negated/* = false*/) const {
6471 std::string low_str = m_low->ConstantExpr() ?
6472 std::to_string(m_low->Eval()) :
6473 m_low->Description();
6474 std::string high_str = m_high->ConstantExpr() ?
6475 std::to_string(m_high->Eval()) :
6476 m_high->Description();
6477 return str(FlexibleFormat((!negated)
6478 ? UserString("DESC_EMPIRE_STOCKPILE_VALUE")
6479 : UserString("DESC_EMPIRE_STOCKPILE_VALUE_NOT"))
6480 % UserString(boost::lexical_cast<std::string>(m_stockpile))
6481 % low_str
6482 % high_str);
6483 }
6484
Dump(unsigned short ntabs) const6485 std::string EmpireStockpileValue::Dump(unsigned short ntabs) const {
6486 std::string retval = DumpIndent(ntabs);
6487 switch (m_stockpile) {
6488 case RE_TRADE: retval += "OwnerTradeStockpile"; break;
6489 case RE_RESEARCH: retval += "OwnerResearchStockpile"; break;
6490 case RE_INDUSTRY: retval += "OwnerIndustryStockpile"; break;
6491 default: retval += "?"; break;
6492 }
6493 if (m_empire_id)
6494 retval += " empire = " + m_empire_id->Dump(ntabs);
6495 if (m_low)
6496 retval += " low = " + m_low->Dump(ntabs);
6497 if (m_high)
6498 retval += " high = " + m_high->Dump(ntabs);
6499 retval += "\n";
6500 return retval;
6501 }
6502
Match(const ScriptingContext & local_context) const6503 bool EmpireStockpileValue::Match(const ScriptingContext& local_context) const {
6504 int empire_id = ALL_EMPIRES;
6505 auto candidate = local_context.condition_local_candidate;
6506 // if m_empire_id not set, default to candidate object's owner
6507 if (!m_empire_id && !candidate) {
6508 ErrorLogger() << "EmpireStockpileValue::Match passed no candidate object but expects one due to having no empire id valueref specified and thus wanting to use the local candidate's owner as the empire id";
6509 return false;
6510
6511 } else if (m_empire_id && !candidate && !m_empire_id->LocalCandidateInvariant()) {
6512 ErrorLogger() << "EmpireStockpileValue::Match passed no candidate object but but empire id valueref references the local candidate";
6513 return false;
6514
6515 } else if (!m_empire_id && candidate) {
6516 // default to candidate's owner if no empire id valueref is specified
6517 empire_id = candidate->Owner();
6518
6519 } else if (m_empire_id) {
6520 // either candidate exists or m_empire_id is local-candidate-invariant (or both)
6521 empire_id = m_empire_id->Eval(local_context);
6522
6523 } else {
6524 ErrorLogger() << "EmpireStockpileValue::Match reached unexpected default case for candidate and empire id valueref existance";
6525 return false;
6526 }
6527
6528 const Empire* empire = GetEmpire(empire_id);
6529 if (!empire)
6530 return false;
6531
6532 try {
6533 float low = (m_low ? m_low->Eval(local_context) : -Meter::LARGE_VALUE);
6534 float high = (m_high ? m_high->Eval(local_context) : Meter::LARGE_VALUE);
6535 float amount = empire->ResourceStockpile(m_stockpile);
6536 return (low <= amount && amount <= high);
6537 } catch (...) {
6538 return false;
6539 }
6540 }
6541
SetTopLevelContent(const std::string & content_name)6542 void EmpireStockpileValue::SetTopLevelContent(const std::string& content_name) {
6543 if (m_empire_id)
6544 m_empire_id->SetTopLevelContent(content_name);
6545 if (m_low)
6546 m_low->SetTopLevelContent(content_name);
6547 if (m_high)
6548 m_high->SetTopLevelContent(content_name);
6549 }
6550
GetCheckSum() const6551 unsigned int EmpireStockpileValue::GetCheckSum() const {
6552 unsigned int retval{0};
6553
6554 CheckSums::CheckSumCombine(retval, "Condition::EmpireStockpileValue");
6555 CheckSums::CheckSumCombine(retval, m_empire_id);
6556 CheckSums::CheckSumCombine(retval, m_stockpile);
6557 CheckSums::CheckSumCombine(retval, m_low);
6558 CheckSums::CheckSumCombine(retval, m_high);
6559
6560 TraceLogger() << "GetCheckSum(EmpireStockpileValue): retval: " << retval;
6561 return retval;
6562 }
6563
6564 ///////////////////////////////////////////////////////////
6565 // OwnerHasTech //
6566 ///////////////////////////////////////////////////////////
OwnerHasTech(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<std::string>> && name)6567 OwnerHasTech::OwnerHasTech(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
6568 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
6569 Condition(),
6570 m_name(std::move(name)),
6571 m_empire_id(std::move(empire_id))
6572 {
6573 m_root_candidate_invariant =
6574 (!m_empire_id || m_empire_id->RootCandidateInvariant()) &&
6575 (!m_name || m_name->RootCandidateInvariant());
6576 m_target_invariant =
6577 (!m_empire_id || m_empire_id->TargetInvariant()) &&
6578 (!m_name || m_name->TargetInvariant());
6579 m_source_invariant =
6580 (!m_empire_id || m_empire_id->SourceInvariant()) &&
6581 (!m_name || m_name->SourceInvariant());
6582 }
6583
OwnerHasTech(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)6584 OwnerHasTech::OwnerHasTech(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
6585 OwnerHasTech(nullptr, std::move(name))
6586 {}
6587
operator ==(const Condition & rhs) const6588 bool OwnerHasTech::operator==(const Condition& rhs) const {
6589 if (this == &rhs)
6590 return true;
6591 if (typeid(*this) != typeid(rhs))
6592 return false;
6593
6594 const OwnerHasTech& rhs_ = static_cast<const OwnerHasTech&>(rhs);
6595
6596 if (m_empire_id != rhs_.m_empire_id)
6597 return false;
6598
6599 CHECK_COND_VREF_MEMBER(m_name)
6600
6601 return true;
6602 }
6603
6604 namespace {
6605 struct OwnerHasTechSimpleMatch {
OwnerHasTechSimpleMatchCondition::__anone9a56faf5e11::OwnerHasTechSimpleMatch6606 OwnerHasTechSimpleMatch(int empire_id, const std::string& name) :
6607 m_empire_id(empire_id),
6608 m_name(name)
6609 {}
6610
operator ()Condition::__anone9a56faf5e11::OwnerHasTechSimpleMatch6611 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
6612 if (!candidate)
6613 return false;
6614
6615 int actual_empire_id = m_empire_id;
6616 if (m_empire_id == ALL_EMPIRES) {
6617 if (candidate->Unowned())
6618 return false;
6619 actual_empire_id = candidate->Owner();
6620 }
6621
6622 const Empire* empire = GetEmpire(actual_empire_id);
6623 if (!empire)
6624 return false;
6625
6626 return empire->TechResearched(m_name);
6627 }
6628
6629 int m_empire_id;
6630 std::string m_name;
6631 };
6632 }
6633
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const6634 void OwnerHasTech::Eval(const ScriptingContext& parent_context,
6635 ObjectSet& matches, ObjectSet& non_matches,
6636 SearchDomain search_domain/* = NON_MATCHES*/) const
6637 {
6638 // if m_empire_id not set, the local candidate's owner is used, which is not target invariant
6639 bool simple_eval_safe = ((m_empire_id && m_empire_id->LocalCandidateInvariant()) &&
6640 (!m_name || m_name->LocalCandidateInvariant()) &&
6641 (parent_context.condition_root_candidate || RootCandidateInvariant()));
6642 if (simple_eval_safe) {
6643 // evaluate number limits once, use to match all candidates
6644 int empire_id = m_empire_id->Eval(parent_context); // check above should ensure m_empire_id is non-null
6645 std::string name = m_name ? m_name->Eval(parent_context) : "";
6646 EvalImpl(matches, non_matches, search_domain, OwnerHasTechSimpleMatch(empire_id, name));
6647 } else {
6648 // re-evaluate allowed turn range for each candidate object
6649 Condition::Eval(parent_context, matches, non_matches, search_domain);
6650 }
6651 }
6652
Description(bool negated) const6653 std::string OwnerHasTech::Description(bool negated/* = false*/) const {
6654 std::string name_str;
6655 if (m_name) {
6656 name_str = m_name->Description();
6657 if (m_name->ConstantExpr() && UserStringExists(name_str))
6658 name_str = UserString(name_str);
6659 }
6660 return str(FlexibleFormat((!negated)
6661 ? UserString("DESC_OWNER_HAS_TECH")
6662 : UserString("DESC_OWNER_HAS_TECH_NOT"))
6663 % name_str);
6664 }
6665
Dump(unsigned short ntabs) const6666 std::string OwnerHasTech::Dump(unsigned short ntabs) const {
6667 std::string retval = DumpIndent(ntabs) + "OwnerHasTech";
6668 if (m_empire_id)
6669 retval += " empire = " + m_empire_id->Dump(ntabs);
6670 if (m_name)
6671 retval += " name = " + m_name->Dump(ntabs);
6672 retval += "\n";
6673 return retval;
6674 }
6675
Match(const ScriptingContext & local_context) const6676 bool OwnerHasTech::Match(const ScriptingContext& local_context) const {
6677 auto candidate = local_context.condition_local_candidate;
6678 if (!candidate) {
6679 ErrorLogger() << "OwnerHasTech::Match passed no candidate object";
6680 return false;
6681 }
6682
6683 int empire_id = (m_empire_id ? m_empire_id->Eval(local_context) : candidate->Owner());
6684 if (empire_id == ALL_EMPIRES)
6685 return false;
6686 std::string name = m_name ? m_name->Eval(local_context) : "";
6687
6688 return OwnerHasTechSimpleMatch(empire_id, name)(candidate);
6689 }
6690
SetTopLevelContent(const std::string & content_name)6691 void OwnerHasTech::SetTopLevelContent(const std::string& content_name) {
6692 if (m_empire_id)
6693 m_empire_id->SetTopLevelContent(content_name);
6694 if (m_name)
6695 m_name->SetTopLevelContent(content_name);
6696 }
6697
GetCheckSum() const6698 unsigned int OwnerHasTech::GetCheckSum() const {
6699 unsigned int retval{0};
6700
6701 CheckSums::CheckSumCombine(retval, "Condition::OwnerHasTech");
6702 CheckSums::CheckSumCombine(retval, m_empire_id);
6703 CheckSums::CheckSumCombine(retval, m_name);
6704
6705 TraceLogger() << "GetCheckSum(OwnerHasTech): retval: " << retval;
6706 return retval;
6707 }
6708
6709 ///////////////////////////////////////////////////////////
6710 // OwnerHasBuildingTypeAvailable //
6711 ///////////////////////////////////////////////////////////
OwnerHasBuildingTypeAvailable(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<std::string>> && name)6712 OwnerHasBuildingTypeAvailable::OwnerHasBuildingTypeAvailable(
6713 std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
6714 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
6715 Condition(),
6716 m_name(std::move(name)),
6717 m_empire_id(std::move(empire_id))
6718 {
6719 m_root_candidate_invariant =
6720 (!m_empire_id || m_empire_id->RootCandidateInvariant()) &&
6721 (!m_name || m_name->RootCandidateInvariant());
6722 m_target_invariant =
6723 (!m_empire_id || m_empire_id->TargetInvariant()) &&
6724 (!m_name || m_name->TargetInvariant());
6725 m_source_invariant =
6726 (!m_empire_id || m_empire_id->SourceInvariant()) &&
6727 (!m_name || m_name->SourceInvariant());
6728 }
6729
OwnerHasBuildingTypeAvailable(const std::string & name)6730 OwnerHasBuildingTypeAvailable::OwnerHasBuildingTypeAvailable(const std::string& name) :
6731 OwnerHasBuildingTypeAvailable(nullptr, std::make_unique<ValueRef::Constant<std::string>>(name))
6732 {}
6733
OwnerHasBuildingTypeAvailable(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)6734 OwnerHasBuildingTypeAvailable::OwnerHasBuildingTypeAvailable(
6735 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
6736 OwnerHasBuildingTypeAvailable(nullptr, std::move(name))
6737 {}
6738
operator ==(const Condition & rhs) const6739 bool OwnerHasBuildingTypeAvailable::operator==(const Condition& rhs) const {
6740 if (this == &rhs)
6741 return true;
6742 if (typeid(*this) != typeid(rhs))
6743 return false;
6744
6745 const OwnerHasBuildingTypeAvailable& rhs_ = static_cast<const OwnerHasBuildingTypeAvailable&>(rhs);
6746
6747 if (m_empire_id != rhs_.m_empire_id)
6748 return false;
6749
6750 CHECK_COND_VREF_MEMBER(m_name)
6751
6752 return true;
6753 }
6754
6755 namespace {
6756 struct OwnerHasBuildingTypeAvailableSimpleMatch {
OwnerHasBuildingTypeAvailableSimpleMatchCondition::__anone9a56faf5f11::OwnerHasBuildingTypeAvailableSimpleMatch6757 OwnerHasBuildingTypeAvailableSimpleMatch(int empire_id, const std::string& name) :
6758 m_empire_id(empire_id),
6759 m_name(name)
6760 {}
6761
operator ()Condition::__anone9a56faf5f11::OwnerHasBuildingTypeAvailableSimpleMatch6762 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
6763 if (!candidate)
6764 return false;
6765
6766 int actual_empire_id = m_empire_id;
6767 if (m_empire_id == ALL_EMPIRES) {
6768 if (candidate->Unowned())
6769 return false;
6770 actual_empire_id = candidate->Owner();
6771 }
6772
6773 const Empire* empire = GetEmpire(actual_empire_id);
6774 if (!empire)
6775 return false;
6776
6777 return empire->BuildingTypeAvailable(m_name);
6778 }
6779
6780 int m_empire_id;
6781 std::string m_name;
6782 };
6783 }
6784
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const6785 void OwnerHasBuildingTypeAvailable::Eval(const ScriptingContext& parent_context,
6786 ObjectSet& matches, ObjectSet& non_matches,
6787 SearchDomain search_domain/* = NON_MATCHES*/) const
6788 {
6789 // if m_empire_id not set, the local candidate's owner is used, which is not target invariant
6790 bool simple_eval_safe = ((m_empire_id && m_empire_id->LocalCandidateInvariant()) &&
6791 (!m_name || m_name->LocalCandidateInvariant()) &&
6792 (parent_context.condition_root_candidate || RootCandidateInvariant()));
6793 if (simple_eval_safe) {
6794 // evaluate number limits once, use to match all candidates
6795 int empire_id = m_empire_id->Eval(parent_context); // check above should ensure m_empire_id is non-null
6796 std::string name = m_name ? m_name->Eval(parent_context) : "";
6797 EvalImpl(matches, non_matches, search_domain, OwnerHasBuildingTypeAvailableSimpleMatch(empire_id, name));
6798 } else {
6799 // re-evaluate allowed turn range for each candidate object
6800 Condition::Eval(parent_context, matches, non_matches, search_domain);
6801 }
6802 }
6803
Description(bool negated) const6804 std::string OwnerHasBuildingTypeAvailable::Description(bool negated/* = false*/) const {
6805 // used internally for a tooltip where context is apparent, so don't need
6806 // to name builing type here
6807 return (!negated)
6808 ? UserString("DESC_OWNER_HAS_BUILDING_TYPE")
6809 : UserString("DESC_OWNER_HAS_BUILDING_TYPE_NOT");
6810 }
6811
Dump(unsigned short ntabs) const6812 std::string OwnerHasBuildingTypeAvailable::Dump(unsigned short ntabs) const {
6813 std::string retval= DumpIndent(ntabs) + "OwnerHasBuildingTypeAvailable";
6814 if (m_empire_id)
6815 retval += " empire = " + m_empire_id->Dump(ntabs);
6816 if (m_name)
6817 retval += " name = " + m_name->Dump(ntabs);
6818 retval += "\n";
6819 return retval;
6820 }
6821
Match(const ScriptingContext & local_context) const6822 bool OwnerHasBuildingTypeAvailable::Match(const ScriptingContext& local_context) const {
6823 auto candidate = local_context.condition_local_candidate;
6824 if (!candidate) {
6825 ErrorLogger() << "OwnerHasTech::Match passed no candidate object";
6826 return false;
6827 }
6828
6829 int empire_id = (m_empire_id ? m_empire_id->Eval(local_context) : candidate->Owner());
6830 if (empire_id == ALL_EMPIRES)
6831 return false;
6832 std::string name = m_name ? m_name->Eval(local_context) : "";
6833
6834 return OwnerHasBuildingTypeAvailableSimpleMatch(empire_id, name)(candidate);
6835 }
6836
SetTopLevelContent(const std::string & content_name)6837 void OwnerHasBuildingTypeAvailable::SetTopLevelContent(const std::string& content_name) {
6838 if (m_empire_id)
6839 m_empire_id->SetTopLevelContent(content_name);
6840 if (m_name)
6841 m_name->SetTopLevelContent(content_name);
6842 }
6843
GetCheckSum() const6844 unsigned int OwnerHasBuildingTypeAvailable::GetCheckSum() const {
6845 unsigned int retval{0};
6846
6847 CheckSums::CheckSumCombine(retval, "Condition::OwnerHasBuildingTypeAvailable");
6848 CheckSums::CheckSumCombine(retval, m_empire_id);
6849 CheckSums::CheckSumCombine(retval, m_name);
6850
6851 TraceLogger() << "GetCheckSum(OwnerHasBuildingTypeAvailable): retval: " << retval;
6852 return retval;
6853 }
6854
6855 ///////////////////////////////////////////////////////////
6856 // OwnerHasShipDesignAvailable //
6857 ///////////////////////////////////////////////////////////
OwnerHasShipDesignAvailable(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<int>> && design_id)6858 OwnerHasShipDesignAvailable::OwnerHasShipDesignAvailable(
6859 std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
6860 std::unique_ptr<ValueRef::ValueRef<int>>&& design_id) :
6861 Condition(),
6862 m_id(std::move(design_id)),
6863 m_empire_id(std::move(empire_id))
6864 {
6865 auto operands = {m_id.get(), m_empire_id.get()};
6866 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
6867 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
6868 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
6869 }
6870
OwnerHasShipDesignAvailable(int design_id)6871 OwnerHasShipDesignAvailable::OwnerHasShipDesignAvailable(int design_id) :
6872 OwnerHasShipDesignAvailable(nullptr, std::make_unique<ValueRef::Constant<int>>(design_id))
6873 {}
6874
OwnerHasShipDesignAvailable(std::unique_ptr<ValueRef::ValueRef<int>> && design_id)6875 OwnerHasShipDesignAvailable::OwnerHasShipDesignAvailable(std::unique_ptr<ValueRef::ValueRef<int>>&& design_id) :
6876 OwnerHasShipDesignAvailable(nullptr, std::move(design_id))
6877 {}
6878
operator ==(const Condition & rhs) const6879 bool OwnerHasShipDesignAvailable::operator==(const Condition& rhs) const {
6880 if (this == &rhs)
6881 return true;
6882 if (typeid(*this) != typeid(rhs))
6883 return false;
6884
6885 const OwnerHasShipDesignAvailable& rhs_ = static_cast<const OwnerHasShipDesignAvailable&>(rhs);
6886
6887 if (m_empire_id != rhs_.m_empire_id)
6888 return false;
6889
6890 CHECK_COND_VREF_MEMBER(m_id)
6891
6892 return true;
6893 }
6894
6895 namespace {
6896 struct OwnerHasShipDesignAvailableSimpleMatch {
OwnerHasShipDesignAvailableSimpleMatchCondition::__anone9a56faf6311::OwnerHasShipDesignAvailableSimpleMatch6897 OwnerHasShipDesignAvailableSimpleMatch(int empire_id, int design_id) :
6898 m_empire_id(empire_id),
6899 m_id(design_id)
6900 {}
6901
operator ()Condition::__anone9a56faf6311::OwnerHasShipDesignAvailableSimpleMatch6902 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
6903 if (!candidate)
6904 return false;
6905
6906 int actual_empire_id = m_empire_id;
6907 if (m_empire_id == ALL_EMPIRES) {
6908 if (candidate->Unowned())
6909 return false;
6910 actual_empire_id = candidate->Owner();
6911 }
6912
6913 const Empire* empire = GetEmpire(actual_empire_id);
6914 if (!empire)
6915 return false;
6916
6917 return empire->ShipDesignAvailable(m_id);
6918 }
6919
6920 int m_empire_id;
6921 int m_id;
6922 };
6923 }
6924
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const6925 void OwnerHasShipDesignAvailable::Eval(const ScriptingContext& parent_context,
6926 ObjectSet& matches, ObjectSet& non_matches,
6927 SearchDomain search_domain/* = NON_MATCHES*/) const
6928 {
6929 // if m_empire_id not set, the local candidate's owner is used, which is not target invariant
6930 bool simple_eval_safe = ((m_empire_id && m_empire_id->LocalCandidateInvariant()) &&
6931 (!m_id || m_id->LocalCandidateInvariant()) &&
6932 (parent_context.condition_root_candidate || RootCandidateInvariant()));
6933 if (simple_eval_safe) {
6934 // evaluate number limits once, use to match all candidates
6935 int empire_id = m_empire_id->Eval(parent_context); // check above should ensure m_empire_id is non-null
6936 int design_id = m_id ? m_id->Eval(parent_context) : INVALID_DESIGN_ID;
6937 EvalImpl(matches, non_matches, search_domain, OwnerHasShipDesignAvailableSimpleMatch(empire_id, design_id));
6938 } else {
6939 // re-evaluate allowed turn range for each candidate object
6940 Condition::Eval(parent_context, matches, non_matches, search_domain);
6941 }
6942 }
6943
Description(bool negated) const6944 std::string OwnerHasShipDesignAvailable::Description(bool negated/* = false*/) const {
6945 // used internally for a tooltip where context is apparent, so don't need
6946 // to specify design here
6947 return (!negated)
6948 ? UserString("DESC_OWNER_HAS_SHIP_DESIGN")
6949 : UserString("DESC_OWNER_HAS_SHIP_DESIGN_NOT");
6950 }
6951
Dump(unsigned short ntabs) const6952 std::string OwnerHasShipDesignAvailable::Dump(unsigned short ntabs) const {
6953 std::string retval = DumpIndent(ntabs) + "OwnerHasShipDesignAvailable";
6954 if (m_empire_id)
6955 retval += " empire = " + m_empire_id->Dump(ntabs);
6956 if (m_id)
6957 retval += " id = " + m_id->Dump(ntabs);
6958 retval += "\n";
6959 return retval;
6960 }
6961
Match(const ScriptingContext & local_context) const6962 bool OwnerHasShipDesignAvailable::Match(const ScriptingContext& local_context) const {
6963 auto candidate = local_context.condition_local_candidate;
6964 if (!candidate) {
6965 ErrorLogger() << "OwnerHasTech::Match passed no candidate object";
6966 return false;
6967 }
6968
6969 int empire_id = (m_empire_id ? m_empire_id->Eval(local_context) : candidate->Owner());
6970 if (empire_id == ALL_EMPIRES)
6971 return false;
6972 int design_id = m_id ? m_id->Eval(local_context) : INVALID_DESIGN_ID;
6973
6974 return OwnerHasShipDesignAvailableSimpleMatch(empire_id, design_id)(candidate);
6975 }
6976
SetTopLevelContent(const std::string & content_name)6977 void OwnerHasShipDesignAvailable::SetTopLevelContent(const std::string& content_name) {
6978 if (m_empire_id)
6979 m_empire_id->SetTopLevelContent(content_name);
6980 if (m_id)
6981 m_id->SetTopLevelContent(content_name);
6982 }
6983
GetCheckSum() const6984 unsigned int OwnerHasShipDesignAvailable::GetCheckSum() const {
6985 unsigned int retval{0};
6986
6987 CheckSums::CheckSumCombine(retval, "Condition::OwnerHasShipDesignAvailable");
6988 CheckSums::CheckSumCombine(retval, m_empire_id);
6989 CheckSums::CheckSumCombine(retval, m_id);
6990
6991 TraceLogger() << "GetCheckSum(OwnerHasShipDesignAvailable): retval: " << retval;
6992 return retval;
6993 }
6994
6995 ///////////////////////////////////////////////////////////
6996 // OwnerHasShipPartAvailable //
6997 ///////////////////////////////////////////////////////////
OwnerHasShipPartAvailable(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<std::string>> && name)6998 OwnerHasShipPartAvailable::OwnerHasShipPartAvailable(
6999 std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
7000 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
7001 Condition(),
7002 m_name(std::move(name)),
7003 m_empire_id(std::move(empire_id))
7004 {
7005 m_root_candidate_invariant =
7006 (!m_empire_id || m_empire_id->RootCandidateInvariant()) &&
7007 (!m_name || m_name->RootCandidateInvariant());
7008 m_target_invariant =
7009 (!m_empire_id || m_empire_id->TargetInvariant()) &&
7010 (!m_name || m_name->TargetInvariant());
7011 m_source_invariant =
7012 (!m_empire_id || m_empire_id->TargetInvariant()) &&
7013 (!m_name || m_name->TargetInvariant());
7014 }
7015
OwnerHasShipPartAvailable(const std::string & name)7016 OwnerHasShipPartAvailable::OwnerHasShipPartAvailable(const std::string& name) :
7017 OwnerHasShipPartAvailable(nullptr, std::make_unique<ValueRef::Constant<std::string>>(name))
7018 {}
7019
OwnerHasShipPartAvailable(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)7020 OwnerHasShipPartAvailable::OwnerHasShipPartAvailable(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
7021 OwnerHasShipPartAvailable(nullptr, std::move(name))
7022 {}
7023
operator ==(const Condition & rhs) const7024 bool OwnerHasShipPartAvailable::operator==(const Condition& rhs) const {
7025 if (this == &rhs)
7026 return true;
7027 if (typeid(*this) != typeid(rhs))
7028 return false;
7029
7030 const OwnerHasShipPartAvailable& rhs_ =
7031 static_cast<const OwnerHasShipPartAvailable&>(rhs);
7032
7033 if (m_empire_id != rhs_.m_empire_id)
7034 return false;
7035
7036 CHECK_COND_VREF_MEMBER(m_name)
7037
7038 return true;
7039 }
7040
7041 namespace {
7042 struct OwnerHasShipPartAvailableSimpleMatch {
OwnerHasShipPartAvailableSimpleMatchCondition::__anone9a56faf6411::OwnerHasShipPartAvailableSimpleMatch7043 OwnerHasShipPartAvailableSimpleMatch(int empire_id, const std::string& name) :
7044 m_empire_id(empire_id),
7045 m_name(name)
7046 {}
7047
operator ()Condition::__anone9a56faf6411::OwnerHasShipPartAvailableSimpleMatch7048 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
7049 if (!candidate)
7050 return false;
7051
7052 int actual_empire_id = m_empire_id;
7053 if (m_empire_id == ALL_EMPIRES) {
7054 if (candidate->Unowned())
7055 return false;
7056 actual_empire_id = candidate->Owner();
7057 }
7058
7059 const Empire* empire = GetEmpire(actual_empire_id);
7060 if (!empire)
7061 return false;
7062
7063 return empire->ShipPartAvailable(m_name);
7064 }
7065
7066 int m_empire_id;
7067 std::string m_name;
7068 };
7069 }
7070
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const7071 void OwnerHasShipPartAvailable::Eval(const ScriptingContext& parent_context,
7072 ObjectSet& matches, ObjectSet& non_matches,
7073 SearchDomain search_domain/* = NON_MATCHES*/) const
7074 {
7075 // if m_empire_id not set, the local candidate's owner is used, which is not target invariant
7076 bool simple_eval_safe = ((m_empire_id && m_empire_id->LocalCandidateInvariant()) &&
7077 (!m_name || m_name->LocalCandidateInvariant()) &&
7078 (parent_context.condition_root_candidate || RootCandidateInvariant()));
7079 if (simple_eval_safe) {
7080 // evaluate number limits once, use to match all candidates
7081 int empire_id = m_empire_id->Eval(parent_context); // check above should ensure m_empire_id is non-null
7082 std::string name = m_name ? m_name->Eval(parent_context) : "";
7083 EvalImpl(matches, non_matches, search_domain, OwnerHasShipPartAvailableSimpleMatch(empire_id, name));
7084 } else {
7085 // re-evaluate allowed turn range for each candidate object
7086 Condition::Eval(parent_context, matches, non_matches, search_domain);
7087 }
7088 }
7089
Description(bool negated) const7090 std::string OwnerHasShipPartAvailable::Description(bool negated/* = false*/) const {
7091 return (!negated)
7092 ? UserString("DESC_OWNER_HAS_SHIP_PART")
7093 : UserString("DESC_OWNER_HAS_SHIP_PART_NOT");
7094 }
7095
Dump(unsigned short ntabs) const7096 std::string OwnerHasShipPartAvailable::Dump(unsigned short ntabs) const {
7097 std::string retval = DumpIndent(ntabs) + "OwnerHasShipPartAvailable";
7098 if (m_empire_id)
7099 retval += " empire = " + m_empire_id->Dump(ntabs);
7100 if (m_name)
7101 retval += " name = " + m_name->Dump(ntabs);
7102 retval += "\n";
7103 return retval;
7104 }
7105
Match(const ScriptingContext & local_context) const7106 bool OwnerHasShipPartAvailable::Match(const ScriptingContext& local_context) const {
7107 auto candidate = local_context.condition_local_candidate;
7108 if (!candidate) {
7109 ErrorLogger() << "OwnerHasShipPart::Match passed no candidate object";
7110 return false;
7111 }
7112
7113 int empire_id = (m_empire_id ? m_empire_id->Eval(local_context) : candidate->Owner());
7114 if (empire_id == ALL_EMPIRES)
7115 return false;
7116 std::string name = m_name ? m_name->Eval(local_context) : "";
7117
7118 return OwnerHasShipPartAvailableSimpleMatch(empire_id, name)(candidate);
7119 }
7120
SetTopLevelContent(const std::string & content_name)7121 void OwnerHasShipPartAvailable::SetTopLevelContent(const std::string& content_name) {
7122 if (m_empire_id)
7123 m_empire_id->SetTopLevelContent(content_name);
7124 if (m_name)
7125 m_name->SetTopLevelContent(content_name);
7126 }
7127
GetCheckSum() const7128 unsigned int OwnerHasShipPartAvailable::GetCheckSum() const {
7129 unsigned int retval{0};
7130
7131 CheckSums::CheckSumCombine(retval, "Condition::OwnerHasShipPartAvailable");
7132 CheckSums::CheckSumCombine(retval, m_empire_id);
7133 CheckSums::CheckSumCombine(retval, m_name);
7134
7135 TraceLogger() << "GetCheckSum(OwnerHasShipPartAvailable): retval: " << retval;
7136 return retval;
7137 }
7138
7139 ///////////////////////////////////////////////////////////
7140 // VisibleToEmpire //
7141 ///////////////////////////////////////////////////////////
VisibleToEmpire(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)7142 VisibleToEmpire::VisibleToEmpire(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
7143 Condition(),
7144 m_empire_id(std::move(empire_id))
7145 {
7146 m_root_candidate_invariant = !m_empire_id || m_empire_id->RootCandidateInvariant();
7147 m_target_invariant = !m_empire_id || m_empire_id->TargetInvariant();
7148 m_source_invariant = !m_empire_id || m_empire_id->SourceInvariant();
7149 }
7150
operator ==(const Condition & rhs) const7151 bool VisibleToEmpire::operator==(const Condition& rhs) const {
7152 if (this == &rhs)
7153 return true;
7154 if (typeid(*this) != typeid(rhs))
7155 return false;
7156
7157 const VisibleToEmpire& rhs_ = static_cast<const VisibleToEmpire&>(rhs);
7158
7159 CHECK_COND_VREF_MEMBER(m_empire_id)
7160
7161 return true;
7162 }
7163
7164 namespace {
7165 struct VisibleToEmpireSimpleMatch {
VisibleToEmpireSimpleMatchCondition::__anone9a56faf6511::VisibleToEmpireSimpleMatch7166 VisibleToEmpireSimpleMatch(int empire_id,
7167 const Universe::EmpireObjectVisibilityMap& vis_map) :
7168 m_empire_id(empire_id),
7169 vis_map(vis_map)
7170 {}
7171
operator ()Condition::__anone9a56faf6511::VisibleToEmpireSimpleMatch7172 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
7173 if (!candidate)
7174 return false;
7175
7176 // if override is empty, use universe state
7177 if (vis_map.empty())
7178 return candidate->GetVisibility(m_empire_id) > VIS_NO_VISIBILITY;
7179
7180 // if override specified, get visibility info from it
7181 auto empire_it = vis_map.find(m_empire_id);
7182 if (empire_it == vis_map.end())
7183 return false;
7184 const auto& object_map = empire_it->second;
7185 auto object_it = object_map.find(candidate->ID());
7186 if (object_it == object_map.end())
7187 return false;
7188 return object_it->second > VIS_NO_VISIBILITY;
7189 }
7190
7191 int m_empire_id;
7192 const Universe::EmpireObjectVisibilityMap& vis_map;
7193 };
7194 }
7195
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const7196 void VisibleToEmpire::Eval(const ScriptingContext& parent_context,
7197 ObjectSet& matches, ObjectSet& non_matches,
7198 SearchDomain search_domain/* = NON_MATCHES*/) const
7199 {
7200 bool simple_eval_safe = m_empire_id->ConstantExpr() ||
7201 (m_empire_id->LocalCandidateInvariant() &&
7202 (parent_context.condition_root_candidate || RootCandidateInvariant()));
7203 if (simple_eval_safe) {
7204 // evaluate empire id once, and use to check all candidate objects
7205 int empire_id = m_empire_id->Eval(parent_context);
7206
7207 // need to check visibility of each candidate object separately
7208 EvalImpl(matches, non_matches, search_domain,
7209 VisibleToEmpireSimpleMatch(empire_id, parent_context.combat_info.empire_object_visibility));
7210 } else {
7211 // re-evaluate empire id for each candidate object
7212 Condition::Eval(parent_context, matches, non_matches, search_domain);
7213 }
7214 }
7215
Description(bool negated) const7216 std::string VisibleToEmpire::Description(bool negated/* = false*/) const {
7217 std::string empire_str;
7218 if (m_empire_id) {
7219 int empire_id = ALL_EMPIRES;
7220 if (m_empire_id->ConstantExpr())
7221 empire_id = m_empire_id->Eval();
7222 if (const Empire* empire = GetEmpire(empire_id))
7223 empire_str = empire->Name();
7224 else
7225 empire_str = m_empire_id->Description();
7226 }
7227
7228 return str(FlexibleFormat((!negated)
7229 ? UserString("DESC_VISIBLE_TO_EMPIRE")
7230 : UserString("DESC_VISIBLE_TO_EMPIRE_NOT"))
7231 % empire_str);
7232 }
7233
Dump(unsigned short ntabs) const7234 std::string VisibleToEmpire::Dump(unsigned short ntabs) const {
7235 std::string retval = DumpIndent(ntabs) + "VisibleToEmpire";
7236 if (m_empire_id)
7237 retval += " empire = " + m_empire_id->Dump(ntabs);
7238 retval += "\n";
7239 return retval;
7240 }
7241
Match(const ScriptingContext & local_context) const7242 bool VisibleToEmpire::Match(const ScriptingContext& local_context) const {
7243 auto candidate = local_context.condition_local_candidate;
7244 if (!candidate) {
7245 ErrorLogger() << "VisibleToEmpire::Match passed no candidate object";
7246 return false;
7247 }
7248
7249 int empire_id = m_empire_id->Eval(local_context);
7250 return VisibleToEmpireSimpleMatch(empire_id, local_context.combat_info.empire_object_visibility)(candidate);
7251 }
7252
SetTopLevelContent(const std::string & content_name)7253 void VisibleToEmpire::SetTopLevelContent(const std::string& content_name) {
7254 if (m_empire_id)
7255 m_empire_id->SetTopLevelContent(content_name);
7256 }
7257
GetCheckSum() const7258 unsigned int VisibleToEmpire::GetCheckSum() const {
7259 unsigned int retval{0};
7260
7261 CheckSums::CheckSumCombine(retval, "Condition::VisibleToEmpire");
7262 CheckSums::CheckSumCombine(retval, m_empire_id);
7263
7264 TraceLogger() << "GetCheckSum(VisibleToEmpire): retval: " << retval;
7265 return retval;
7266 }
7267
7268 ///////////////////////////////////////////////////////////
7269 // WithinDistance //
7270 ///////////////////////////////////////////////////////////
WithinDistance(std::unique_ptr<ValueRef::ValueRef<double>> && distance,std::unique_ptr<Condition> && condition)7271 WithinDistance::WithinDistance(std::unique_ptr<ValueRef::ValueRef<double>>&& distance,
7272 std::unique_ptr<Condition>&& condition) :
7273 Condition(),
7274 m_distance(std::move(distance)),
7275 m_condition(std::move(condition))
7276 {
7277 m_root_candidate_invariant =
7278 m_distance->RootCandidateInvariant() &&
7279 m_condition->RootCandidateInvariant();
7280 m_target_invariant =
7281 m_distance->TargetInvariant() &&
7282 m_condition->TargetInvariant();
7283 m_source_invariant =
7284 m_distance->SourceInvariant() &&
7285 m_condition->SourceInvariant();
7286 }
7287
operator ==(const Condition & rhs) const7288 bool WithinDistance::operator==(const Condition& rhs) const {
7289 if (this == &rhs)
7290 return true;
7291 if (typeid(*this) != typeid(rhs))
7292 return false;
7293
7294 const WithinDistance& rhs_ = static_cast<const WithinDistance&>(rhs);
7295
7296 CHECK_COND_VREF_MEMBER(m_distance)
7297 CHECK_COND_VREF_MEMBER(m_condition)
7298
7299 return true;
7300 }
7301
7302 namespace {
7303 struct WithinDistanceSimpleMatch {
WithinDistanceSimpleMatchCondition::__anone9a56faf6611::WithinDistanceSimpleMatch7304 WithinDistanceSimpleMatch(const ObjectSet& from_objects, double distance) :
7305 m_from_objects(from_objects),
7306 m_distance2(distance*distance)
7307 {}
7308
operator ()Condition::__anone9a56faf6611::WithinDistanceSimpleMatch7309 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
7310 if (!candidate)
7311 return false;
7312
7313 // is candidate object close enough to any of the passed-in objects?
7314 for (auto& obj : m_from_objects) {
7315 double delta_x = candidate->X() - obj->X();
7316 double delta_y = candidate->Y() - obj->Y();
7317 if (delta_x*delta_x + delta_y*delta_y <= m_distance2)
7318 return true;
7319 }
7320
7321 return false;
7322 }
7323
7324 const ObjectSet& m_from_objects;
7325 double m_distance2;
7326 };
7327 }
7328
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const7329 void WithinDistance::Eval(const ScriptingContext& parent_context,
7330 ObjectSet& matches, ObjectSet& non_matches,
7331 SearchDomain search_domain/* = NON_MATCHES*/) const
7332 {
7333 bool simple_eval_safe = m_distance->LocalCandidateInvariant() &&
7334 (parent_context.condition_root_candidate || RootCandidateInvariant());
7335 if (simple_eval_safe) {
7336 // evaluate contained objects and distance once and check for all candidates
7337 TraceLogger(conditions) << "WithinDistance::Eval simple case";
7338
7339 // get subcondition matches
7340 ObjectSet subcondition_matches;
7341 m_condition->Eval(parent_context, subcondition_matches);
7342 double distance = m_distance->Eval(parent_context);
7343
7344 // need to check locations (with respect to subcondition matches) of candidates separately
7345 EvalImpl(matches, non_matches, search_domain, WithinDistanceSimpleMatch(subcondition_matches, distance));
7346 } else {
7347 // re-evaluate contained objects for each candidate object
7348 TraceLogger(conditions) << "WithinDistance::Eval full case";
7349 Condition::Eval(parent_context, matches, non_matches, search_domain);
7350 }
7351 }
7352
Description(bool negated) const7353 std::string WithinDistance::Description(bool negated/* = false*/) const {
7354 std::string value_str = m_distance->ConstantExpr() ?
7355 std::to_string(m_distance->Eval()) :
7356 m_distance->Description();
7357 return str(FlexibleFormat((!negated)
7358 ? UserString("DESC_WITHIN_DISTANCE")
7359 : UserString("DESC_WITHIN_DISTANCE_NOT"))
7360 % value_str
7361 % m_condition->Description());
7362 }
7363
Dump(unsigned short ntabs) const7364 std::string WithinDistance::Dump(unsigned short ntabs) const {
7365 std::string retval = DumpIndent(ntabs) + "WithinDistance distance = " + m_distance->Dump(ntabs) + " condition =\n";
7366 retval += m_condition->Dump(ntabs+1);
7367 return retval;
7368 }
7369
Match(const ScriptingContext & local_context) const7370 bool WithinDistance::Match(const ScriptingContext& local_context) const {
7371 auto candidate = local_context.condition_local_candidate;
7372 if (!candidate) {
7373 ErrorLogger() << "WithinDistance::Match passed no candidate object";
7374 return false;
7375 }
7376
7377 // get subcondition matches
7378 ObjectSet subcondition_matches;
7379 m_condition->Eval(local_context, subcondition_matches);
7380 if (subcondition_matches.empty())
7381 return false;
7382
7383 return WithinDistanceSimpleMatch(subcondition_matches, m_distance->Eval(local_context))(candidate);
7384 }
7385
SetTopLevelContent(const std::string & content_name)7386 void WithinDistance::SetTopLevelContent(const std::string& content_name) {
7387 if (m_distance)
7388 m_distance->SetTopLevelContent(content_name);
7389 if (m_condition)
7390 m_condition->SetTopLevelContent(content_name);
7391 }
7392
GetCheckSum() const7393 unsigned int WithinDistance::GetCheckSum() const {
7394 unsigned int retval{0};
7395
7396 CheckSums::CheckSumCombine(retval, "Condition::WithinDistance");
7397 CheckSums::CheckSumCombine(retval, m_distance);
7398 CheckSums::CheckSumCombine(retval, m_condition);
7399
7400 TraceLogger() << "GetCheckSum(WithinDistance): retval: " << retval;
7401 return retval;
7402 }
7403
7404 ///////////////////////////////////////////////////////////
7405 // WithinStarlaneJumps //
7406 ///////////////////////////////////////////////////////////
WithinStarlaneJumps(std::unique_ptr<ValueRef::ValueRef<int>> && jumps,std::unique_ptr<Condition> && condition)7407 WithinStarlaneJumps::WithinStarlaneJumps(std::unique_ptr<ValueRef::ValueRef<int>>&& jumps,
7408 std::unique_ptr<Condition>&& condition) :
7409 Condition(),
7410 m_jumps(std::move(jumps)),
7411 m_condition(std::move(condition))
7412 {
7413 m_root_candidate_invariant = m_jumps->RootCandidateInvariant() && m_condition->RootCandidateInvariant();
7414 m_target_invariant = m_jumps->TargetInvariant() && m_condition->TargetInvariant();
7415 m_source_invariant = m_jumps->SourceInvariant() && m_condition->SourceInvariant();
7416 }
7417
operator ==(const Condition & rhs) const7418 bool WithinStarlaneJumps::operator==(const Condition& rhs) const {
7419 if (this == &rhs)
7420 return true;
7421 if (typeid(*this) != typeid(rhs))
7422 return false;
7423
7424 const WithinStarlaneJumps& rhs_ = static_cast<const WithinStarlaneJumps&>(rhs);
7425
7426 CHECK_COND_VREF_MEMBER(m_jumps)
7427 CHECK_COND_VREF_MEMBER(m_condition)
7428
7429 return true;
7430 }
7431
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const7432 void WithinStarlaneJumps::Eval(const ScriptingContext& parent_context,
7433 ObjectSet& matches, ObjectSet& non_matches,
7434 SearchDomain search_domain/* = NON_MATCHES*/) const
7435 {
7436 bool simple_eval_safe = m_jumps->LocalCandidateInvariant() &&
7437 (parent_context.condition_root_candidate || RootCandidateInvariant());
7438 if (simple_eval_safe) {
7439 // evaluate contained objects and jumps limit once and check for all candidates
7440
7441 // get subcondition matches
7442 ObjectSet subcondition_matches;
7443 m_condition->Eval(parent_context, subcondition_matches);
7444 int jump_limit = m_jumps->Eval(parent_context);
7445 ObjectSet &from_set(search_domain == MATCHES ? matches : non_matches);
7446
7447 std::tie(matches, non_matches) = GetPathfinder()->WithinJumpsOfOthers(jump_limit, from_set, subcondition_matches);
7448
7449 } else {
7450 // re-evaluate contained objects for each candidate object
7451 Condition::Eval(parent_context, matches, non_matches, search_domain);
7452 }
7453 }
7454
Description(bool negated) const7455 std::string WithinStarlaneJumps::Description(bool negated/* = false*/) const {
7456 std::string value_str = m_jumps->ConstantExpr() ? std::to_string(m_jumps->Eval()) : m_jumps->Description();
7457 return str(FlexibleFormat((!negated)
7458 ? UserString("DESC_WITHIN_STARLANE_JUMPS")
7459 : UserString("DESC_WITHIN_STARLANE_JUMPS_NOT"))
7460 % value_str
7461 % m_condition->Description());
7462 }
7463
Dump(unsigned short ntabs) const7464 std::string WithinStarlaneJumps::Dump(unsigned short ntabs) const {
7465 std::string retval = DumpIndent(ntabs) + "WithinStarlaneJumps jumps = " + m_jumps->Dump(ntabs) + " condition =\n";
7466 retval += m_condition->Dump(ntabs+1);
7467 return retval;
7468 }
7469
Match(const ScriptingContext & local_context) const7470 bool WithinStarlaneJumps::Match(const ScriptingContext& local_context) const {
7471 auto candidate = local_context.condition_local_candidate;
7472 if (!candidate) {
7473 ErrorLogger() << "WithinStarlaneJumps::Match passed no candidate object";
7474 return false;
7475 }
7476
7477 // get subcondition matches
7478 ObjectSet subcondition_matches;
7479 m_condition->Eval(local_context, subcondition_matches);
7480 if (subcondition_matches.empty())
7481 return false;
7482
7483 int jump_limit = m_jumps->Eval(local_context);
7484 if (jump_limit < 0)
7485 return false;
7486
7487 ObjectSet candidate_set{candidate};
7488
7489 // candidate objects within jumps of subcondition_matches objects
7490 ObjectSet near_objs;
7491
7492 std::tie(near_objs, std::ignore) =
7493 GetPathfinder()->WithinJumpsOfOthers(jump_limit, candidate_set, subcondition_matches);
7494 return !near_objs.empty();
7495 }
7496
SetTopLevelContent(const std::string & content_name)7497 void WithinStarlaneJumps::SetTopLevelContent(const std::string& content_name) {
7498 if (m_jumps)
7499 m_jumps->SetTopLevelContent(content_name);
7500 if (m_condition)
7501 m_condition->SetTopLevelContent(content_name);
7502 }
7503
GetCheckSum() const7504 unsigned int WithinStarlaneJumps::GetCheckSum() const {
7505 unsigned int retval{0};
7506
7507 CheckSums::CheckSumCombine(retval, "Condition::WithinStarlaneJumps");
7508 CheckSums::CheckSumCombine(retval, m_jumps);
7509 CheckSums::CheckSumCombine(retval, m_condition);
7510
7511 TraceLogger() << "GetCheckSum(WithinStarlaneJumps): retval: " << retval;
7512 return retval;
7513 }
7514
7515 ///////////////////////////////////////////////////////////
7516 // CanAddStarlaneConnection //
7517 ///////////////////////////////////////////////////////////
CanAddStarlaneConnection(std::unique_ptr<Condition> && condition)7518 CanAddStarlaneConnection::CanAddStarlaneConnection(std::unique_ptr<Condition>&& condition) :
7519 Condition(),
7520 m_condition(std::move(condition))
7521 {
7522 m_root_candidate_invariant = m_condition->RootCandidateInvariant();
7523 m_target_invariant = m_condition->TargetInvariant();
7524 m_source_invariant = m_condition->SourceInvariant();
7525 }
7526
operator ==(const Condition & rhs) const7527 bool CanAddStarlaneConnection::operator==(const Condition& rhs) const {
7528 if (this == &rhs)
7529 return true;
7530 if (typeid(*this) != typeid(rhs))
7531 return false;
7532
7533 const CanAddStarlaneConnection& rhs_ = static_cast<const CanAddStarlaneConnection&>(rhs);
7534
7535 CHECK_COND_VREF_MEMBER(m_condition)
7536
7537 return true;
7538 }
7539
7540 namespace {
7541 // check if two destination systems, connected to the same origin system
7542 // would have starlanes too close angularly to eachother
LanesAngularlyTooClose(std::shared_ptr<const UniverseObject> sys1,std::shared_ptr<const UniverseObject> lane1_sys2,std::shared_ptr<const UniverseObject> lane2_sys2)7543 bool LanesAngularlyTooClose(std::shared_ptr<const UniverseObject> sys1,
7544 std::shared_ptr<const UniverseObject> lane1_sys2,
7545 std::shared_ptr<const UniverseObject> lane2_sys2)
7546 {
7547 if (!sys1 || !lane1_sys2 || !lane2_sys2)
7548 return true;
7549 if (sys1 == lane1_sys2 || sys1 == lane2_sys2 || lane1_sys2 == lane2_sys2)
7550 return true;
7551
7552 float dx1 = lane1_sys2->X() - sys1->X();
7553 float dy1 = lane1_sys2->Y() - sys1->Y();
7554 float mag = std::sqrt(dx1*dx1 + dy1*dy1);
7555 if (mag == 0.0f)
7556 return true;
7557 dx1 /= mag;
7558 dy1 /= mag;
7559
7560 float dx2 = lane2_sys2->X() - sys1->X();
7561 float dy2 = lane2_sys2->Y() - sys1->Y();
7562 mag = std::sqrt(dx2*dx2 + dy2*dy2);
7563 if (mag == 0.0f)
7564 return true;
7565 dx2 /= mag;
7566 dy2 /= mag;
7567
7568
7569 const float MAX_LANE_DOT_PRODUCT = 0.87f; // magic limit adjusted to allow no more than 12 starlanes from a system
7570 // arccos(0.87) = 0.515594 rad = 29.5 degrees
7571
7572 float dp = (dx1 * dx2) + (dy1 * dy2);
7573 //TraceLogger() << "systems: " << sys1->UniverseObject::Name() << " " << lane1_sys2->UniverseObject::Name() << " " << lane2_sys2->UniverseObject::Name() << " dp: " << dp << std::endl;
7574
7575 return dp >= MAX_LANE_DOT_PRODUCT; // if dot product too high after normalizing vectors, angles are adequately separated
7576 }
7577
7578 // check the distance between a system and a (possibly nonexistant)
7579 // starlane between two other systems. distance here is how far the third
7580 // system is from the line passing through the lane endpoint systems, as
7581 // long as the third system is closer to either end point than the endpoints
7582 // are to eachother. if the third system is further than the endpoints, than
7583 // the distance to the line is not considered and the lane is considered
7584 // acceptable
ObjectTooCloseToLane(std::shared_ptr<const UniverseObject> lane_end_sys1,std::shared_ptr<const UniverseObject> lane_end_sys2,std::shared_ptr<const UniverseObject> obj)7585 bool ObjectTooCloseToLane(std::shared_ptr<const UniverseObject> lane_end_sys1,
7586 std::shared_ptr<const UniverseObject> lane_end_sys2,
7587 std::shared_ptr<const UniverseObject> obj)
7588 {
7589 if (!lane_end_sys1 || !lane_end_sys2 || !obj)
7590 return true;
7591 if (lane_end_sys1 == lane_end_sys2 || obj == lane_end_sys1 || obj == lane_end_sys2)
7592 return true;
7593
7594 // check distances (squared) between object and lane-end systems
7595 float v_12_x = lane_end_sys2->X() - lane_end_sys1->X();
7596 float v_12_y = lane_end_sys2->Y() - lane_end_sys1->Y();
7597 float v_o1_x = lane_end_sys1->X() - obj->X();
7598 float v_o1_y = lane_end_sys1->Y() - obj->Y();
7599 float v_o2_x = lane_end_sys2->X() - obj->X();
7600 float v_o2_y = lane_end_sys2->Y() - obj->Y();
7601
7602 float dist2_12 = v_12_x*v_12_x + v_12_y*v_12_y;
7603 float dist2_o1 = v_o1_x*v_o1_x + v_o1_y*v_o1_y;
7604 float dist2_o2 = v_o2_x*v_o2_x + v_o2_y*v_o2_y;
7605
7606 // object to zero-length lanes
7607 if (dist2_12 == 0.0f || dist2_o1 == 0.0f || dist2_o2 == 0.0f)
7608 return true;
7609
7610 // if object is further from either of the lane end systems than they
7611 // are from eachother, it is fine, regardless of the right-angle
7612 // distance to the line between the systems
7613 if (dist2_12 < dist2_o1 || dist2_12 < dist2_o2)
7614 return false;
7615
7616
7617 // check right-angle distance between obj and lane
7618
7619 // normalize vector components of lane vector
7620 float mag_12 = std::sqrt(dist2_12);
7621 if (mag_12 == 0.0f)
7622 return true;
7623 v_12_x /= mag_12;
7624 v_12_y /= mag_12;
7625
7626 // distance to point from line from vector projection / dot products
7627 //.......O
7628 // /|
7629 // / |
7630 // / |d
7631 // / |
7632 // /a___|___
7633 // 1 2
7634 // (1O).(12) = |1O| |12| cos(a)
7635 // d = |1O| cos(a) = (1O).(12) / |12|
7636 // d = -(O1).(12 / |12|)
7637
7638 const float MIN_PERP_DIST = 20; // magic limit, in units of universe units (uu)
7639
7640 float perp_dist = std::abs(v_o1_x*v_12_x + v_o1_y*v_12_y);
7641
7642 return perp_dist < MIN_PERP_DIST;
7643 }
7644
CrossProduct(float dx1,float dy1,float dx2,float dy2)7645 inline float CrossProduct(float dx1, float dy1, float dx2, float dy2)
7646 { return dx1*dy2 - dy1*dx2; }
7647
LanesCross(std::shared_ptr<const System> lane1_end_sys1,std::shared_ptr<const System> lane1_end_sys2,std::shared_ptr<const System> lane2_end_sys1,std::shared_ptr<const System> lane2_end_sys2)7648 bool LanesCross(std::shared_ptr<const System> lane1_end_sys1,
7649 std::shared_ptr<const System> lane1_end_sys2,
7650 std::shared_ptr<const System> lane2_end_sys1,
7651 std::shared_ptr<const System> lane2_end_sys2)
7652 {
7653 // are all endpoints valid systems?
7654 if (!lane1_end_sys1 || !lane1_end_sys2 || !lane2_end_sys1 || !lane2_end_sys2)
7655 return false;
7656
7657 // is either lane degenerate (same start and endpoints)
7658 if (lane1_end_sys1 == lane1_end_sys2 || lane2_end_sys1 == lane2_end_sys2)
7659 return false;
7660
7661 // do the two lanes share endpoints?
7662 bool share_endpoint_1 = lane1_end_sys1 == lane2_end_sys1 || lane1_end_sys1 == lane2_end_sys2;
7663 bool share_endpoint_2 = lane1_end_sys2 == lane2_end_sys1 || lane1_end_sys2 == lane2_end_sys2;
7664 if (share_endpoint_1 && share_endpoint_2)
7665 return true; // two copies of the same lane?
7666 if (share_endpoint_1 || share_endpoint_2)
7667 return false; // one common endpoing, but not both common, so can't cross in middle
7668
7669 // calculate vector components for lanes
7670 // lane 1
7671 float v_11_12_x = lane1_end_sys2->X() - lane1_end_sys1->X();
7672 float v_11_12_y = lane1_end_sys2->Y() - lane1_end_sys1->Y();
7673 // lane 2
7674 float v_21_22_x = lane2_end_sys2->X() - lane2_end_sys1->X();
7675 float v_21_22_y = lane2_end_sys2->Y() - lane2_end_sys1->Y();
7676
7677 // calculate vector components from lane 1 system 1 to lane 2 endpoints
7678 // lane 1 endpoint 1 to lane 2 endpoint 1
7679 float v_11_21_x = lane2_end_sys1->X() - lane1_end_sys1->X();
7680 float v_11_21_y = lane2_end_sys1->Y() - lane1_end_sys1->Y();
7681 // lane 1 endpoint 1 to lane 2 endpoint 2
7682 float v_11_22_x = lane2_end_sys2->X() - lane1_end_sys1->X();
7683 float v_11_22_y = lane2_end_sys2->Y() - lane1_end_sys1->Y();
7684
7685 // find cross products of vectors to check on which sides of lane 1 the
7686 // endpoints of lane 2 are located...
7687 float cp_1_21 = CrossProduct(v_11_12_x, v_11_12_y, v_11_21_x, v_11_21_y);
7688 float cp_1_22 = CrossProduct(v_11_12_x, v_11_12_y, v_11_22_x, v_11_22_y);
7689 if (cp_1_21*cp_1_22 >= 0) // product of same sign numbers is positive, of different sign numbers is negative
7690 return false; // if same sign, points are on same side of line, so can't cross it
7691
7692 // calculate vector components from lane 2 system 1 to lane 1 endpoints
7693 // lane 2 endpoint 1 to lane 1 endpoint 1
7694 float v_21_11_x = -v_11_21_x;
7695 float v_21_11_y = -v_11_21_y;
7696 // lane 2 endpoint 1 to lane 1 endpoint 2
7697 float v_21_12_x = lane1_end_sys2->X() - lane2_end_sys1->X();
7698 float v_21_12_y = lane1_end_sys2->Y() - lane2_end_sys1->Y();
7699
7700 // find cross products of vectors to check on which sides of lane 2 the
7701 // endpoints of lane 1 are located...
7702 float cp_2_11 = CrossProduct(v_21_22_x, v_21_22_y, v_21_11_x, v_21_11_y);
7703 float cp_2_12 = CrossProduct(v_21_22_x, v_21_22_y, v_21_12_x, v_21_12_y);
7704 if (cp_2_11*cp_2_12 >= 0)
7705 return false;
7706
7707 // endpoints of both lines are on opposite sides of the other line, so
7708 // the lines must cross
7709
7710 return true;
7711 }
7712
LaneCrossesExistingLane(std::shared_ptr<const System> lane_end_sys1,std::shared_ptr<const System> lane_end_sys2,const ObjectMap & objects)7713 bool LaneCrossesExistingLane(std::shared_ptr<const System> lane_end_sys1,
7714 std::shared_ptr<const System> lane_end_sys2,
7715 const ObjectMap& objects)
7716 {
7717 if (!lane_end_sys1 || !lane_end_sys2 || lane_end_sys1 == lane_end_sys2)
7718 return true;
7719
7720 // loop over all existing lanes in all systems, checking if a lane
7721 // beween the specified systems would cross any of the existing lanes
7722 for (auto& system : objects.all<System>()) {
7723 if (system == lane_end_sys1 || system == lane_end_sys2)
7724 continue;
7725
7726 const auto& sys_existing_lanes = system->StarlanesWormholes();
7727
7728 // check all existing lanes of currently-being-checked system
7729 for (const auto& lane : sys_existing_lanes) {
7730 auto lane_end_sys3 = objects.get<System>(lane.first);
7731 if (!lane_end_sys3)
7732 continue;
7733 // don't need to check against existing lanes that include one
7734 // of the endpoints of the lane is one of the specified systems
7735 if (lane_end_sys3 == lane_end_sys1 || lane_end_sys3 == lane_end_sys2)
7736 continue;
7737
7738 if (LanesCross(lane_end_sys1, lane_end_sys2, system, lane_end_sys3)) {
7739 //TraceLogger() << "... ... ... lane from: " << lane_end_sys1->UniverseObject::Name() << " to: " << lane_end_sys2->UniverseObject::Name()
7740 // << " crosses lane from: " << system->UniverseObject::Name() << " to: " << lane_end_sys3->UniverseObject::Name() << std::endl;
7741 return true;
7742 }
7743 }
7744 }
7745
7746 return false;
7747 }
7748
LaneTooCloseToOtherSystem(std::shared_ptr<const System> lane_end_sys1,std::shared_ptr<const System> lane_end_sys2,const ObjectMap & objects)7749 bool LaneTooCloseToOtherSystem(std::shared_ptr<const System> lane_end_sys1,
7750 std::shared_ptr<const System> lane_end_sys2,
7751 const ObjectMap& objects)
7752 {
7753 if (!lane_end_sys1 || !lane_end_sys2 || lane_end_sys1 == lane_end_sys2)
7754 return true;
7755
7756 // loop over all existing systems, checking if each is too close to a
7757 // lane between the specified lane endpoints
7758 for (auto& system : objects.all<System>()) {
7759 if (system == lane_end_sys1 || system == lane_end_sys2)
7760 continue;
7761
7762 if (ObjectTooCloseToLane(lane_end_sys1, lane_end_sys2, system))
7763 return true;
7764 }
7765
7766 return false;
7767 }
7768
7769 struct CanAddStarlaneConnectionSimpleMatch {
CanAddStarlaneConnectionSimpleMatchCondition::__anone9a56faf6711::CanAddStarlaneConnectionSimpleMatch7770 CanAddStarlaneConnectionSimpleMatch(const ObjectSet& destination_objects, const ObjectMap& objects) :
7771 m_destination_systems(),
7772 m_objects(objects)
7773 {
7774 // get (one of each of) set of systems that are or that contain any
7775 // destination objects
7776 std::set<std::shared_ptr<const System>> dest_systems;
7777 for (auto& obj : destination_objects) {
7778 if (auto sys = m_objects.get<System>(obj->SystemID()))
7779 dest_systems.insert(sys);
7780 }
7781 std::copy(dest_systems.begin(), dest_systems.end(), std::inserter(m_destination_systems, m_destination_systems.end()));
7782 }
7783
operator ()Condition::__anone9a56faf6711::CanAddStarlaneConnectionSimpleMatch7784 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
7785 if (!candidate)
7786 return false;
7787
7788 // get system from candidate
7789 auto candidate_sys = std::dynamic_pointer_cast<const System>(candidate);
7790 if (!candidate_sys)
7791 candidate_sys = m_objects.get<System>(candidate->SystemID());
7792 if (!candidate_sys)
7793 return false;
7794
7795
7796 // check if candidate is one of the destination systems
7797 for (auto& destination : m_destination_systems) {
7798 if (candidate_sys->ID() == destination->ID())
7799 return false;
7800 }
7801
7802
7803 // check if candidate already has a lane to any of the destination systems
7804 for (auto& destination : m_destination_systems) {
7805 if (candidate_sys->HasStarlaneTo(destination->ID()))
7806 return false;
7807 }
7808
7809 // check if any of the proposed lanes are too close to any already-
7810 // present lanes of the candidate system
7811 //TraceLogger() << "... Checking lanes of candidate system: " << candidate->UniverseObject::Name() << std::endl;
7812 for (const auto& lane : candidate_sys->StarlanesWormholes()) {
7813 auto candidate_existing_lane_end_sys = m_objects.get<System>(lane.first);
7814 if (!candidate_existing_lane_end_sys)
7815 continue;
7816
7817 // check this existing lane against potential lanes to all destination systems
7818 for (auto& dest_sys : m_destination_systems) {
7819 if (LanesAngularlyTooClose(candidate_sys, candidate_existing_lane_end_sys, dest_sys)) {
7820 //TraceLogger() << " ... ... can't add lane from candidate: " << candidate_sys->UniverseObject::Name() << " to " << dest_sys->UniverseObject::Name() << " due to existing lane to " << candidate_existing_lane_end_sys->UniverseObject::Name() << std::endl;
7821 return false;
7822 }
7823 }
7824 }
7825
7826
7827 // check if any of the proposed lanes are too close to any already-
7828 // present lanes of any of the destination systems
7829 //TraceLogger() << "... Checking lanes of destination systems:" << std::endl;
7830 for (auto& dest_sys : m_destination_systems) {
7831 // check this destination system's existing lanes against a lane
7832 // to the candidate system
7833 for (const auto& dest_lane : dest_sys->StarlanesWormholes()) {
7834 auto dest_lane_end_sys = m_objects.get<System>(dest_lane.first);
7835 if (!dest_lane_end_sys)
7836 continue;
7837
7838 if (LanesAngularlyTooClose(dest_sys, candidate_sys, dest_lane_end_sys)) {
7839 //TraceLogger() << " ... ... can't add lane from candidate: " << candidate_sys->UniverseObject::Name() << " to " << dest_sys->UniverseObject::Name() << " due to existing lane from dest to " << dest_lane_end_sys->UniverseObject::Name() << std::endl;
7840 return false;
7841 }
7842 }
7843 }
7844
7845
7846 // check if any of the proposed lanes are too close to eachother
7847 //TraceLogger() << "... Checking proposed lanes against eachother" << std::endl;
7848 for (auto it1 = m_destination_systems.begin();
7849 it1 != m_destination_systems.end(); ++it1)
7850 {
7851 auto dest_sys1 = *it1;
7852
7853 // don't need to check a lane in both directions, so start at one past it1
7854 auto it2 = it1;
7855 ++it2;
7856 for (; it2 != m_destination_systems.end(); ++it2) {
7857 auto dest_sys2 = *it2;
7858 if (LanesAngularlyTooClose(candidate_sys, dest_sys1, dest_sys2)) {
7859 //TraceLogger() << " ... ... can't add lane from candidate: " << candidate_sys->UniverseObject::Name() << " to " << dest_sys1->UniverseObject::Name() << " and also to " << dest_sys2->UniverseObject::Name() << std::endl;
7860 return false;
7861 }
7862 }
7863 }
7864
7865
7866 // check that the proposed lanes are not too close to any existing
7867 // system they are not connected to
7868 //TraceLogger() << "... Checking proposed lanes for proximity to other systems" <<std::endl;
7869 for (auto& dest_sys : m_destination_systems) {
7870 if (LaneTooCloseToOtherSystem(candidate_sys, dest_sys, m_objects)) {
7871 //TraceLogger() << " ... ... can't add lane from candidate: " << candidate_sys->Name() << " to " << dest_sys->Name() << " due to proximity to another system." << std::endl;
7872 return false;
7873 }
7874 }
7875
7876
7877 // check that there are no lanes already existing that cross the proposed lanes
7878 //TraceLogger() << "... Checking for potential lanes crossing existing lanes" << std::endl;
7879 for (auto& dest_sys : m_destination_systems) {
7880 if (LaneCrossesExistingLane(candidate_sys, dest_sys, m_objects)) {
7881 //TraceLogger() << " ... ... can't add lane from candidate: " << candidate_sys->Name() << " to " << dest_sys->Name() << " due to crossing an existing lane." << std::endl;
7882 return false;
7883 }
7884 }
7885
7886 return true;
7887 }
7888
7889 std::vector<std::shared_ptr<const System>> m_destination_systems;
7890 const ObjectMap& m_objects;
7891 };
7892 }
7893
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const7894 void CanAddStarlaneConnection::Eval(const ScriptingContext& parent_context,
7895 ObjectSet& matches, ObjectSet& non_matches,
7896 SearchDomain search_domain/* = NON_MATCHES*/) const
7897 {
7898 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
7899 if (simple_eval_safe) {
7900 // evaluate contained objects once and check for all candidates
7901
7902 // get subcondition matches
7903 ObjectSet subcondition_matches;
7904 m_condition->Eval(parent_context, subcondition_matches);
7905
7906 EvalImpl(matches, non_matches, search_domain, CanAddStarlaneConnectionSimpleMatch(subcondition_matches, parent_context.ContextObjects()));
7907 } else {
7908 // re-evaluate contained objects for each candidate object
7909 Condition::Eval(parent_context, matches, non_matches, search_domain);
7910 }
7911 }
7912
Description(bool negated) const7913 std::string CanAddStarlaneConnection::Description(bool negated/* = false*/) const {
7914 return str(FlexibleFormat((!negated)
7915 ? UserString("DESC_CAN_ADD_STARLANE_CONNECTION") : UserString("DESC_CAN_ADD_STARLANE_CONNECTION_NOT"))
7916 % m_condition->Description());
7917 }
7918
Dump(unsigned short ntabs) const7919 std::string CanAddStarlaneConnection::Dump(unsigned short ntabs) const {
7920 std::string retval = DumpIndent(ntabs) + "CanAddStarlanesTo condition =\n";
7921 retval += m_condition->Dump(ntabs+1);
7922 return retval;
7923 }
7924
Match(const ScriptingContext & local_context) const7925 bool CanAddStarlaneConnection::Match(const ScriptingContext& local_context) const {
7926 auto candidate = local_context.condition_local_candidate;
7927 if (!candidate) {
7928 ErrorLogger() << "CanAddStarlaneConnection::Match passed no candidate object";
7929 return false;
7930 }
7931
7932 // get subcondition matches
7933 ObjectSet subcondition_matches;
7934 m_condition->Eval(local_context, subcondition_matches);
7935
7936 return CanAddStarlaneConnectionSimpleMatch(subcondition_matches, local_context.ContextObjects())(candidate);
7937 }
7938
SetTopLevelContent(const std::string & content_name)7939 void CanAddStarlaneConnection::SetTopLevelContent(const std::string& content_name) {
7940 if (m_condition)
7941 m_condition->SetTopLevelContent(content_name);
7942 }
7943
GetCheckSum() const7944 unsigned int CanAddStarlaneConnection::GetCheckSum() const {
7945 unsigned int retval{0};
7946
7947 CheckSums::CheckSumCombine(retval, "Condition::CanAddStarlaneConnection");
7948 CheckSums::CheckSumCombine(retval, m_condition);
7949
7950 TraceLogger() << "GetCheckSum(CanAddStarlaneConnection): retval: " << retval;
7951 return retval;
7952 }
7953
7954 ///////////////////////////////////////////////////////////
7955 // ExploredByEmpire //
7956 ///////////////////////////////////////////////////////////
ExploredByEmpire(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)7957 ExploredByEmpire::ExploredByEmpire(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
7958 Condition(),
7959 m_empire_id(std::move(empire_id))
7960 {
7961 m_root_candidate_invariant = m_empire_id->RootCandidateInvariant();
7962 m_target_invariant = m_empire_id->TargetInvariant();
7963 m_source_invariant = m_empire_id->SourceInvariant();
7964 }
7965
operator ==(const Condition & rhs) const7966 bool ExploredByEmpire::operator==(const Condition& rhs) const {
7967 if (this == &rhs)
7968 return true;
7969 if (typeid(*this) != typeid(rhs))
7970 return false;
7971
7972 const ExploredByEmpire& rhs_ = static_cast<const ExploredByEmpire&>(rhs);
7973
7974 CHECK_COND_VREF_MEMBER(m_empire_id)
7975
7976 return true;
7977 }
7978
7979 namespace {
7980 struct ExploredByEmpireSimpleMatch {
ExploredByEmpireSimpleMatchCondition::__anone9a56faf6811::ExploredByEmpireSimpleMatch7981 ExploredByEmpireSimpleMatch(int empire_id) :
7982 m_empire_id(empire_id)
7983 {}
7984
operator ()Condition::__anone9a56faf6811::ExploredByEmpireSimpleMatch7985 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
7986 if (!candidate)
7987 return false;
7988
7989 const Empire* empire = GetEmpire(m_empire_id);
7990 if (!empire)
7991 return false;
7992 return empire->HasExploredSystem(candidate->ID());
7993 }
7994
7995 int m_empire_id;
7996 };
7997 }
7998
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const7999 void ExploredByEmpire::Eval(const ScriptingContext& parent_context,
8000 ObjectSet& matches, ObjectSet& non_matches,
8001 SearchDomain search_domain/* = NON_MATCHES*/) const
8002 {
8003 bool simple_eval_safe = m_empire_id->ConstantExpr() ||
8004 (m_empire_id->LocalCandidateInvariant() &&
8005 (parent_context.condition_root_candidate || RootCandidateInvariant()));
8006 if (simple_eval_safe) {
8007 // evaluate empire id once, and use to check all candidate objects
8008 int empire_id = m_empire_id->Eval(parent_context);
8009
8010 // need to check each candidate separately to test if it has been explored
8011 EvalImpl(matches, non_matches, search_domain, ExploredByEmpireSimpleMatch(empire_id));
8012 } else {
8013 // re-evaluate empire id for each candidate object
8014 Condition::Eval(parent_context, matches, non_matches, search_domain);
8015 }
8016 }
8017
Description(bool negated) const8018 std::string ExploredByEmpire::Description(bool negated/* = false*/) const {
8019 std::string empire_str;
8020 if (m_empire_id) {
8021 int empire_id = ALL_EMPIRES;
8022 if (m_empire_id->ConstantExpr())
8023 empire_id = m_empire_id->Eval();
8024 if (const Empire* empire = GetEmpire(empire_id))
8025 empire_str = empire->Name();
8026 else
8027 empire_str = m_empire_id->Description();
8028 }
8029
8030 return str(FlexibleFormat((!negated)
8031 ? UserString("DESC_EXPLORED_BY_EMPIRE")
8032 : UserString("DESC_EXPLORED_BY_EMPIRE_NOT"))
8033 % empire_str);
8034 }
8035
Dump(unsigned short ntabs) const8036 std::string ExploredByEmpire::Dump(unsigned short ntabs) const
8037 { return DumpIndent(ntabs) + "ExploredByEmpire empire_id = " + m_empire_id->Dump(ntabs); }
8038
Match(const ScriptingContext & local_context) const8039 bool ExploredByEmpire::Match(const ScriptingContext& local_context) const {
8040 auto candidate = local_context.condition_local_candidate;
8041 if (!candidate) {
8042 ErrorLogger() << "ExploredByEmpire::Match passed no candidate object";
8043 return false;
8044 }
8045
8046 return ExploredByEmpireSimpleMatch(m_empire_id->Eval(local_context))(candidate);
8047 }
8048
SetTopLevelContent(const std::string & content_name)8049 void ExploredByEmpire::SetTopLevelContent(const std::string& content_name) {
8050 if (m_empire_id)
8051 m_empire_id->SetTopLevelContent(content_name);
8052 }
8053
GetCheckSum() const8054 unsigned int ExploredByEmpire::GetCheckSum() const {
8055 unsigned int retval{0};
8056
8057 CheckSums::CheckSumCombine(retval, "Condition::ExploredByEmpire");
8058 CheckSums::CheckSumCombine(retval, m_empire_id);
8059
8060 TraceLogger() << "GetCheckSum(ExploredByEmpire): retval: " << retval;
8061 return retval;
8062 }
8063
8064 ///////////////////////////////////////////////////////////
8065 // Stationary //
8066 ///////////////////////////////////////////////////////////
Stationary()8067 Stationary::Stationary() :
8068 Condition()
8069 {
8070 m_root_candidate_invariant = true;
8071 m_target_invariant = true;
8072 m_source_invariant = true;
8073 }
8074
operator ==(const Condition & rhs) const8075 bool Stationary::operator==(const Condition& rhs) const
8076 { return Condition::operator==(rhs); }
8077
Description(bool negated) const8078 std::string Stationary::Description(bool negated/* = false*/) const {
8079 return (!negated)
8080 ? UserString("DESC_STATIONARY")
8081 : UserString("DESC_STATIONARY_NOT");
8082 }
8083
Dump(unsigned short ntabs) const8084 std::string Stationary::Dump(unsigned short ntabs) const
8085 { return DumpIndent(ntabs) + "Stationary\n"; }
8086
Match(const ScriptingContext & local_context) const8087 bool Stationary::Match(const ScriptingContext& local_context) const {
8088 auto candidate = local_context.condition_local_candidate;
8089 if (!candidate) {
8090 ErrorLogger() << "Stationary::Match passed no candidate object";
8091 return false;
8092 }
8093
8094 // the only objects that can move are fleets and the ships in them. so,
8095 // attempt to cast the candidate object to a fleet or ship, and if it's a ship
8096 // get the fleet of that ship
8097 auto fleet = std::dynamic_pointer_cast<const Fleet>(candidate);
8098 if (!fleet)
8099 if (auto ship = std::dynamic_pointer_cast<const Ship>(candidate))
8100 fleet = local_context.ContextObjects().get<Fleet>(ship->FleetID());
8101
8102 if (fleet) {
8103 // if a fleet is available, it is "moving", or not stationary, if it's
8104 // next system is a system and isn't the current system. This will
8105 // mean fleets that have arrived at a system on the current turn will
8106 // be stationary, but fleets departing won't be stationary.
8107 int next_id = fleet->NextSystemID();
8108 int cur_id = fleet->SystemID();
8109 if (next_id != INVALID_OBJECT_ID && next_id != cur_id)
8110 return false;
8111 }
8112
8113 return true;
8114 }
8115
GetCheckSum() const8116 unsigned int Stationary::GetCheckSum() const {
8117 unsigned int retval{0};
8118
8119 CheckSums::CheckSumCombine(retval, "Condition::Stationary");
8120
8121 TraceLogger() << "GetCheckSum(Stationary): retval: " << retval;
8122 return retval;
8123 }
8124
8125 ///////////////////////////////////////////////////////////
8126 // Aggressive //
8127 ///////////////////////////////////////////////////////////
Aggressive()8128 Aggressive::Aggressive() :
8129 Aggressive(true)
8130 {}
8131
Aggressive(bool aggressive)8132 Aggressive::Aggressive(bool aggressive) :
8133 Condition(),
8134 m_aggressive(aggressive)
8135 {
8136 m_root_candidate_invariant = true;
8137 m_target_invariant = true;
8138 m_source_invariant = true;
8139 }
8140
operator ==(const Condition & rhs) const8141 bool Aggressive::operator==(const Condition& rhs) const
8142 { return Condition::operator==(rhs); }
8143
Description(bool negated) const8144 std::string Aggressive::Description(bool negated/* = false*/) const {
8145 if (m_aggressive)
8146 return (!negated)
8147 ? UserString("DESC_AGGRESSIVE")
8148 : UserString("DESC_AGGRESSIVE_NOT");
8149 else
8150 return (!negated)
8151 ? UserString("DESC_PASSIVE")
8152 : UserString("DESC_PASSIVE_NOT");
8153 }
8154
Dump(unsigned short ntabs) const8155 std::string Aggressive::Dump(unsigned short ntabs) const
8156 { return DumpIndent(ntabs) + (m_aggressive ? "Aggressive\n" : "Passive\n"); }
8157
Match(const ScriptingContext & local_context) const8158 bool Aggressive::Match(const ScriptingContext& local_context) const {
8159 auto candidate = local_context.condition_local_candidate;
8160 if (!candidate) {
8161 ErrorLogger() << "Aggressive::Match passed no candidate object";
8162 return false;
8163 }
8164
8165 // the only objects that can be aggressive are fleets and the ships in them.
8166 // so, attempt to cast the candidate object to a fleet or ship, and if it's
8167 // a ship get the fleet of that ship
8168 auto fleet = std::dynamic_pointer_cast<const Fleet>(candidate);
8169 if (!fleet)
8170 if (auto ship = std::dynamic_pointer_cast<const Ship>(candidate))
8171 fleet = local_context.ContextObjects().get<Fleet>(ship->FleetID());
8172
8173 if (!fleet)
8174 return false;
8175
8176 return m_aggressive == fleet->Aggressive();
8177 }
8178
GetCheckSum() const8179 unsigned int Aggressive::GetCheckSum() const {
8180 unsigned int retval{0};
8181
8182 CheckSums::CheckSumCombine(retval, "Condition::Aggressive");
8183 CheckSums::CheckSumCombine(retval, m_aggressive);
8184
8185 TraceLogger() << "GetCheckSum(Aggressive): retval: " << retval;
8186 return retval;
8187 }
8188
8189 ///////////////////////////////////////////////////////////
8190 // FleetSupplyableByEmpire //
8191 ///////////////////////////////////////////////////////////
FleetSupplyableByEmpire(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)8192 FleetSupplyableByEmpire::FleetSupplyableByEmpire(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
8193 Condition(),
8194 m_empire_id(std::move(empire_id))
8195 {
8196 m_root_candidate_invariant = m_empire_id->RootCandidateInvariant();
8197 m_target_invariant = m_empire_id->TargetInvariant();
8198 m_source_invariant = m_empire_id->SourceInvariant();
8199 }
8200
operator ==(const Condition & rhs) const8201 bool FleetSupplyableByEmpire::operator==(const Condition& rhs) const {
8202 if (this == &rhs)
8203 return true;
8204 if (typeid(*this) != typeid(rhs))
8205 return false;
8206
8207 const FleetSupplyableByEmpire& rhs_ = static_cast<const FleetSupplyableByEmpire&>(rhs);
8208
8209 CHECK_COND_VREF_MEMBER(m_empire_id)
8210
8211 return true;
8212 }
8213
8214 namespace {
8215 struct FleetSupplyableSimpleMatch {
FleetSupplyableSimpleMatchCondition::__anone9a56faf6911::FleetSupplyableSimpleMatch8216 FleetSupplyableSimpleMatch(int empire_id) :
8217 m_empire_id(empire_id)
8218 {}
8219
operator ()Condition::__anone9a56faf6911::FleetSupplyableSimpleMatch8220 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
8221 if (!candidate)
8222 return false;
8223
8224 const Empire* empire = GetEmpire(m_empire_id);
8225 if (!empire)
8226 return false;
8227
8228 const SupplyManager& supply = GetSupplyManager();
8229 const auto& empire_supplyable_systems = supply.FleetSupplyableSystemIDs();
8230 auto it = empire_supplyable_systems.find(m_empire_id);
8231 if (it == empire_supplyable_systems.end())
8232 return false;
8233 return it->second.count(candidate->SystemID());
8234 }
8235
8236 int m_empire_id;
8237 };
8238 }
8239
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const8240 void FleetSupplyableByEmpire::Eval(const ScriptingContext& parent_context,
8241 ObjectSet& matches, ObjectSet& non_matches,
8242 SearchDomain search_domain/* = NON_MATCHES*/) const
8243 {
8244 bool simple_eval_safe = m_empire_id->ConstantExpr() ||
8245 (m_empire_id->LocalCandidateInvariant() &&
8246 (parent_context.condition_root_candidate || RootCandidateInvariant()));
8247 if (simple_eval_safe) {
8248 // evaluate empire id once, and use to check all candidate objects
8249 int empire_id = m_empire_id->Eval(parent_context);
8250 EvalImpl(matches, non_matches, search_domain, FleetSupplyableSimpleMatch(empire_id));
8251 } else {
8252 // re-evaluate empire id for each candidate object
8253 Condition::Eval(parent_context, matches, non_matches, search_domain);
8254 }
8255 }
8256
Description(bool negated) const8257 std::string FleetSupplyableByEmpire::Description(bool negated/* = false*/) const {
8258 std::string empire_str;
8259 if (m_empire_id) {
8260 int empire_id = ALL_EMPIRES;
8261 if (m_empire_id->ConstantExpr())
8262 empire_id = m_empire_id->Eval();
8263 if (const Empire* empire = GetEmpire(empire_id))
8264 empire_str = empire->Name();
8265 else
8266 empire_str = m_empire_id->Description();
8267 }
8268
8269 return str(FlexibleFormat((!negated)
8270 ? UserString("DESC_SUPPLY_CONNECTED_FLEET")
8271 : UserString("DESC_SUPPLY_CONNECTED_FLEET_NOT"))
8272 % empire_str);
8273 }
8274
Dump(unsigned short ntabs) const8275 std::string FleetSupplyableByEmpire::Dump(unsigned short ntabs) const
8276 { return DumpIndent(ntabs) + "ResupplyableBy empire_id = " + m_empire_id->Dump(ntabs); }
8277
Match(const ScriptingContext & local_context) const8278 bool FleetSupplyableByEmpire::Match(const ScriptingContext& local_context) const {
8279 auto candidate = local_context.condition_local_candidate;
8280 if (!candidate) {
8281 ErrorLogger() << "FleetSupplyableByEmpire::Match passed no candidate object";
8282 return false;
8283 }
8284
8285 int empire_id = m_empire_id->Eval(local_context);
8286
8287 return FleetSupplyableSimpleMatch(empire_id)(candidate);
8288 }
8289
SetTopLevelContent(const std::string & content_name)8290 void FleetSupplyableByEmpire::SetTopLevelContent(const std::string& content_name) {
8291 if (m_empire_id)
8292 m_empire_id->SetTopLevelContent(content_name);
8293 }
8294
GetCheckSum() const8295 unsigned int FleetSupplyableByEmpire::GetCheckSum() const {
8296 unsigned int retval{0};
8297
8298 CheckSums::CheckSumCombine(retval, "Condition::FleetSupplyableByEmpire");
8299 CheckSums::CheckSumCombine(retval, m_empire_id);
8300
8301 TraceLogger() << "GetCheckSum(FleetSupplyableByEmpire): retval: " << retval;
8302 return retval;
8303 }
8304
8305 ///////////////////////////////////////////////////////////
8306 // ResourceSupplyConnectedByEmpire //
8307 ///////////////////////////////////////////////////////////
ResourceSupplyConnectedByEmpire(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<Condition> && condition)8308 ResourceSupplyConnectedByEmpire::ResourceSupplyConnectedByEmpire(
8309 std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
8310 std::unique_ptr<Condition>&& condition) :
8311 Condition(),
8312 m_empire_id(std::move(empire_id)),
8313 m_condition(std::move(condition))
8314 {
8315 m_root_candidate_invariant = m_empire_id->RootCandidateInvariant() && m_condition->RootCandidateInvariant();
8316 m_target_invariant = m_empire_id->TargetInvariant() && m_condition->TargetInvariant();
8317 m_source_invariant = m_empire_id->SourceInvariant() && m_condition->SourceInvariant();
8318 }
8319
operator ==(const Condition & rhs) const8320 bool ResourceSupplyConnectedByEmpire::operator==(const Condition& rhs) const {
8321 if (this == &rhs)
8322 return true;
8323 if (typeid(*this) != typeid(rhs))
8324 return false;
8325
8326 const ResourceSupplyConnectedByEmpire& rhs_ = static_cast<const ResourceSupplyConnectedByEmpire&>(rhs);
8327
8328 CHECK_COND_VREF_MEMBER(m_empire_id)
8329
8330 return true;
8331 }
8332
8333 namespace {
8334 struct ResourceSupplySimpleMatch {
ResourceSupplySimpleMatchCondition::__anone9a56faf6a11::ResourceSupplySimpleMatch8335 ResourceSupplySimpleMatch(int empire_id, const ObjectSet& from_objects, const ObjectMap& objects) :
8336 m_empire_id(empire_id),
8337 m_from_objects(from_objects),
8338 m_objects(objects)
8339 {}
8340
operator ()Condition::__anone9a56faf6a11::ResourceSupplySimpleMatch8341 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
8342 if (!candidate)
8343 return false;
8344 if (m_from_objects.empty())
8345 return false;
8346 const auto& groups = GetSupplyManager().ResourceSupplyGroups(m_empire_id);
8347 if (groups.empty())
8348 return false;
8349
8350 // is candidate object connected to a subcondition matching object by resource supply?
8351 // first check if candidate object is (or is a building on) a blockaded planet
8352 // "isolated" objects are anything not in a non-blockaded system
8353 bool is_isolated = true;
8354 for (const auto& group : groups) {
8355 if (group.count(candidate->SystemID())) {
8356 is_isolated = false;
8357 break;
8358 }
8359 }
8360 if (is_isolated) {
8361 // planets are still supply-connected to themselves even if blockaded
8362 auto candidate_planet = std::dynamic_pointer_cast<const Planet>(candidate);
8363 std::shared_ptr<const ::Building> building;
8364 if (!candidate_planet && (building = std::dynamic_pointer_cast<const ::Building>(candidate)))
8365 candidate_planet = m_objects.get<Planet>(building->PlanetID());
8366 if (candidate_planet) {
8367 int candidate_planet_id = candidate_planet->ID();
8368 // can only match if the from_object is (or is on) the same planet
8369 for (auto& from_object : m_from_objects) {
8370 auto from_obj_planet = std::dynamic_pointer_cast<const Planet>(from_object);
8371 std::shared_ptr<const ::Building> from_building;
8372 if (!from_obj_planet && (from_building = std::dynamic_pointer_cast<const ::Building>(from_object)))
8373 from_obj_planet = m_objects.get<Planet>(from_building->PlanetID());
8374 if (from_obj_planet && from_obj_planet->ID() == candidate_planet_id)
8375 return true;
8376 }
8377 }
8378 // candidate is isolated, but did not match planet for any test object
8379 return false;
8380 }
8381 // candidate is not blockaded, so check for system group matches
8382 for (auto& from_object : m_from_objects) {
8383 for (const auto& group : groups) {
8384 if (group.count(from_object->SystemID())) {
8385 // found resource sharing group containing test object system. Does it also contain candidate?
8386 if (group.count(candidate->SystemID()))
8387 return true; // test object and candidate object are in same resourse sharing group
8388 else
8389 // test object is not in resource sharing group with candidate
8390 // as each object can be in only one group, no need to check the remaining groups
8391 break;
8392 }
8393 // current subcondition-matching object is not in this resource sharing group
8394 }
8395 // current subcondition-matching object is not in any resource sharing group for this empire
8396 }
8397
8398 return false;
8399 }
8400
8401 int m_empire_id;
8402 const ObjectSet& m_from_objects;
8403 const ObjectMap& m_objects;
8404 };
8405 }
8406
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const8407 void ResourceSupplyConnectedByEmpire::Eval(const ScriptingContext& parent_context,
8408 ObjectSet& matches, ObjectSet& non_matches,
8409 SearchDomain search_domain/* = NON_MATCHES*/) const
8410 {
8411 bool simple_eval_safe = m_empire_id->ConstantExpr() ||
8412 (m_empire_id->LocalCandidateInvariant() &&
8413 (parent_context.condition_root_candidate || RootCandidateInvariant()));
8414 if (simple_eval_safe) {
8415 // evaluate contained objects once and check for all candidates
8416
8417 // get objects to be considering for matching against subcondition
8418 ObjectSet subcondition_matches;
8419 m_condition->Eval(parent_context, subcondition_matches);
8420 int empire_id = m_empire_id->Eval(parent_context);
8421
8422 EvalImpl(matches, non_matches, search_domain, ResourceSupplySimpleMatch(empire_id, subcondition_matches, parent_context.ContextObjects()));
8423 } else {
8424 // re-evaluate empire id for each candidate object
8425 Condition::Eval(parent_context, matches, non_matches, search_domain);
8426 }
8427 }
8428
Match(const ScriptingContext & local_context) const8429 bool ResourceSupplyConnectedByEmpire::Match(const ScriptingContext& local_context) const {
8430 auto candidate = local_context.condition_local_candidate;
8431 if (!candidate) {
8432 ErrorLogger() << "ResourceSupplyConnectedByEmpire::Match passed no candidate object";
8433 return false;
8434 }
8435
8436 // get subcondition matches
8437 ObjectSet subcondition_matches;
8438 m_condition->Eval(local_context, subcondition_matches);
8439 int empire_id = m_empire_id->Eval(local_context);
8440
8441 return ResourceSupplySimpleMatch(empire_id, subcondition_matches, local_context.ContextObjects())(candidate);
8442 }
8443
Description(bool negated) const8444 std::string ResourceSupplyConnectedByEmpire::Description(bool negated/* = false*/) const {
8445 std::string empire_str;
8446 if (m_empire_id) {
8447 int empire_id = ALL_EMPIRES;
8448 if (m_empire_id->ConstantExpr())
8449 empire_id = m_empire_id->Eval();
8450 if (const Empire* empire = GetEmpire(empire_id))
8451 empire_str = empire->Name();
8452 else
8453 empire_str = m_empire_id->Description();
8454 }
8455
8456 return str(FlexibleFormat((!negated)
8457 ? UserString("DESC_SUPPLY_CONNECTED_RESOURCE")
8458 : UserString("DESC_SUPPLY_CONNECTED_RESOURCE_NOT"))
8459 % empire_str
8460 % m_condition->Description());
8461 }
8462
Dump(unsigned short ntabs) const8463 std::string ResourceSupplyConnectedByEmpire::Dump(unsigned short ntabs) const {
8464 std::string retval = DumpIndent(ntabs) + "ResourceSupplyConnectedBy empire_id = "
8465 + m_empire_id->Dump(ntabs) + " condition = \n";
8466 retval += m_condition->Dump(ntabs+1);
8467 return retval;
8468 }
8469
SetTopLevelContent(const std::string & content_name)8470 void ResourceSupplyConnectedByEmpire::SetTopLevelContent(const std::string& content_name) {
8471 if (m_empire_id)
8472 m_empire_id->SetTopLevelContent(content_name);
8473 if (m_condition)
8474 m_condition->SetTopLevelContent(content_name);
8475 }
8476
GetCheckSum() const8477 unsigned int ResourceSupplyConnectedByEmpire::GetCheckSum() const {
8478 unsigned int retval{0};
8479
8480 CheckSums::CheckSumCombine(retval, "Condition::ResourceSupplyConnectedByEmpire");
8481 CheckSums::CheckSumCombine(retval, m_empire_id);
8482 CheckSums::CheckSumCombine(retval, m_condition);
8483
8484 TraceLogger() << "GetCheckSum(ResourceSupplyConnectedByEmpire): retval: " << retval;
8485 return retval;
8486 }
8487
8488 ///////////////////////////////////////////////////////////
8489 // CanColonize //
8490 ///////////////////////////////////////////////////////////
CanColonize()8491 CanColonize::CanColonize() :
8492 Condition()
8493 {
8494 m_root_candidate_invariant = true;
8495 m_target_invariant = true;
8496 m_source_invariant = true;
8497 }
8498
operator ==(const Condition & rhs) const8499 bool CanColonize::operator==(const Condition& rhs) const
8500 { return Condition::operator==(rhs); }
8501
Description(bool negated) const8502 std::string CanColonize::Description(bool negated/* = false*/) const {
8503 return str(FlexibleFormat((!negated)
8504 ? UserString("DESC_CAN_COLONIZE")
8505 : UserString("DESC_CAN_COLONIZE_NOT")));
8506 }
8507
Dump(unsigned short ntabs) const8508 std::string CanColonize::Dump(unsigned short ntabs) const
8509 { return DumpIndent(ntabs) + "CanColonize\n"; }
8510
Match(const ScriptingContext & local_context) const8511 bool CanColonize::Match(const ScriptingContext& local_context) const {
8512 auto candidate = local_context.condition_local_candidate;
8513 if (!candidate) {
8514 ErrorLogger() << "CanColonize::Match passed no candidate object";
8515 return false;
8516 }
8517
8518 // is it a ship, a planet, or a building on a planet?
8519 std::string species_name;
8520 if (candidate->ObjectType() == OBJ_PLANET) {
8521 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
8522 if (!planet) {
8523 ErrorLogger() << "CanColonize couldn't cast supposedly planet candidate";
8524 return false;
8525 }
8526 species_name = planet->SpeciesName();
8527
8528 } else if (candidate->ObjectType() == OBJ_BUILDING) {
8529 auto building = std::dynamic_pointer_cast<const ::Building>(candidate);
8530 if (!building) {
8531 ErrorLogger() << "CanColonize couldn't cast supposedly building candidate";
8532 return false;
8533 }
8534 auto planet = local_context.ContextObjects().get<Planet>(building->PlanetID());
8535 if (!planet) {
8536 ErrorLogger() << "CanColonize couldn't get building's planet";
8537 return false;
8538 }
8539 species_name = planet->SpeciesName();
8540
8541 } else if (candidate->ObjectType() == OBJ_SHIP) {
8542 auto ship = std::dynamic_pointer_cast<const Ship>(candidate);
8543 if (!ship) {
8544 ErrorLogger() << "CanColonize couldn't cast supposedly ship candidate";
8545 return false;
8546 }
8547 species_name = ship->SpeciesName();
8548 }
8549
8550 if (species_name.empty())
8551 return false;
8552 auto species = GetSpecies(species_name);
8553 if (!species) {
8554 ErrorLogger() << "CanColonize couldn't get species: " << species_name;
8555 return false;
8556 }
8557 return species->CanColonize();
8558 }
8559
GetCheckSum() const8560 unsigned int CanColonize::GetCheckSum() const {
8561 unsigned int retval{0};
8562
8563 CheckSums::CheckSumCombine(retval, "Condition::CanColonize");
8564
8565 TraceLogger() << "GetCheckSum(CanColonize): retval: " << retval;
8566 return retval;
8567 }
8568
8569 ///////////////////////////////////////////////////////////
8570 // CanProduceShips //
8571 ///////////////////////////////////////////////////////////
CanProduceShips()8572 CanProduceShips::CanProduceShips() :
8573 Condition()
8574 {
8575 m_root_candidate_invariant = true;
8576 m_target_invariant = true;
8577 m_source_invariant = true;
8578 }
8579
operator ==(const Condition & rhs) const8580 bool CanProduceShips::operator==(const Condition& rhs) const
8581 { return Condition::operator==(rhs); }
8582
Description(bool negated) const8583 std::string CanProduceShips::Description(bool negated/* = false*/) const {
8584 return str(FlexibleFormat((!negated)
8585 ? UserString("DESC_CAN_PRODUCE_SHIPS")
8586 : UserString("DESC_CAN_PRODUCE_SHIPS_NOT")));
8587 }
8588
Dump(unsigned short ntabs) const8589 std::string CanProduceShips::Dump(unsigned short ntabs) const
8590 { return DumpIndent(ntabs) + "CanColonize\n"; }
8591
Match(const ScriptingContext & local_context) const8592 bool CanProduceShips::Match(const ScriptingContext& local_context) const {
8593 auto candidate = local_context.condition_local_candidate;
8594 if (!candidate) {
8595 ErrorLogger() << "CanProduceShips::Match passed no candidate object";
8596 return false;
8597 }
8598
8599 // is it a ship, a planet, or a building on a planet?
8600 std::string species_name;
8601 if (candidate->ObjectType() == OBJ_PLANET) {
8602 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
8603 if (!planet) {
8604 ErrorLogger() << "CanProduceShips couldn't cast supposedly planet candidate";
8605 return false;
8606 }
8607 species_name = planet->SpeciesName();
8608
8609 } else if (candidate->ObjectType() == OBJ_BUILDING) {
8610 auto building = std::dynamic_pointer_cast<const ::Building>(candidate);
8611 if (!building) {
8612 ErrorLogger() << "CanProduceShips couldn't cast supposedly building candidate";
8613 return false;
8614 }
8615 auto planet = local_context.ContextObjects().get<Planet>(building->PlanetID());
8616 if (!planet) {
8617 ErrorLogger() << "CanProduceShips couldn't get building's planet";
8618 return false;
8619 }
8620 species_name = planet->SpeciesName();
8621
8622 } else if (candidate->ObjectType() == OBJ_SHIP) {
8623 auto ship = std::dynamic_pointer_cast<const Ship>(candidate);
8624 if (!ship) {
8625 ErrorLogger() << "CanProduceShips couldn't cast supposedly ship candidate";
8626 return false;
8627 }
8628 species_name = ship->SpeciesName();
8629 }
8630
8631 if (species_name.empty())
8632 return false;
8633 auto species = GetSpecies(species_name);
8634 if (!species) {
8635 ErrorLogger() << "CanProduceShips couldn't get species: " << species_name;
8636 return false;
8637 }
8638 return species->CanProduceShips();
8639 }
8640
GetCheckSum() const8641 unsigned int CanProduceShips::GetCheckSum() const {
8642 unsigned int retval{0};
8643
8644 CheckSums::CheckSumCombine(retval, "Condition::CanProduceShips");
8645
8646 TraceLogger() << "GetCheckSum(CanProduceShips): retval: " << retval;
8647 return retval;
8648 }
8649
8650 ///////////////////////////////////////////////////////////
8651 // OrderedBombarded //
8652 ///////////////////////////////////////////////////////////
OrderedBombarded(std::unique_ptr<Condition> && by_object_condition)8653 OrderedBombarded::OrderedBombarded(std::unique_ptr<Condition>&& by_object_condition) :
8654 Condition(),
8655 m_by_object_condition(std::move(by_object_condition))
8656 {
8657 m_root_candidate_invariant = m_by_object_condition->RootCandidateInvariant();
8658 m_target_invariant = m_by_object_condition->TargetInvariant();
8659 m_source_invariant = m_by_object_condition->SourceInvariant();
8660 }
8661
operator ==(const Condition & rhs) const8662 bool OrderedBombarded::operator==(const Condition& rhs) const {
8663 if (this == &rhs)
8664 return true;
8665 if (typeid(*this) != typeid(rhs))
8666 return false;
8667
8668 const OrderedBombarded& rhs_ = static_cast<const OrderedBombarded&>(rhs);
8669
8670 CHECK_COND_VREF_MEMBER(m_by_object_condition)
8671
8672 return true;
8673 }
8674
8675 namespace {
8676 struct OrderedBombardedSimpleMatch {
OrderedBombardedSimpleMatchCondition::__anone9a56faf6b11::OrderedBombardedSimpleMatch8677 OrderedBombardedSimpleMatch(const ObjectSet& by_objects) :
8678 m_by_objects(by_objects)
8679 {}
8680
operator ()Condition::__anone9a56faf6b11::OrderedBombardedSimpleMatch8681 bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
8682 if (!candidate)
8683 return false;
8684 if (m_by_objects.empty())
8685 return false;
8686 auto planet = std::dynamic_pointer_cast<const Planet>(candidate);
8687 if (!planet)
8688 return false;
8689 int planet_id = planet->ID();
8690 if (planet_id == INVALID_OBJECT_ID)
8691 return false;
8692
8693 // check if any of the by_objects is ordered to bombard the candidate planet
8694 for (auto& obj : m_by_objects) {
8695 auto ship = std::dynamic_pointer_cast<const Ship>(obj);
8696 if (!ship)
8697 continue;
8698 if (ship->OrderedBombardPlanet() == planet_id)
8699 return true;
8700 }
8701 return false;
8702 }
8703
8704 const ObjectSet& m_by_objects;
8705 };
8706 }
8707
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const8708 void OrderedBombarded::Eval(const ScriptingContext& parent_context,
8709 ObjectSet& matches, ObjectSet& non_matches,
8710 SearchDomain search_domain/* = NON_MATCHES*/) const
8711 {
8712 bool simple_eval_safe = parent_context.condition_root_candidate || RootCandidateInvariant();
8713 if (simple_eval_safe) {
8714 // evaluate contained objects once and check for all candidates
8715
8716 // get subcondition matches
8717 ObjectSet subcondition_matches;
8718 m_by_object_condition->Eval(parent_context, subcondition_matches);
8719
8720 EvalImpl(matches, non_matches, search_domain, OrderedBombardedSimpleMatch(subcondition_matches));
8721 } else {
8722 // re-evaluate contained objects for each candidate object
8723 Condition::Eval(parent_context, matches, non_matches, search_domain);
8724 }
8725 }
8726
Description(bool negated) const8727 std::string OrderedBombarded::Description(bool negated/* = false*/) const {
8728 std::string by_str;
8729 if (m_by_object_condition)
8730 by_str = m_by_object_condition->Description();
8731
8732 return str(FlexibleFormat((!negated)
8733 ? UserString("DESC_ORDERED_BOMBARDED")
8734 : UserString("DESC_ORDERED_BOMBARDED_NOT"))
8735 % by_str);
8736 }
8737
Dump(unsigned short ntabs) const8738 std::string OrderedBombarded::Dump(unsigned short ntabs) const
8739 { return DumpIndent(ntabs) + "OrderedBombarded by_object = " + m_by_object_condition->Dump(ntabs); }
8740
Match(const ScriptingContext & local_context) const8741 bool OrderedBombarded::Match(const ScriptingContext& local_context) const {
8742 auto candidate = local_context.condition_local_candidate;
8743 if (!candidate) {
8744 ErrorLogger() << "OrderedBombarded::Match passed no candidate object";
8745 return false;
8746 }
8747
8748 // get subcondition matches
8749 ObjectSet subcondition_matches;
8750 m_by_object_condition->Eval(local_context, subcondition_matches);
8751
8752 return OrderedBombardedSimpleMatch(subcondition_matches)(candidate);
8753 }
8754
SetTopLevelContent(const std::string & content_name)8755 void OrderedBombarded::SetTopLevelContent(const std::string& content_name) {
8756 if (m_by_object_condition)
8757 m_by_object_condition->SetTopLevelContent(content_name);
8758 }
8759
GetCheckSum() const8760 unsigned int OrderedBombarded::GetCheckSum() const {
8761 unsigned int retval{0};
8762
8763 CheckSums::CheckSumCombine(retval, "Condition::OrderedBombarded");
8764 CheckSums::CheckSumCombine(retval, m_by_object_condition);
8765
8766 TraceLogger() << "GetCheckSum(OrderedBombarded): retval: " << retval;
8767 return retval;
8768 }
8769
8770 ///////////////////////////////////////////////////////////
8771 // ValueTest //
8772 ///////////////////////////////////////////////////////////
8773 namespace {
Comparison(float val1,ComparisonType comp,float val2)8774 bool Comparison(float val1, ComparisonType comp, float val2) {
8775 switch (comp) {
8776 case EQUAL: return val1 == val2;
8777 case GREATER_THAN: return val1 > val2;
8778 case GREATER_THAN_OR_EQUAL: return val1 >= val2;
8779 case LESS_THAN: return val1 < val2;
8780 case LESS_THAN_OR_EQUAL: return val1 <= val2;
8781 case NOT_EQUAL: return val1 != val2;
8782 case INVALID_COMPARISON:
8783 default: return false;
8784 }
8785 }
Comparison(const std::string & val1,ComparisonType comp,const std::string & val2)8786 bool Comparison(const std::string& val1, ComparisonType comp,
8787 const std::string& val2)
8788 {
8789 switch (comp) {
8790 case EQUAL: return val1 == val2;
8791 case NOT_EQUAL: return val1 != val2;
8792 case INVALID_COMPARISON:
8793 default: return false;
8794 }
8795 }
8796
CompareTypeString(ComparisonType comp)8797 std::string CompareTypeString(ComparisonType comp) {
8798 switch (comp) {
8799 case EQUAL: return "=";
8800 case GREATER_THAN: return ">";
8801 case GREATER_THAN_OR_EQUAL: return ">=";
8802 case LESS_THAN: return "<";
8803 case LESS_THAN_OR_EQUAL: return "<=";
8804 case NOT_EQUAL: return "!=";
8805 case INVALID_COMPARISON:
8806 default: return "";
8807 }
8808 }
8809 }
8810
ValueTest(std::unique_ptr<ValueRef::ValueRef<double>> && value_ref1,ComparisonType comp1,std::unique_ptr<ValueRef::ValueRef<double>> && value_ref2,ComparisonType comp2,std::unique_ptr<ValueRef::ValueRef<double>> && value_ref3)8811 ValueTest::ValueTest(std::unique_ptr<ValueRef::ValueRef<double>>&& value_ref1,
8812 ComparisonType comp1,
8813 std::unique_ptr<ValueRef::ValueRef<double>>&& value_ref2,
8814 ComparisonType comp2,
8815 std::unique_ptr<ValueRef::ValueRef<double>>&& value_ref3) :
8816 Condition(),
8817 m_value_ref1(std::move(value_ref1)),
8818 m_value_ref2(std::move(value_ref2)),
8819 m_value_ref3(std::move(value_ref3)),
8820 m_compare_type1(comp1),
8821 m_compare_type2(comp2)
8822 {
8823 auto operands = {m_value_ref1.get(), m_value_ref2.get(), m_value_ref3.get()};
8824 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
8825 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
8826 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
8827 }
8828
ValueTest(std::unique_ptr<ValueRef::ValueRef<std::string>> && value_ref1,ComparisonType comp1,std::unique_ptr<ValueRef::ValueRef<std::string>> && value_ref2,ComparisonType comp2,std::unique_ptr<ValueRef::ValueRef<std::string>> && value_ref3)8829 ValueTest::ValueTest(std::unique_ptr<ValueRef::ValueRef<std::string>>&& value_ref1,
8830 ComparisonType comp1,
8831 std::unique_ptr<ValueRef::ValueRef<std::string>>&& value_ref2,
8832 ComparisonType comp2,
8833 std::unique_ptr<ValueRef::ValueRef<std::string>>&& value_ref3) :
8834 Condition(),
8835 m_string_value_ref1(std::move(value_ref1)),
8836 m_string_value_ref2(std::move(value_ref2)),
8837 m_string_value_ref3(std::move(value_ref3)),
8838 m_compare_type1(comp1),
8839 m_compare_type2(comp2)
8840 {
8841 auto operands = {m_string_value_ref1.get(), m_string_value_ref2.get(), m_string_value_ref3.get()};
8842 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
8843 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
8844 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
8845 }
8846
ValueTest(std::unique_ptr<ValueRef::ValueRef<int>> && value_ref1,ComparisonType comp1,std::unique_ptr<ValueRef::ValueRef<int>> && value_ref2,ComparisonType comp2,std::unique_ptr<ValueRef::ValueRef<int>> && value_ref3)8847 ValueTest::ValueTest(std::unique_ptr<ValueRef::ValueRef<int>>&& value_ref1,
8848 ComparisonType comp1,
8849 std::unique_ptr<ValueRef::ValueRef<int>>&& value_ref2,
8850 ComparisonType comp2,
8851 std::unique_ptr<ValueRef::ValueRef<int>>&& value_ref3) :
8852 Condition(),
8853 m_int_value_ref1(std::move(value_ref1)),
8854 m_int_value_ref2(std::move(value_ref2)),
8855 m_int_value_ref3(std::move(value_ref3)),
8856 m_compare_type1(comp1),
8857 m_compare_type2(comp2)
8858 {
8859 auto operands = {m_int_value_ref1.get(), m_int_value_ref2.get(), m_int_value_ref3.get()};
8860 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
8861 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
8862 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
8863 }
8864
operator ==(const Condition & rhs) const8865 bool ValueTest::operator==(const Condition& rhs) const {
8866 if (this == &rhs)
8867 return true;
8868 if (typeid(*this) != typeid(rhs))
8869 return false;
8870
8871 const ValueTest& rhs_ = static_cast<const ValueTest&>(rhs);
8872
8873 CHECK_COND_VREF_MEMBER(m_value_ref1)
8874 CHECK_COND_VREF_MEMBER(m_value_ref2)
8875 CHECK_COND_VREF_MEMBER(m_value_ref3)
8876 CHECK_COND_VREF_MEMBER(m_string_value_ref1)
8877 CHECK_COND_VREF_MEMBER(m_string_value_ref2)
8878 CHECK_COND_VREF_MEMBER(m_string_value_ref3)
8879 CHECK_COND_VREF_MEMBER(m_int_value_ref1)
8880 CHECK_COND_VREF_MEMBER(m_int_value_ref2)
8881 CHECK_COND_VREF_MEMBER(m_int_value_ref3)
8882
8883 if (m_compare_type1 != rhs_.m_compare_type1)
8884 return false;
8885 if (m_compare_type2 != rhs_.m_compare_type2)
8886 return false;
8887
8888 return true;
8889 }
8890
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const8891 void ValueTest::Eval(const ScriptingContext& parent_context,
8892 ObjectSet& matches, ObjectSet& non_matches,
8893 SearchDomain search_domain/* = NON_MATCHES*/) const
8894 {
8895 bool simple_eval_safe = ((!m_value_ref1 || m_value_ref1->LocalCandidateInvariant()) &&
8896 (!m_value_ref2 || m_value_ref2->LocalCandidateInvariant()) &&
8897 (!m_value_ref3 || m_value_ref3->LocalCandidateInvariant()) &&
8898 (!m_string_value_ref1 || m_string_value_ref1->LocalCandidateInvariant()) &&
8899 (!m_string_value_ref2 || m_string_value_ref2->LocalCandidateInvariant()) &&
8900 (!m_string_value_ref3 || m_string_value_ref3->LocalCandidateInvariant()) &&
8901 (!m_int_value_ref1 || m_int_value_ref1->LocalCandidateInvariant()) &&
8902 (!m_int_value_ref2 || m_int_value_ref2->LocalCandidateInvariant()) &&
8903 (!m_int_value_ref3 || m_int_value_ref3->LocalCandidateInvariant()) &&
8904 (parent_context.condition_root_candidate || RootCandidateInvariant()));
8905
8906 if (simple_eval_safe) {
8907 // evaluate value and range limits once, use to match all candidates
8908 bool passed = Match(parent_context);
8909
8910 // transfer objects to or from candidate set, according to whether the value comparisons were true
8911 if (search_domain == MATCHES && !passed) {
8912 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
8913 matches.clear();
8914 }
8915 if (search_domain == NON_MATCHES && passed) {
8916 matches.insert(matches.end(), non_matches.begin(), non_matches.end());
8917 non_matches.clear();
8918 }
8919
8920 } else {
8921 // re-evaluate value and ranges for each candidate object
8922 Condition::Eval(parent_context, matches, non_matches, search_domain);
8923 }
8924 }
8925
Description(bool negated) const8926 std::string ValueTest::Description(bool negated/* = false*/) const {
8927 std::string value_str1, value_str2, value_str3;
8928 if (m_value_ref1)
8929 value_str1 = m_value_ref1->Description();
8930 else if (m_string_value_ref1)
8931 value_str1 = m_string_value_ref1->Description();
8932 else if (m_int_value_ref1)
8933 value_str1 = m_int_value_ref1->Description();
8934
8935 if (m_value_ref2)
8936 value_str2 = m_value_ref2->Description();
8937 else if (m_string_value_ref2)
8938 value_str2 = m_string_value_ref2->Description();
8939 else if (m_int_value_ref2)
8940 value_str2 = m_int_value_ref2->Description();
8941
8942 if (m_value_ref3)
8943 value_str3 = m_value_ref3->Description();
8944 else if (m_string_value_ref3)
8945 value_str3 = m_string_value_ref3->Description();
8946 else if (m_int_value_ref3)
8947 value_str3 = m_int_value_ref3->Description();
8948
8949 std::string comp_str1 = CompareTypeString(m_compare_type1);
8950 std::string comp_str2 = CompareTypeString(m_compare_type2);
8951
8952 std::string composed_comparison = value_str1 + " " + comp_str1 + " " + value_str2;
8953 if (!comp_str2.empty())
8954 composed_comparison += " " + comp_str2;
8955 if (!value_str3.empty())
8956 composed_comparison += +" " + value_str3;
8957
8958 return str(FlexibleFormat((!negated)
8959 ? UserString("DESC_VALUE_TEST")
8960 : UserString("DESC_VALUE_TEST_NOT"))
8961 % composed_comparison);
8962 }
8963
Dump(unsigned short ntabs) const8964 std::string ValueTest::Dump(unsigned short ntabs) const {
8965 std::string retval = DumpIndent(ntabs) + "(";
8966 if (m_value_ref1)
8967 retval += m_value_ref1->Dump(ntabs);
8968 else if (m_string_value_ref1)
8969 retval += m_string_value_ref1->Dump(ntabs);
8970 else if (m_int_value_ref1)
8971 retval += m_int_value_ref1->Dump(ntabs);
8972
8973 if (m_compare_type1 != INVALID_COMPARISON)
8974 retval += " " + CompareTypeString(m_compare_type1);
8975
8976 if (m_value_ref2)
8977 retval += " " + m_value_ref2->Dump(ntabs);
8978 else if (m_string_value_ref2)
8979 retval += " " + m_string_value_ref2->Dump(ntabs);
8980 else if (m_int_value_ref2)
8981 retval += " " + m_int_value_ref2->Dump(ntabs);
8982
8983 if (m_compare_type2 != INVALID_COMPARISON)
8984 retval += " " + CompareTypeString(m_compare_type2);
8985
8986 if (m_value_ref3)
8987 retval += " " + m_value_ref3->Dump(ntabs);
8988 else if (m_string_value_ref3)
8989 retval += " " + m_string_value_ref3->Dump(ntabs);
8990 else if (m_int_value_ref3)
8991 retval += " " + m_int_value_ref3->Dump(ntabs);
8992
8993 retval += ")\n";
8994 return retval;
8995 }
8996
Match(const ScriptingContext & local_context) const8997 bool ValueTest::Match(const ScriptingContext& local_context) const {
8998 if (m_compare_type1 == INVALID_COMPARISON)
8999 return false;
9000
9001 // simple evaluation should have only local-candidate-invariation sub-value-refs
9002 // base class evalulation should have defined local candidate
9003
9004 if (m_value_ref1) {
9005 if (!m_value_ref2)
9006 return false;
9007
9008 float val1 = m_value_ref1->Eval(local_context);
9009 float val2 = m_value_ref2->Eval(local_context);
9010 if (!Comparison(val1, m_compare_type1, val2))
9011 return false;
9012
9013 if (m_compare_type2 == INVALID_COMPARISON || !m_value_ref3)
9014 return true;
9015
9016 float val3 = m_value_ref3->Eval(local_context);
9017 return Comparison(val2, m_compare_type1, val3);
9018
9019 } else if (m_string_value_ref1) {
9020 if (!m_string_value_ref2)
9021 return false;
9022
9023 std::string val1 = m_string_value_ref1->Eval(local_context);
9024 std::string val2 = m_string_value_ref2->Eval(local_context);
9025 if (!Comparison(val1, m_compare_type1, val2))
9026 return false;
9027
9028 if (m_compare_type2 == INVALID_COMPARISON || !m_value_ref3)
9029 return true;
9030
9031 std::string val3 = m_string_value_ref3->Eval(local_context);
9032 return Comparison(val2, m_compare_type1, val3);
9033
9034 } else if (m_int_value_ref1) {
9035 if (!m_int_value_ref2)
9036 return false;
9037
9038 int val1 = m_int_value_ref1->Eval(local_context);
9039 int val2 = m_int_value_ref2->Eval(local_context);
9040 if (!Comparison(val1, m_compare_type1, val2))
9041 return false;
9042
9043 if (m_compare_type2 == INVALID_COMPARISON || !m_value_ref3)
9044 return true;
9045
9046 int val3 = m_int_value_ref3->Eval(local_context);
9047 return Comparison(val2, m_compare_type1, val3);
9048 }
9049
9050 return false;
9051 }
9052
SetTopLevelContent(const std::string & content_name)9053 void ValueTest::SetTopLevelContent(const std::string& content_name) {
9054 if (m_value_ref1)
9055 m_value_ref1->SetTopLevelContent(content_name);
9056 if (m_value_ref2)
9057 m_value_ref2->SetTopLevelContent(content_name);
9058 if (m_value_ref3)
9059 m_value_ref3->SetTopLevelContent(content_name);
9060 if (m_string_value_ref1)
9061 m_string_value_ref1->SetTopLevelContent(content_name);
9062 if (m_string_value_ref2)
9063 m_string_value_ref2->SetTopLevelContent(content_name);
9064 if (m_string_value_ref3)
9065 m_string_value_ref3->SetTopLevelContent(content_name);
9066 if (m_int_value_ref1)
9067 m_int_value_ref1->SetTopLevelContent(content_name);
9068 if (m_int_value_ref2)
9069 m_int_value_ref2->SetTopLevelContent(content_name);
9070 if (m_int_value_ref3)
9071 m_int_value_ref3->SetTopLevelContent(content_name);
9072 }
9073
GetCheckSum() const9074 unsigned int ValueTest::GetCheckSum() const {
9075 unsigned int retval{0};
9076
9077 CheckSums::CheckSumCombine(retval, "Condition::ValueTest");
9078 CheckSums::CheckSumCombine(retval, m_value_ref1);
9079 CheckSums::CheckSumCombine(retval, m_value_ref2);
9080 CheckSums::CheckSumCombine(retval, m_value_ref3);
9081 CheckSums::CheckSumCombine(retval, m_string_value_ref1);
9082 CheckSums::CheckSumCombine(retval, m_string_value_ref2);
9083 CheckSums::CheckSumCombine(retval, m_string_value_ref3);
9084 CheckSums::CheckSumCombine(retval, m_int_value_ref1);
9085 CheckSums::CheckSumCombine(retval, m_int_value_ref2);
9086 CheckSums::CheckSumCombine(retval, m_int_value_ref3);
9087 CheckSums::CheckSumCombine(retval, m_compare_type1);
9088 CheckSums::CheckSumCombine(retval, m_compare_type2);
9089
9090 TraceLogger() << "GetCheckSum(ValueTest): retval: " << retval;
9091 return retval;
9092 }
9093
9094 ///////////////////////////////////////////////////////////
9095 // Location //
9096 ///////////////////////////////////////////////////////////
9097 namespace {
GetLocationCondition(ContentType content_type,const std::string & name1,const std::string & name2)9098 const Condition* GetLocationCondition(ContentType content_type,
9099 const std::string& name1,
9100 const std::string& name2)
9101 {
9102 if (name1.empty())
9103 return nullptr;
9104 switch (content_type) {
9105 case CONTENT_BUILDING: {
9106 if (auto bt = GetBuildingType(name1))
9107 return bt->Location();
9108 break;
9109 }
9110 case CONTENT_SPECIES: {
9111 if (auto s = GetSpecies(name1))
9112 return s->Location();
9113 break;
9114 }
9115 case CONTENT_SHIP_HULL: {
9116 if (auto h = GetShipHull(name1))
9117 return h->Location();
9118 break;
9119 }
9120 case CONTENT_SHIP_PART: {
9121 if (auto p = GetShipPart(name1))
9122 return p->Location();
9123 break;
9124 }
9125 case CONTENT_SPECIAL: {
9126 if (auto s = GetSpecial(name1))
9127 return s->Location();
9128 break;
9129 }
9130 case CONTENT_FOCUS: {
9131 if (name2.empty())
9132 return nullptr;
9133 // get species, then focus from that species
9134 if (auto s = GetSpecies(name1)) {
9135 for (auto& f : s->Foci()) {
9136 if (f.Name() == name2)
9137 return f.Location();
9138 }
9139 }
9140 break;
9141 }
9142 default:
9143 return nullptr;
9144 }
9145 return nullptr;
9146 }
9147
GetContentTypeName(ContentType content_type)9148 const std::string& GetContentTypeName(ContentType content_type) {
9149 switch (content_type) {
9150 case CONTENT_BUILDING: return UserString("UIT_BUILDING"); break;
9151 case CONTENT_SPECIES: return UserString("ENC_SPECIES"); break;
9152 case CONTENT_SHIP_HULL: return UserString("UIT_SHIP_HULL"); break;
9153 case CONTENT_SHIP_PART: return UserString("UIT_SHIP_PART"); break;
9154 case CONTENT_SPECIAL: return UserString("ENC_SPECIAL"); break;
9155 case CONTENT_FOCUS: return UserString("PLANETARY_FOCUS_TITLE"); break;
9156 default: return EMPTY_STRING; break;
9157 }
9158 }
9159 }
9160
Location(ContentType content_type,std::unique_ptr<ValueRef::ValueRef<std::string>> && name1,std::unique_ptr<ValueRef::ValueRef<std::string>> && name2)9161 Location::Location(ContentType content_type,
9162 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name1,
9163 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name2) :
9164 Condition(),
9165 m_name1(std::move(name1)),
9166 m_name2(std::move(name2)),
9167 m_content_type(content_type)
9168 {
9169 auto operands = {m_name1.get(), m_name2.get()};
9170 m_root_candidate_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
9171 m_target_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->TargetInvariant(); });
9172 m_source_invariant = boost::algorithm::all_of(operands, [](auto& e){ return !e || e->SourceInvariant(); });
9173 }
9174
operator ==(const Condition & rhs) const9175 bool Location::operator==(const Condition& rhs) const {
9176 if (this == &rhs)
9177 return true;
9178 if (typeid(*this) != typeid(rhs))
9179 return false;
9180
9181 const Location& rhs_ = static_cast<const Location&>(rhs);
9182
9183 if (m_content_type != rhs_.m_content_type)
9184 return false;
9185
9186 CHECK_COND_VREF_MEMBER(m_name1)
9187 CHECK_COND_VREF_MEMBER(m_name2)
9188
9189 return true;
9190 }
9191
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const9192 void Location::Eval(const ScriptingContext& parent_context,
9193 ObjectSet& matches, ObjectSet& non_matches,
9194 SearchDomain search_domain/* = NON_MATCHES*/) const
9195 {
9196 bool simple_eval_safe = ((!m_name1 || m_name1->LocalCandidateInvariant()) &&
9197 (!m_name2 || m_name2->LocalCandidateInvariant()) &&
9198 (parent_context.condition_root_candidate || RootCandidateInvariant()));
9199
9200 if (simple_eval_safe) {
9201 // evaluate value and range limits once, use to match all candidates
9202
9203 std::string name1 = (m_name1 ? m_name1->Eval(parent_context) : "");
9204 std::string name2 = (m_name2 ? m_name2->Eval(parent_context) : "");
9205
9206 // get condition from content, apply to matches / non_matches
9207 const auto condition = GetLocationCondition(m_content_type, name1, name2);
9208 if (condition && condition != this) {
9209 condition->Eval(parent_context, matches, non_matches, search_domain);
9210 } else {
9211 // if somehow in a cyclical loop because some content's location
9212 // was defined as Location or if there is no location
9213 // condition, match nothing
9214 if (search_domain == MATCHES) {
9215 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
9216 matches.clear();
9217 }
9218 }
9219
9220 } else {
9221 // re-evaluate value and ranges for each candidate object
9222 Condition::Eval(parent_context, matches, non_matches, search_domain);
9223 }
9224 }
9225
Description(bool negated) const9226 std::string Location::Description(bool negated/* = false*/) const {
9227 std::string name1_str;
9228 if (m_name1)
9229 name1_str = m_name1->Description();
9230
9231 std::string name2_str;
9232 if (m_name2)
9233 name2_str = m_name2->Description();
9234
9235 std::string content_type_str = GetContentTypeName(m_content_type);
9236 std::string name_str = (m_content_type == CONTENT_FOCUS ? name2_str : name1_str);
9237
9238 return str(FlexibleFormat((!negated)
9239 ? UserString("DESC_LOCATION")
9240 : UserString("DESC_LOCATION_NOT"))
9241 % content_type_str
9242 % name_str);
9243 }
9244
Dump(unsigned short ntabs) const9245 std::string Location::Dump(unsigned short ntabs) const {
9246 std::string retval = DumpIndent(ntabs) + "Location content_type = ";
9247
9248 switch (m_content_type) {
9249 case CONTENT_BUILDING: retval += "Building"; break;
9250 case CONTENT_FOCUS: retval += "Focus"; break;
9251 case CONTENT_SHIP_HULL: retval += "Hull"; break;
9252 case CONTENT_SHIP_PART: retval += "Part"; break;
9253 case CONTENT_SPECIAL: retval += "Special"; break;
9254 case CONTENT_SPECIES: retval += "Species"; break;
9255 default: retval += "???";
9256 }
9257
9258 if (m_name1)
9259 retval += " name1 = " + m_name1->Dump(ntabs);
9260 if (m_name2)
9261 retval += " name2 = " + m_name2->Dump(ntabs);
9262 return retval;
9263 }
9264
Match(const ScriptingContext & local_context) const9265 bool Location::Match(const ScriptingContext& local_context) const {
9266 auto candidate = local_context.condition_local_candidate;
9267 if (!candidate) {
9268 ErrorLogger() << "Location::Match passed no candidate object";
9269 return false;
9270 }
9271
9272 std::string name1 = (m_name1 ? m_name1->Eval(local_context) : "");
9273 std::string name2 = (m_name2 ? m_name2->Eval(local_context) : "");
9274
9275 const auto condition = GetLocationCondition(m_content_type, name1, name2);
9276 if (!condition || condition == this)
9277 return false;
9278
9279 // other Conditions' Match functions not directly callable, so can't do any
9280 // better than just calling Eval for each candidate...
9281 return condition->Eval(local_context, candidate);
9282 }
9283
SetTopLevelContent(const std::string & content_name)9284 void Location::SetTopLevelContent(const std::string& content_name) {
9285 if (m_name1)
9286 m_name1->SetTopLevelContent(content_name);
9287 if (m_name2)
9288 m_name2->SetTopLevelContent(content_name);
9289 }
9290
GetCheckSum() const9291 unsigned int Location::GetCheckSum() const {
9292 unsigned int retval{0};
9293
9294 CheckSums::CheckSumCombine(retval, "Condition::Location");
9295 CheckSums::CheckSumCombine(retval, m_name1);
9296 CheckSums::CheckSumCombine(retval, m_name2);
9297 CheckSums::CheckSumCombine(retval, m_content_type);
9298
9299 TraceLogger() << "GetCheckSum(Location): retval: " << retval;
9300 return retval;
9301 }
9302
9303 ///////////////////////////////////////////////////////////
9304 // CombatTarget //
9305 ///////////////////////////////////////////////////////////
9306 namespace {
GetCombatTargetCondition(ContentType content_type,const std::string & name)9307 const Condition* GetCombatTargetCondition(
9308 ContentType content_type, const std::string& name)
9309 {
9310 if (name.empty())
9311 return nullptr;
9312 switch (content_type) {
9313 case CONTENT_SPECIES: {
9314 if (auto s = GetSpecies(name))
9315 return s->CombatTargets();
9316 break;
9317 }
9318 case CONTENT_SHIP_PART: {
9319 if (auto p = GetShipPart(name))
9320 return p->CombatTargets();
9321 break;
9322 }
9323 case CONTENT_BUILDING:
9324 case CONTENT_SHIP_HULL:
9325 case CONTENT_SPECIAL:
9326 case CONTENT_FOCUS:
9327 default:
9328 return nullptr;
9329 }
9330 return nullptr;
9331 }
9332 }
9333
CombatTarget(ContentType content_type,std::unique_ptr<ValueRef::ValueRef<std::string>> && name)9334 CombatTarget::CombatTarget(ContentType content_type,
9335 std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
9336 Condition(),
9337 m_name(std::move(name)),
9338 m_content_type(content_type)
9339 {
9340 m_root_candidate_invariant = !m_name || m_name->RootCandidateInvariant();
9341 m_target_invariant = !m_name|| m_name->TargetInvariant();
9342 m_source_invariant = !m_name || m_name->SourceInvariant();
9343 }
9344
operator ==(const Condition & rhs) const9345 bool CombatTarget::operator==(const Condition& rhs) const {
9346 if (this == &rhs)
9347 return true;
9348 if (typeid(*this) != typeid(rhs))
9349 return false;
9350
9351 const CombatTarget& rhs_ = static_cast<const CombatTarget&>(rhs);
9352
9353 if (m_content_type != rhs_.m_content_type)
9354 return false;
9355
9356 CHECK_COND_VREF_MEMBER(m_name)
9357
9358 return true;
9359 }
9360
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const9361 void CombatTarget::Eval(const ScriptingContext& parent_context,
9362 ObjectSet& matches, ObjectSet& non_matches,
9363 SearchDomain search_domain/* = NON_MATCHES*/) const
9364 {
9365 bool simple_eval_safe = ((!m_name || m_name->LocalCandidateInvariant()) &&
9366 (parent_context.condition_root_candidate || RootCandidateInvariant()));
9367
9368 if (simple_eval_safe) {
9369 // evaluate value and range limits once, use to match all candidates
9370
9371 std::string name = (m_name ? m_name->Eval(parent_context) : "");
9372
9373 // get condition from content, apply to matches / non_matches
9374 const auto condition = GetCombatTargetCondition(m_content_type, name);
9375 if (condition && condition != this) {
9376 condition->Eval(parent_context, matches, non_matches, search_domain);
9377 } else {
9378 // if somehow in a cyclical loop because some content's location
9379 // was defined as CombatTarget or if there is no available combat
9380 // targetting condition (eg. in valid content type, or name of
9381 // a bit of content that doesn't exist), match nothing
9382 if (search_domain == MATCHES) {
9383 non_matches.insert(non_matches.end(), matches.begin(), matches.end());
9384 matches.clear();
9385 }
9386 }
9387
9388 } else {
9389 // re-evaluate value and ranges for each candidate object
9390 Condition::Eval(parent_context, matches, non_matches, search_domain);
9391 }
9392 }
9393
Description(bool negated) const9394 std::string CombatTarget::Description(bool negated/* = false*/) const {
9395 std::string name_str;
9396 if (m_name)
9397 name_str = m_name->Description();
9398
9399 std::string content_type_str = GetContentTypeName(m_content_type);
9400
9401 return str(FlexibleFormat((!negated)
9402 ? UserString("DESC_COMBAT_TARGET")
9403 : UserString("DESC_COMBAT_TARGET_NOT"))
9404 % content_type_str
9405 % name_str);
9406 }
9407
Dump(unsigned short ntabs) const9408 std::string CombatTarget::Dump(unsigned short ntabs) const {
9409 std::string retval = DumpIndent(ntabs) + "CombatTarget content_type = ";
9410
9411 switch (m_content_type) {
9412 case CONTENT_BUILDING: retval += "Building"; break;
9413 case CONTENT_FOCUS: retval += "Focus"; break;
9414 case CONTENT_SHIP_HULL: retval += "Hull"; break;
9415 case CONTENT_SHIP_PART: retval += "Part"; break;
9416 case CONTENT_SPECIAL: retval += "Special"; break;
9417 case CONTENT_SPECIES: retval += "Species"; break;
9418 default: retval += "???";
9419 }
9420
9421 if (m_name)
9422 retval += " name = " + m_name->Dump(ntabs);
9423 return retval;
9424 }
9425
Match(const ScriptingContext & local_context) const9426 bool CombatTarget::Match(const ScriptingContext& local_context) const {
9427 auto candidate = local_context.condition_local_candidate;
9428 if (!candidate) {
9429 ErrorLogger() << "CombatTarget::Match passed no candidate object";
9430 return false;
9431 }
9432
9433 std::string name = (m_name ? m_name->Eval(local_context) : "");
9434
9435 const auto condition = GetCombatTargetCondition(m_content_type, name);
9436 if (!condition || condition == this)
9437 return false;
9438
9439 // other Conditions' Match functions not directly callable, so can't do any
9440 // better than just calling Eval for each candidate...
9441 return condition->Eval(local_context, candidate);
9442 }
9443
SetTopLevelContent(const std::string & content_name)9444 void CombatTarget::SetTopLevelContent(const std::string& content_name) {
9445 if (m_name)
9446 m_name->SetTopLevelContent(content_name);
9447 }
9448
GetCheckSum() const9449 unsigned int CombatTarget::GetCheckSum() const {
9450 unsigned int retval{0};
9451
9452 CheckSums::CheckSumCombine(retval, "Condition::CombatTarget");
9453 CheckSums::CheckSumCombine(retval, m_name);
9454 CheckSums::CheckSumCombine(retval, m_content_type);
9455
9456 TraceLogger() << "GetCheckSum(CombatTarget): retval: " << retval;
9457 return retval;
9458 }
9459
9460 ///////////////////////////////////////////////////////////
9461 // And //
9462 ///////////////////////////////////////////////////////////
And(std::vector<std::unique_ptr<Condition>> && operands)9463 And::And(std::vector<std::unique_ptr<Condition>>&& operands) :
9464 Condition(),
9465 m_operands(std::move(operands))
9466 {
9467 m_root_candidate_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
9468 m_target_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->TargetInvariant(); });
9469 m_source_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->SourceInvariant(); });
9470 }
9471
And(std::unique_ptr<Condition> && operand1,std::unique_ptr<Condition> && operand2,std::unique_ptr<Condition> && operand3,std::unique_ptr<Condition> && operand4)9472 And::And(std::unique_ptr<Condition>&& operand1, std::unique_ptr<Condition>&& operand2,
9473 std::unique_ptr<Condition>&& operand3, std::unique_ptr<Condition>&& operand4) :
9474 Condition()
9475 {
9476 // would prefer to initialize the vector m_operands in the initializer list, but this is difficult with non-copyable unique_ptr parameters
9477 if (operand1)
9478 m_operands.push_back(std::move(operand1));
9479 if (operand2)
9480 m_operands.push_back(std::move(operand2));
9481 if (operand3)
9482 m_operands.push_back(std::move(operand3));
9483 if (operand4)
9484 m_operands.push_back(std::move(operand4));
9485
9486 m_root_candidate_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
9487 m_target_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->TargetInvariant(); });
9488 m_source_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->SourceInvariant(); });
9489 }
9490
operator ==(const Condition & rhs) const9491 bool And::operator==(const Condition& rhs) const {
9492 if (this == &rhs)
9493 return true;
9494 if (typeid(*this) != typeid(rhs))
9495 return false;
9496
9497 const And& rhs_ = static_cast<const And&>(rhs);
9498
9499 if (m_operands.size() != rhs_.m_operands.size())
9500 return false;
9501 for (unsigned int i = 0; i < m_operands.size(); ++i) {
9502 CHECK_COND_VREF_MEMBER(m_operands.at(i))
9503 }
9504
9505 return true;
9506 }
9507
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const9508 void And::Eval(const ScriptingContext& parent_context, ObjectSet& matches,
9509 ObjectSet& non_matches, SearchDomain search_domain/* = NON_MATCHES*/) const
9510 {
9511 if (m_operands.empty()) {
9512 ErrorLogger() << "And::Eval given no operands!";
9513 return;
9514 }
9515 for (auto& operand : m_operands) {
9516 if (!operand) {
9517 ErrorLogger() << "And::Eval given null operand!";
9518 return;
9519 }
9520 }
9521
9522 auto ObjList = [](const ObjectSet& objs) -> std::string {
9523 std::stringstream ss;
9524 for (const auto& obj : objs)
9525 ss << obj->Name() << " (" << std::to_string(obj->ID()) << ") ";
9526 return ss.str();
9527 };
9528
9529 TraceLogger(conditions) << "And::Eval searching " << (search_domain == MATCHES ? "matches" : "non_matches")
9530 << " with input matches (" << matches.size() << "): " << ObjList(matches)
9531 << " and input non_matches(" << non_matches.size() << "): " << ObjList(non_matches);
9532
9533 if (search_domain == NON_MATCHES) {
9534 ObjectSet partly_checked_non_matches;
9535 partly_checked_non_matches.reserve(non_matches.size());
9536
9537 // move items in non_matches set that pass first operand condition into
9538 // partly_checked_non_matches set
9539 m_operands[0]->Eval(parent_context, partly_checked_non_matches, non_matches, NON_MATCHES);
9540 TraceLogger(conditions) << "Subcondition: " << m_operands[0]->Dump()
9541 <<"\npartly_checked_non_matches (" << partly_checked_non_matches.size() << "): " << ObjList(partly_checked_non_matches);
9542
9543 // move items that don't pass one of the other conditions back to non_matches
9544 for (unsigned int i = 1; i < m_operands.size(); ++i) {
9545 if (partly_checked_non_matches.empty()) break;
9546 m_operands[i]->Eval(parent_context, partly_checked_non_matches, non_matches, MATCHES);
9547 TraceLogger(conditions) << "Subcondition: " << m_operands[i]->Dump()
9548 <<"\npartly_checked_non_matches (" << partly_checked_non_matches.size() << "): " << ObjList(partly_checked_non_matches);
9549 }
9550
9551 // merge items that passed all operand conditions into matches
9552 matches.insert(matches.end(), partly_checked_non_matches.begin(), partly_checked_non_matches.end());
9553
9554 // items already in matches set are not checked, and remain in matches set even if
9555 // they don't match one of the operand conditions
9556
9557 } else /*(search_domain == MATCHES)*/ {
9558 // check all operand conditions on all objects in the matches set, moving those
9559 // that don't pass a condition to the non-matches set
9560
9561 for (auto& operand : m_operands) {
9562 if (matches.empty()) break;
9563 operand->Eval(parent_context, matches, non_matches, MATCHES);
9564 TraceLogger(conditions) << "Subcondition: " << operand->Dump()
9565 <<"\nremaining matches (" << matches.size() << "): " << ObjList(matches);
9566 }
9567
9568 // items already in non_matches set are not checked, and remain in non_matches set
9569 // even if they pass all operand conditions
9570 }
9571 TraceLogger(conditions) << "And::Eval final matches (" << matches.size() << "): " << ObjList(matches)
9572 << " and non_matches (" << non_matches.size() << "): " << ObjList(non_matches);
9573 }
9574
Description(bool negated) const9575 std::string And::Description(bool negated/* = false*/) const {
9576 std::string values_str;
9577 if (m_operands.size() == 1) {
9578 values_str += (!negated)
9579 ? UserString("DESC_AND_BEFORE_SINGLE_OPERAND")
9580 : UserString("DESC_NOT_AND_BEFORE_SINGLE_OPERAND");
9581 // Pushing the negation to the enclosed conditions
9582 values_str += m_operands[0]->Description(negated);
9583 values_str += (!negated)
9584 ? UserString("DESC_AND_AFTER_SINGLE_OPERAND")
9585 : UserString("DESC_NOT_AND_AFTER_SINGLE_OPERAND");
9586 } else {
9587 values_str += (!negated)
9588 ? UserString("DESC_AND_BEFORE_OPERANDS")
9589 : UserString("DESC_NOT_AND_BEFORE_OPERANDS");
9590 for (unsigned int i = 0; i < m_operands.size(); ++i) {
9591 // Pushing the negation to the enclosed conditions
9592 values_str += m_operands[i]->Description(negated);
9593 if (i != m_operands.size() - 1) {
9594 values_str += (!negated)
9595 ? UserString("DESC_AND_BETWEEN_OPERANDS")
9596 : UserString("DESC_NOT_AND_BETWEEN_OPERANDS");
9597 }
9598 }
9599 values_str += (!negated)
9600 ? UserString("DESC_AND_AFTER_OPERANDS")
9601 : UserString("DESC_NOT_AND_AFTER_OPERANDS");
9602 }
9603 return values_str;
9604 }
9605
Dump(unsigned short ntabs) const9606 std::string And::Dump(unsigned short ntabs) const {
9607 std::string retval = DumpIndent(ntabs) + "And [\n";
9608 for (auto& operand : m_operands)
9609 retval += operand->Dump(ntabs+1);
9610 retval += DumpIndent(ntabs) + "]\n";
9611 return retval;
9612 }
9613
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const9614 void And::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context, ObjectSet& condition_non_targets) const {
9615 if (!m_operands.empty()) {
9616 m_operands[0]->GetDefaultInitialCandidateObjects(parent_context, condition_non_targets); // gets condition_non_targets from first operand condition
9617 } else {
9618 Condition::GetDefaultInitialCandidateObjects(parent_context, condition_non_targets);
9619 }
9620 }
9621
SetTopLevelContent(const std::string & content_name)9622 void And::SetTopLevelContent(const std::string& content_name) {
9623 for (auto& operand : m_operands) {
9624 operand->SetTopLevelContent(content_name);
9625 }
9626 }
9627
GetCheckSum() const9628 unsigned int And::GetCheckSum() const {
9629 unsigned int retval{0};
9630
9631 CheckSums::CheckSumCombine(retval, "Condition::And");
9632 CheckSums::CheckSumCombine(retval, m_operands);
9633
9634 TraceLogger() << "GetCheckSum(And): retval: " << retval;
9635 return retval;
9636 }
9637
Operands() const9638 const std::vector<Condition*> And::Operands() const {
9639 std::vector<Condition*> retval(m_operands.size());
9640 std::transform(m_operands.begin(), m_operands.end(), retval.begin(),
9641 [](const std::unique_ptr<Condition>& xx) {return xx.get();});
9642 return retval;
9643 }
9644
9645 ///////////////////////////////////////////////////////////
9646 // Or //
9647 ///////////////////////////////////////////////////////////
Or(std::vector<std::unique_ptr<Condition>> && operands)9648 Or::Or(std::vector<std::unique_ptr<Condition>>&& operands) :
9649 Condition(),
9650 m_operands(std::move(operands))
9651 {
9652 m_root_candidate_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
9653 m_target_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->TargetInvariant(); });
9654 m_source_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->SourceInvariant(); });
9655 }
9656
Or(std::unique_ptr<Condition> && operand1,std::unique_ptr<Condition> && operand2,std::unique_ptr<Condition> && operand3,std::unique_ptr<Condition> && operand4)9657 Or::Or(std::unique_ptr<Condition>&& operand1,
9658 std::unique_ptr<Condition>&& operand2,
9659 std::unique_ptr<Condition>&& operand3,
9660 std::unique_ptr<Condition>&& operand4) :
9661 Condition()
9662 {
9663 // would prefer to initialize the vector m_operands in the initializer list, but this is difficult with non-copyable unique_ptr parameters
9664 if (operand1)
9665 m_operands.push_back(std::move(operand1));
9666 if (operand2)
9667 m_operands.push_back(std::move(operand2));
9668 if (operand3)
9669 m_operands.push_back(std::move(operand3));
9670 if (operand4)
9671 m_operands.push_back(std::move(operand4));
9672
9673 m_root_candidate_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
9674 m_target_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->TargetInvariant(); });
9675 m_source_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->SourceInvariant(); });
9676 }
9677
operator ==(const Condition & rhs) const9678 bool Or::operator==(const Condition& rhs) const {
9679 if (this == &rhs)
9680 return true;
9681 if (typeid(*this) != typeid(rhs))
9682 return false;
9683
9684 const Or& rhs_ = static_cast<const Or&>(rhs);
9685
9686 if (m_operands.size() != rhs_.m_operands.size())
9687 return false;
9688 for (unsigned int i = 0; i < m_operands.size(); ++i) {
9689 CHECK_COND_VREF_MEMBER(m_operands.at(i))
9690 }
9691
9692 return true;
9693 }
9694
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const9695 void Or::Eval(const ScriptingContext& parent_context, ObjectSet& matches,
9696 ObjectSet& non_matches, SearchDomain search_domain/* = NON_MATCHES*/) const
9697 {
9698 if (m_operands.empty()) {
9699 ErrorLogger() << "Or::Eval given no operands!";
9700 return;
9701 }
9702 for (auto& operand : m_operands) {
9703 if (!operand) {
9704 ErrorLogger() << "Or::Eval given null operand!";
9705 return;
9706 }
9707 }
9708
9709 if (search_domain == NON_MATCHES) {
9710 // check each item in the non-matches set against each of the operand conditions
9711 // if a non-candidate item matches an operand condition, move the item to the
9712 // matches set.
9713
9714 for (auto& operand : m_operands) {
9715 if (non_matches.empty()) break;
9716 operand->Eval(parent_context, matches, non_matches, NON_MATCHES);
9717 }
9718
9719 // items already in matches set are not checked and remain in the
9720 // matches set even if they fail all the operand conditions
9721
9722 } else {
9723 ObjectSet partly_checked_matches;
9724 partly_checked_matches.reserve(matches.size());
9725
9726 // move items in matches set the fail the first operand condition into
9727 // partly_checked_matches set
9728 m_operands[0]->Eval(parent_context, matches, partly_checked_matches, MATCHES);
9729
9730 // move items that pass any of the other conditions back into matches
9731 for (auto& operand : m_operands) {
9732 if (partly_checked_matches.empty()) break;
9733 operand->Eval(parent_context, matches, partly_checked_matches, NON_MATCHES);
9734 }
9735
9736 // merge items that failed all operand conditions into non_matches
9737 non_matches.insert(non_matches.end(), partly_checked_matches.begin(), partly_checked_matches.end());
9738
9739 // items already in non_matches set are not checked and remain in
9740 // non_matches set even if they pass one or more of the operand
9741 // conditions
9742 }
9743 }
9744
Description(bool negated) const9745 std::string Or::Description(bool negated/* = false*/) const {
9746 std::string values_str;
9747 if (m_operands.size() == 1) {
9748 values_str += (!negated)
9749 ? UserString("DESC_OR_BEFORE_SINGLE_OPERAND")
9750 : UserString("DESC_NOT_OR_BEFORE_SINGLE_OPERAND");
9751 // Pushing the negation to the enclosed conditions
9752 values_str += m_operands[0]->Description(negated);
9753 values_str += (!negated)
9754 ? UserString("DESC_OR_AFTER_SINGLE_OPERAND")
9755 : UserString("DESC_NOT_OR_AFTER_SINGLE_OPERAND");
9756 } else {
9757 // TODO: use per-operand-type connecting language
9758 values_str += (!negated)
9759 ? UserString("DESC_OR_BEFORE_OPERANDS")
9760 : UserString("DESC_NOT_OR_BEFORE_OPERANDS");
9761 for (unsigned int i = 0; i < m_operands.size(); ++i) {
9762 // Pushing the negation to the enclosed conditions
9763 values_str += m_operands[i]->Description(negated);
9764 if (i != m_operands.size() - 1) {
9765 values_str += (!negated)
9766 ? UserString("DESC_OR_BETWEEN_OPERANDS")
9767 : UserString("DESC_NOT_OR_BETWEEN_OPERANDS");
9768 }
9769 }
9770 values_str += (!negated)
9771 ? UserString("DESC_OR_AFTER_OPERANDS")
9772 : UserString("DESC_NOT_OR_AFTER_OPERANDS");
9773 }
9774 return values_str;
9775 }
9776
GetDefaultInitialCandidateObjects(const ScriptingContext & parent_context,ObjectSet & condition_non_targets) const9777 void Or::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context, ObjectSet& condition_non_targets) const {
9778 if (m_operands.empty())
9779 return;
9780
9781 if (m_operands.size() == 1) {
9782 // get condition_non_targets from the single / only operand condition
9783 m_operands[0]->GetDefaultInitialCandidateObjects(parent_context, condition_non_targets);
9784 return;
9785 }
9786
9787 if (parent_context.source && m_operands.size() == 2) {
9788 if (/*auto* src_condition =*/ dynamic_cast<const Source*>(m_operands[0].get())) {
9789 // special case when first of two subconditions is just Source:
9790 // get the default candidates of the second and add the source if
9791 // it is not already present.
9792 // TODO: extend to other single-match conditions: RootCandidate, Target
9793 // TODO: predetermine this situation to avoid repeat runtime dynamic-casts
9794 // TODO: fancier deep inspection of m_operands to determine optimal
9795 // way to combine the default candidates...
9796
9797 m_operands[1]->GetDefaultInitialCandidateObjects(parent_context, condition_non_targets);
9798 if (std::find(condition_non_targets.begin(), condition_non_targets.end(), parent_context.source) ==
9799 condition_non_targets.end())
9800 { condition_non_targets.push_back(parent_context.source); }
9801 return;
9802 }
9803 }
9804
9805 // default / fallback
9806 Condition::GetDefaultInitialCandidateObjects(parent_context, condition_non_targets);
9807
9808 // Also tried looping over all subconditions in m_operands and putting all
9809 // of their initial candidates into an unordered_set (to remove duplicates)
9810 // and then copying the result back into condition_non_targets but this was
9811 // substantially slower for many Or conditions
9812 }
9813
Dump(unsigned short ntabs) const9814 std::string Or::Dump(unsigned short ntabs) const {
9815 std::string retval = DumpIndent(ntabs) + "Or [\n";
9816 for (auto& operand : m_operands)
9817 retval += operand->Dump(ntabs+1);
9818 retval += "\n" + DumpIndent(ntabs) + "]\n";
9819 return retval;
9820 }
9821
SetTopLevelContent(const std::string & content_name)9822 void Or::SetTopLevelContent(const std::string& content_name) {
9823 for (auto& operand : m_operands) {
9824 operand->SetTopLevelContent(content_name);
9825 }
9826 }
9827
GetCheckSum() const9828 unsigned int Or::GetCheckSum() const {
9829 unsigned int retval{0};
9830
9831 CheckSums::CheckSumCombine(retval, "Condition::Or");
9832 CheckSums::CheckSumCombine(retval, m_operands);
9833
9834 TraceLogger() << "GetCheckSum(Or): retval: " << retval;
9835 return retval;
9836 }
9837
9838 ///////////////////////////////////////////////////////////
9839 // Not //
9840 ///////////////////////////////////////////////////////////
Not(std::unique_ptr<Condition> && operand)9841 Not::Not(std::unique_ptr<Condition>&& operand) :
9842 Condition(),
9843 m_operand(std::move(operand))
9844 {
9845 m_root_candidate_invariant = m_operand->RootCandidateInvariant();
9846 m_target_invariant = m_operand->TargetInvariant();
9847 m_source_invariant = m_operand->SourceInvariant();
9848 }
9849
operator ==(const Condition & rhs) const9850 bool Not::operator==(const Condition& rhs) const {
9851 if (this == &rhs)
9852 return true;
9853 if (typeid(*this) != typeid(rhs))
9854 return false;
9855
9856 const Not& rhs_ = static_cast<const Not&>(rhs);
9857
9858 CHECK_COND_VREF_MEMBER(m_operand)
9859
9860 return true;
9861 }
9862
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const9863 void Not::Eval(const ScriptingContext& parent_context, ObjectSet& matches, ObjectSet& non_matches,
9864 SearchDomain search_domain/* = NON_MATCHES*/) const
9865 {
9866 if (!m_operand) {
9867 ErrorLogger() << "Not::Eval found no subcondition to evaluate!";
9868 return;
9869 }
9870
9871 if (search_domain == NON_MATCHES) {
9872 // search non_matches set for items that don't meet the operand
9873 // condition, and move those to the matches set
9874 m_operand->Eval(parent_context, non_matches, matches, MATCHES); // swapping order of matches and non_matches set parameters and MATCHES / NON_MATCHES search domain effects NOT on requested search domain
9875 } else {
9876 // search matches set for items that meet the operand condition
9877 // condition, and move those to the non_matches set
9878 m_operand->Eval(parent_context, non_matches, matches, NON_MATCHES);
9879 }
9880 }
9881
Description(bool negated) const9882 std::string Not::Description(bool negated/* = false*/) const
9883 { return m_operand->Description(!negated); }
9884
Dump(unsigned short ntabs) const9885 std::string Not::Dump(unsigned short ntabs) const {
9886 std::string retval = DumpIndent(ntabs) + "Not\n";
9887 retval += m_operand->Dump(ntabs+1);
9888 return retval;
9889 }
9890
SetTopLevelContent(const std::string & content_name)9891 void Not::SetTopLevelContent(const std::string& content_name) {
9892 if (m_operand)
9893 m_operand->SetTopLevelContent(content_name);
9894 }
9895
GetCheckSum() const9896 unsigned int Not::GetCheckSum() const {
9897 unsigned int retval{0};
9898
9899 CheckSums::CheckSumCombine(retval, "Condition::Not");
9900 CheckSums::CheckSumCombine(retval, m_operand);
9901
9902 TraceLogger() << "GetCheckSum(Not): retval: " << retval;
9903 return retval;
9904 }
9905
9906 ///////////////////////////////////////////////////////////
9907 // OrderedAlternativesOf
9908 ///////////////////////////////////////////////////////////
FCMoveContent(ObjectSet & from_set,ObjectSet & to_set)9909 void FCMoveContent(ObjectSet& from_set, ObjectSet& to_set) {
9910 to_set.insert(to_set.end(),
9911 std::make_move_iterator(from_set.begin()),
9912 std::make_move_iterator(from_set.end()));
9913 from_set.clear();
9914 }
9915
OrderedAlternativesOf(std::vector<std::unique_ptr<Condition>> && operands)9916 OrderedAlternativesOf::OrderedAlternativesOf(
9917 std::vector<std::unique_ptr<Condition>>&& operands) :
9918 Condition(),
9919 m_operands(std::move(operands))
9920 {
9921 m_root_candidate_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->RootCandidateInvariant(); });
9922 m_target_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->TargetInvariant(); });
9923 m_source_invariant = boost::algorithm::all_of(m_operands, [](auto& e){ return !e || e->SourceInvariant(); });
9924 }
9925
operator ==(const Condition & rhs) const9926 bool OrderedAlternativesOf::operator==(const Condition& rhs) const {
9927 if (this == &rhs)
9928 return true;
9929 if (typeid(*this) != typeid(rhs))
9930 return false;
9931
9932 const OrderedAlternativesOf& rhs_ = static_cast<const OrderedAlternativesOf&>(rhs);
9933
9934 if (m_operands.size() != rhs_.m_operands.size())
9935 return false;
9936 for (unsigned int i = 0; i < m_operands.size(); ++i) {
9937 CHECK_COND_VREF_MEMBER(m_operands.at(i))
9938 }
9939
9940 return true;
9941 }
9942
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const9943 void OrderedAlternativesOf::Eval(const ScriptingContext& parent_context,
9944 ObjectSet& matches, ObjectSet& non_matches,
9945 SearchDomain search_domain/* = NON_MATCHES*/) const
9946 {
9947 if (m_operands.empty()) {
9948 ErrorLogger() << "OrderedAlternativesOf::Eval given no operands!";
9949 return;
9950 }
9951 for (auto& operand : m_operands) {
9952 if (!operand) {
9953 ErrorLogger() << "OrderedAlternativesOf::Eval given null operand!";
9954 return;
9955 }
9956 }
9957
9958 // OrderedAlternativesOf [ A B C ] matches all candidates which match the topmost condition.
9959 // If any candidate matches A, then all candidates that match A are matched,
9960 // or if no candidate matches A but any candidate matches B, then all candidates that match B are matched,
9961 // or if no candidate matches A or B but any candidate matches C, then all candidates that match C are matched.
9962 // If no candidate matches A, B, or C, then nothing is matched.
9963 //
9964 // Not OrderedAlternativesOf [ A B C ] finds the topmost condition which has matches and then matches its non-matches.
9965 // If any candidate matches A, then all candidates that do not match A are matched,
9966 // or if no candidates match A but any candidate matches B, then all candidates that do not match B are matched,
9967 // or if no candidate matches A or B but any candidate matches C, then all candidates that do not match C are matched.
9968 // If no candidate matches A, B, or C, then all candidates are matched.
9969 if (search_domain == NON_MATCHES) {
9970 // Check each operand condition on objects in the input matches and non_matches sets, until an operand condition matches an object.
9971 // If an operand condition is selected, apply it to the input non_matches set, moving matching candidates to matches.
9972 // If no operand condition is selected, because no candidate is matched by any operand condition, then do nothing.
9973 ObjectSet temp_objects;
9974 temp_objects.reserve(std::max(matches.size(),non_matches.size()));
9975
9976 for (auto& operand : m_operands) {
9977 operand->Eval(parent_context, temp_objects, non_matches, NON_MATCHES);
9978 if (!temp_objects.empty()) {
9979 // Select the current operand condition. Apply it to the NON_MATCHES candidate set.
9980 // We alread did the application, so we use the results
9981 matches.reserve(temp_objects.size() + matches.size());
9982 FCMoveContent(temp_objects, matches);
9983 return;
9984 }
9985 // Check if the operand condition matches an object in the other input set
9986 operand->Eval(parent_context, matches, temp_objects, MATCHES);
9987 if (!matches.empty()) {
9988 // Select the current operand condition. Apply it to the NON_MATCHES candidate set.
9989 // We already did the application before, but there were no matches.
9990 // restore state before checking the operand
9991 FCMoveContent(temp_objects, matches);
9992 return;
9993 }
9994
9995 // restore state before checking the operand
9996 FCMoveContent(temp_objects, matches);
9997 // try the next operand
9998 }
9999
10000 // No operand condition was selected. State is restored. Nothing should be moved to matches input set
10001 } else /*(search_domain == MATCHES)*/ {
10002 // Check each operand condition on objects in the input matches and non_matches sets, until an operand condition matches an object.
10003 // If an operand condition is selected, apply it to the input matches set, moving non-matching candidates to non_matches.
10004 // If no operand condition is selected, because no candidate is matched by any operand condition, then move all of the input matches into non_matches.
10005 ObjectSet temp_objects;
10006 temp_objects.reserve(std::max(matches.size(),non_matches.size()));
10007
10008 for (auto& operand : m_operands) {
10009 // Apply the current operand optimistically. Select it if there are any matching objects in the input sets
10010 operand->Eval(parent_context, temp_objects, matches, NON_MATCHES);
10011 // temp_objects are objects from input matches set which also match the operand
10012 // matches are objects from input matches set which do not match the operand
10013 if (!temp_objects.empty()) {
10014 // Select and apply this operand. Objects in matches do not match this condition.
10015 non_matches.reserve(matches.size() + non_matches.size());
10016 FCMoveContent(matches, non_matches);
10017 FCMoveContent(temp_objects, matches);
10018 return;
10019 }
10020 // Select this operand if there are matching objects in the non_matches input set.
10021 operand->Eval(parent_context, temp_objects, non_matches, NON_MATCHES);
10022 if (!temp_objects.empty()) {
10023 // Select and apply this operand. But no matching objects exist in the matches input set,
10024 // so all objects need to be moved into the non_matches set
10025 non_matches.reserve(matches.size() + non_matches.size() + temp_objects.size());
10026 FCMoveContent(matches, non_matches);
10027 FCMoveContent(temp_objects, non_matches);
10028 return;
10029 }
10030
10031 // Operand was not selected. Restore state before. Try next operand.
10032 FCMoveContent(temp_objects, matches);
10033 }
10034
10035 // No operand condition was selected. Objects in matches input set do not match, so move those to non_matches input set.
10036 non_matches.reserve(matches.size() + non_matches.size());
10037 FCMoveContent(matches, non_matches);
10038 }
10039 }
10040
Description(bool negated) const10041 std::string OrderedAlternativesOf::Description(bool negated/* = false*/) const {
10042 std::string values_str;
10043 if (m_operands.size() == 1) {
10044 values_str += (!negated)
10045 ? UserString("DESC_ORDERED_ALTERNATIVES_BEFORE_SINGLE_OPERAND")
10046 : UserString("DESC_NOT_ORDERED_ALTERNATIVES_BEFORE_SINGLE_OPERAND");
10047 // Pushing the negation of matches to the enclosed conditions
10048 values_str += m_operands[0]->Description(negated);
10049 values_str += (!negated)
10050 ? UserString("DESC_ORDERED_ALTERNATIVES_AFTER_SINGLE_OPERAND")
10051 : UserString("DESC_NOT_ORDERED_ALTERNATIVES_AFTER_SINGLE_OPERAND");
10052 } else {
10053 // TODO: use per-operand-type connecting language
10054 values_str += (!negated)
10055 ? UserString("DESC_ORDERED_ALTERNATIVES_BEFORE_OPERANDS")
10056 : UserString("DESC_NOT_ORDERED_ALTERNATIVES_BEFORE_OPERANDS");
10057 for (unsigned int i = 0; i < m_operands.size(); ++i) {
10058 // Pushing the negation of matches to the enclosed conditions
10059 values_str += m_operands[i]->Description(negated);
10060 if (i != m_operands.size() - 1) {
10061 values_str += (!negated)
10062 ? UserString("DESC_ORDERED_ALTERNATIVES_BETWEEN_OPERANDS")
10063 : UserString("DESC_NOT_ORDERED_ALTERNATIVES_BETWEEN_OPERANDS");
10064 }
10065 }
10066 values_str += (!negated)
10067 ? UserString("DESC_ORDERED_ALTERNATIVES_AFTER_OPERANDS")
10068 : UserString("DESC_NOT_ORDERED_ALTERNATIVES_AFTER_OPERANDS");
10069 }
10070 return values_str;
10071 }
10072
Dump(unsigned short ntabs) const10073 std::string OrderedAlternativesOf::Dump(unsigned short ntabs) const {
10074 std::string retval = DumpIndent(ntabs) + "OrderedAlternativesOf [\n";
10075 for (auto& operand : m_operands)
10076 retval += operand->Dump(ntabs+1);
10077 retval += DumpIndent(ntabs) + "]\n";
10078 return retval;
10079 }
10080
SetTopLevelContent(const std::string & content_name)10081 void OrderedAlternativesOf::SetTopLevelContent(const std::string& content_name) {
10082 for (auto& operand : m_operands) {
10083 operand->SetTopLevelContent(content_name);
10084 }
10085 }
10086
GetCheckSum() const10087 unsigned int OrderedAlternativesOf::GetCheckSum() const {
10088 unsigned int retval{0};
10089
10090 CheckSums::CheckSumCombine(retval, "Condition::OrderedAlternativesOf");
10091 CheckSums::CheckSumCombine(retval, m_operands);
10092
10093 TraceLogger() << "GetCheckSum(OrderedAlternativesOf): retval: " << retval;
10094 return retval;
10095 }
10096
Operands() const10097 const std::vector<Condition*> OrderedAlternativesOf::Operands() const {
10098 std::vector<Condition*> retval(m_operands.size());
10099 std::transform(m_operands.begin(), m_operands.end(), retval.begin(),
10100 [](const std::unique_ptr<Condition>& xx) {return xx.get();});
10101 return retval;
10102 }
10103
10104 ///////////////////////////////////////////////////////////
10105 // Described //
10106 ///////////////////////////////////////////////////////////
Described(std::unique_ptr<Condition> && condition,const std::string & desc_stringtable_key)10107 Described::Described(std::unique_ptr<Condition>&& condition, const std::string& desc_stringtable_key) :
10108 Condition(),
10109 m_condition(std::move(condition)),
10110 m_desc_stringtable_key(desc_stringtable_key)
10111 {
10112 m_root_candidate_invariant = !m_condition || m_condition->RootCandidateInvariant();
10113 m_target_invariant = !m_condition || m_condition->TargetInvariant();
10114 m_source_invariant = !m_condition || m_condition->SourceInvariant();
10115 }
10116
operator ==(const Condition & rhs) const10117 bool Described::operator==(const Condition& rhs) const {
10118 if (this == &rhs)
10119 return true;
10120 if (typeid(*this) != typeid(rhs))
10121 return false;
10122
10123 const Described& rhs_ = static_cast<const Described&>(rhs);
10124
10125 if (m_desc_stringtable_key != rhs_.m_desc_stringtable_key)
10126 return false;
10127
10128 CHECK_COND_VREF_MEMBER(m_condition)
10129
10130 return true;
10131 }
10132
Eval(const ScriptingContext & parent_context,ObjectSet & matches,ObjectSet & non_matches,SearchDomain search_domain) const10133 void Described::Eval(const ScriptingContext& parent_context, ObjectSet& matches, ObjectSet& non_matches,
10134 SearchDomain search_domain/* = NON_MATCHES*/) const
10135 {
10136 if (!m_condition) {
10137 ErrorLogger() << "Described::Eval found no subcondition to evaluate!";
10138 return;
10139 }
10140 return m_condition->Eval(parent_context, matches, non_matches, search_domain);
10141 }
10142
Description(bool negated) const10143 std::string Described::Description(bool negated/* = false*/) const {
10144 if (!m_desc_stringtable_key.empty() && UserStringExists(m_desc_stringtable_key))
10145 return UserString(m_desc_stringtable_key);
10146 if (m_condition)
10147 return m_condition->Description(negated);
10148 return "";
10149 }
10150
SetTopLevelContent(const std::string & content_name)10151 void Described::SetTopLevelContent(const std::string& content_name) {
10152 if (m_condition)
10153 m_condition->SetTopLevelContent(content_name);
10154 }
10155
GetCheckSum() const10156 unsigned int Described::GetCheckSum() const {
10157 unsigned int retval{0};
10158
10159 CheckSums::CheckSumCombine(retval, "Condition::Described");
10160 CheckSums::CheckSumCombine(retval, m_condition);
10161 CheckSums::CheckSumCombine(retval, m_desc_stringtable_key);
10162
10163 TraceLogger() << "GetCheckSum(Described): retval: " << retval;
10164 return retval;
10165 }
10166 } // namespace Condition
10167