1 #include "ValueRefs.h"
2
3 #include "Building.h"
4 #include "Fleet.h"
5 #include "ShipDesign.h"
6 #include "ShipPart.h"
7 #include "ShipHull.h"
8 #include "Ship.h"
9 #include "Planet.h"
10 #include "Predicates.h"
11 #include "Species.h"
12 #include "System.h"
13 #include "Field.h"
14 #include "Fighter.h"
15 #include "Pathfinder.h"
16 #include "Universe.h"
17 #include "UniverseObject.h"
18 #include "Enums.h"
19 #include "Tech.h"
20 #include "../Empire/EmpireManager.h"
21 #include "../Empire/Empire.h"
22 #include "../Empire/Supply.h"
23 #include "../util/Random.h"
24 #include "../util/Logger.h"
25 #include "../util/MultiplayerCommon.h"
26 #include "../util/GameRules.h"
27
28 #include <boost/algorithm/string/classification.hpp>
29 #include <boost/algorithm/string/predicate.hpp>
30 #include <boost/algorithm/string/split.hpp>
31 #include <boost/range/adaptor/filtered.hpp>
32 #include <boost/range/adaptor/map.hpp>
33 #include <boost/range/numeric.hpp>
34
35 #include <functional>
36 #include <iomanip>
37 #include <iterator>
38
39
40 std::string DoubleToString(double val, int digits, bool always_show_sign);
41 bool UserStringExists(const std::string& str);
42
43 FO_COMMON_API extern const int INVALID_DESIGN_ID;
44
45 namespace {
FollowReference(std::vector<std::string>::const_iterator first,std::vector<std::string>::const_iterator last,ValueRef::ReferenceType ref_type,const ScriptingContext & context)46 std::shared_ptr<const UniverseObject> FollowReference(
47 std::vector<std::string>::const_iterator first,
48 std::vector<std::string>::const_iterator last,
49 ValueRef::ReferenceType ref_type,
50 const ScriptingContext& context)
51 {
52 //DebugLogger() << "FollowReference: source: " << (context.source ? context.source->Name() : "0")
53 // << " target: " << (context.effect_target ? context.effect_target->Name() : "0")
54 // << " local c: " << (context.condition_local_candidate ? context.condition_local_candidate->Name() : "0")
55 // << " root c: " << (context.condition_root_candidate ? context.condition_root_candidate->Name() : "0");
56
57 std::shared_ptr<const UniverseObject> obj;
58 switch (ref_type) {
59 case ValueRef::NON_OBJECT_REFERENCE: return context.condition_local_candidate; break;
60 case ValueRef::SOURCE_REFERENCE: obj = context.source; break;
61 case ValueRef::EFFECT_TARGET_REFERENCE: obj = context.effect_target; break;
62 case ValueRef::CONDITION_ROOT_CANDIDATE_REFERENCE: obj = context.condition_root_candidate; break;
63 case ValueRef::CONDITION_LOCAL_CANDIDATE_REFERENCE:
64 default: obj = context.condition_local_candidate; break;
65 }
66
67 if (!obj) {
68 std::string type_string;
69 switch (ref_type) {
70 case ValueRef::SOURCE_REFERENCE: type_string = "Source"; break;
71 case ValueRef::EFFECT_TARGET_REFERENCE: type_string = "Target"; break;
72 case ValueRef::CONDITION_ROOT_CANDIDATE_REFERENCE: type_string = "RootCandidate"; break;
73 case ValueRef::CONDITION_LOCAL_CANDIDATE_REFERENCE:
74 default: type_string = "LocalCandidate"; break;
75 }
76 ErrorLogger() << "FollowReference : top level object (" << type_string << ") not defined in scripting context";
77 return nullptr;
78 }
79
80 while (first != last) {
81 std::string property_name = *first;
82 if (property_name == "Planet") {
83 if (auto b = std::dynamic_pointer_cast<const Building>(obj)) {
84 obj = context.ContextObjects().get<Planet>(b->PlanetID());
85 } else {
86 ErrorLogger() << "FollowReference : object not a building, so can't get its planet.";
87 obj = nullptr;
88 }
89 } else if (property_name == "System") {
90 if (obj)
91 obj = context.ContextObjects().get<System>(obj->SystemID());
92 if (!obj)
93 ErrorLogger() << "FollowReference : Unable to get system for object";
94 } else if (property_name == "Fleet") {
95 if (auto s = std::dynamic_pointer_cast<const Ship>(obj)) {
96 obj = context.ContextObjects().get<Fleet>(s->FleetID());
97 } else {
98 ErrorLogger() << "FollowReference : object not a ship, so can't get its fleet";
99 obj = nullptr;
100 }
101 }
102 ++first;
103 }
104 return obj;
105 }
106
107 // Generates a debug trace that can be included in error logs, augmenting
108 // the ReconstructName() info with additional info identifying the object
109 // references that were successfully followed.
TraceReference(const std::vector<std::string> & property_name,ValueRef::ReferenceType ref_type,const ScriptingContext & context)110 std::string TraceReference(const std::vector<std::string>& property_name,
111 ValueRef::ReferenceType ref_type,
112 const ScriptingContext& context)
113 {
114 std::shared_ptr<const UniverseObject> obj, initial_obj;
115 std::string retval = ReconstructName(property_name, ref_type, false) + " : ";
116 switch (ref_type) {
117 case ValueRef::NON_OBJECT_REFERENCE:
118 retval += " | Non Object Reference |";
119 return retval;
120 break;
121 case ValueRef::SOURCE_REFERENCE:
122 retval += " | Source: ";
123 obj = context.source;
124 break;
125 case ValueRef::EFFECT_TARGET_REFERENCE:
126 retval += " | Effect Target: ";
127 obj = context.effect_target;
128 break;
129 case ValueRef::CONDITION_ROOT_CANDIDATE_REFERENCE:
130 retval += " | Root Candidate: ";
131 obj = context.condition_root_candidate;
132 break;
133 case ValueRef::CONDITION_LOCAL_CANDIDATE_REFERENCE:
134 default:
135 retval += " | Local Candidate: ";
136 obj = context.condition_local_candidate;
137 break;
138 }
139 if (obj) {
140 retval += UserString(boost::lexical_cast<std::string>(obj->ObjectType())) + " "
141 + std::to_string(obj->ID()) + " ( " + obj->Name() + " ) ";
142 initial_obj = obj;
143 }
144 retval += " | ";
145
146 auto first = property_name.begin();
147 auto last = property_name.end();
148 while (first != last) {
149 std::string property_name_part = *first;
150 retval += " " + property_name_part + " ";
151 if (property_name_part == "Planet") {
152 if (auto b = std::dynamic_pointer_cast<const Building>(obj)) {
153 retval += "(" + std::to_string(b->PlanetID()) + "): ";
154 obj = context.ContextObjects().get<Planet>(b->PlanetID());
155 } else
156 obj = nullptr;
157 } else if (property_name_part == "System") {
158 if (obj) {
159 retval += "(" + std::to_string(obj->SystemID()) + "): ";
160 obj = context.ContextObjects().get<System>(obj->SystemID());
161 }
162 } else if (property_name_part == "Fleet") {
163 if (auto s = std::dynamic_pointer_cast<const Ship>(obj)) {
164 retval += "(" + std::to_string(s->FleetID()) + "): ";
165 obj = context.ContextObjects().get<Fleet>(s->FleetID());
166 } else
167 obj = nullptr;
168 }
169
170 ++first;
171
172 if (obj && initial_obj != obj) {
173 retval += " Referenced Object: " + UserString(boost::lexical_cast<std::string>(obj->ObjectType())) + " "
174 + std::to_string(obj->ID()) + " ( " + obj->Name() + " )";
175 }
176 retval += " | ";
177 }
178 return retval;
179 }
180
181 struct ObjectTypeVisitor : UniverseObjectVisitor {
ObjectTypeVisitor__anonbe5bf2420111::ObjectTypeVisitor182 ObjectTypeVisitor() : m_type(INVALID_UNIVERSE_OBJECT_TYPE) {}
183
Visit__anonbe5bf2420111::ObjectTypeVisitor184 std::shared_ptr<UniverseObject> Visit(std::shared_ptr<Building> obj) const override
185 { m_type = OBJ_BUILDING; return obj; }
186
Visit__anonbe5bf2420111::ObjectTypeVisitor187 std::shared_ptr<UniverseObject> Visit(std::shared_ptr<Fleet> obj) const override
188 { m_type = OBJ_FLEET; return obj; }
189
Visit__anonbe5bf2420111::ObjectTypeVisitor190 std::shared_ptr<UniverseObject> Visit(std::shared_ptr<Planet> obj) const override
191 { m_type = OBJ_PLANET; return obj; }
192
Visit__anonbe5bf2420111::ObjectTypeVisitor193 std::shared_ptr<UniverseObject> Visit(std::shared_ptr<Ship> obj) const override
194 { m_type = OBJ_SHIP; return obj; }
195
Visit__anonbe5bf2420111::ObjectTypeVisitor196 std::shared_ptr<UniverseObject> Visit(std::shared_ptr<System> obj) const override
197 { m_type = OBJ_SYSTEM; return obj; }
198
Visit__anonbe5bf2420111::ObjectTypeVisitor199 std::shared_ptr<UniverseObject> Visit(std::shared_ptr<Field> obj) const override
200 { m_type = OBJ_FIELD; return obj; }
201
202 mutable UniverseObjectType m_type;
203 };
204
GetMeterNameMap()205 const std::map<std::string, MeterType>& GetMeterNameMap() {
206 static const std::map<std::string, MeterType> meter_name_map{
207 {"Population", METER_POPULATION},
208 {"TargetPopulation", METER_TARGET_POPULATION},
209 {"Industry", METER_INDUSTRY},
210 {"TargetIndustry", METER_TARGET_INDUSTRY},
211 {"Research", METER_RESEARCH},
212 {"TargetResearch", METER_TARGET_RESEARCH},
213 {"Trade", METER_TRADE},
214 {"TargetTrade", METER_TARGET_TRADE},
215 {"Construction", METER_CONSTRUCTION},
216 {"TargetConstruction", METER_TARGET_CONSTRUCTION},
217 {"Happiness", METER_HAPPINESS},
218 {"TargetHappiness", METER_TARGET_HAPPINESS},
219 {"MaxFuel", METER_MAX_FUEL},
220 {"Fuel", METER_FUEL},
221 {"MaxStructure", METER_MAX_STRUCTURE},
222 {"Structure", METER_STRUCTURE},
223 {"MaxShield", METER_MAX_SHIELD},
224 {"Shield", METER_SHIELD},
225 {"MaxDefense", METER_MAX_DEFENSE},
226 {"Defense", METER_DEFENSE},
227 {"MaxTroops", METER_MAX_TROOPS},
228 {"Troops", METER_TROOPS},
229 {"RebelTroops", METER_REBEL_TROOPS},
230 {"Supply", METER_SUPPLY},
231 {"MaxSupply", METER_MAX_SUPPLY},
232 {"Stockpile", METER_STOCKPILE},
233 {"MaxStockpile", METER_MAX_STOCKPILE},
234 {"Stealth", METER_STEALTH},
235 {"Detection", METER_DETECTION},
236 {"Speed", METER_SPEED},
237 {"Damage", METER_CAPACITY},
238 {"Capacity", METER_CAPACITY},
239 {"MaxCapacity", METER_MAX_CAPACITY},
240 {"SecondaryStat", METER_SECONDARY_STAT},
241 {"MaxSecondaryStat", METER_MAX_SECONDARY_STAT},
242 {"Size", METER_SIZE}
243 };
244 return meter_name_map;
245 }
246
247 // force early init to avoid threading issues later
248 std::map<std::string, MeterType> dummy = GetMeterNameMap();
249 }
250
251 namespace ValueRef {
NameToMeter(const std::string & name)252 MeterType NameToMeter(const std::string& name) {
253 MeterType retval = INVALID_METER_TYPE;
254 auto it = GetMeterNameMap().find(name);
255 if (it != GetMeterNameMap().end())
256 retval = it->second;
257 return retval;
258 }
259
MeterToName(MeterType meter)260 std::string MeterToName(MeterType meter) {
261 for (const auto& entry : GetMeterNameMap()) {
262 if (entry.second == meter)
263 return entry.first;
264 }
265 return "";
266 }
267
ReconstructName(const std::vector<std::string> & property_name,ReferenceType ref_type,bool return_immediate_value)268 std::string ReconstructName(const std::vector<std::string>& property_name,
269 ReferenceType ref_type,
270 bool return_immediate_value)
271 {
272 std::string retval;
273
274 if (return_immediate_value)
275 retval += "Value(";
276
277 switch (ref_type) {
278 case SOURCE_REFERENCE: retval = "Source"; break;
279 case EFFECT_TARGET_REFERENCE: retval = "Target"; break;
280 case EFFECT_TARGET_VALUE_REFERENCE: retval = "Value"; break;
281 case CONDITION_LOCAL_CANDIDATE_REFERENCE: retval = "LocalCandidate"; break;
282 case CONDITION_ROOT_CANDIDATE_REFERENCE: retval = "RootCandidate"; break;
283 case NON_OBJECT_REFERENCE: retval = ""; break;
284 default: retval = "?????"; break;
285 }
286
287 if (ref_type != EFFECT_TARGET_VALUE_REFERENCE) {
288 for (const std::string& property_name_part : property_name) {
289 if (!retval.empty())
290 retval += '.';
291 retval += property_name_part;
292 }
293 }
294
295 if (return_immediate_value)
296 retval += ")";
297
298 return retval;
299 }
300
FormatedDescriptionPropertyNames(ReferenceType ref_type,const std::vector<std::string> & property_names,bool return_immediate_value)301 std::string FormatedDescriptionPropertyNames(ReferenceType ref_type,
302 const std::vector<std::string>& property_names,
303 bool return_immediate_value)
304 {
305 int num_references = property_names.size();
306 if (ref_type == NON_OBJECT_REFERENCE)
307 num_references--;
308 for (const std::string& property_name_part : property_names)
309 if (property_name_part.empty())
310 num_references--;
311 num_references = std::max(0, num_references);
312 std::string format_string;
313 switch (num_references) {
314 case 0: format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLE0"); break;
315 case 1: format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLE1"); break;
316 case 2: format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLE2"); break;
317 case 3: format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLE3"); break;
318 case 4: format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLE4"); break;
319 case 5: format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLE5"); break;
320 case 6: format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLE6"); break;
321 default:format_string = UserString("DESC_VALUE_REF_MULTIPART_VARIABLEMANY"); break;
322 }
323
324 boost::format formatter = FlexibleFormat(format_string);
325
326 switch (ref_type) {
327 case SOURCE_REFERENCE: formatter % UserString("DESC_VAR_SOURCE"); break;
328 case EFFECT_TARGET_REFERENCE: formatter % UserString("DESC_VAR_TARGET"); break;
329 case EFFECT_TARGET_VALUE_REFERENCE: formatter % UserString("DESC_VAR_VALUE"); break;
330 case CONDITION_LOCAL_CANDIDATE_REFERENCE: formatter % UserString("DESC_VAR_LOCAL_CANDIDATE"); break;
331 case CONDITION_ROOT_CANDIDATE_REFERENCE: formatter % UserString("DESC_VAR_ROOT_CANDIDATE"); break;
332 case NON_OBJECT_REFERENCE: break;
333 default: formatter % "???"; break;
334 }
335
336 for (const std::string& property_name_part : property_names) {
337 if (property_name_part.empty()) // apparently is empty for a EFFECT_TARGET_VALUE_REFERENCE
338 continue;
339 std::string stringtable_key("DESC_VAR_" + boost::to_upper_copy(property_name_part));
340 formatter % UserString(stringtable_key);
341 }
342
343 std::string retval = boost::io::str(formatter);
344 //std::cerr << "ret: " << retval << std::endl;
345 return retval;
346 }
347
ComplexVariableDescription(const std::vector<std::string> & property_names,const ValueRef<int> * int_ref1,const ValueRef<int> * int_ref2,const ValueRef<int> * int_ref3,const ValueRef<std::string> * string_ref1,const ValueRef<std::string> * string_ref2)348 std::string ComplexVariableDescription(const std::vector<std::string>& property_names,
349 const ValueRef<int>* int_ref1,
350 const ValueRef<int>* int_ref2,
351 const ValueRef<int>* int_ref3,
352 const ValueRef<std::string>* string_ref1,
353 const ValueRef<std::string>* string_ref2)
354 {
355 if (property_names.empty()) {
356 ErrorLogger() << "ComplexVariableDescription passed empty property names?!";
357 return "";
358 }
359
360 std::string stringtable_key("DESC_VAR_" + boost::to_upper_copy(property_names.back()));
361 if (!UserStringExists(stringtable_key))
362 return "";
363
364 boost::format formatter = FlexibleFormat(UserString(stringtable_key));
365
366 if (int_ref1)
367 formatter % int_ref1->Description();
368 if (int_ref2)
369 formatter % int_ref2->Description();
370 if (int_ref3)
371 formatter % int_ref3->Description();
372 if (string_ref1)
373 formatter % string_ref1->Description();
374 if (string_ref2)
375 formatter % string_ref2->Description();
376
377 return boost::io::str(formatter);
378 }
379
ComplexVariableDump(const std::vector<std::string> & property_names,const ValueRef<int> * int_ref1,const ValueRef<int> * int_ref2,const ValueRef<int> * int_ref3,const ValueRef<std::string> * string_ref1,const ValueRef<std::string> * string_ref2)380 std::string ComplexVariableDump(const std::vector<std::string>& property_names,
381 const ValueRef<int>* int_ref1,
382 const ValueRef<int>* int_ref2,
383 const ValueRef<int>* int_ref3,
384 const ValueRef<std::string>* string_ref1,
385 const ValueRef<std::string>* string_ref2)
386 {
387 std::string retval;
388 if (property_names.empty()) {
389 ErrorLogger() << "ComplexVariableDump passed empty property names?!";
390 return "ComplexVariable";
391 } else {
392 retval += property_names.back();
393 }
394
395 // TODO: Depending on the property name, the parameter names will vary.
396 // Need to handle each case correctly, in order for the Dumped
397 // text to be parsable as the correct ComplexVariable.
398
399 if (int_ref1)
400 retval += " int1 = " + int_ref1->Dump();
401 if (int_ref2)
402 retval += " int2 = " + int_ref2->Dump();
403 if (int_ref3)
404 retval += " int3 = " + int_ref3->Dump();
405 if (string_ref1)
406 retval += " string1 = " + string_ref1->Dump();
407 if (string_ref2)
408 retval += " string2 = " + string_ref2->Dump();
409
410 return retval;
411 }
412
StatisticDescription(StatisticType stat_type,const std::string & value_desc,const std::string & condition_desc)413 std::string StatisticDescription(StatisticType stat_type,
414 const std::string& value_desc,
415 const std::string& condition_desc)
416 {
417 std::string stringtable_key("DESC_VAR_" + boost::to_upper_copy(
418 boost::lexical_cast<std::string>(stat_type)));
419
420 if (UserStringExists(stringtable_key)) {
421 boost::format formatter = FlexibleFormat(stringtable_key);
422 formatter % value_desc % condition_desc;
423 return boost::io::str(formatter);
424 }
425
426 return UserString("DESC_VAR_STATISITIC");
427 }
428
429 ///////////////////////////////////////////////////////////
430 // Constant //
431 ///////////////////////////////////////////////////////////
432 template <>
Description() const433 std::string Constant<int>::Description() const
434 {
435 if (std::abs(m_value) < 1000)
436 return std::to_string(m_value);
437 return DoubleToString(m_value, 3, false);
438 }
439
440 template <>
Description() const441 std::string Constant<double>::Description() const
442 { return DoubleToString(m_value, 3, false); }
443
444 template <>
Description() const445 std::string Constant<std::string>::Description() const
446 {
447 if (m_value == "CurrentContent")
448 return m_top_level_content;
449 return m_value;
450 }
451
452 template <>
Dump(unsigned short ntabs) const453 std::string Constant<PlanetSize>::Dump(unsigned short ntabs) const
454 {
455 switch (m_value) {
456 case SZ_TINY: return "Tiny";
457 case SZ_SMALL: return "Small";
458 case SZ_MEDIUM: return "Medium";
459 case SZ_LARGE: return "Large";
460 case SZ_HUGE: return "Huge";
461 case SZ_ASTEROIDS: return "Asteroids";
462 case SZ_GASGIANT: return "GasGiant";
463 default: return "?";
464 }
465 }
466
467 template <>
Dump(unsigned short ntabs) const468 std::string Constant<PlanetType>::Dump(unsigned short ntabs) const
469 {
470 switch (m_value) {
471 case PT_SWAMP: return "Swamp";
472 case PT_TOXIC: return "Toxic";
473 case PT_INFERNO: return "Inferno";
474 case PT_RADIATED: return "Radiated";
475 case PT_BARREN: return "Barren";
476 case PT_TUNDRA: return "Tundra";
477 case PT_DESERT: return "Desert";
478 case PT_TERRAN: return "Terran";
479 case PT_OCEAN: return "Ocean";
480 case PT_ASTEROIDS: return "Asteroids";
481 case PT_GASGIANT: return "GasGiant";
482 default: return "?";
483 }
484 }
485
486 template <>
Dump(unsigned short ntabs) const487 std::string Constant<PlanetEnvironment>::Dump(unsigned short ntabs) const
488 {
489 switch (m_value) {
490 case PE_UNINHABITABLE: return "Uninhabitable";
491 case PE_HOSTILE: return "Hostile";
492 case PE_POOR: return "Poor";
493 case PE_ADEQUATE: return "Adequate";
494 case PE_GOOD: return "Good";
495 default: return "?";
496 }
497 }
498
499 template <>
Dump(unsigned short ntabs) const500 std::string Constant<UniverseObjectType>::Dump(unsigned short ntabs) const
501 {
502 switch (m_value) {
503 case OBJ_BUILDING: return "Building";
504 case OBJ_SHIP: return "Ship";
505 case OBJ_FLEET: return "Fleet";
506 case OBJ_PLANET: return "Planet";
507 case OBJ_POP_CENTER: return "PopulationCenter";
508 case OBJ_PROD_CENTER: return "ProductionCenter";
509 case OBJ_SYSTEM: return "System";
510 case OBJ_FIELD: return "Field";
511 default: return "?";
512 }
513 }
514
515 template <>
Dump(unsigned short ntabs) const516 std::string Constant<StarType>::Dump(unsigned short ntabs) const
517 {
518 switch (m_value) {
519 case STAR_BLUE: return "Blue";
520 case STAR_WHITE: return "White";
521 case STAR_YELLOW: return "Yellow";
522 case STAR_ORANGE: return "Orange";
523 case STAR_RED: return "Red";
524 case STAR_NEUTRON: return "Neutron";
525 case STAR_BLACK: return "BlackHole";
526 case STAR_NONE: return "NoStar";
527 default: return "Unknown";
528 }
529 }
530
531 template <>
Dump(unsigned short ntabs) const532 std::string Constant<Visibility>::Dump(unsigned short ntabs) const
533 {
534 switch (m_value) {
535 case VIS_NO_VISIBILITY: return "Invisible";
536 case VIS_BASIC_VISIBILITY: return "Basic";
537 case VIS_PARTIAL_VISIBILITY:return "Partial";
538 case VIS_FULL_VISIBILITY: return "Full";
539 default: return "Unknown";
540 }
541 }
542
543 template <>
Dump(unsigned short ntabs) const544 std::string Constant<int>::Dump(unsigned short ntabs) const
545 { return std::to_string(m_value); }
546
547 template <>
Dump(unsigned short ntabs) const548 std::string Constant<double>::Dump(unsigned short ntabs) const
549 { return std::to_string(m_value); }
550
551 template <>
Dump(unsigned short ntabs) const552 std::string Constant<std::string>::Dump(unsigned short ntabs) const
553 { return "\"" + Description() + "\""; }
554
555 template <>
Eval(const ScriptingContext & context) const556 std::string Constant<std::string>::Eval(const ScriptingContext& context) const
557 {
558 if (m_value == "CurrentContent")
559 return m_top_level_content;
560 return m_value;
561 }
562
563 ///////////////////////////////////////////////////////////
564 // Variable //
565 ///////////////////////////////////////////////////////////
566 #define IF_CURRENT_VALUE(T) \
567 if (m_ref_type == EFFECT_TARGET_VALUE_REFERENCE) { \
568 if (context.current_value.empty()) \
569 throw std::runtime_error( \
570 "Variable<" #T ">::Eval(): Value could not be evaluated, " \
571 "because no current value was provided."); \
572 try { \
573 return boost::any_cast<T>(context.current_value); \
574 } catch (const boost::bad_any_cast&) { \
575 throw std::runtime_error( \
576 "Variable<" #T ">::Eval(): Value could not be evaluated, " \
577 "because the provided current value is not an " #T "."); \
578 } \
579 }
580
581 #define LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(T) \
582 ErrorLogger() << "Variable<" #T ">::Eval unrecognized object " \
583 "property: " \
584 << TraceReference(m_property_name, m_ref_type, context); \
585 if (context.source) \
586 ErrorLogger() << "source: " << context.source->ObjectType() << " " \
587 << context.source->ID() << " ( " \
588 << context.source->Name() << " ) "; \
589 else \
590 ErrorLogger() << "source (none)";
591
592 template <>
Eval(const ScriptingContext & context) const593 PlanetSize Variable<PlanetSize>::Eval(const ScriptingContext& context) const
594 {
595 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
596
597 IF_CURRENT_VALUE(PlanetSize)
598
599 auto object = FollowReference(m_property_name.begin(), m_property_name.end(),
600 m_ref_type, context);
601 if (!object) {
602 ErrorLogger() << "Variable<PlanetSize>::Eval unable to follow reference: " << TraceReference(m_property_name, m_ref_type, context);
603 return INVALID_PLANET_SIZE;
604 }
605
606 std::function<PlanetSize (const Planet&)> planet_property{nullptr};
607
608 if (property_name == "PlanetSize")
609 planet_property = &Planet::Size;
610 else if (property_name == "NextLargerPlanetSize")
611 planet_property = &Planet::NextLargerPlanetSize;
612 else if (property_name == "NextSmallerPlanetSize")
613 planet_property = &Planet::NextSmallerPlanetSize;
614
615 if (planet_property) {
616 if (auto p = std::dynamic_pointer_cast<const Planet>(object))
617 return planet_property(*p);
618 return INVALID_PLANET_SIZE;
619 }
620
621 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(PlanetSize)
622
623 return INVALID_PLANET_SIZE;
624 }
625
626 template <>
Eval(const ScriptingContext & context) const627 PlanetType Variable<PlanetType>::Eval(const ScriptingContext& context) const
628 {
629 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
630
631 IF_CURRENT_VALUE(PlanetType)
632
633 auto object = FollowReference(m_property_name.begin(), m_property_name.end(),
634 m_ref_type, context);
635 if (!object) {
636 ErrorLogger() << "Variable<PlanetType>::Eval unable to follow reference: " << TraceReference(m_property_name, m_ref_type, context);
637 return INVALID_PLANET_TYPE;
638 }
639
640 std::function<PlanetType (const Planet&)> planet_property{nullptr};
641
642 #if BOOST_VERSION >= 106000
643 using boost::placeholders::_1;
644 #endif
645
646 if (property_name == "PlanetType")
647 planet_property = &Planet::Type;
648 else if (property_name == "OriginalType")
649 planet_property = &Planet::OriginalType;
650 else if (property_name == "NextCloserToOriginalPlanetType")
651 planet_property = &Planet::NextCloserToOriginalPlanetType;
652 else if (property_name == "NextBetterPlanetType")
653 planet_property = boost::bind(&Planet::NextBetterPlanetTypeForSpecies, _1, "");
654 else if (property_name == "ClockwiseNextPlanetType")
655 planet_property = &Planet::ClockwiseNextPlanetType;
656 else if (property_name == "CounterClockwiseNextPlanetType")
657 planet_property = &Planet::CounterClockwiseNextPlanetType;
658
659 if (planet_property) {
660 if (auto p = std::dynamic_pointer_cast<const Planet>(object))
661 return planet_property(*p);
662 return INVALID_PLANET_TYPE;
663 }
664
665 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(PlanetType)
666
667 return INVALID_PLANET_TYPE;
668 }
669
670 template <>
Eval(const ScriptingContext & context) const671 PlanetEnvironment Variable<PlanetEnvironment>::Eval(const ScriptingContext& context) const
672 {
673 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
674
675 IF_CURRENT_VALUE(PlanetEnvironment)
676
677 if (property_name == "PlanetEnvironment") {
678 auto object = FollowReference(m_property_name.begin(), m_property_name.end(), m_ref_type, context);
679 if (!object) {
680 ErrorLogger() << "Variable<PlanetEnvironment>::Eval unable to follow reference: " << TraceReference(m_property_name, m_ref_type, context);
681 return INVALID_PLANET_ENVIRONMENT;
682 }
683 if (auto p = std::dynamic_pointer_cast<const Planet>(object))
684 return p->EnvironmentForSpecies();
685
686 return INVALID_PLANET_ENVIRONMENT;
687 }
688
689 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(PlanetEnvironment)
690
691 return INVALID_PLANET_ENVIRONMENT;
692 }
693
694 template <>
Eval(const ScriptingContext & context) const695 UniverseObjectType Variable<UniverseObjectType>::Eval(const ScriptingContext& context) const
696 {
697 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
698
699 IF_CURRENT_VALUE(UniverseObjectType)
700
701 if (property_name == "ObjectType") {
702 auto object = FollowReference(m_property_name.begin(), m_property_name.end(), m_ref_type, context);
703 if (!object) {
704 ErrorLogger() << "Variable<UniverseObjectType>::Eval unable to follow reference: " << TraceReference(m_property_name, m_ref_type, context);
705 return INVALID_UNIVERSE_OBJECT_TYPE;
706 }
707 ObjectTypeVisitor v;
708 if (object->Accept(v))
709 return v.m_type;
710 else if (std::dynamic_pointer_cast<const PopCenter>(object))
711 return OBJ_POP_CENTER;
712 else if (std::dynamic_pointer_cast<const ResourceCenter>(object))
713 return OBJ_PROD_CENTER;
714
715 return INVALID_UNIVERSE_OBJECT_TYPE;
716 }
717
718 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(UniverseObjectType)
719
720 return INVALID_UNIVERSE_OBJECT_TYPE;
721 }
722
723 template <>
Eval(const ScriptingContext & context) const724 StarType Variable<StarType>::Eval(const ScriptingContext& context) const
725 {
726 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
727
728 IF_CURRENT_VALUE(StarType)
729
730 auto object = FollowReference(m_property_name.begin(), m_property_name.end(),
731 m_ref_type, context);
732 if (!object) {
733 ErrorLogger() << "Variable<StarType>::Eval unable to follow reference: " << TraceReference(m_property_name, m_ref_type, context);
734 return INVALID_STAR_TYPE;
735 }
736
737 std::function<StarType (const System&)> system_property{nullptr};
738
739 if (property_name == "StarType")
740 system_property = &System::GetStarType;
741 else if (property_name == "NextOlderStarType")
742 system_property = &System::NextOlderStarType;
743 else if (property_name == "NextYoungerStarType")
744 system_property = &System::NextYoungerStarType;
745
746 if (system_property) {
747 if (auto s = std::dynamic_pointer_cast<const System>(object))
748 return system_property(*s);
749 return INVALID_STAR_TYPE;
750 }
751
752 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(StarType)
753
754 return INVALID_STAR_TYPE;
755 }
756
757 template <>
Eval(const ScriptingContext & context) const758 Visibility Variable<Visibility>::Eval(const ScriptingContext& context) const
759 {
760 IF_CURRENT_VALUE(Visibility)
761
762 // As of this writing, there are no properties of objects that directly
763 // return a Visibility, as it will normally need to be queried for a
764 // particular empire
765
766 ErrorLogger() << "Variable<Visibility>::Eval unrecognized object property: " << TraceReference(m_property_name, m_ref_type, context);
767
768 return INVALID_VISIBILITY;
769 }
770
771 template <>
Eval(const ScriptingContext & context) const772 double Variable<double>::Eval(const ScriptingContext& context) const
773 {
774 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
775
776 IF_CURRENT_VALUE(float)
777
778 if (m_ref_type == NON_OBJECT_REFERENCE) {
779 if ((property_name == "UniverseCentreX") |
780 (property_name == "UniverseCentreY"))
781 {
782 return GetUniverse().UniverseWidth() / 2;
783 } else if (property_name == "UniverseWidth") {
784 return GetUniverse().UniverseWidth();
785 }
786
787 // add more non-object reference double functions here
788
789 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(float)
790
791 return 0.0;
792 }
793
794 auto object = FollowReference(m_property_name.begin(), m_property_name.end(),
795 m_ref_type, context);
796 if (!object) {
797 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(float)
798
799 return 0.0;
800 }
801
802 MeterType meter_type = NameToMeter(property_name);
803 if (object && meter_type != INVALID_METER_TYPE) {
804 if (auto* m = object->GetMeter(meter_type))
805 return m_return_immediate_value ? m->Current() : m->Initial();
806 return 0.0;
807
808 } else if (property_name == "X") {
809 return object->X();
810
811 } else if (property_name == "Y") {
812 return object->Y();
813
814 }
815
816 std::function<double (const Planet&)> planet_property{nullptr};
817
818 if (property_name == "SizeAsDouble")
819 planet_property = &Planet::Size;
820 else if (property_name == "HabitableSize")
821 planet_property = &Planet::HabitableSize;
822 else if (property_name == "DistanceFromOriginalType")
823 planet_property = &Planet::DistanceFromOriginalType;
824
825 if (planet_property) {
826 if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
827 return planet_property(*planet);
828 return 0.0;
829
830 }
831
832 if (property_name == "CombatBout") {
833 return context.combat_info.bout;
834
835 } else if (property_name == "CurrentTurn") {
836 return CurrentTurn();
837
838 } else if (property_name == "Attack") {
839 if (auto fleet = std::dynamic_pointer_cast<const Fleet>(object))
840 return fleet->Damage();
841 if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
842 return ship->TotalWeaponsDamage();
843 if (auto fighter = std::dynamic_pointer_cast<const Fighter>(object))
844 return fighter->Damage();
845 return 0.0;
846
847 } else if (property_name == "PropagatedSupplyRange") {
848 const auto& ranges = GetSupplyManager().PropagatedSupplyRanges();
849 auto range_it = ranges.find(object->SystemID());
850 if (range_it == ranges.end())
851 return 0.0;
852 return range_it->second;
853
854 } else if (property_name == "PropagatedSupplyDistance") {
855 const auto& ranges = GetSupplyManager().PropagatedSupplyDistances();
856 auto range_it = ranges.find(object->SystemID());
857 if (range_it == ranges.end())
858 return 0.0;
859 return range_it->second;
860 }
861
862 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(float)
863
864 return 0.0;
865 }
866
867 template <>
Eval(const ScriptingContext & context) const868 int Variable<int>::Eval(const ScriptingContext& context) const
869 {
870 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
871
872 IF_CURRENT_VALUE(int)
873
874 if (m_ref_type == NON_OBJECT_REFERENCE) {
875 if (property_name == "CombatBout")
876 return context.combat_info.bout;
877 if (property_name == "CurrentTurn")
878 return CurrentTurn();
879 if (property_name == "GalaxySize")
880 return GetGalaxySetupData().GetSize();
881 if (property_name == "GalaxyShape")
882 return static_cast<int>(GetGalaxySetupData().GetShape());
883 if (property_name == "GalaxyAge")
884 return static_cast<int>(GetGalaxySetupData().GetAge());
885 if (property_name == "GalaxyStarlaneFrequency")
886 return static_cast<int>(GetGalaxySetupData().GetStarlaneFreq());
887 if (property_name == "GalaxyPlanetDensity")
888 return static_cast<int>(GetGalaxySetupData().GetPlanetDensity());
889 if (property_name == "GalaxySpecialFrequency")
890 return static_cast<int>(GetGalaxySetupData().GetSpecialsFreq());
891 if (property_name == "GalaxyMonsterFrequency")
892 return static_cast<int>(GetGalaxySetupData().GetMonsterFreq());
893 if (property_name == "GalaxyNativeFrequency")
894 return static_cast<int>(GetGalaxySetupData().GetNativeFreq());
895 if (property_name == "GalaxyMaxAIAggression")
896 return static_cast<int>(GetGalaxySetupData().GetAggression());
897
898 // non-object values passed by abuse of context.current_value
899 if (property_name == "UsedInDesignID") {
900 // check if an int was passed as the current_value, as would be
901 // done when evaluating a ValueRef for the cost or production
902 // time of a part or hull in a ship design. this should be the id
903 // of the design.
904 try {
905 return boost::any_cast<int>(context.current_value);
906 } catch (...) {
907 ErrorLogger() << "Variable<int>::Eval could get ship design id for property: " << TraceReference(m_property_name, m_ref_type, context);
908 }
909 return 0;
910 }
911
912 // add more non-object reference int functions here
913
914 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(int)
915
916 return 0;
917 }
918
919 auto object = FollowReference(m_property_name.begin(), m_property_name.end(),
920 m_ref_type, context);
921 if (!object) {
922 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(int)
923
924 return 0;
925 }
926
927 if (property_name == "Owner") {
928 return object->Owner();
929 }
930 else if (property_name == "SystemID") {
931 return object->SystemID();
932
933 }
934 else if (property_name == "ContainerID") {
935 return object->ContainerObjectID();
936
937 }
938 else if (property_name == "SupplyingEmpire") {
939 return GetSupplyManager().EmpireThatCanSupplyAt(object->SystemID());
940 }
941 else if (property_name == "ID") {
942 return object->ID();
943 }
944 else if (property_name == "CreationTurn") {
945 return object->CreationTurn();
946 }
947 else if (property_name == "Age") {
948 return object->AgeInTurns();
949
950 }
951
952 std::function<int (const Ship&)> ship_property{nullptr};
953
954 if (property_name == "ArrivedOnTurn")
955 ship_property = &Ship::ArrivedOnTurn;
956 else if (property_name == "LastTurnActiveInBattle")
957 ship_property = &Ship::LastTurnActiveInCombat;
958 else if (property_name == "LastTurnResupplied")
959 ship_property = &Ship::LastResuppliedOnTurn;
960
961 if (ship_property) {
962 if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
963 return ship_property(*ship);
964 return INVALID_GAME_TURN;
965 }
966
967 std::function<int (const Fleet&)> fleet_property{nullptr};
968
969 if (property_name == "FinalDestinationID")
970 fleet_property = &Fleet::FinalDestinationID;
971 else if (property_name == "NextSystemID")
972 fleet_property = &Fleet::NextSystemID;
973 else if (property_name == "PreviousSystemID")
974 fleet_property = &Fleet::PreviousSystemID;
975 else if (property_name == "ArrivalStarlaneID")
976 fleet_property = &Fleet::ArrivalStarlane;
977
978 if (fleet_property) {
979 if (auto fleet = std::dynamic_pointer_cast<const Fleet>(object))
980 return fleet_property(*fleet);
981 return INVALID_OBJECT_ID;
982 }
983
984 std::function<int (const Planet&)> planet_property{nullptr};
985
986 if (property_name == "LastTurnAttackedByShip")
987 planet_property = &Planet::LastTurnAttackedByShip;
988 else if (property_name == "LastTurnColonized")
989 planet_property = &Planet::LastTurnColonized;
990 else if (property_name == "LastTurnConquered")
991 planet_property = &Planet::LastTurnConquered;
992
993 if (planet_property) {
994 if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
995 return planet_property(*planet);
996 return INVALID_GAME_TURN;
997 }
998
999 if (property_name == "TurnsSinceFocusChange") {
1000 if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
1001 return planet->TurnsSinceFocusChange();
1002 return 0;
1003
1004 }
1005 else if (property_name == "ProducedByEmpireID") {
1006 if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
1007 return ship->ProducedByEmpireID();
1008 else if (auto building = std::dynamic_pointer_cast<const Building>(object))
1009 return building->ProducedByEmpireID();
1010 return ALL_EMPIRES;
1011
1012 }
1013 else if (property_name == "DesignID") {
1014 if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
1015 return ship->DesignID();
1016 return INVALID_DESIGN_ID;
1017
1018 }
1019 else if (property_name == "SpeciesID") {
1020 if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
1021 return GetSpeciesManager().GetSpeciesID(planet->SpeciesName());
1022 else if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
1023 return GetSpeciesManager().GetSpeciesID(ship->SpeciesName());
1024 return -1;
1025
1026 }
1027 else if (property_name == "FleetID") {
1028 if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
1029 return ship->FleetID();
1030 else if (auto fleet = std::dynamic_pointer_cast<const Fleet>(object))
1031 return fleet->ID();
1032 return INVALID_OBJECT_ID;
1033
1034 }
1035 else if (property_name == "PlanetID") {
1036 if (auto building = std::dynamic_pointer_cast<const Building>(object))
1037 return building->PlanetID();
1038 else if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
1039 return planet->ID();
1040 return INVALID_OBJECT_ID;
1041
1042 }
1043 else if (property_name == "NearestSystemID") {
1044 if (object->SystemID() != INVALID_OBJECT_ID)
1045 return object->SystemID();
1046 return GetPathfinder()->NearestSystemTo(object->X(), object->Y());
1047
1048 }
1049 else if (property_name == "NumShips") {
1050 if (auto fleet = std::dynamic_pointer_cast<const Fleet>(object))
1051 return fleet->NumShips();
1052 return 0;
1053
1054 }
1055 else if (property_name == "NumStarlanes") {
1056 if (auto system = std::dynamic_pointer_cast<const System>(object))
1057 return system->NumStarlanes();
1058 return 0;
1059
1060 }
1061 else if (property_name == "LastTurnBattleHere") {
1062 if (auto const_system = std::dynamic_pointer_cast<const System>(object))
1063 return const_system->LastTurnBattleHere();
1064 else if (auto system = context.ContextObjects().get<System>(object->SystemID()))
1065 return system->LastTurnBattleHere();
1066 return INVALID_GAME_TURN;
1067
1068 }
1069 else if (property_name == "Orbit") {
1070 if (auto system = context.ContextObjects().get<System>(object->SystemID()))
1071 return system->OrbitOfPlanet(object->ID());
1072 return -1;
1073
1074 }
1075 else if (property_name == "ETA") {
1076 if (auto fleet = std::dynamic_pointer_cast<const Fleet>(object))
1077 return fleet->ETA().first;
1078 return 0;
1079
1080 }
1081 else if (property_name == "NumSpecials") {
1082 return object->Specials().size();
1083
1084 }
1085 else if (property_name == "LaunchedFrom") {
1086 if (auto fighter = std::dynamic_pointer_cast<const Fighter>(object))
1087 return fighter->LaunchedFrom();
1088 return INVALID_OBJECT_ID;
1089 }
1090
1091 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(int)
1092
1093 return 0;
1094 }
1095
1096 template <>
Eval(const ScriptingContext & context) const1097 std::vector<std::string> Variable<std::vector<std::string>>::Eval(
1098 const ScriptingContext& context) const
1099 {
1100 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
1101
1102 IF_CURRENT_VALUE(std::vector<std::string>)
1103
1104 if (m_ref_type == NON_OBJECT_REFERENCE) {
1105 // add more non-object reference string vector functions here
1106 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(std::vector<std::string>)
1107
1108 return {};
1109 }
1110
1111 auto object = FollowReference(m_property_name.begin(), m_property_name.end(),
1112 m_ref_type, context);
1113 if (!object) {
1114 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(std::vector<std::string>)
1115
1116 return {};
1117 }
1118
1119 if (property_name == "Tags") {
1120 std::vector<std::string> retval;
1121 for (auto tag : object->Tags())
1122 retval.push_back(tag);
1123 return retval;
1124 }
1125 else if (property_name == "Specials") {
1126 std::vector<std::string> retval;
1127 for (auto spec : object->Specials())
1128 retval.push_back(spec.first);
1129 return retval;
1130 }
1131 else if (property_name == "AvailableFoci") {
1132 if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
1133 return planet->AvailableFoci();
1134 return {};
1135 }
1136 else if (property_name == "Parts") {
1137 if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
1138 if (const ShipDesign* design = ship->Design())
1139 return design->Parts();
1140 return {};
1141 }
1142
1143 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(std::vector<std::string>)
1144
1145 return {};
1146 }
1147
1148 template <>
Eval(const ScriptingContext & context) const1149 std::string Variable<std::string>::Eval(const ScriptingContext& context) const
1150 {
1151 const std::string& property_name = m_property_name.empty() ? "" : m_property_name.back();
1152
1153 IF_CURRENT_VALUE(std::string)
1154
1155 if (m_ref_type == NON_OBJECT_REFERENCE) {
1156 if (property_name == "GalaxySeed")
1157 return GetGalaxySetupData().GetSeed();
1158
1159 // add more non-object reference string functions here
1160 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(std::string)
1161
1162 return "";
1163 }
1164
1165 auto object = FollowReference(m_property_name.begin(), m_property_name.end(),
1166 m_ref_type, context);
1167 if (!object) {
1168 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(std::string)
1169
1170 return "";
1171 }
1172
1173 if (property_name == "Name") {
1174 return object->Name();
1175
1176 } else if (property_name == "OwnerName") {
1177 int owner_empire_id = object->Owner();
1178 if (Empire* empire = GetEmpire(owner_empire_id))
1179 return empire->Name();
1180 return "";
1181
1182 } else if (property_name == "TypeName") {
1183 return boost::lexical_cast<std::string>(object->ObjectType());
1184
1185 }
1186
1187 std::function<std::string (const Empire&)> empire_property{nullptr};
1188
1189 if (property_name == "OwnerLeastExpensiveEnqueuedTech")
1190 empire_property = &Empire::LeastExpensiveEnqueuedTech;
1191 else if (property_name == "OwnerMostExpensiveEnqueuedTech")
1192 empire_property = &Empire::MostExpensiveEnqueuedTech;
1193 else if (property_name == "OwnerMostRPCostLeftEnqueuedTech")
1194 empire_property = &Empire::MostRPCostLeftEnqueuedTech;
1195 else if (property_name == "OwnerMostRPSpentEnqueuedTech")
1196 empire_property = &Empire::MostRPSpentEnqueuedTech;
1197 else if (property_name == "OwnerTopPriorityEnqueuedTech")
1198 empire_property = &Empire::TopPriorityEnqueuedTech;
1199
1200 if (empire_property) {
1201 const Empire* empire = GetEmpire(object->Owner());
1202 if (!empire)
1203 return "";
1204 return empire_property(*empire);
1205 }
1206
1207 if (property_name == "Species") {
1208 if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
1209 return planet->SpeciesName();
1210 else if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
1211 return ship->SpeciesName();
1212 else if (auto fighter = std::dynamic_pointer_cast<const Fighter>(object))
1213 return fighter->SpeciesName();
1214 return "";
1215
1216 } else if (property_name == "Hull") {
1217 if (auto ship = std::dynamic_pointer_cast<const Ship>(object))
1218 if (const ShipDesign* design = ship->Design())
1219 return design->Hull();
1220 return "";
1221
1222 } else if (property_name == "FieldType") {
1223 if (auto field = std::dynamic_pointer_cast<const Field>(object))
1224 return field->FieldTypeName();
1225 return "";
1226
1227 } else if (property_name == "BuildingType") {
1228 if (auto building = std::dynamic_pointer_cast<const Building>(object))
1229 return building->BuildingTypeName();
1230 return "";
1231
1232 } else if (property_name == "Focus") {
1233 if (auto planet = std::dynamic_pointer_cast<const Planet>(object))
1234 return planet->Focus();
1235 return "";
1236
1237 } else if (property_name == "PreferredFocus") {
1238 const Species* species = nullptr;
1239 if (auto planet = std::dynamic_pointer_cast<const Planet>(object)) {
1240 species = GetSpecies(planet->SpeciesName());
1241 } else if (auto ship = std::dynamic_pointer_cast<const Ship>(object)) {
1242 species = GetSpecies(ship->SpeciesName());
1243 }
1244 if (species)
1245 return species->PreferredFocus();
1246 return "";
1247
1248 }
1249
1250 LOG_UNKNOWN_VARIABLE_PROPERTY_TRACE(std::string)
1251
1252 return "";
1253 }
1254
1255 #undef IF_CURRENT_VALUE
1256
1257 ///////////////////////////////////////////////////////////
1258 // Statistic //
1259 ///////////////////////////////////////////////////////////
1260 template <>
Eval(const ScriptingContext & context) const1261 double Statistic<double>::Eval(const ScriptingContext& context) const
1262 {
1263 Condition::ObjectSet condition_matches;
1264 GetConditionMatches(context, condition_matches, m_sampling_condition.get());
1265
1266 // these two statistic types don't depend on the object property values,
1267 // so can be evaluated without getting those values.
1268 if (m_stat_type == COUNT)
1269 return static_cast<double>(condition_matches.size());
1270 if (m_stat_type == IF)
1271 return condition_matches.empty() ? 0.0 : 1.0;
1272
1273 // evaluate property for each condition-matched object
1274 std::map<std::shared_ptr<const UniverseObject>, double> object_property_values;
1275 GetObjectPropertyValues(context, condition_matches, object_property_values);
1276
1277 return ReduceData(object_property_values);
1278 }
1279
1280 template <>
Eval(const ScriptingContext & context) const1281 int Statistic<int>::Eval(const ScriptingContext& context) const
1282 {
1283 Condition::ObjectSet condition_matches;
1284 GetConditionMatches(context, condition_matches, m_sampling_condition.get());
1285
1286 // these two statistic types don't depend on the object property values,
1287 // so can be evaluated without getting those values.
1288 if (m_stat_type == COUNT)
1289 return static_cast<int>(condition_matches.size());
1290 if (m_stat_type == IF)
1291 return condition_matches.empty() ? 0 : 1;
1292
1293 // evaluate property for each condition-matched object
1294 std::map<std::shared_ptr<const UniverseObject>, int> object_property_values;
1295 GetObjectPropertyValues(context, condition_matches, object_property_values);
1296
1297 return ReduceData(object_property_values);
1298 }
1299
1300 template <>
Eval(const ScriptingContext & context) const1301 std::string Statistic<std::string>::Eval(const ScriptingContext& context) const
1302 {
1303 Condition::ObjectSet condition_matches;
1304 GetConditionMatches(context, condition_matches, m_sampling_condition.get());
1305
1306 if (condition_matches.empty())
1307 return "";
1308
1309 // special case for IF statistic... return a non-empty string for true
1310 if (m_stat_type == IF)
1311 return " "; // not an empty string
1312
1313 // todo: consider allowing MAX and MIN using string sorting?
1314
1315 // the only other statistic that can be computed on non-number property
1316 // types and that is itself of a non-number type is the most common value
1317 if (m_stat_type != MODE) {
1318 ErrorLogger() << "Statistic<std::string>::Eval has invalid statistic type: "
1319 << m_stat_type;
1320 return "";
1321 }
1322
1323 // evaluate property for each condition-matched object
1324 std::map<std::shared_ptr<const UniverseObject>, std::string> object_property_values;
1325 GetObjectPropertyValues(context, condition_matches, object_property_values);
1326
1327 // count number of each result, tracking which has the most occurances
1328 std::map<std::string, unsigned int> histogram;
1329 auto most_common_property_value_it = histogram.begin();
1330 unsigned int max_seen(0);
1331
1332 for (const auto& entry : object_property_values) {
1333 const std::string& property_value = entry.second;
1334
1335 auto hist_it = histogram.find(property_value);
1336 if (hist_it == histogram.end())
1337 hist_it = histogram.insert({property_value, 0}).first;
1338 unsigned int& num_seen = hist_it->second;
1339
1340 num_seen++;
1341
1342 if (num_seen > max_seen) {
1343 most_common_property_value_it = hist_it;
1344 max_seen = num_seen;
1345 }
1346 }
1347
1348 // return result (property value) that occured most frequently
1349 return most_common_property_value_it->first;
1350 }
1351
1352 ///////////////////////////////////////////////////////////
1353 // ComplexVariable //
1354 ///////////////////////////////////////////////////////////
1355 template <>
Eval(const ScriptingContext & context) const1356 PlanetSize ComplexVariable<PlanetSize>::Eval(const ScriptingContext& context) const
1357 { return INVALID_PLANET_SIZE; }
1358
1359 template <>
Eval(const ScriptingContext & context) const1360 PlanetType ComplexVariable<PlanetType>::Eval(const ScriptingContext& context) const
1361 { return INVALID_PLANET_TYPE; } // TODO: Species favourite planet type?
1362
1363 template <>
Eval(const ScriptingContext & context) const1364 PlanetEnvironment ComplexVariable<PlanetEnvironment>::Eval(const ScriptingContext& context) const
1365 {
1366 const std::string& variable_name = m_property_name.back();
1367
1368 if (variable_name == "PlanetEnvironmentForSpecies") {
1369 int planet_id = INVALID_OBJECT_ID;
1370 if (m_int_ref1)
1371 planet_id = m_int_ref1->Eval(context);
1372 const auto planet = context.ContextObjects().get<Planet>(planet_id);
1373 if (!planet)
1374 return INVALID_PLANET_ENVIRONMENT;
1375
1376 std::string species_name;
1377 if (m_string_ref1)
1378 species_name = m_string_ref1->Eval(context);
1379 return planet->EnvironmentForSpecies(species_name);
1380 }
1381
1382 return INVALID_PLANET_ENVIRONMENT;
1383 }
1384
1385 template <>
Eval(const ScriptingContext & context) const1386 UniverseObjectType ComplexVariable<UniverseObjectType>::Eval(const ScriptingContext& context) const
1387 { return INVALID_UNIVERSE_OBJECT_TYPE; }
1388
1389 template <>
Eval(const ScriptingContext & context) const1390 StarType ComplexVariable<StarType>::Eval(const ScriptingContext& context) const
1391 { return INVALID_STAR_TYPE; }
1392
1393 template <>
Eval(const ScriptingContext & context) const1394 Visibility ComplexVariable<Visibility>::Eval(const ScriptingContext& context) const
1395 {
1396 const std::string& variable_name = m_property_name.back();
1397
1398 if (variable_name == "EmpireObjectVisiblity") {
1399 int empire_id = ALL_EMPIRES;
1400 if (m_int_ref1) {
1401 empire_id = m_int_ref1->Eval(context);
1402 if (empire_id == ALL_EMPIRES)
1403 return VIS_NO_VISIBILITY;
1404 }
1405
1406 int object_id = INVALID_OBJECT_ID;
1407 if (m_int_ref2) {
1408 object_id = m_int_ref2->Eval(context);
1409 if (object_id == INVALID_OBJECT_ID)
1410 return VIS_NO_VISIBILITY;
1411 }
1412
1413 return GetUniverse().GetObjectVisibilityByEmpire(object_id, empire_id);
1414 }
1415
1416 return INVALID_VISIBILITY;
1417 }
1418
1419 template <>
Eval(const ScriptingContext & context) const1420 int ComplexVariable<int>::Eval(const ScriptingContext& context) const
1421 {
1422 const std::string& variable_name = m_property_name.back();
1423
1424 std::function<const std::map<std::string, int>& (const Empire&)> empire_property_string_key{nullptr};
1425
1426 if (variable_name == "BuildingTypesOwned")
1427 empire_property_string_key = &Empire::BuildingTypesOwned;
1428 if (variable_name == "BuildingTypesProduced")
1429 empire_property_string_key = &Empire::BuildingTypesProduced;
1430 if (variable_name == "BuildingTypesScrapped")
1431 empire_property_string_key = &Empire::BuildingTypesScrapped;
1432 if (variable_name == "SpeciesColoniesOwned")
1433 empire_property_string_key = &Empire::SpeciesColoniesOwned;
1434 if (variable_name == "SpeciesPlanetsBombed")
1435 empire_property_string_key = &Empire::SpeciesPlanetsBombed;
1436 if (variable_name == "SpeciesPlanetsDepoped")
1437 empire_property_string_key = &Empire::SpeciesPlanetsDepoped;
1438 if (variable_name == "SpeciesPlanetsInvaded")
1439 empire_property_string_key = &Empire::SpeciesPlanetsInvaded;
1440 if (variable_name == "SpeciesShipsDestroyed")
1441 empire_property_string_key = &Empire::SpeciesShipsDestroyed;
1442 if (variable_name == "SpeciesShipsLost")
1443 empire_property_string_key = &Empire::SpeciesShipsLost;
1444 if (variable_name == "SpeciesShipsOwned")
1445 empire_property_string_key = &Empire::SpeciesShipsOwned;
1446 if (variable_name == "SpeciesShipsProduced")
1447 empire_property_string_key = &Empire::SpeciesShipsProduced;
1448 if (variable_name == "SpeciesShipsScrapped")
1449 empire_property_string_key = &Empire::SpeciesShipsScrapped;
1450 if (variable_name == "ShipPartsOwned")
1451 empire_property_string_key = &Empire::ShipPartsOwned;
1452 if (variable_name == "TurnTechResearched")
1453 empire_property_string_key = &Empire::ResearchedTechs;
1454
1455 // empire properties indexed by strings
1456 if (empire_property_string_key) {
1457 using namespace boost::adaptors;
1458
1459 Empire* empire{nullptr};
1460 if (m_int_ref1) {
1461 int empire_id = m_int_ref1->Eval(context);
1462 if (empire_id == ALL_EMPIRES)
1463 return 0;
1464 empire = GetEmpire(empire_id);
1465 }
1466
1467 std::function<bool (const std::map<std::string, int>::value_type&)> key_filter{nullptr};
1468 key_filter = [](auto e){ return true; };
1469
1470 if (m_string_ref1) {
1471 std::string key_string = m_string_ref1->Eval(context);
1472 if (key_string.empty())
1473 return 0;
1474
1475 if (empire && variable_name == "TurnTechResearched" && !empire->TechResearched(key_string))
1476 // special case for techs: make unresearched-tech's research-turn a big number
1477 return IMPOSSIBLY_LARGE_TURN;
1478
1479 key_filter = [k = key_string](auto e){ return k == e.first; };
1480 }
1481 else if (variable_name == "ShipPartsOwned" && m_int_ref2) {
1482 int key_int = m_int_ref2->Eval(context);
1483 if (key_int <= INVALID_SHIP_PART_CLASS || key_int >= NUM_SHIP_PART_CLASSES)
1484 return 0;
1485
1486 auto key_filter = [part_class = ShipPartClass(key_int)](const std::map<ShipPartClass, int>::value_type& e){ return e.first == part_class; };
1487
1488 if (empire)
1489 return boost::accumulate(empire->ShipPartClassOwned() | filtered(key_filter) | map_values, 0);
1490
1491 int sum = 0;
1492 for (const auto& empire_entry : Empires())
1493 sum += boost::accumulate(empire_entry.second->ShipPartClassOwned() | filtered(key_filter) | map_values, 0);
1494 return sum;
1495 }
1496
1497 if (empire)
1498 return boost::accumulate(empire_property_string_key(*empire) | filtered(key_filter) | map_values, 0);
1499
1500 int sum = 0;
1501 for (const auto& empire_entry : Empires())
1502 sum += boost::accumulate(empire_property_string_key(*(empire_entry.second)) | filtered(key_filter) | map_values, 0);
1503 return sum;
1504 }
1505
1506 std::function<const std::map<int, int>& (const Empire&)> empire_property_int_key{nullptr};
1507
1508 if (variable_name == "EmpireShipsDestroyed")
1509 empire_property_int_key = &Empire::EmpireShipsDestroyed;
1510 if (variable_name == "ShipDesignsDestroyed")
1511 empire_property_int_key = &Empire::ShipDesignsDestroyed;
1512 if (variable_name == "ShipDesignsLost")
1513 empire_property_int_key = &Empire::ShipDesignsLost;
1514 if (variable_name == "ShipDesignsOwned")
1515 empire_property_int_key = &Empire::ShipDesignsOwned;
1516 if (variable_name == "ShipDesignsInProduction")
1517 empire_property_int_key = &Empire::ShipDesignsInProduction;
1518 if (variable_name == "ShipDesignsProduced")
1519 empire_property_int_key = &Empire::ShipDesignsProduced;
1520 if (variable_name == "ShipDesignsScrapped")
1521 empire_property_int_key = &Empire::ShipDesignsScrapped;
1522
1523 // empire properties indexed by integers
1524 if (empire_property_int_key) {
1525 using namespace boost::adaptors;
1526
1527 Empire* empire{nullptr};
1528 if (m_int_ref1) {
1529 int empire_id = m_int_ref1->Eval(context);
1530 if (empire_id == ALL_EMPIRES)
1531 return 0;
1532 empire = GetEmpire(empire_id);
1533 }
1534
1535 std::function<bool (const std::map<int, int>::value_type&)> key_filter{nullptr};
1536 key_filter = [](auto e){ return true; };
1537
1538 // if a key integer specified, get just that entry (for single empire or sum of all empires)
1539 if (m_int_ref2)
1540 key_filter = [k = m_int_ref2->Eval(context)](auto e){ return k == e.first; };
1541
1542 // although indexed by integers, some of these may be specified by a
1543 // string that needs to be looked up. if a key string specified, get
1544 // just that entry (for single empire or sum of all empires)
1545 if (m_string_ref1) {
1546 std::string key_string = m_string_ref1->Eval(context);
1547 if (key_string.empty())
1548 return 0;
1549 int key_int = -1;
1550 if (boost::istarts_with(variable_name, "ShipDesign")) {
1551 // look up ship design id corresponding to specified predefined ship design name
1552 const ShipDesign* design = GetPredefinedShipDesign(key_string);
1553 if (design)
1554 key_int = design->ID();
1555 }
1556 key_filter = [k = key_int](auto e){ return k == e.first; };
1557 }
1558
1559 if (empire)
1560 return boost::accumulate(empire_property_int_key(*empire) | filtered(key_filter) | map_values, 0);
1561
1562 int sum = 0;
1563 for (const auto& empire_entry : Empires())
1564 sum += boost::accumulate(empire_property_int_key(*(empire_entry.second)) | filtered(key_filter) | map_values, 0);
1565 return sum;
1566 }
1567
1568 // unindexed empire proprties
1569 if (variable_name == "OutpostsOwned") {
1570 Empire* empire{nullptr};
1571 if (m_int_ref1) {
1572 int empire_id = m_int_ref1->Eval(context);
1573 if (empire_id == ALL_EMPIRES)
1574 return 0;
1575 empire = GetEmpire(empire_id);
1576 if (!empire)
1577 return 0;
1578 }
1579
1580 std::function<int (const Empire*)> empire_property{nullptr};
1581 empire_property = &Empire::OutpostsOwned;
1582
1583 using namespace boost::adaptors;
1584
1585 if (!empire)
1586 return boost::accumulate(Empires() | map_values | transformed(empire_property), 0);
1587
1588 return empire_property(empire);
1589 }
1590
1591 // non-empire properties
1592 if (variable_name == "GameRule") {
1593 if (!m_string_ref1)
1594 return 0;
1595 std::string rule_name = m_string_ref1->Eval();
1596 if (rule_name.empty())
1597 return 0;
1598 if (!GetGameRules().RuleExists(rule_name))
1599 return 0;
1600 try {
1601 // can cast boolean, int, or double-valued rules to int
1602 switch (GetGameRules().GetType(rule_name)) {
1603 case GameRules::Type::TOGGLE: {
1604 return GetGameRules().Get<bool>(rule_name);
1605 break;
1606 }
1607 case GameRules::Type::INT: {
1608 return GetGameRules().Get<int>(rule_name);
1609 break;
1610 }
1611 case GameRules::Type::DOUBLE: {
1612 return static_cast<int>(GetGameRules().Get<double>(rule_name));
1613 break;
1614 }
1615 default:
1616 break;
1617 }
1618 } catch (...) {
1619 }
1620 return 0;
1621 }
1622 else if (variable_name == "PartsInShipDesign") {
1623 int design_id = INVALID_DESIGN_ID;
1624 if (m_int_ref1) {
1625 design_id = m_int_ref1->Eval(context);
1626 if (design_id == INVALID_DESIGN_ID)
1627 return 0;
1628 } else {
1629 return 0;
1630 }
1631
1632 std::string ship_part_name;
1633 if (m_string_ref1) {
1634 ship_part_name = m_string_ref1->Eval(context);
1635 }
1636
1637 const ShipDesign* design = GetShipDesign(design_id);
1638 if (!design)
1639 return 0;
1640
1641 if (ship_part_name.empty())
1642 return design->PartCount();
1643
1644 int count = 0;
1645 for (const std::string& part : design->Parts()) {
1646 if (ship_part_name == part)
1647 count++;
1648 }
1649 return count;
1650 }
1651 else if (variable_name == "PartOfClassInShipDesign") {
1652 int design_id = INVALID_DESIGN_ID;
1653 if (m_int_ref1) {
1654 design_id = m_int_ref1->Eval(context);
1655 if (design_id == INVALID_DESIGN_ID)
1656 return 0;
1657 } else {
1658 return 0;
1659 }
1660
1661 const ShipDesign* design = GetShipDesign(design_id);
1662 if (!design)
1663 return 0;
1664
1665 std::string part_class_name;
1666 if (m_string_ref1) {
1667 part_class_name = m_string_ref1->Eval(context);
1668 } else {
1669 return 0;
1670 }
1671 ShipPartClass part_class = INVALID_SHIP_PART_CLASS;
1672 try {
1673 part_class = boost::lexical_cast<ShipPartClass>(part_class_name);
1674 } catch (...) {
1675 return 0;
1676 }
1677
1678 int count = 0;
1679 for (const std::string& part_name : design->Parts()) {
1680 if (part_name.empty())
1681 continue;
1682 const ShipPart* part = GetShipPart(part_name);
1683 if (!part)
1684 continue;
1685 if (part->Class() == part_class)
1686 count++;
1687 }
1688 return count;
1689 }
1690 else if (variable_name == "JumpsBetween") {
1691 int object1_id = INVALID_OBJECT_ID;
1692 if (m_int_ref1)
1693 object1_id = m_int_ref1->Eval(context);
1694
1695 int object2_id = INVALID_OBJECT_ID;
1696 if (m_int_ref2)
1697 object2_id = m_int_ref2->Eval(context);
1698
1699 int retval = GetPathfinder()->JumpDistanceBetweenObjects(object1_id, object2_id);
1700 if (retval == INT_MAX)
1701 return -1;
1702 return retval;
1703 }
1704 else if (variable_name == "JumpsBetweenByEmpireSupplyConnections") {
1705 int object1_id = INVALID_OBJECT_ID;
1706 if (m_int_ref1)
1707 object1_id = m_int_ref1->Eval(context);
1708
1709 int object2_id = INVALID_OBJECT_ID;
1710 if (m_int_ref2)
1711 object2_id = m_int_ref2->Eval(context);
1712
1713 // TODO: implement supply-connect-restriction path length determination...
1714 // in the meantime, leave empire_id commented out to avoid unused var warning
1715 //int empire_id = ALL_EMPIRES;
1716 //if (m_int_ref3)
1717 // empire_id = m_int_ref3->Eval(context);
1718
1719 int retval = GetPathfinder()->JumpDistanceBetweenObjects(object1_id, object2_id/*, empire_id*/);
1720 if (retval == INT_MAX)
1721 return -1;
1722 return retval;
1723 }
1724 else if (variable_name == "SlotsInHull") {
1725 const ShipHull* ship_hull = nullptr;
1726 if (m_string_ref1) {
1727 std::string hull_name = m_string_ref1->Eval(context);
1728 ship_hull = GetShipHull(hull_name);
1729 if (!ship_hull)
1730 return 0;
1731 } else {
1732 return 0;
1733 }
1734 return ship_hull->Slots().size();
1735 }
1736 else if (variable_name == "SlotsInShipDesign") {
1737 int design_id = INVALID_DESIGN_ID;
1738 if (m_int_ref1) {
1739 design_id = m_int_ref1->Eval(context);
1740 if (design_id == INVALID_DESIGN_ID)
1741 return 0;
1742 } else {
1743 return 0;
1744 }
1745
1746 const ShipDesign* design = GetShipDesign(design_id);
1747 if (!design)
1748 return 0;
1749
1750 const ShipHull* ship_hull = GetShipHull(design->Hull());
1751 if (!ship_hull)
1752 return 0;
1753 return ship_hull->Slots().size();
1754 }
1755 else if (variable_name == "SpecialAddedOnTurn") {
1756 int object_id = INVALID_OBJECT_ID;
1757 if (m_int_ref1)
1758 object_id = m_int_ref1->Eval(context);
1759 if (object_id == INVALID_OBJECT_ID)
1760 return 0;
1761 auto object = context.ContextObjects().get(object_id);
1762 if (!object)
1763 return 0;
1764
1765 std::string special_name;
1766 if (m_string_ref1)
1767 special_name = m_string_ref1->Eval(context);
1768 if (special_name.empty())
1769 return 0;
1770
1771 return object->SpecialAddedOnTurn(special_name);
1772 }
1773
1774 return 0;
1775 }
1776
1777 template <>
Eval(const ScriptingContext & context) const1778 double ComplexVariable<double>::Eval(const ScriptingContext& context) const
1779 {
1780 const std::string& variable_name = m_property_name.back();
1781
1782 std::function<float (const ShipHull&)> hull_property{nullptr};
1783
1784 if (variable_name == "HullFuel")
1785 hull_property = &ShipHull::Fuel;
1786 else if (variable_name == "HullStealth")
1787 hull_property = &ShipHull::Stealth;
1788 else if (variable_name == "HullStructure")
1789 hull_property = &ShipHull::Structure;
1790 else if (variable_name == "HullSpeed")
1791 hull_property = &ShipHull::Speed;
1792
1793 if (hull_property) {
1794 std::string ship_hull_name;
1795 if (m_string_ref1)
1796 ship_hull_name = m_string_ref1->Eval(context);
1797
1798 const ShipHull* ship_hull = GetShipHull(ship_hull_name);
1799 if (!ship_hull)
1800 return 0.0f;
1801
1802 return hull_property(*ship_hull);
1803 }
1804
1805 // empire properties indexed by integers
1806 std::function<const std::map<int, float>& (const Empire&)> empire_property{nullptr};
1807
1808 if (variable_name == "PropagatedSystemSupplyRange")
1809 empire_property = [](const Empire& empire){ return GetSupplyManager().PropagatedSupplyRanges(empire.EmpireID()); };
1810 if (variable_name == "SystemSupplyRange")
1811 empire_property = &Empire::SystemSupplyRanges;
1812 if (variable_name == "PropagatedSystemSupplyDistance")
1813 empire_property = [](const Empire& empire){ return GetSupplyManager().PropagatedSupplyDistances(empire.EmpireID()); };
1814
1815 if (empire_property) {
1816 using namespace boost::adaptors;
1817
1818 Empire* empire{nullptr};
1819
1820 if (m_int_ref1) {
1821 int empire_id = m_int_ref1->Eval(context);
1822 if (empire_id == ALL_EMPIRES)
1823 return 0.0;
1824 empire = GetEmpire(empire_id);
1825 }
1826
1827 std::function<bool (const std::map<int, float>::value_type&)> key_filter;
1828 key_filter = [](auto k){ return true; };
1829
1830 if (m_int_ref2)
1831 key_filter = [k = m_int_ref2->Eval(context)](auto e){ return k == e.first; };
1832
1833 if (empire)
1834 return boost::accumulate(empire_property(*empire) | filtered(key_filter) | map_values, 0.0f);
1835
1836 float sum = 0.0f;
1837 for (const auto& empire_entry : Empires())
1838 sum += boost::accumulate(empire_property(*(empire_entry.second)) | filtered(key_filter) | map_values, 0.0f);
1839 return sum;
1840 }
1841
1842 // non-empire properties
1843 if (variable_name == "GameRule") {
1844 if (!m_string_ref1)
1845 return 0.0;
1846 std::string rule_name = m_string_ref1->Eval();
1847 if (rule_name.empty())
1848 return 0.0;
1849 if (!GetGameRules().RuleExists(rule_name))
1850 return 0.0;
1851 try {
1852 // can cast boolean, int, or double-valued rules to double
1853 switch (GetGameRules().GetType(rule_name)) {
1854 case GameRules::Type::TOGGLE: {
1855 return GetGameRules().Get<bool>(rule_name);
1856 break;
1857 }
1858 case GameRules::Type::INT: {
1859 return GetGameRules().Get<int>(rule_name);
1860 break;
1861 }
1862 case GameRules::Type::DOUBLE: {
1863 return GetGameRules().Get<double>(rule_name);
1864 break;
1865 }
1866 default:
1867 break;
1868 }
1869 } catch (...) {
1870 }
1871 return 0.0;
1872 }
1873 else if (variable_name == "PartCapacity") {
1874 std::string ship_part_name;
1875 if (m_string_ref1)
1876 ship_part_name = m_string_ref1->Eval(context);
1877
1878 const ShipPart* ship_part = GetShipPart(ship_part_name);
1879 if (!ship_part)
1880 return 0.0;
1881
1882 return ship_part->Capacity();
1883
1884 }
1885 else if (variable_name == "PartSecondaryStat") {
1886 std::string ship_part_name;
1887 if (m_string_ref1)
1888 ship_part_name = m_string_ref1->Eval(context);
1889
1890 const ShipPart* ship_part = GetShipPart(ship_part_name);
1891 if (!ship_part)
1892 return 0.0;
1893
1894 return ship_part->SecondaryStat();
1895
1896 }
1897 else if (variable_name == "ShipDesignCost") {
1898 int design_id = INVALID_DESIGN_ID;
1899 if (m_int_ref1)
1900 design_id = m_int_ref1->Eval(context);
1901
1902 const ShipDesign* design = GetShipDesign(design_id);
1903 if (!design)
1904 return 0.0;
1905
1906 int empire_id = ALL_EMPIRES;
1907 if (m_int_ref2)
1908 empire_id = m_int_ref2->Eval(context);
1909
1910 int location_id = INVALID_OBJECT_ID;
1911 if (m_int_ref3)
1912 location_id = m_int_ref3->Eval(context);
1913
1914 return design->ProductionCost(empire_id, location_id);
1915
1916 }
1917 else if (variable_name == "EmpireMeterValue") {
1918 int empire_id = ALL_EMPIRES;
1919 if (m_int_ref1)
1920 empire_id = m_int_ref1->Eval(context);
1921 Empire* empire = GetEmpire(empire_id);
1922 if (!empire)
1923 return 0.0;
1924
1925 std::string empire_meter_name;
1926 if (m_string_ref1)
1927 empire_meter_name = m_string_ref1->Eval(context);
1928 Meter* meter = empire->GetMeter(empire_meter_name);
1929 if (!meter)
1930 return 0.0;
1931 return meter->Current();
1932 }
1933 else if (variable_name == "DirectDistanceBetween") {
1934 int object1_id = INVALID_OBJECT_ID;
1935 if (m_int_ref1)
1936 object1_id = m_int_ref1->Eval(context);
1937 auto obj1 = context.ContextObjects().get(object1_id);
1938 if (!obj1)
1939 return 0.0;
1940
1941 int object2_id = INVALID_OBJECT_ID;
1942 if (m_int_ref2)
1943 object2_id = m_int_ref2->Eval(context);
1944 auto obj2 = context.ContextObjects().get(object2_id);
1945 if (!obj2)
1946 return 0.0;
1947
1948 double dx = obj2->X() - obj1->X();
1949 double dy = obj2->Y() - obj1->Y();
1950 return static_cast<float>(std::sqrt(dx*dx + dy*dy));
1951
1952 }
1953 else if (variable_name == "ShortestPath") {
1954 int object1_id = INVALID_OBJECT_ID;
1955 if (m_int_ref1)
1956 object1_id = m_int_ref1->Eval(context);
1957
1958 int object2_id = INVALID_OBJECT_ID;
1959 if (m_int_ref2)
1960 object2_id = m_int_ref2->Eval(context);
1961
1962 return GetPathfinder()->ShortestPathDistance(object1_id, object2_id);
1963
1964 }
1965 else if (variable_name == "SpeciesEmpireOpinion") {
1966 int empire_id = ALL_EMPIRES;
1967 if (m_int_ref1)
1968 empire_id = m_int_ref1->Eval(context);
1969
1970 std::string species_name;
1971 if (m_string_ref1)
1972 species_name = m_string_ref1->Eval(context);
1973
1974 return GetSpeciesManager().SpeciesEmpireOpinion(species_name, empire_id);
1975
1976 }
1977 else if (variable_name == "SpeciesSpeciesOpinion") {
1978 std::string opinionated_species_name;
1979 if (m_string_ref1)
1980 opinionated_species_name = m_string_ref1->Eval(context);
1981
1982 std::string rated_species_name;
1983 if (m_string_ref2)
1984 rated_species_name = m_string_ref2->Eval(context);
1985
1986 return GetSpeciesManager().SpeciesSpeciesOpinion(opinionated_species_name, rated_species_name);
1987 }
1988 else if (variable_name == "SpecialCapacity") {
1989 int object_id = INVALID_OBJECT_ID;
1990 if (m_int_ref1)
1991 object_id = m_int_ref1->Eval(context);
1992 auto object = context.ContextObjects().get(object_id);
1993 if (!object)
1994 return 0.0;
1995
1996 std::string special_name;
1997 if (m_string_ref1)
1998 special_name = m_string_ref1->Eval(context);
1999 if (special_name.empty())
2000 return 0.0;
2001
2002 return object->SpecialCapacity(special_name);
2003 }
2004 else if (variable_name == "ShipPartMeter") {
2005 int object_id = INVALID_OBJECT_ID;
2006 if (m_int_ref1)
2007 object_id = m_int_ref1->Eval(context);
2008 auto object = context.ContextObjects().get(object_id);
2009 if (!object)
2010 return 0.0;
2011 auto ship = std::dynamic_pointer_cast<const Ship>(object);
2012 if (!ship)
2013 return 0.0;
2014
2015 std::string part_name;
2016 if (m_string_ref1)
2017 part_name = m_string_ref1->Eval(context);
2018 if (part_name.empty())
2019 return 0.0;
2020
2021 std::string meter_name;
2022 if (m_string_ref2)
2023 meter_name = m_string_ref2->Eval(context);
2024 if (meter_name.empty())
2025 return 0.0;
2026
2027 MeterType meter_type = NameToMeter(meter_name);
2028 if (meter_type != INVALID_METER_TYPE) {
2029 if (m_return_immediate_value)
2030 return ship->CurrentPartMeterValue(meter_type, part_name);
2031 else
2032 return ship->InitialPartMeterValue(meter_type, part_name);
2033 }
2034 }
2035
2036 return 0.0;
2037 }
2038
2039 namespace {
TechsResearchedByEmpire(int empire_id)2040 std::vector<std::string> TechsResearchedByEmpire(int empire_id) {
2041 std::vector<std::string> retval;
2042 const Empire* empire = GetEmpire(empire_id);
2043 if (!empire)
2044 return retval;
2045 for (const auto& tech : GetTechManager()) {
2046 if (empire->TechResearched(tech->Name()))
2047 retval.push_back(tech->Name());
2048 }
2049 return retval;
2050 }
2051
TechsResearchableByEmpire(int empire_id)2052 std::vector<std::string> TechsResearchableByEmpire(int empire_id) {
2053 std::vector<std::string> retval;
2054 const Empire* empire = GetEmpire(empire_id);
2055 if (!empire)
2056 return retval;
2057 for (const auto& tech : GetTechManager()) {
2058 if (empire->ResearchableTech(tech->Name()))
2059 retval.push_back(tech->Name());
2060 }
2061 return retval;
2062 }
2063
TransferrableTechs(int sender_empire_id,int receipient_empire_id)2064 std::vector<std::string> TransferrableTechs(int sender_empire_id, int receipient_empire_id) {
2065 std::vector<std::string> sender_researched_techs = TechsResearchedByEmpire(sender_empire_id);
2066 std::vector<std::string> recepient_researchable = TechsResearchableByEmpire(receipient_empire_id);
2067
2068 std::vector<std::string> retval;
2069
2070 if (sender_researched_techs.empty() || recepient_researchable.empty())
2071 return retval;
2072
2073 // find intersection of two lists
2074 std::sort(sender_researched_techs.begin(), sender_researched_techs.end());
2075 std::sort(recepient_researchable.begin(), recepient_researchable.end());
2076 std::set_intersection(sender_researched_techs.begin(), sender_researched_techs.end(),
2077 recepient_researchable.begin(), recepient_researchable.end(),
2078 std::back_inserter(retval));
2079
2080 // find techs common to both lists
2081 return retval;
2082 }
2083 }
2084
2085 template <>
Eval(const ScriptingContext & context) const2086 std::string ComplexVariable<std::string>::Eval(const ScriptingContext& context) const
2087 {
2088 const std::string& variable_name = m_property_name.back();
2089
2090 std::function<std::string (const Empire&)> empire_property{nullptr};
2091 auto null_property = [](const Empire&) -> std::string { return ""; };
2092
2093 // unindexed empire properties
2094 if (variable_name == "LowestCostEnqueuedTech")
2095 empire_property = &Empire::LeastExpensiveEnqueuedTech;
2096 else if (variable_name == "HighestCostEnqueuedTech")
2097 empire_property = &Empire::MostExpensiveEnqueuedTech;
2098 else if (variable_name == "TopPriorityEnqueuedTech")
2099 empire_property = &Empire::TopPriorityEnqueuedTech;
2100 else if (variable_name == "MostSpentEnqueuedTech")
2101 empire_property = &Empire::MostRPSpentEnqueuedTech;
2102 else if (variable_name == "LowestCostResearchableTech")
2103 empire_property = &Empire::LeastExpensiveResearchableTech;
2104 else if (variable_name == "HighestCostResearchableTech")
2105 empire_property = &Empire::MostExpensiveResearchableTech;
2106 else if (variable_name == "TopPriorityResearchableTech")
2107 empire_property = &Empire::TopPriorityResearchableTech;
2108 else if (variable_name == "MostSpentResearchableTech")
2109 empire_property = &Empire::MostExpensiveResearchableTech;
2110 else if (variable_name == "MostSpentTransferrableTech")
2111 empire_property = null_property;
2112 else if (variable_name == "RandomTransferrableTech")
2113 empire_property = null_property;
2114 else if (variable_name == "MostPopulousSpecies")
2115 empire_property = null_property;
2116 else if (variable_name == "MostHappySpecies")
2117 empire_property = null_property;
2118 else if (variable_name == "LeastHappySpecies")
2119 empire_property = null_property;
2120 else if (variable_name == "RandomColonizableSpecies")
2121 empire_property = null_property;
2122 else if (variable_name == "RandomControlledSpecies")
2123 empire_property = null_property;
2124
2125 if (empire_property) {
2126 int empire_id = ALL_EMPIRES;
2127 if (m_int_ref1) {
2128 empire_id = m_int_ref1->Eval(context);
2129 if (empire_id == ALL_EMPIRES)
2130 return "";
2131 }
2132 const Empire* empire = GetEmpire(empire_id);
2133 if (!empire)
2134 return "";
2135
2136 return empire_property(*empire);
2137 }
2138
2139 if (variable_name == "RandomEnqueuedTech") {
2140 int empire_id = ALL_EMPIRES;
2141 if (m_int_ref1) {
2142 empire_id = m_int_ref1->Eval(context);
2143 if (empire_id == ALL_EMPIRES)
2144 return "";
2145 }
2146 const Empire* empire = GetEmpire(empire_id);
2147 if (!empire)
2148 return "";
2149 // get all techs on queue, randomly pick one
2150 const ResearchQueue& queue = empire->GetResearchQueue();
2151 std::vector<std::string> all_enqueued_techs = queue.AllEnqueuedProjects();
2152 if (all_enqueued_techs.empty())
2153 return "";
2154 std::size_t idx = RandSmallInt(0, static_cast<int>(all_enqueued_techs.size()) - 1);
2155 return *std::next(all_enqueued_techs.begin(), idx);
2156
2157 } else if (variable_name == "RandomResearchableTech") {
2158 int empire_id = ALL_EMPIRES;
2159 if (m_int_ref1) {
2160 empire_id = m_int_ref1->Eval(context);
2161 if (empire_id == ALL_EMPIRES)
2162 return "";
2163 }
2164 const Empire* empire = GetEmpire(empire_id);
2165 if (!empire)
2166 return "";
2167
2168 std::vector<std::string> researchable_techs = TechsResearchableByEmpire(empire_id);
2169 if (researchable_techs.empty())
2170 return "";
2171 std::size_t idx = RandSmallInt(0, static_cast<int>(researchable_techs.size()) - 1);
2172 return *std::next(researchable_techs.begin(), idx);
2173 } else if (variable_name == "RandomCompleteTech") {
2174 int empire_id = ALL_EMPIRES;
2175 if (m_int_ref1) {
2176 empire_id = m_int_ref1->Eval(context);
2177 if (empire_id == ALL_EMPIRES)
2178 return "";
2179 }
2180 const Empire* empire = GetEmpire(empire_id);
2181 if (!empire)
2182 return "";
2183
2184 std::vector<std::string> complete_techs = TechsResearchedByEmpire(empire_id);
2185 if (complete_techs.empty())
2186 return "";
2187 std::size_t idx = RandSmallInt(0, static_cast<int>(complete_techs.size()) - 1);
2188 return *std::next(complete_techs.begin(), idx);
2189 } else if (variable_name == "LowestCostTransferrableTech") {
2190 int empire1_id = ALL_EMPIRES;
2191 if (m_int_ref1) {
2192 empire1_id = m_int_ref1->Eval(context);
2193 if (empire1_id == ALL_EMPIRES)
2194 return "";
2195 }
2196
2197 int empire2_id = ALL_EMPIRES;
2198 if (m_int_ref2) {
2199 empire2_id = m_int_ref2->Eval(context);
2200 if (empire2_id == ALL_EMPIRES)
2201 return "";
2202 }
2203
2204 std::vector<std::string> sendable_techs = TransferrableTechs(empire1_id, empire2_id);
2205 if (sendable_techs.empty())
2206 return "";
2207 std::size_t idx = RandSmallInt(0, static_cast<int>(sendable_techs.size()) - 1);
2208 return *std::next(sendable_techs.begin(), idx);
2209
2210 } else if (variable_name == "HighestCostTransferrableTech") {
2211 int empire1_id = ALL_EMPIRES;
2212 if (m_int_ref1) {
2213 empire1_id = m_int_ref1->Eval(context);
2214 if (empire1_id == ALL_EMPIRES)
2215 return "";
2216 }
2217
2218 int empire2_id = ALL_EMPIRES;
2219 if (m_int_ref2) {
2220 empire2_id = m_int_ref2->Eval(context);
2221 if (empire2_id == ALL_EMPIRES)
2222 return "";
2223 }
2224
2225 std::vector<std::string> sendable_techs = TransferrableTechs(empire1_id, empire2_id);
2226 if (sendable_techs.empty())
2227 return "";
2228
2229 std::string retval;
2230 float highest_cost = 0.0f;
2231 for (const std::string& tech_name : sendable_techs) {
2232 const Tech* tech = GetTech(tech_name);
2233 if (!tech)
2234 continue;
2235 float rc = tech->ResearchCost(empire2_id);
2236 if (rc > highest_cost) {
2237 highest_cost = rc;
2238 retval = tech_name;
2239 }
2240 }
2241 return retval;
2242
2243 } else if (variable_name == "TopPriorityTransferrableTech") {
2244 int empire1_id = ALL_EMPIRES;
2245 if (m_int_ref1) {
2246 empire1_id = m_int_ref1->Eval(context);
2247 if (empire1_id == ALL_EMPIRES)
2248 return "";
2249 }
2250
2251 int empire2_id = ALL_EMPIRES;
2252 if (m_int_ref2) {
2253 empire2_id = m_int_ref2->Eval(context);
2254 if (empire2_id == ALL_EMPIRES)
2255 return "";
2256 }
2257 const Empire* empire2 = GetEmpire(empire2_id);
2258 if (!empire2)
2259 return "";
2260
2261 std::vector<std::string> sendable_techs = TransferrableTechs(empire1_id, empire2_id);
2262 if (sendable_techs.empty())
2263 return "";
2264
2265 std::string retval = *sendable_techs.begin(); // pick first tech by default, hopefully to be replaced below
2266 int position_of_top_found_tech = INT_MAX;
2267
2268 // search queue to find which transferrable tech is at the top of the list
2269 const ResearchQueue& queue = empire2->GetResearchQueue();
2270 for (const std::string& tech : sendable_techs) {
2271 auto queue_it = queue.find(tech);
2272 if (queue_it == queue.end())
2273 continue;
2274 int queue_pos = std::distance(queue.begin(), queue_it);
2275 if (queue_pos < position_of_top_found_tech) {
2276 retval = tech;
2277 position_of_top_found_tech = queue_pos;
2278 }
2279 }
2280 return retval;
2281 }
2282
2283 // non-empire properties
2284 if (variable_name == "GameRule") {
2285 if (!m_string_ref1)
2286 return "";
2287 std::string rule_name = m_string_ref1->Eval();
2288 if (rule_name.empty())
2289 return "";
2290 if (!GetGameRules().RuleExists(rule_name))
2291 return "";
2292 try {
2293 // can cast boolean, int, double, or string-valued rules to strings
2294 switch (GetGameRules().GetType(rule_name)) {
2295 case GameRules::Type::TOGGLE: {
2296 return std::to_string(GetGameRules().Get<bool>(rule_name));
2297 break;
2298 }
2299 case GameRules::Type::INT: {
2300 return std::to_string(GetGameRules().Get<int>(rule_name));
2301 break;
2302 }
2303 case GameRules::Type::DOUBLE: {
2304 return DoubleToString(GetGameRules().Get<double>(rule_name), 3, false);
2305 break;
2306 }
2307 case GameRules::Type::STRING: {
2308 return GetGameRules().Get<std::string>(rule_name);
2309 break;
2310 }
2311 default:
2312 break;
2313 }
2314 } catch (...) {
2315 }
2316 return "";
2317 }
2318
2319 return "";
2320 }
2321
2322 #undef IF_CURRENT_VALUE
2323
2324 template <>
Dump(unsigned short ntabs) const2325 std::string ComplexVariable<Visibility>::Dump(unsigned short ntabs) const
2326 {
2327 const std::string& variable_name = m_property_name.back();
2328 std::string retval = variable_name;
2329
2330 if (variable_name == "EmpireObjectVisiblity") {
2331 if (m_int_ref1)
2332 retval += " empire = " + m_int_ref1->Dump(ntabs);
2333 if (m_int_ref2)
2334 retval += " object = " + m_int_ref2->Dump(ntabs);
2335 }
2336
2337 return retval;
2338 }
2339
2340 template <>
Dump(unsigned short ntabs) const2341 std::string ComplexVariable<double>::Dump(unsigned short ntabs) const
2342 {
2343 const std::string& variable_name = m_property_name.back();
2344 std::string retval = variable_name;
2345
2346 // empire properties indexed by integers
2347 if (variable_name == "PropagatedSystemSupplyRange" ||
2348 variable_name == "SystemSupplyRange" ||
2349 variable_name == "PropagatedSystemSupplyDistance")
2350 {
2351 if (m_int_ref1)
2352 retval += " empire = " + m_int_ref1->Dump(ntabs);
2353 if (m_int_ref2)
2354 retval += " system = " + m_int_ref2->Dump(ntabs);
2355
2356 }
2357 else if (variable_name == "GameRule" ||
2358 variable_name == "HullFuel" ||
2359 variable_name == "HullStealth" ||
2360 variable_name == "HullStructure" ||
2361 variable_name == "HullSpeed" ||
2362 variable_name == "PartCapacity" ||
2363 variable_name == "PartSecondaryStat")
2364 {
2365 if (m_string_ref1)
2366 retval += " name = " + m_string_ref1->Dump(ntabs);
2367
2368 }
2369 else if (variable_name == "EmpireMeterValue") {
2370 if (m_int_ref1)
2371 retval += " empire = " + m_int_ref1->Dump(ntabs);
2372 if (m_string_ref1)
2373 retval += " meter = " + m_string_ref1->Dump(ntabs);
2374
2375 }
2376 else if (variable_name == "ShipPartMeter") {
2377 // ShipPartMeter part = "SR_WEAPON_1_1" meter = Capacity object = Source.ID
2378 if (m_string_ref1)
2379 retval += " part = " + m_string_ref1->Dump(ntabs);
2380 if (m_string_ref2)
2381 retval += " meter = " + m_string_ref2->Dump(ntabs); // wrapped in quotes " but shouldn't be to be consistent with parser
2382 if (m_int_ref1)
2383 retval += " object = " + m_int_ref1->Dump(ntabs);
2384
2385 }
2386 else if (variable_name == "DirectDistanceBetween" ||
2387 variable_name == "ShortestPath")
2388 {
2389 if (m_int_ref1)
2390 retval += " object = " + m_int_ref1->Dump(ntabs);
2391 if (m_int_ref2)
2392 retval += " object = " + m_int_ref2->Dump(ntabs);
2393
2394 }
2395 else if (variable_name == "SpeciesEmpireOpinion") {
2396 if (m_int_ref1)
2397 retval += " empire = " + m_int_ref1->Dump(ntabs);
2398 if (m_string_ref1)
2399 retval += " species = " + m_string_ref1->Dump(ntabs);
2400
2401 }
2402 else if (variable_name == "SpeciesSpeciesOpinion") {
2403 if (m_string_ref1)
2404 retval += " species = " + m_string_ref1->Dump(ntabs);
2405 if (m_string_ref2)
2406 retval += " species = " + m_string_ref2->Dump(ntabs);
2407
2408 }
2409 else if (variable_name == "SpecialCapacity") {
2410 if (m_string_ref1)
2411 retval += " name = " + m_string_ref1->Dump(ntabs);
2412 if (m_int_ref1)
2413 retval += " object = " + m_int_ref1->Dump(ntabs);
2414
2415 }
2416
2417 return retval;
2418 }
2419
2420 template <>
Dump(unsigned short ntabs) const2421 std::string ComplexVariable<int>::Dump(unsigned short ntabs) const
2422 {
2423 const std::string& variable_name = m_property_name.back();
2424 std::string retval = variable_name;
2425
2426 return retval;
2427 }
2428
2429 template <>
Dump(unsigned short ntabs) const2430 std::string ComplexVariable<std::string>::Dump(unsigned short ntabs) const
2431 {
2432 const std::string& variable_name = m_property_name.back();
2433 std::string retval = variable_name;
2434
2435 return retval;
2436 }
2437
2438 ///////////////////////////////////////////////////////////
2439 // StringCast //
2440 ///////////////////////////////////////////////////////////
2441 template <>
Eval(const ScriptingContext & context) const2442 std::string StringCast<double>::Eval(const ScriptingContext& context) const
2443 {
2444 if (!m_value_ref)
2445 return "";
2446 double temp = m_value_ref->Eval(context);
2447
2448 // special case for a few sub-value-refs to help with UI representation
2449 if (Variable<double>* int_var = dynamic_cast<Variable<double>*>(m_value_ref.get())) {
2450 if (int_var->PropertyName().back() == "X" || int_var->PropertyName().back() == "Y") {
2451 if (temp == UniverseObject::INVALID_POSITION)
2452 return UserString("INVALID_POSITION");
2453
2454 std::stringstream ss;
2455 ss << std::setprecision(6) << temp;
2456 return ss.str();
2457 }
2458 }
2459
2460 return DoubleToString(temp, 3, false);
2461 }
2462
2463 template <>
Eval(const ScriptingContext & context) const2464 std::string StringCast<int>::Eval(const ScriptingContext& context) const
2465 {
2466 if (!m_value_ref)
2467 return "";
2468 int temp = m_value_ref->Eval(context);
2469
2470 // special case for a few sub-value-refs to help with UI representation
2471 if (Variable<int>* int_var = dynamic_cast<Variable<int>*>(m_value_ref.get())) {
2472 if (int_var->PropertyName().back() == "ETA") {
2473 if (temp == Fleet::ETA_UNKNOWN) {
2474 return UserString("FW_FLEET_ETA_UNKNOWN");
2475 } else if (temp == Fleet::ETA_NEVER) {
2476 return UserString("FW_FLEET_ETA_NEVER");
2477 } else if (temp == Fleet::ETA_OUT_OF_RANGE) {
2478 return UserString("FW_FLEET_ETA_OUT_OF_RANGE");
2479 }
2480 }
2481 }
2482
2483 return std::to_string(temp);
2484 }
2485
2486 template <>
Eval(const ScriptingContext & context) const2487 std::string StringCast<std::vector<std::string>>::Eval(const ScriptingContext& context) const
2488 {
2489 if (!m_value_ref)
2490 return "";
2491 std::vector<std::string> temp = m_value_ref->Eval(context);
2492
2493 // concatenate strings into one big string
2494 std::string retval;
2495 for (auto str : temp)
2496 retval += str + " ";
2497 return retval;
2498 }
2499
2500 ///////////////////////////////////////////////////////////
2501 // UserStringLookup //
2502 ///////////////////////////////////////////////////////////
2503 template <>
Eval(const ScriptingContext & context) const2504 std::string UserStringLookup<std::string>::Eval(const ScriptingContext& context) const {
2505 if (!m_value_ref)
2506 return "";
2507 std::string ref_val = m_value_ref->Eval(context);
2508 if (ref_val.empty() || !UserStringExists(ref_val))
2509 return "";
2510 return UserString(ref_val);
2511 }
2512
2513 template <>
Eval(const ScriptingContext & context) const2514 std::string UserStringLookup<std::vector<std::string>>::Eval(const ScriptingContext& context) const {
2515 if (!m_value_ref)
2516 return "";
2517 std::vector<std::string> ref_vals = m_value_ref->Eval(context);
2518 if (ref_vals.empty())
2519 return "";
2520 std::string retval;
2521 for (auto val : ref_vals) {
2522 if (val.empty() || !UserStringExists(val))
2523 continue;
2524 retval += UserString(val) + " ";
2525 }
2526 return retval;
2527 }
2528
2529 /////////////////////////////////////////////////////
2530 // NameLookup //
2531 /////////////////////////////////////////////////////
NameLookup(std::unique_ptr<ValueRef<int>> && value_ref,LookupType lookup_type)2532 NameLookup::NameLookup(std::unique_ptr<ValueRef<int>>&& value_ref, LookupType lookup_type) :
2533 Variable<std::string>(NON_OBJECT_REFERENCE),
2534 m_value_ref(std::move(value_ref)),
2535 m_lookup_type(lookup_type)
2536 {}
2537
operator ==(const ValueRef<std::string> & rhs) const2538 bool NameLookup::operator==(const ValueRef<std::string>& rhs) const {
2539 if (&rhs == this)
2540 return true;
2541 if (typeid(rhs) != typeid(*this))
2542 return false;
2543 const NameLookup& rhs_ =
2544 static_cast<const NameLookup&>(rhs);
2545
2546 if (m_lookup_type == rhs_.m_lookup_type) {
2547 // check next member
2548 } else {
2549 return false;
2550 }
2551
2552 if (m_value_ref == rhs_.m_value_ref) {
2553 // check next member
2554 } else if (!m_value_ref || !rhs_.m_value_ref) {
2555 return false;
2556 } else {
2557 if (*m_value_ref != *(rhs_.m_value_ref))
2558 return false;
2559 }
2560
2561 return true;
2562 }
2563
Eval(const ScriptingContext & context) const2564 std::string NameLookup::Eval(const ScriptingContext& context) const {
2565 if (!m_value_ref || m_lookup_type == INVALID_LOOKUP)
2566 return "";
2567
2568 switch (m_lookup_type) {
2569 case OBJECT_NAME: {
2570 auto obj = context.ContextObjects().get(m_value_ref->Eval(context));
2571 return obj ? obj->Name() : "";
2572 break;
2573 }
2574 case EMPIRE_NAME: {
2575 const Empire* empire = GetEmpire(m_value_ref->Eval(context));
2576 return empire ? empire->Name() : "";
2577 break;
2578 }
2579 case SHIP_DESIGN_NAME: {
2580 const ShipDesign* design = GetShipDesign(m_value_ref->Eval(context));
2581 return design ? design->Name() : "";
2582 break;
2583 }
2584 default:
2585 return "";
2586 }
2587 }
2588
RootCandidateInvariant() const2589 bool NameLookup::RootCandidateInvariant() const
2590 { return m_value_ref->RootCandidateInvariant(); }
2591
LocalCandidateInvariant() const2592 bool NameLookup::LocalCandidateInvariant() const
2593 { return !m_value_ref || m_value_ref->LocalCandidateInvariant(); }
2594
TargetInvariant() const2595 bool NameLookup::TargetInvariant() const
2596 { return !m_value_ref || m_value_ref->TargetInvariant(); }
2597
SourceInvariant() const2598 bool NameLookup::SourceInvariant() const
2599 { return !m_value_ref || m_value_ref->SourceInvariant(); }
2600
Description() const2601 std::string NameLookup::Description() const
2602 { return m_value_ref->Description(); }
2603
Dump(unsigned short ntabs) const2604 std::string NameLookup::Dump(unsigned short ntabs) const
2605 { return m_value_ref->Dump(ntabs); }
2606
SetTopLevelContent(const std::string & content_name)2607 void NameLookup::SetTopLevelContent(const std::string& content_name) {
2608 if (m_value_ref)
2609 m_value_ref->SetTopLevelContent(content_name);
2610 }
2611
GetCheckSum() const2612 unsigned int NameLookup::GetCheckSum() const {
2613 unsigned int retval{0};
2614
2615 CheckSums::CheckSumCombine(retval, "ValueRef::NameLookup");
2616 CheckSums::CheckSumCombine(retval, m_value_ref);
2617 CheckSums::CheckSumCombine(retval, m_lookup_type);
2618 std::cout << "GetCheckSum(NameLookup): " << typeid(*this).name() << " retval: " << retval << std::endl << std::endl;
2619 return retval;
2620 }
2621
2622 ///////////////////////////////////////////////////////////
2623 // Operation //
2624 ///////////////////////////////////////////////////////////
2625 template <>
EvalImpl(const ScriptingContext & context) const2626 std::string Operation<std::string>::EvalImpl(const ScriptingContext& context) const
2627 {
2628 if (m_op_type == PLUS) {
2629 return LHS()->Eval(context) + RHS()->Eval(context);
2630
2631 } else if (m_op_type == TIMES) {
2632 // useful for writing a "Statistic If" expression with strings. Number-
2633 // valued types return 0 or 1 for nothing or something matching the sampling
2634 // condition. For strings, an empty string indicates no matches, and non-empty
2635 // string indicates matches, which is treated like a multiplicative identity
2636 // operation, so just returns the RHS of the expression.
2637 if (LHS()->Eval(context).empty())
2638 return "";
2639 return RHS()->Eval(context);
2640
2641 } else if (m_op_type == MINIMUM || m_op_type == MAXIMUM) {
2642 // evaluate all operands, return sorted first/last
2643 std::set<std::string> vals;
2644 for (auto& vr : m_operands) {
2645 if (vr)
2646 vals.insert(vr->Eval(context));
2647 }
2648 if (m_op_type == MINIMUM)
2649 return vals.empty() ? "" : *vals.begin();
2650 else
2651 return vals.empty() ? "" : *vals.rbegin();
2652
2653 } else if (m_op_type == RANDOM_PICK) {
2654 // select one operand, evaluate it, return result
2655 if (m_operands.empty())
2656 return "";
2657 unsigned int idx = RandSmallInt(0, m_operands.size() - 1);
2658 auto& vr = *std::next(m_operands.begin(), idx);
2659 if (!vr)
2660 return "";
2661 return vr->Eval(context);
2662
2663 } else if (m_op_type == SUBSTITUTION) {
2664 // insert string into other string in place of %1% or similar placeholder
2665 if (m_operands.empty())
2666 return "";
2667 auto& template_op = *(m_operands.begin());
2668 if (!template_op)
2669 return "";
2670 std::string template_str = template_op->Eval(context);
2671
2672 boost::format formatter = FlexibleFormat(template_str);
2673
2674 for (auto& op : m_operands) {
2675 if (!op) {
2676 formatter % "";
2677 continue;
2678 }
2679 formatter % op->Eval(context);
2680 }
2681 return formatter.str();
2682
2683 } else if (m_op_type >= COMPARE_EQUAL && m_op_type <= COMPARE_NOT_EQUAL) {
2684 const std::string&& lhs_val = LHS()->Eval(context);
2685 const std::string&& rhs_val = RHS()->Eval(context);
2686 bool test_result = false;
2687 switch (m_op_type) {
2688 case COMPARE_EQUAL: test_result = lhs_val == rhs_val; break;
2689 case COMPARE_GREATER_THAN: test_result = lhs_val > rhs_val; break;
2690 case COMPARE_GREATER_THAN_OR_EQUAL: test_result = lhs_val >= rhs_val; break;
2691 case COMPARE_LESS_THAN: test_result = lhs_val < rhs_val; break;
2692 case COMPARE_LESS_THAN_OR_EQUAL: test_result = lhs_val <= rhs_val; break;
2693 case COMPARE_NOT_EQUAL: test_result = lhs_val != rhs_val; break;
2694 default: break; // ??? do nothing, default to false
2695 }
2696 if (m_operands.size() < 3) {
2697 return test_result ? "true" : "false";
2698 } else if (m_operands.size() < 4) {
2699 if (test_result)
2700 return m_operands[2]->Eval(context);
2701 else
2702 return "false";
2703 } else {
2704 if (test_result)
2705 return m_operands[2]->Eval(context);
2706 else
2707 return m_operands[3]->Eval(context);
2708 }
2709 }
2710
2711 throw std::runtime_error("std::string ValueRef evaluated with an unknown or invalid OpType.");
2712 return "";
2713 }
2714
2715 template <>
EvalImpl(const ScriptingContext & context) const2716 double Operation<double>::EvalImpl(const ScriptingContext& context) const
2717 {
2718 switch (m_op_type) {
2719 case PLUS:
2720 return LHS()->Eval(context) + RHS()->Eval(context); break;
2721
2722 case MINUS:
2723 return LHS()->Eval(context) - RHS()->Eval(context); break;
2724
2725 case TIMES: {
2726 double op1 = LHS()->Eval(context);
2727 if (op1 == 0.0)
2728 return 0.0;
2729 return op1 * RHS()->Eval(context);
2730 break;
2731 }
2732
2733 case DIVIDE: {
2734 double op2 = RHS()->Eval(context);
2735 if (op2 == 0.0)
2736 return 0.0;
2737 return LHS()->Eval(context) / op2;
2738 break;
2739 }
2740
2741 case NEGATE:
2742 return -(LHS()->Eval(context)); break;
2743
2744 case EXPONENTIATE: {
2745 double op2 = RHS()->Eval(context);
2746 if (op2 == 0.0)
2747 return 1.0;
2748 try {
2749 double op1 = LHS()->Eval(context);
2750 return std::pow(op1, op2);
2751 } catch (...) {
2752 ErrorLogger() << "Error evaluating exponentiation ValueRef::Operation";
2753 return 0.0;
2754 }
2755 break;
2756 }
2757
2758 case ABS:
2759 return std::abs(LHS()->Eval(context)); break;
2760
2761 case LOGARITHM: {
2762 double op1 = LHS()->Eval(context);
2763 if (op1 <= 0.0)
2764 return 0.0;
2765 return std::log(op1);
2766 break;
2767 }
2768
2769 case SINE:
2770 return std::sin(LHS()->Eval(context)); break;
2771
2772 case COSINE:
2773 return std::cos(LHS()->Eval(context)); break;
2774
2775 case MINIMUM:
2776 case MAXIMUM: {
2777 std::set<double> vals;
2778 for (auto& vr : m_operands) {
2779 if (vr)
2780 vals.insert(vr->Eval(context));
2781 }
2782 if (m_op_type == MINIMUM)
2783 return vals.empty() ? 0.0 : *vals.begin();
2784 else
2785 return vals.empty() ? 0.0 : *vals.rbegin();
2786 break;
2787 }
2788
2789 case RANDOM_UNIFORM: {
2790 double op1 = LHS()->Eval(context);
2791 double op2 = RHS()->Eval(context);
2792 double min_val = std::min(op1, op2);
2793 double max_val = std::max(op1, op2);
2794 return RandDouble(min_val, max_val);
2795 break;
2796 }
2797
2798 case RANDOM_PICK: {
2799 // select one operand, evaluate it, return result
2800 if (m_operands.empty())
2801 return 0.0;
2802 unsigned int idx = RandSmallInt(0, m_operands.size() - 1);
2803 auto& vr = *std::next(m_operands.begin(), idx);
2804 if (!vr)
2805 return 0.0;
2806 return vr->Eval(context);
2807 break;
2808 }
2809
2810 case COMPARE_EQUAL:
2811 case COMPARE_GREATER_THAN:
2812 case COMPARE_GREATER_THAN_OR_EQUAL:
2813 case COMPARE_LESS_THAN:
2814 case COMPARE_LESS_THAN_OR_EQUAL:
2815 case COMPARE_NOT_EQUAL: {
2816 const double&& lhs_val = LHS()->Eval(context);
2817 const double&& rhs_val = RHS()->Eval(context);
2818 bool test_result = false;
2819 switch (m_op_type) {
2820 case COMPARE_EQUAL: test_result = lhs_val == rhs_val; break;
2821 case COMPARE_GREATER_THAN: test_result = lhs_val > rhs_val; break;
2822 case COMPARE_GREATER_THAN_OR_EQUAL: test_result = lhs_val >= rhs_val; break;
2823 case COMPARE_LESS_THAN: test_result = lhs_val < rhs_val; break;
2824 case COMPARE_LESS_THAN_OR_EQUAL: test_result = lhs_val <= rhs_val; break;
2825 case COMPARE_NOT_EQUAL: test_result = lhs_val != rhs_val; break;
2826 default: break; // ??? do nothing, default to false
2827 }
2828 if (m_operands.size() < 3) {
2829 return static_cast<double>(test_result);
2830 } else if (m_operands.size() < 4) {
2831 if (test_result)
2832 return m_operands[2]->Eval(context);
2833 else
2834 return 0.0;
2835 } else {
2836 if (test_result)
2837 return m_operands[2]->Eval(context);
2838 else
2839 return m_operands[3]->Eval(context);
2840 }
2841 }
2842
2843 case ROUND_NEAREST:
2844 return std::round(LHS()->Eval(context)); break;
2845 case ROUND_UP:
2846 return std::ceil(LHS()->Eval(context)); break;
2847 case ROUND_DOWN:
2848 return std::floor(LHS()->Eval(context)); break;
2849
2850 default:
2851 break;
2852 }
2853
2854 throw std::runtime_error("double ValueRef evaluated with an unknown or invalid OpType.");
2855 return 0.0;
2856 }
2857
2858 template <>
EvalImpl(const ScriptingContext & context) const2859 int Operation<int>::EvalImpl(const ScriptingContext& context) const
2860 {
2861 switch (m_op_type) {
2862 case PLUS:
2863 return LHS()->Eval(context) + RHS()->Eval(context); break;
2864
2865 case MINUS:
2866 return LHS()->Eval(context) - RHS()->Eval(context); break;
2867
2868 case TIMES: {
2869 double op1 = LHS()->Eval(context);
2870 if (op1 == 0)
2871 return 0;
2872 return op1 * RHS()->Eval(context);
2873 break;
2874 }
2875
2876 case DIVIDE: {
2877 int op2 = RHS()->Eval(context);
2878 if (op2 == 0)
2879 return 0;
2880 return LHS()->Eval(context) / op2;
2881 break;
2882 }
2883
2884 case NEGATE:
2885 return -LHS()->Eval(context); break;
2886
2887 case EXPONENTIATE: {
2888 double op2 = RHS()->Eval(context);
2889 if (op2 == 0)
2890 return 1;
2891 try {
2892 double op1 = LHS()->Eval(context);
2893 return static_cast<int>(std::pow(op1, op2));
2894 } catch (...) {
2895 ErrorLogger() << "Error evaluating exponentiation ValueRef::Operation";
2896 return 0;
2897 }
2898 break;
2899 }
2900
2901 case ABS: {
2902 return static_cast<int>(std::abs(LHS()->Eval(context)));
2903 break;
2904 }
2905
2906 case LOGARITHM: {
2907 double op1 = LHS()->Eval(context);
2908 if (op1 <= 0.0)
2909 return 0;
2910 return static_cast<int>(std::log(op1));
2911 break;
2912 }
2913
2914 case SINE: {
2915 double op1 = LHS()->Eval(context);
2916 return static_cast<int>(std::sin(op1));
2917 break;
2918 }
2919
2920 case COSINE: {
2921 double op1 = LHS()->Eval(context);
2922 return static_cast<int>(std::cos(op1));
2923 break;
2924 }
2925
2926 case MINIMUM:
2927 case MAXIMUM: {
2928 std::set<int> vals;
2929 for (auto& vr : m_operands) {
2930 if (vr)
2931 vals.insert(vr->Eval(context));
2932 }
2933 if (m_op_type == MINIMUM)
2934 return vals.empty() ? 0 : *vals.begin();
2935 else
2936 return vals.empty() ? 0 : *vals.rbegin();
2937 break;
2938 }
2939
2940 case RANDOM_UNIFORM: {
2941 double op1 = LHS()->Eval(context);
2942 double op2 = RHS()->Eval(context);
2943 int min_val = static_cast<int>(std::min(op1, op2));
2944 int max_val = static_cast<int>(std::max(op1, op2));
2945 return RandInt(min_val, max_val);
2946 break;
2947 }
2948
2949 case RANDOM_PICK: {
2950 // select one operand, evaluate it, return result
2951 if (m_operands.empty())
2952 return 0;
2953 unsigned int idx = RandSmallInt(0, m_operands.size() - 1);
2954 auto& vr = *std::next(m_operands.begin(), idx);
2955 if (!vr)
2956 return 0;
2957 return vr->Eval(context);
2958 break;
2959 }
2960
2961 case ROUND_NEAREST:
2962 case ROUND_UP:
2963 case ROUND_DOWN: {
2964 // integers don't need to be rounded...
2965 return LHS()->Eval(context);
2966 break;
2967 }
2968
2969 case COMPARE_EQUAL:
2970 case COMPARE_GREATER_THAN:
2971 case COMPARE_GREATER_THAN_OR_EQUAL:
2972 case COMPARE_LESS_THAN:
2973 case COMPARE_LESS_THAN_OR_EQUAL:
2974 case COMPARE_NOT_EQUAL: {
2975 const int&& lhs_val = LHS()->Eval(context);
2976 const int&& rhs_val = RHS()->Eval(context);
2977 bool test_result = false;
2978 switch (m_op_type) {
2979 case COMPARE_EQUAL: test_result = lhs_val == rhs_val; break;
2980 case COMPARE_GREATER_THAN: test_result = lhs_val > rhs_val; break;
2981 case COMPARE_GREATER_THAN_OR_EQUAL: test_result = lhs_val >= rhs_val; break;
2982 case COMPARE_LESS_THAN: test_result = lhs_val < rhs_val; break;
2983 case COMPARE_LESS_THAN_OR_EQUAL: test_result = lhs_val <= rhs_val; break;
2984 case COMPARE_NOT_EQUAL: test_result = lhs_val != rhs_val; break;
2985 default: break; // ??? do nothing, default to false
2986 }
2987 if (m_operands.size() < 3) {
2988 return static_cast<int>(test_result);
2989 } else if (m_operands.size() < 4) {
2990 if (test_result)
2991 return m_operands[2]->Eval(context);
2992 else
2993 return 0;
2994 } else {
2995 if (test_result)
2996 return m_operands[2]->Eval(context);
2997 else
2998 return m_operands[3]->Eval(context);
2999 }
3000 }
3001
3002 default: break;
3003 }
3004
3005 throw std::runtime_error("double ValueRef evaluated with an unknown or invalid OpType.");
3006 return 0;
3007 }
3008 } // namespace ValueRef
3009