1 #include "Effects.h"
2 
3 #include "../util/Logger.h"
4 #include "../util/OptionsDB.h"
5 #include "../util/Random.h"
6 #include "../util/Directories.h"
7 #include "../util/i18n.h"
8 #include "../util/SitRepEntry.h"
9 #include "../Empire/EmpireManager.h"
10 #include "../Empire/Empire.h"
11 #include "ValueRefs.h"
12 #include "Condition.h"
13 #include "Pathfinder.h"
14 #include "Universe.h"
15 #include "UniverseObject.h"
16 #include "Building.h"
17 #include "BuildingType.h"
18 #include "Planet.h"
19 #include "System.h"
20 #include "Field.h"
21 #include "FieldType.h"
22 #include "Fleet.h"
23 #include "Ship.h"
24 #include "ShipDesign.h"
25 #include "Tech.h"
26 #include "Species.h"
27 #include "Enums.h"
28 
29 #include <boost/filesystem/fstream.hpp>
30 
31 #include <cctype>
32 #include <iterator>
33 
34 namespace {
35     DeclareThreadSafeLogger(effects);
36 }
37 
38 using boost::io::str;
39 
40 FO_COMMON_API extern const int INVALID_DESIGN_ID;
41 
42 namespace {
43     /** creates a new fleet at a specified \a x and \a y location within the
44      * Universe, and and inserts \a ship into it.  Used when a ship has been
45      * moved by the MoveTo effect separately from the fleet that previously
46      * held it.  All ships need to be within fleets. */
CreateNewFleet(double x,double y,std::shared_ptr<Ship> ship)47     std::shared_ptr<Fleet> CreateNewFleet(double x, double y, std::shared_ptr<Ship> ship) {
48         Universe& universe = GetUniverse();
49         if (!ship)
50             return nullptr;
51 
52         auto fleet = universe.InsertNew<Fleet>("", x, y, ship->Owner());
53 
54         fleet->Rename(fleet->GenerateFleetName());
55         fleet->GetMeter(METER_STEALTH)->SetCurrent(Meter::LARGE_VALUE);
56 
57         fleet->AddShips({ship->ID()});
58         ship->SetFleetID(fleet->ID());
59         fleet->SetAggressive(fleet->HasArmedShips());
60 
61         return fleet;
62     }
63 
64     /** Creates a new fleet at \a system and inserts \a ship into it.  Used
65      * when a ship has been moved by the MoveTo effect separately from the
66      * fleet that previously held it.  Also used by CreateShip effect to give
67      * the new ship a fleet.  All ships need to be within fleets. */
CreateNewFleet(std::shared_ptr<System> system,std::shared_ptr<Ship> ship,ObjectMap & objects)68     std::shared_ptr<Fleet> CreateNewFleet(std::shared_ptr<System> system, std::shared_ptr<Ship> ship, ObjectMap& objects) {
69         if (!system || !ship)
70             return nullptr;
71 
72         // remove ship from old fleet / system, put into new system if necessary
73         if (ship->SystemID() != system->ID()) {
74             if (auto old_system = objects.get<System>(ship->SystemID())) {
75                 old_system->Remove(ship->ID());
76                 ship->SetSystem(INVALID_OBJECT_ID);
77             }
78             system->Insert(ship);
79         }
80 
81         if (ship->FleetID() != INVALID_OBJECT_ID) {
82             if (auto old_fleet = objects.get<Fleet>(ship->FleetID())) {
83                 old_fleet->RemoveShips({ship->ID()});
84             }
85         }
86 
87         // create new fleet for ship, and put it in new system
88         auto fleet = CreateNewFleet(system->X(), system->Y(), ship);
89         system->Insert(fleet);
90 
91         return fleet;
92     }
93 
94     /** Explores the system with the specified \a system_id for the owner of
95       * the specified \a target_object.  Used when moving objects into a system
96       * with the MoveTo effect, as otherwise the system wouldn't get explored,
97       * and objects being moved into unexplored systems might disappear for
98       * players or confuse the AI. */
ExploreSystem(int system_id,std::shared_ptr<const UniverseObject> target_object)99     void ExploreSystem(int system_id, std::shared_ptr<const UniverseObject> target_object) {
100         if (!target_object)
101             return;
102         if (Empire* empire = GetEmpire(target_object->Owner()))
103             empire->AddExploredSystem(system_id);
104     }
105 
106     /** Resets the previous and next systems of \a fleet and recalcultes /
107      * resets the fleet's move route.  Used after a fleet has been moved with
108      * the MoveTo effect, as its previous route was assigned based on its
109      * previous location, and may not be valid for its new location. */
UpdateFleetRoute(std::shared_ptr<Fleet> fleet,int new_next_system,int new_previous_system,const ObjectMap & objects)110     void UpdateFleetRoute(std::shared_ptr<Fleet> fleet, int new_next_system, int new_previous_system, const ObjectMap& objects) {
111         if (!fleet) {
112             ErrorLogger() << "UpdateFleetRoute passed a null fleet pointer";
113             return;
114         }
115 
116         auto next_system = objects.get<System>(new_next_system);
117         if (!next_system) {
118             ErrorLogger() << "UpdateFleetRoute couldn't get new next system with id: " << new_next_system;
119             return;
120         }
121 
122         if (new_previous_system != INVALID_OBJECT_ID && !objects.get<System>(new_previous_system)) {
123             ErrorLogger() << "UpdateFleetRoute couldn't get new previous system with id: " << new_previous_system;
124         }
125 
126         fleet->SetNextAndPreviousSystems(new_next_system, new_previous_system);
127 
128 
129         // recalculate route from the shortest path between first system on path and final destination
130         int start_system = fleet->SystemID();
131         if (start_system == INVALID_OBJECT_ID)
132             start_system = new_next_system;
133 
134         int dest_system = fleet->FinalDestinationID();
135 
136         std::pair<std::list<int>, double> route_pair = GetPathfinder()->ShortestPath(start_system, dest_system, fleet->Owner());
137 
138         // if shortest path is empty, the route may be impossible or trivial, so just set route to move fleet
139         // to the next system that it was just set to move to anyway.
140         if (route_pair.first.empty())
141             route_pair.first.push_back(new_next_system);
142 
143 
144         // set fleet with newly recalculated route
145         try {
146             fleet->SetRoute(route_pair.first);
147         } catch (const std::exception& e) {
148             ErrorLogger() << "Caught exception updating fleet route in effect code: " << e.what();
149         }
150     }
151 
GenerateSystemName(const ObjectMap & objects)152     std::string GenerateSystemName(const ObjectMap& objects) {
153         static std::vector<std::string> star_names = UserStringList("STAR_NAMES");
154 
155         // pick a name for the system
156         for (const std::string& star_name : star_names) {
157             // does an existing system have this name?
158             bool dupe = false;
159             for (auto& system : objects.all<System>()) {
160                 if (system->Name() == star_name) {
161                     dupe = true;
162                     break;  // another systme has this name. skip to next potential name.
163                 }
164             }
165             if (!dupe)
166                 return star_name; // no systems have this name yet. use it.
167         }
168         return "";  // fallback to empty name.
169     }
170 }
171 
172 namespace Effect {
173 ///////////////////////////////////////////////////////////
174 // EffectsGroup                                          //
175 ///////////////////////////////////////////////////////////
EffectsGroup(std::unique_ptr<Condition::Condition> && scope,std::unique_ptr<Condition::Condition> && activation,std::vector<std::unique_ptr<Effect>> && effects,const std::string & accounting_label,const std::string & stacking_group,int priority,const std::string & description,const std::string & content_name)176 EffectsGroup::EffectsGroup(std::unique_ptr<Condition::Condition>&& scope,
177                            std::unique_ptr<Condition::Condition>&& activation,
178                            std::vector<std::unique_ptr<Effect>>&& effects,
179                            const std::string& accounting_label,
180                            const std::string& stacking_group, int priority,
181                            const std::string& description,
182                            const std::string& content_name):
183     m_scope(std::move(scope)),
184     m_activation(std::move(activation)),
185     m_stacking_group(stacking_group),
186     m_effects(std::move(effects)),
187     m_accounting_label(accounting_label),
188     m_priority(priority),
189     m_description(description),
190     m_content_name(content_name)
191 {}
192 
~EffectsGroup()193 EffectsGroup::~EffectsGroup()
194 {}
195 
Execute(ScriptingContext & context,const TargetsAndCause & targets_cause,AccountingMap * accounting_map,bool only_meter_effects,bool only_appearance_effects,bool include_empire_meter_effects,bool only_generate_sitrep_effects) const196 void EffectsGroup::Execute(ScriptingContext& context,
197                            const TargetsAndCause& targets_cause,
198                            AccountingMap* accounting_map,
199                            bool only_meter_effects, bool only_appearance_effects,
200                            bool include_empire_meter_effects,
201                            bool only_generate_sitrep_effects) const
202 {
203     if (!context.source)    // todo: move into loop and check only when an effect is source-variant
204         WarnLogger() << "EffectsGroup being executed without a defined source object";
205 
206     // execute each effect of the group one by one, unless filtered by flags
207     for (auto& effect : m_effects) {
208         // skip excluded effect types
209         if (   (only_appearance_effects       && !effect->IsAppearanceEffect())
210             || (only_meter_effects            && !effect->IsMeterEffect())
211             || (!include_empire_meter_effects &&  effect->IsEmpireMeterEffect())
212             || (only_generate_sitrep_effects  && !effect->IsSitrepEffect()))
213         { continue; }
214 
215         effect->Execute(context, targets_cause.target_set, accounting_map,
216                         targets_cause.effect_cause,
217                         only_meter_effects, only_appearance_effects,
218                         include_empire_meter_effects, only_generate_sitrep_effects);
219     }
220 }
221 
EffectsList() const222 const std::vector<Effect*>  EffectsGroup::EffectsList() const {
223     std::vector<Effect*> retval(m_effects.size());
224     std::transform(m_effects.begin(), m_effects.end(), retval.begin(),
225                    [](const std::unique_ptr<Effect>& xx) {return xx.get();});
226     return retval;
227 }
228 
GetDescription() const229 const std::string& EffectsGroup::GetDescription() const
230 { return m_description; }
231 
Dump(unsigned short ntabs) const232 std::string EffectsGroup::Dump(unsigned short ntabs) const {
233     std::string retval = DumpIndent(ntabs) + "EffectsGroup";
234     if (!m_content_name.empty())
235         retval += " // from " + m_content_name;
236     retval += "\n";
237     retval += DumpIndent(ntabs+1) + "scope =\n";
238     retval += m_scope->Dump(ntabs+2);
239     if (m_activation) {
240         retval += DumpIndent(ntabs+1) + "activation =\n";
241         retval += m_activation->Dump(ntabs+2);
242     }
243     if (!m_stacking_group.empty())
244         retval += DumpIndent(ntabs+1) + "stackinggroup = \"" + m_stacking_group + "\"\n";
245     if (m_effects.size() == 1) {
246         retval += DumpIndent(ntabs+1) + "effects =\n";
247         retval += m_effects[0]->Dump(ntabs+2);
248     } else {
249         retval += DumpIndent(ntabs+1) + "effects = [\n";
250         for (auto& effect : m_effects) {
251             retval += effect->Dump(ntabs+2);
252         }
253         retval += DumpIndent(ntabs+1) + "]\n";
254     }
255     return retval;
256 }
257 
HasMeterEffects() const258 bool EffectsGroup::HasMeterEffects() const {
259     for (auto& effect : m_effects) {
260         if (effect->IsMeterEffect())
261             return true;
262     }
263     return false;
264 }
265 
HasAppearanceEffects() const266 bool EffectsGroup::HasAppearanceEffects() const {
267     for (auto& effect : m_effects) {
268         if (effect->IsAppearanceEffect())
269             return true;
270     }
271     return false;
272 }
273 
HasSitrepEffects() const274 bool EffectsGroup::HasSitrepEffects() const {
275     for (auto& effect : m_effects) {
276         if (effect->IsSitrepEffect())
277             return true;
278     }
279     return false;
280 }
281 
SetTopLevelContent(const std::string & content_name)282 void EffectsGroup::SetTopLevelContent(const std::string& content_name) {
283     m_content_name = content_name;
284     if (m_scope)
285         m_scope->SetTopLevelContent(content_name);
286     if (m_activation)
287         m_activation->SetTopLevelContent(content_name);
288     for (auto& effect : m_effects) {
289         effect->SetTopLevelContent(content_name);
290     }
291 }
292 
GetCheckSum() const293 unsigned int EffectsGroup::GetCheckSum() const {
294     unsigned int retval{0};
295 
296     CheckSums::CheckSumCombine(retval, "EffectsGroup");
297     CheckSums::CheckSumCombine(retval, m_scope);
298     CheckSums::CheckSumCombine(retval, m_activation);
299     CheckSums::CheckSumCombine(retval, m_stacking_group);
300     CheckSums::CheckSumCombine(retval, m_effects);
301     CheckSums::CheckSumCombine(retval, m_accounting_label);
302     CheckSums::CheckSumCombine(retval, m_priority);
303     CheckSums::CheckSumCombine(retval, m_description);
304 
305     TraceLogger() << "GetCheckSum(EffectsGroup): retval: " << retval;
306     return retval;
307 }
308 
309 
310 ///////////////////////////////////////////////////////////
311 // Dump function                                         //
312 ///////////////////////////////////////////////////////////
Dump(const std::vector<std::shared_ptr<EffectsGroup>> & effects_groups)313 std::string Dump(const std::vector<std::shared_ptr<EffectsGroup>>& effects_groups) {
314     std::stringstream retval;
315 
316     for (auto& effects_group : effects_groups) {
317         retval << "\n" << effects_group->Dump();
318     }
319 
320     return retval.str();
321 }
322 
323 
324 ///////////////////////////////////////////////////////////
325 // Effect                                            //
326 ///////////////////////////////////////////////////////////
~Effect()327 Effect::~Effect()
328 {}
329 
Execute(ScriptingContext & context,const TargetSet & targets,AccountingMap * accounting_map,const EffectCause & effect_cause,bool only_meter_effects,bool only_appearance_effects,bool include_empire_meter_effects,bool only_generate_sitrep_effects) const330 void Effect::Execute(ScriptingContext& context,
331                      const TargetSet& targets,
332                      AccountingMap* accounting_map,
333                      const EffectCause& effect_cause,
334                      bool only_meter_effects,
335                      bool only_appearance_effects,
336                      bool include_empire_meter_effects,
337                      bool only_generate_sitrep_effects) const
338 {
339     if (   (only_appearance_effects       && !this->IsAppearanceEffect())
340         || (only_meter_effects            && !this->IsMeterEffect())
341         || (!include_empire_meter_effects &&  this->IsEmpireMeterEffect())
342         || (only_generate_sitrep_effects  && !this->IsSitrepEffect()))
343     { return; }
344     // generic / most effects don't do anything special for accounting, so just
345     // use standard Execute. overrides may implement something else.
346     Execute(context, targets);
347 }
348 
Execute(ScriptingContext & context,const TargetSet & targets) const349 void Effect::Execute(ScriptingContext& context, const TargetSet& targets) const {
350     if (targets.empty())
351         return;
352 
353     // execute effects on targets
354     ScriptingContext local_context = context;
355     for (const auto& target : targets) {
356         local_context.effect_target = target;
357         Execute(local_context);
358     }
359 }
360 
GetCheckSum() const361 unsigned int Effect::GetCheckSum() const {
362     unsigned int retval{0};
363 
364     CheckSums::CheckSumCombine(retval, "Effect");
365 
366     TraceLogger() << "GetCheckSum(EffectsGroup): retval: " << retval;
367     return retval;
368 }
369 
370 
371 ///////////////////////////////////////////////////////////
372 // NoOp                                                  //
373 ///////////////////////////////////////////////////////////
NoOp()374 NoOp::NoOp()
375 {}
376 
Execute(ScriptingContext & context) const377 void NoOp::Execute(ScriptingContext& context) const
378 {}
379 
Dump(unsigned short ntabs) const380 std::string NoOp::Dump(unsigned short ntabs) const
381 { return DumpIndent(ntabs) + "NoOp\n"; }
382 
GetCheckSum() const383 unsigned int NoOp::GetCheckSum() const {
384     unsigned int retval{0};
385 
386     CheckSums::CheckSumCombine(retval, "NoOp");
387 
388     TraceLogger() << "GetCheckSum(NoOp): retval: " << retval;
389     return retval;
390 }
391 
392 
393 ///////////////////////////////////////////////////////////
394 // SetMeter                                              //
395 ///////////////////////////////////////////////////////////
SetMeter(MeterType meter,std::unique_ptr<ValueRef::ValueRef<double>> && value,const boost::optional<std::string> & accounting_label)396 SetMeter::SetMeter(MeterType meter,
397                    std::unique_ptr<ValueRef::ValueRef<double>>&& value,
398                    const boost::optional<std::string>& accounting_label) :
399     m_meter(meter),
400     m_value(std::move(value)),
401     m_accounting_label(accounting_label ? *accounting_label : std::string())
402 {}
403 
Execute(ScriptingContext & context) const404 void SetMeter::Execute(ScriptingContext& context) const {
405     if (!context.effect_target) return;
406     Meter* m = context.effect_target->GetMeter(m_meter);
407     if (!m) return;
408 
409     float val = m_value->Eval(ScriptingContext(context, m->Current()));
410     m->SetCurrent(val);
411 }
412 
Execute(ScriptingContext & context,const TargetSet & targets,AccountingMap * accounting_map,const EffectCause & effect_cause,bool only_meter_effects,bool only_appearance_effects,bool include_empire_meter_effects,bool only_generate_sitrep_effects) const413 void SetMeter::Execute(ScriptingContext& context,
414                        const TargetSet& targets,
415                        AccountingMap* accounting_map,
416                        const EffectCause& effect_cause,
417                        bool only_meter_effects,
418                        bool only_appearance_effects,
419                        bool include_empire_meter_effects,
420                        bool only_generate_sitrep_effects) const
421 {
422     if (only_appearance_effects || only_generate_sitrep_effects)
423         return;
424 
425     TraceLogger(effects) << "\n\nExecute SetMeter effect: \n" << Dump();
426     TraceLogger(effects) << "SetMeter execute targets before: ";
427     for (const auto& target : targets)
428         TraceLogger(effects) << " ... " << target->Dump(1);
429 
430     if (!accounting_map) {
431         // without accounting, can do default batch execute
432         Execute(context, targets);
433 
434     } else {
435         // accounting info for this effect on this meter, starting with non-target-dependent info
436         AccountingInfo info;
437         info.cause_type =     effect_cause.cause_type;
438         info.specific_cause = effect_cause.specific_cause;
439         info.custom_label =   (m_accounting_label.empty() ? effect_cause.custom_label : m_accounting_label);
440         info.source_id =      context.source->ID();
441 
442         // process each target separately in order to do effect accounting for each
443         for (auto& target : targets) {
444             // get Meter for this effect and target
445             Meter* meter = target->GetMeter(m_meter);
446             if (!meter)
447                 continue;   // some objects might match target conditions, but not actually have the relevant meter. In that case, don't need to do accounting.
448 
449             // record pre-effect meter values...
450 
451             // accounting info for this effect on this meter of this target
452             info.running_meter_total =  meter->Current();
453 
454             // actually execute effect to modify meter
455             float val = m_value->Eval(ScriptingContext(context, target, meter->Current()));
456             meter->SetCurrent(val);
457 
458             // update for meter change and new total
459             info.meter_change = meter->Current() - info.running_meter_total;
460             info.running_meter_total = meter->Current();
461 
462             // add accounting for this effect to end of vector
463             (*accounting_map)[target->ID()][m_meter].push_back(info);
464         }
465     }
466 
467     TraceLogger(effects) << "SetMeter execute targets after: ";
468     for (auto& target : targets)
469         TraceLogger(effects) << " ... " << target->Dump();
470 }
471 
Execute(ScriptingContext & context,const TargetSet & targets) const472 void SetMeter::Execute(ScriptingContext& context, const TargetSet& targets) const {
473     if (targets.empty())
474         return;
475 
476     if (m_value->TargetInvariant()) {
477         // meter value does not depend on target, so handle with single ValueRef evaluation
478         float val = m_value->Eval(context);
479         for (auto& target : targets) {
480             if (Meter* m = target->GetMeter(m_meter))
481                 m->SetCurrent(val);
482         }
483         return;
484 
485     } else if (m_value->SimpleIncrement()) {
486         // meter value is a consistent constant increment for each target, so handle with
487         // deep inspection single ValueRef evaluation
488         auto op = dynamic_cast<ValueRef::Operation<double>*>(m_value.get());
489         if (!op) {
490             ErrorLogger() << "SetMeter::Execute couldn't cast simple increment ValueRef to an Operation. Reverting to standard execute.";
491             Effect::Execute(context, targets);
492             return;
493         }
494 
495         // LHS should be a reference to the target's meter's immediate value, but
496         // RHS should be target-invariant, so safe to evaluate once and use for
497         // all target objects
498         float increment = 0.0f;
499         if (op->GetOpType() == ValueRef::PLUS) {
500             increment = op->RHS()->Eval(context);
501         } else if (op->GetOpType() == ValueRef::MINUS) {
502             increment = -op->RHS()->Eval(context);
503         } else {
504             ErrorLogger() << "SetMeter::Execute got invalid increment optype (not PLUS or MINUS). Reverting to standard execute.";
505             Effect::Execute(context, targets);
506             return;
507         }
508 
509         //DebugLogger() << "simple increment: " << increment;
510         // increment all target meters...
511         for (auto& target : targets) {
512             if (Meter* m = target->GetMeter(m_meter))
513                 m->AddToCurrent(increment);
514         }
515         return;
516     }
517 
518     // meter value depends on target non-trivially, so handle with default case of per-target ValueRef evaluation
519     Effect::Execute(context, targets);
520 }
521 
Dump(unsigned short ntabs) const522 std::string SetMeter::Dump(unsigned short ntabs) const {
523     std::string retval = DumpIndent(ntabs) + "Set";
524     switch (m_meter) {
525     case METER_TARGET_POPULATION:   retval += "TargetPopulation"; break;
526     case METER_TARGET_INDUSTRY:     retval += "TargetIndustry"; break;
527     case METER_TARGET_RESEARCH:     retval += "TargetResearch"; break;
528     case METER_TARGET_TRADE:        retval += "TargetTrade"; break;
529     case METER_TARGET_CONSTRUCTION: retval += "TargetConstruction"; break;
530     case METER_TARGET_HAPPINESS:    retval += "TargetHappiness"; break;
531 
532     case METER_MAX_CAPACITY:        retval += "MaxCapacity"; break;
533 
534     case METER_MAX_FUEL:            retval += "MaxFuel"; break;
535     case METER_MAX_SHIELD:          retval += "MaxShield"; break;
536     case METER_MAX_STRUCTURE:       retval += "MaxStructure"; break;
537     case METER_MAX_DEFENSE:         retval += "MaxDefense"; break;
538     case METER_MAX_SUPPLY:          retval += "MaxSupply"; break;
539     case METER_MAX_STOCKPILE:       retval += "MaxStockpile"; break;
540     case METER_MAX_TROOPS:          retval += "MaxTroops"; break;
541 
542     case METER_POPULATION:          retval += "Population"; break;
543     case METER_INDUSTRY:            retval += "Industry"; break;
544     case METER_RESEARCH:            retval += "Research"; break;
545     case METER_TRADE:               retval += "Trade"; break;
546     case METER_CONSTRUCTION:        retval += "Construction"; break;
547     case METER_HAPPINESS:           retval += "Happiness"; break;
548 
549     case METER_CAPACITY:            retval += "Capacity"; break;
550 
551     case METER_FUEL:                retval += "Fuel"; break;
552     case METER_SHIELD:              retval += "Shield"; break;
553     case METER_STRUCTURE:           retval += "Structure"; break;
554     case METER_DEFENSE:             retval += "Defense"; break;
555     case METER_SUPPLY:              retval += "Supply"; break;
556     case METER_STOCKPILE:           retval += "Stockpile"; break;
557     case METER_TROOPS:              retval += "Troops"; break;
558 
559     case METER_REBEL_TROOPS:        retval += "RebelTroops"; break;
560     case METER_SIZE:                retval += "Size"; break;
561     case METER_STEALTH:             retval += "Stealth"; break;
562     case METER_DETECTION:           retval += "Detection"; break;
563     case METER_SPEED:               retval += "Speed"; break;
564 
565     default:                        retval += "?"; break;
566     }
567     retval += " value = " + m_value->Dump(ntabs) + "\n";
568     return retval;
569 }
570 
SetTopLevelContent(const std::string & content_name)571 void SetMeter::SetTopLevelContent(const std::string& content_name) {
572     if (m_value)
573         m_value->SetTopLevelContent(content_name);
574 }
575 
GetCheckSum() const576 unsigned int SetMeter::GetCheckSum() const {
577     unsigned int retval{0};
578 
579     CheckSums::CheckSumCombine(retval, "SetMeter");
580     CheckSums::CheckSumCombine(retval, m_meter);
581     CheckSums::CheckSumCombine(retval, m_value);
582     CheckSums::CheckSumCombine(retval, m_accounting_label);
583 
584     TraceLogger() << "GetCheckSum(SetMeter): retval: " << retval;
585     return retval;
586 }
587 
588 
589 ///////////////////////////////////////////////////////////
590 // SetShipPartMeter                                      //
591 ///////////////////////////////////////////////////////////
SetShipPartMeter(MeterType meter,std::unique_ptr<ValueRef::ValueRef<std::string>> && part_name,std::unique_ptr<ValueRef::ValueRef<double>> && value)592 SetShipPartMeter::SetShipPartMeter(MeterType meter,
593                                    std::unique_ptr<ValueRef::ValueRef<std::string>>&& part_name,
594                                    std::unique_ptr<ValueRef::ValueRef<double>>&& value) :
595     m_part_name(std::move(part_name)),
596     m_meter(meter),
597     m_value(std::move(value))
598 {}
599 
Execute(ScriptingContext & context) const600 void SetShipPartMeter::Execute(ScriptingContext& context) const {
601     if (!context.effect_target) {
602         DebugLogger() << "SetShipPartMeter::Execute passed null target pointer";
603         return;
604     }
605 
606     if (!m_part_name || !m_value) {
607         ErrorLogger() << "SetShipPartMeter::Execute missing part name or value ValueRefs";
608         return;
609     }
610 
611     auto ship = std::dynamic_pointer_cast<Ship>(context.effect_target);
612     if (!ship) {
613         ErrorLogger() << "SetShipPartMeter::Execute acting on non-ship target:";
614         //context.effect_target->Dump();
615         return;
616     }
617 
618     std::string part_name = m_part_name->Eval(context);
619 
620     // get meter, evaluate new value, assign
621     Meter* meter = ship->GetPartMeter(m_meter, part_name);
622     if (!meter)
623         return;
624 
625     double val = m_value->Eval(ScriptingContext(context, meter->Current()));
626     meter->SetCurrent(val);
627 }
628 
Execute(ScriptingContext & context,const TargetSet & targets,AccountingMap * accounting_map,const EffectCause & effect_cause,bool only_meter_effects,bool only_appearance_effects,bool include_empire_meter_effects,bool only_generate_sitrep_effects) const629 void SetShipPartMeter::Execute(ScriptingContext& context,
630                                const TargetSet& targets,
631                                AccountingMap* accounting_map,
632                                const EffectCause& effect_cause,
633                                bool only_meter_effects,
634                                bool only_appearance_effects,
635                                bool include_empire_meter_effects,
636                                bool only_generate_sitrep_effects) const
637 {
638     if (only_appearance_effects || only_generate_sitrep_effects)
639         return;
640 
641     TraceLogger(effects) << "\n\nExecute SetShipPartMeter effect: \n" << Dump();
642     TraceLogger(effects) << "SetShipPartMeter execute targets before: ";
643     for (auto& target : targets)
644         TraceLogger(effects) << " ... " << target->Dump(1);
645 
646     Execute(context, targets);
647 
648     TraceLogger(effects) << "SetShipPartMeter execute targets after: ";
649     for (auto& target : targets)
650         TraceLogger(effects) << " ... " << target->Dump(1);
651 }
652 
Execute(ScriptingContext & context,const TargetSet & targets) const653 void SetShipPartMeter::Execute(ScriptingContext& context, const TargetSet& targets) const {
654     if (targets.empty())
655         return;
656     if (!m_part_name || !m_value) {
657         ErrorLogger() << "SetShipPartMeter::Execute missing part name or value ValueRefs";
658         return;
659     }
660 
661     // TODO: Handle efficiently the case where the part name varies from target
662     // to target, but the value is target-invariant
663     if (!m_part_name->TargetInvariant()) {
664         DebugLogger() << "SetShipPartMeter::Execute has target-variant part name, which it is not (yet) coded to handle efficiently!";
665         Effect::Execute(context, targets);
666         return;
667     }
668 
669     // part name doesn't depend on target, so handle with single ValueRef evaluation
670     std::string part_name = m_part_name->Eval(context);
671 
672     if (m_value->TargetInvariant()) {
673         // meter value does not depend on target, so handle with single ValueRef evaluation
674         float val = m_value->Eval(context);
675         for (auto& target : targets) {
676             if (target->ObjectType() != OBJ_SHIP)
677                 continue;
678             auto ship = std::dynamic_pointer_cast<Ship>(target);
679             if (!ship)
680                 continue;
681             if (Meter* m = ship->GetPartMeter(m_meter, part_name))
682                 m->SetCurrent(val);
683         }
684         return;
685 
686     } else if (m_value->SimpleIncrement()) {
687         // meter value is a consistent constant increment for each target, so handle with
688         // deep inspection single ValueRef evaluation
689         auto op = dynamic_cast<ValueRef::Operation<double>*>(m_value.get());
690         if (!op) {
691             ErrorLogger() << "SetShipPartMeter::Execute couldn't cast simple increment ValueRef to an Operation. Reverting to standard execute.";
692             Effect::Execute(context, targets);
693             return;
694         }
695 
696         // LHS should be a reference to the target's meter's immediate value, but
697         // RHS should be target-invariant, so safe to evaluate once and use for
698         // all target objects
699         float increment = 0.0f;
700         if (op->GetOpType() == ValueRef::PLUS) {
701             increment = op->RHS()->Eval(context);
702         } else if (op->GetOpType() == ValueRef::MINUS) {
703             increment = -op->RHS()->Eval(context);
704         } else {
705             ErrorLogger() << "SetShipPartMeter::Execute got invalid increment optype (not PLUS or MINUS). Reverting to standard execute.";
706             Effect::Execute(context, targets);
707             return;
708         }
709 
710         //DebugLogger() << "simple increment: " << increment;
711         // increment all target meters...
712         for (auto& target : targets) {
713             if (target->ObjectType() != OBJ_SHIP)
714                 continue;
715             auto ship = std::dynamic_pointer_cast<Ship>(target);
716             if (!ship)
717                 continue;
718             if (Meter* m = ship->GetPartMeter(m_meter, part_name))
719                 m->AddToCurrent(increment);
720         }
721         return;
722 
723     } else {
724         //DebugLogger() << "complicated meter adjustment...";
725         // meter value depends on target non-trivially, so handle with default case of per-target ValueRef evaluation
726         Effect::Execute(context, targets);
727     }
728 }
729 
Dump(unsigned short ntabs) const730 std::string SetShipPartMeter::Dump(unsigned short ntabs) const {
731     std::string retval = DumpIndent(ntabs);
732     switch (m_meter) {
733         case METER_CAPACITY:            retval += "SetCapacity";        break;
734         case METER_MAX_CAPACITY:        retval += "SetMaxCapacity";     break;
735         case METER_SECONDARY_STAT:      retval += "SetSecondaryStat";   break;
736         case METER_MAX_SECONDARY_STAT:  retval += "SetMaxSecondaryStat";break;
737         default:                retval += "Set???";         break;
738     }
739 
740     if (m_part_name)
741         retval += " partname = " + m_part_name->Dump(ntabs);
742 
743     retval += " value = " + m_value->Dump(ntabs);
744 
745     return retval;
746 }
747 
SetTopLevelContent(const std::string & content_name)748 void SetShipPartMeter::SetTopLevelContent(const std::string& content_name) {
749     if (m_value)
750         m_value->SetTopLevelContent(content_name);
751     if (m_part_name)
752         m_part_name->SetTopLevelContent(content_name);
753 }
754 
GetCheckSum() const755 unsigned int SetShipPartMeter::GetCheckSum() const {
756     unsigned int retval{0};
757 
758     CheckSums::CheckSumCombine(retval, "SetShipPartMeter");
759     CheckSums::CheckSumCombine(retval, m_part_name);
760     CheckSums::CheckSumCombine(retval, m_meter);
761     CheckSums::CheckSumCombine(retval, m_value);
762 
763     TraceLogger() << "GetCheckSum(SetShipPartMeter): retval: " << retval;
764     return retval;
765 }
766 
767 
768 ///////////////////////////////////////////////////////////
769 // SetEmpireMeter                                        //
770 ///////////////////////////////////////////////////////////
SetEmpireMeter(const std::string & meter,std::unique_ptr<ValueRef::ValueRef<double>> && value)771 SetEmpireMeter::SetEmpireMeter(const std::string& meter, std::unique_ptr<ValueRef::ValueRef<double>>&& value) :
772     m_empire_id(std::make_unique<ValueRef::Variable<int>>(ValueRef::EFFECT_TARGET_REFERENCE, std::vector<std::string>(1, "Owner"))),
773     m_meter(meter),
774     m_value(std::move(value))
775 {}
776 
SetEmpireMeter(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,const std::string & meter,std::unique_ptr<ValueRef::ValueRef<double>> && value)777 SetEmpireMeter::SetEmpireMeter(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id, const std::string& meter,
778                                std::unique_ptr<ValueRef::ValueRef<double>>&& value) :
779     m_empire_id(std::move(empire_id)),
780     m_meter(meter),
781     m_value(std::move(value))
782 {}
783 
Execute(ScriptingContext & context) const784 void SetEmpireMeter::Execute(ScriptingContext& context) const {
785     if (!context.effect_target) {
786         DebugLogger() << "SetEmpireMeter::Execute passed null target pointer";
787         return;
788     }
789     if (!m_empire_id || !m_value || m_meter.empty()) {
790         ErrorLogger() << "SetEmpireMeter::Execute missing empire id or value ValueRefs, or given empty meter name";
791         return;
792     }
793 
794     int empire_id = m_empire_id->Eval(context);
795     Empire* empire = GetEmpire(empire_id);
796     if (!empire) {
797         DebugLogger() << "SetEmpireMeter::Execute unable to find empire with id " << empire_id;
798         return;
799     }
800 
801     Meter* meter = empire->GetMeter(m_meter);
802     if (!meter) {
803         DebugLogger() << "SetEmpireMeter::Execute empire " << empire->Name() << " doesn't have a meter named " << m_meter;
804         return;
805     }
806 
807     double&& value = m_value->Eval(ScriptingContext(context, meter->Current()));
808 
809     meter->SetCurrent(value);
810 }
811 
Execute(ScriptingContext & context,const TargetSet & targets,AccountingMap * accounting_map,const EffectCause & effect_cause,bool only_meter_effects,bool only_appearance_effects,bool include_empire_meter_effects,bool only_generate_sitrep_effects) const812 void SetEmpireMeter::Execute(ScriptingContext& context,
813                              const TargetSet& targets,
814                              AccountingMap* accounting_map,
815                              const EffectCause& effect_cause,
816                              bool only_meter_effects,
817                              bool only_appearance_effects,
818                              bool include_empire_meter_effects,
819                              bool only_generate_sitrep_effects) const
820 {
821     if (!include_empire_meter_effects ||
822         only_appearance_effects ||
823         only_generate_sitrep_effects)
824     { return; }
825     // presently no accounting done for empire meters.
826     // TODO: maybe implement empire meter effect accounting?
827     Execute(context, targets);
828 }
829 
Execute(ScriptingContext & context,const TargetSet & targets) const830 void SetEmpireMeter::Execute(ScriptingContext& context, const TargetSet& targets) const {
831     if (targets.empty())
832         return;
833     if (!m_empire_id || m_meter.empty() || !m_value) {
834         ErrorLogger() << "SetEmpireMeter::Execute missing empire id or value ValueRefs or meter name";
835         return;
836     }
837 
838     // TODO: efficiently handle target invariant empire id and value
839     Effect::Execute(context, targets);
840     //return;
841 
842     //if (m_empire_id->TargetInvariant() && m_value->TargetInvariant()) {
843     //}
844 
845     ////DebugLogger() << "complicated meter adjustment...";
846     //// meter value depends on target non-trivially, so handle with default case of per-target ValueRef evaluation
847     //Effect::Execute(context, targets);
848 }
849 
Dump(unsigned short ntabs) const850 std::string SetEmpireMeter::Dump(unsigned short ntabs) const
851 { return DumpIndent(ntabs) + "SetEmpireMeter meter = " + m_meter + " empire = " + m_empire_id->Dump(ntabs) + " value = " + m_value->Dump(ntabs); }
852 
SetTopLevelContent(const std::string & content_name)853 void SetEmpireMeter::SetTopLevelContent(const std::string& content_name) {
854     if (m_empire_id)
855         m_empire_id->SetTopLevelContent(content_name);
856     if (m_value)
857         m_value->SetTopLevelContent(content_name);
858 }
859 
GetCheckSum() const860 unsigned int SetEmpireMeter::GetCheckSum() const {
861     unsigned int retval{0};
862 
863     CheckSums::CheckSumCombine(retval, "SetEmpireMeter");
864     CheckSums::CheckSumCombine(retval, m_empire_id);
865     CheckSums::CheckSumCombine(retval, m_meter);
866     CheckSums::CheckSumCombine(retval, m_value);
867 
868     TraceLogger() << "GetCheckSum(SetEmpireMeter): retval: " << retval;
869     return retval;
870 }
871 
872 
873 ///////////////////////////////////////////////////////////
874 // SetEmpireStockpile                                    //
875 ///////////////////////////////////////////////////////////
SetEmpireStockpile(ResourceType stockpile,std::unique_ptr<ValueRef::ValueRef<double>> && value)876 SetEmpireStockpile::SetEmpireStockpile(ResourceType stockpile,
877                                        std::unique_ptr<ValueRef::ValueRef<double>>&& value) :
878     m_empire_id(std::make_unique<ValueRef::Variable<int>>(ValueRef::EFFECT_TARGET_REFERENCE, std::vector<std::string>(1, "Owner"))),
879     m_stockpile(stockpile),
880     m_value(std::move(value))
881 {}
882 
SetEmpireStockpile(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,ResourceType stockpile,std::unique_ptr<ValueRef::ValueRef<double>> && value)883 SetEmpireStockpile::SetEmpireStockpile(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
884                                        ResourceType stockpile,
885                                        std::unique_ptr<ValueRef::ValueRef<double>>&& value) :
886     m_empire_id(std::move(empire_id)),
887     m_stockpile(stockpile),
888     m_value(std::move(value))
889 {}
890 
Execute(ScriptingContext & context) const891 void SetEmpireStockpile::Execute(ScriptingContext& context) const {
892     int empire_id = m_empire_id->Eval(context);
893 
894     Empire* empire = GetEmpire(empire_id);
895     if (!empire) {
896         DebugLogger() << "SetEmpireStockpile::Execute couldn't find an empire with id " << empire_id;
897         return;
898     }
899 
900     double value = m_value->Eval(ScriptingContext(context, empire->ResourceStockpile(m_stockpile)));
901     empire->SetResourceStockpile(m_stockpile, value);
902 }
903 
Dump(unsigned short ntabs) const904 std::string SetEmpireStockpile::Dump(unsigned short ntabs) const {
905     std::string retval = DumpIndent(ntabs);
906     switch (m_stockpile) {
907         // TODO: Support for other resource stockpiles?
908     case RE_INDUSTRY:   retval += "SetEmpireStockpile"; break;
909     default:            retval += "?"; break;
910     }
911     retval += " empire = " + m_empire_id->Dump(ntabs) + " value = " + m_value->Dump(ntabs) + "\n";
912     return retval;
913 }
914 
SetTopLevelContent(const std::string & content_name)915 void SetEmpireStockpile::SetTopLevelContent(const std::string& content_name) {
916     if (m_empire_id)
917         m_empire_id->SetTopLevelContent(content_name);
918     if (m_value)
919         m_value->SetTopLevelContent(content_name);
920 }
921 
GetCheckSum() const922 unsigned int SetEmpireStockpile::GetCheckSum() const {
923     unsigned int retval{0};
924 
925     CheckSums::CheckSumCombine(retval, "SetEmpireStockpile");
926     CheckSums::CheckSumCombine(retval, m_empire_id);
927     CheckSums::CheckSumCombine(retval, m_stockpile);
928     CheckSums::CheckSumCombine(retval, m_value);
929 
930     TraceLogger() << "GetCheckSum(SetEmpireStockpile): retval: " << retval;
931     return retval;
932 }
933 
934 
935 ///////////////////////////////////////////////////////////
936 // SetEmpireCapital                                      //
937 ///////////////////////////////////////////////////////////
SetEmpireCapital()938 SetEmpireCapital::SetEmpireCapital() :
939     m_empire_id(std::make_unique<ValueRef::Variable<int>>(ValueRef::EFFECT_TARGET_REFERENCE, std::vector<std::string>(1, "Owner")))
940 {}
941 
SetEmpireCapital(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)942 SetEmpireCapital::SetEmpireCapital(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
943     m_empire_id(std::move(empire_id))
944 {}
945 
Execute(ScriptingContext & context) const946 void SetEmpireCapital::Execute(ScriptingContext& context) const {
947     int empire_id = m_empire_id->Eval(context);
948 
949     Empire* empire = GetEmpire(empire_id);
950     if (!empire)
951         return;
952 
953     auto planet = std::dynamic_pointer_cast<const Planet>(context.effect_target);
954     if (!planet)
955         return;
956 
957     empire->SetCapitalID(planet->ID());
958 }
959 
Dump(unsigned short ntabs) const960 std::string SetEmpireCapital::Dump(unsigned short ntabs) const
961 { return DumpIndent(ntabs) + "SetEmpireCapital empire = " + m_empire_id->Dump(ntabs) + "\n"; }
962 
SetTopLevelContent(const std::string & content_name)963 void SetEmpireCapital::SetTopLevelContent(const std::string& content_name) {
964     if (m_empire_id)
965         m_empire_id->SetTopLevelContent(content_name);
966 }
967 
GetCheckSum() const968 unsigned int SetEmpireCapital::GetCheckSum() const {
969     unsigned int retval{0};
970 
971     CheckSums::CheckSumCombine(retval, "SetEmpireCapital");
972     CheckSums::CheckSumCombine(retval, m_empire_id);
973 
974     TraceLogger() << "GetCheckSum(SetEmpireCapital): retval: " << retval;
975     return retval;
976 }
977 
978 
979 ///////////////////////////////////////////////////////////
980 // SetPlanetType                                         //
981 ///////////////////////////////////////////////////////////
SetPlanetType(std::unique_ptr<ValueRef::ValueRef<PlanetType>> && type)982 SetPlanetType::SetPlanetType(std::unique_ptr<ValueRef::ValueRef<PlanetType>>&& type) :
983     m_type(std::move(type))
984 {}
985 
Execute(ScriptingContext & context) const986 void SetPlanetType::Execute(ScriptingContext& context) const {
987     if (auto p = std::dynamic_pointer_cast<Planet>(context.effect_target)) {
988         PlanetType type = m_type->Eval(ScriptingContext(context, p->Type()));
989         p->SetType(type);
990         if (type == PT_ASTEROIDS)
991             p->SetSize(SZ_ASTEROIDS);
992         else if (type == PT_GASGIANT)
993             p->SetSize(SZ_GASGIANT);
994         else if (p->Size() == SZ_ASTEROIDS)
995             p->SetSize(SZ_TINY);
996         else if (p->Size() == SZ_GASGIANT)
997             p->SetSize(SZ_HUGE);
998     }
999 }
1000 
Dump(unsigned short ntabs) const1001 std::string SetPlanetType::Dump(unsigned short ntabs) const
1002 { return DumpIndent(ntabs) + "SetPlanetType type = " + m_type->Dump(ntabs) + "\n"; }
1003 
SetTopLevelContent(const std::string & content_name)1004 void SetPlanetType::SetTopLevelContent(const std::string& content_name) {
1005     if (m_type)
1006         m_type->SetTopLevelContent(content_name);
1007 }
1008 
GetCheckSum() const1009 unsigned int SetPlanetType::GetCheckSum() const {
1010     unsigned int retval{0};
1011 
1012     CheckSums::CheckSumCombine(retval, "SetPlanetType");
1013     CheckSums::CheckSumCombine(retval, m_type);
1014 
1015     TraceLogger() << "GetCheckSum(SetPlanetType): retval: " << retval;
1016     return retval;
1017 }
1018 
1019 
1020 ///////////////////////////////////////////////////////////
1021 // SetPlanetSize                                         //
1022 ///////////////////////////////////////////////////////////
SetPlanetSize(std::unique_ptr<ValueRef::ValueRef<PlanetSize>> && size)1023 SetPlanetSize::SetPlanetSize(std::unique_ptr<ValueRef::ValueRef<PlanetSize>>&& size) :
1024     m_size(std::move(size))
1025 {}
1026 
Execute(ScriptingContext & context) const1027 void SetPlanetSize::Execute(ScriptingContext& context) const {
1028     if (auto p = std::dynamic_pointer_cast<Planet>(context.effect_target)) {
1029         PlanetSize size = m_size->Eval(ScriptingContext(context, p->Size()));
1030         p->SetSize(size);
1031         if (size == SZ_ASTEROIDS)
1032             p->SetType(PT_ASTEROIDS);
1033         else if (size == SZ_GASGIANT)
1034             p->SetType(PT_GASGIANT);
1035         else if (p->Type() == PT_ASTEROIDS || p->Type() == PT_GASGIANT)
1036             p->SetType(PT_BARREN);
1037     }
1038 }
1039 
Dump(unsigned short ntabs) const1040 std::string SetPlanetSize::Dump(unsigned short ntabs) const
1041 { return DumpIndent(ntabs) + "SetPlanetSize size = " + m_size->Dump(ntabs) + "\n"; }
1042 
SetTopLevelContent(const std::string & content_name)1043 void SetPlanetSize::SetTopLevelContent(const std::string& content_name) {
1044     if (m_size)
1045         m_size->SetTopLevelContent(content_name);
1046 }
1047 
GetCheckSum() const1048 unsigned int SetPlanetSize::GetCheckSum() const {
1049     unsigned int retval{0};
1050 
1051     CheckSums::CheckSumCombine(retval, "SetPlanetSize");
1052     CheckSums::CheckSumCombine(retval, m_size);
1053 
1054     TraceLogger() << "GetCheckSum(SetPlanetSize): retval: " << retval;
1055     return retval;
1056 }
1057 
1058 
1059 ///////////////////////////////////////////////////////////
1060 // SetSpecies                                            //
1061 ///////////////////////////////////////////////////////////
SetSpecies(std::unique_ptr<ValueRef::ValueRef<std::string>> && species)1062 SetSpecies::SetSpecies(std::unique_ptr<ValueRef::ValueRef<std::string>>&& species) :
1063     m_species_name(std::move(species))
1064 {}
1065 
Execute(ScriptingContext & context) const1066 void SetSpecies::Execute(ScriptingContext& context) const {
1067     if (auto planet = std::dynamic_pointer_cast<Planet>(context.effect_target)) {
1068         std::string species_name = m_species_name->Eval(ScriptingContext(context, planet->SpeciesName()));
1069         planet->SetSpecies(species_name);
1070 
1071         // ensure non-empty and permissible focus setting for new species
1072         std::string initial_focus = planet->Focus();
1073         std::vector<std::string> available_foci = planet->AvailableFoci();
1074 
1075         // leave current focus unchanged if available.
1076         for (const std::string& available_focus : available_foci) {
1077             if (available_focus == initial_focus) {
1078                 return;
1079             }
1080         }
1081 
1082         // need to set new focus
1083         std::string new_focus;
1084 
1085         const Species* species = GetSpecies(species_name);
1086         std::string preferred_focus;
1087         if (species)
1088             preferred_focus = species->PreferredFocus();
1089 
1090         // chose preferred focus if available. otherwise use any available focus
1091         bool preferred_available = false;
1092         for (const std::string& available_focus : available_foci) {
1093             if (available_focus == preferred_focus) {
1094                 preferred_available = true;
1095                 break;
1096             }
1097         }
1098 
1099         if (preferred_available) {
1100             new_focus = std::move(preferred_focus);
1101         } else if (!available_foci.empty()) {
1102             new_focus = *available_foci.begin();
1103         }
1104 
1105         planet->SetFocus(new_focus);
1106 
1107     } else if (auto ship = std::dynamic_pointer_cast<Ship>(context.effect_target)) {
1108         std::string species_name = m_species_name->Eval(ScriptingContext(context, ship->SpeciesName()));
1109         ship->SetSpecies(species_name);
1110     }
1111 }
1112 
Dump(unsigned short ntabs) const1113 std::string SetSpecies::Dump(unsigned short ntabs) const
1114 { return DumpIndent(ntabs) + "SetSpecies name = " + m_species_name->Dump(ntabs) + "\n"; }
1115 
SetTopLevelContent(const std::string & content_name)1116 void SetSpecies::SetTopLevelContent(const std::string& content_name) {
1117     if (m_species_name)
1118         m_species_name->SetTopLevelContent(content_name);
1119 }
1120 
GetCheckSum() const1121 unsigned int SetSpecies::GetCheckSum() const {
1122     unsigned int retval{0};
1123 
1124     CheckSums::CheckSumCombine(retval, "SetSpecies");
1125     CheckSums::CheckSumCombine(retval, m_species_name);
1126 
1127     TraceLogger() << "GetCheckSum(SetSpecies): retval: " << retval;
1128     return retval;
1129 }
1130 
1131 
1132 ///////////////////////////////////////////////////////////
1133 // SetOwner                                              //
1134 ///////////////////////////////////////////////////////////
SetOwner(std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)1135 SetOwner::SetOwner(std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
1136     m_empire_id(std::move(empire_id))
1137 {}
1138 
Execute(ScriptingContext & context) const1139 void SetOwner::Execute(ScriptingContext& context) const {
1140     if (!context.effect_target)
1141         return;
1142     int initial_owner = context.effect_target->Owner();
1143 
1144     int empire_id = m_empire_id->Eval(ScriptingContext(context, initial_owner));
1145     if (initial_owner == empire_id)
1146         return;
1147 
1148     context.effect_target->SetOwner(empire_id);
1149 
1150     if (auto ship = std::dynamic_pointer_cast<Ship>(context.effect_target)) {
1151         // assigning ownership of a ship requires updating the containing
1152         // fleet, or splitting ship off into a new fleet at the same location
1153         auto fleet = context.ContextObjects().get<Fleet>(ship->FleetID());
1154         if (!fleet)
1155             return;
1156         if (fleet->Owner() == empire_id)
1157             return;
1158 
1159         // move ship into new fleet
1160         std::shared_ptr<Fleet> new_fleet;
1161         if (auto system = context.ContextObjects().get<System>(ship->SystemID()))
1162             new_fleet = CreateNewFleet(system, ship, context.ContextObjects());
1163         else
1164             new_fleet = CreateNewFleet(ship->X(), ship->Y(), ship);
1165         if (new_fleet) {
1166             new_fleet->SetNextAndPreviousSystems(fleet->NextSystemID(), fleet->PreviousSystemID());
1167         }
1168 
1169         // if old fleet is empty, destroy it.  Don't reassign ownership of fleet
1170         // in case that would reval something to the recipient that shouldn't be...
1171         if (fleet->Empty())
1172             GetUniverse().EffectDestroy(fleet->ID(), INVALID_OBJECT_ID);    // no particular source destroyed the fleet in this case
1173     }
1174 }
1175 
Dump(unsigned short ntabs) const1176 std::string SetOwner::Dump(unsigned short ntabs) const
1177 { return DumpIndent(ntabs) + "SetOwner empire = " + m_empire_id->Dump(ntabs) + "\n"; }
1178 
SetTopLevelContent(const std::string & content_name)1179 void SetOwner::SetTopLevelContent(const std::string& content_name) {
1180     if (m_empire_id)
1181         m_empire_id->SetTopLevelContent(content_name);
1182 }
1183 
GetCheckSum() const1184 unsigned int SetOwner::GetCheckSum() const {
1185     unsigned int retval{0};
1186 
1187     CheckSums::CheckSumCombine(retval, "SetOwner");
1188     CheckSums::CheckSumCombine(retval, m_empire_id);
1189 
1190     TraceLogger() << "GetCheckSum(SetOwner): retval: " << retval;
1191     return retval;
1192 }
1193 
1194 
1195 ///////////////////////////////////////////////////////////
1196 // SetSpeciesEmpireOpinion                               //
1197 ///////////////////////////////////////////////////////////
SetSpeciesEmpireOpinion(std::unique_ptr<ValueRef::ValueRef<std::string>> && species_name,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<double>> && opinion)1198 SetSpeciesEmpireOpinion::SetSpeciesEmpireOpinion(
1199     std::unique_ptr<ValueRef::ValueRef<std::string>>&& species_name,
1200     std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
1201     std::unique_ptr<ValueRef::ValueRef<double>>&& opinion
1202 ) :
1203     m_species_name(std::move(species_name)),
1204     m_empire_id(std::move(empire_id)),
1205     m_opinion(std::move(opinion))
1206 {}
1207 
Execute(ScriptingContext & context) const1208 void SetSpeciesEmpireOpinion::Execute(ScriptingContext& context) const {
1209     if (!context.effect_target)
1210         return;
1211     if (!m_species_name || !m_opinion || !m_empire_id)
1212         return;
1213 
1214     int empire_id = m_empire_id->Eval(context);
1215     if (empire_id == ALL_EMPIRES)
1216         return;
1217 
1218     std::string species_name = m_species_name->Eval(context);
1219     if (species_name.empty())
1220         return;
1221 
1222     double initial_opinion = GetSpeciesManager().SpeciesEmpireOpinion(species_name, empire_id);
1223     double opinion = m_opinion->Eval(ScriptingContext(context, initial_opinion));
1224 
1225     GetSpeciesManager().SetSpeciesEmpireOpinion(species_name, empire_id, opinion);
1226 }
1227 
Dump(unsigned short ntabs) const1228 std::string SetSpeciesEmpireOpinion::Dump(unsigned short ntabs) const
1229 { return DumpIndent(ntabs) + "SetSpeciesEmpireOpinion empire = " + m_empire_id->Dump(ntabs) + "\n"; }
1230 
SetTopLevelContent(const std::string & content_name)1231 void SetSpeciesEmpireOpinion::SetTopLevelContent(const std::string& content_name) {
1232     if (m_empire_id)
1233         m_empire_id->SetTopLevelContent(content_name);
1234     if (m_species_name)
1235         m_species_name->SetTopLevelContent(content_name);
1236     if (m_opinion)
1237         m_opinion->SetTopLevelContent(content_name);
1238 }
1239 
GetCheckSum() const1240 unsigned int SetSpeciesEmpireOpinion::GetCheckSum() const {
1241     unsigned int retval{0};
1242 
1243     CheckSums::CheckSumCombine(retval, "SetSpeciesEmpireOpinion");
1244     CheckSums::CheckSumCombine(retval, m_species_name);
1245     CheckSums::CheckSumCombine(retval, m_empire_id);
1246     CheckSums::CheckSumCombine(retval, m_opinion);
1247 
1248     TraceLogger() << "GetCheckSum(SetSpeciesEmpireOpinion): retval: " << retval;
1249     return retval;
1250 }
1251 
1252 
1253 ///////////////////////////////////////////////////////////
1254 // SetSpeciesSpeciesOpinion                              //
1255 ///////////////////////////////////////////////////////////
SetSpeciesSpeciesOpinion(std::unique_ptr<ValueRef::ValueRef<std::string>> && opinionated_species_name,std::unique_ptr<ValueRef::ValueRef<std::string>> && rated_species_name,std::unique_ptr<ValueRef::ValueRef<double>> && opinion)1256 SetSpeciesSpeciesOpinion::SetSpeciesSpeciesOpinion(
1257     std::unique_ptr<ValueRef::ValueRef<std::string>>&& opinionated_species_name,
1258     std::unique_ptr<ValueRef::ValueRef<std::string>>&& rated_species_name,
1259     std::unique_ptr<ValueRef::ValueRef<double>>&& opinion
1260 ) :
1261     m_opinionated_species_name(std::move(opinionated_species_name)),
1262     m_rated_species_name(std::move(rated_species_name)),
1263     m_opinion(std::move(opinion))
1264 {}
1265 
Execute(ScriptingContext & context) const1266 void SetSpeciesSpeciesOpinion::Execute(ScriptingContext& context) const {
1267     if (!context.effect_target)
1268         return;
1269     if (!m_opinionated_species_name || !m_opinion || !m_rated_species_name)
1270         return;
1271 
1272     std::string opinionated_species_name = m_opinionated_species_name->Eval(context);
1273     if (opinionated_species_name.empty())
1274         return;
1275 
1276     std::string rated_species_name = m_rated_species_name->Eval(context);
1277     if (rated_species_name.empty())
1278         return;
1279 
1280     float initial_opinion = GetSpeciesManager().SpeciesSpeciesOpinion(opinionated_species_name, rated_species_name);
1281     float opinion = m_opinion->Eval(ScriptingContext(context, initial_opinion));
1282 
1283     GetSpeciesManager().SetSpeciesSpeciesOpinion(opinionated_species_name, rated_species_name, opinion);
1284 }
1285 
Dump(unsigned short ntabs) const1286 std::string SetSpeciesSpeciesOpinion::Dump(unsigned short ntabs) const
1287 { return DumpIndent(ntabs) + "SetSpeciesSpeciesOpinion" + "\n"; }
1288 
SetTopLevelContent(const std::string & content_name)1289 void SetSpeciesSpeciesOpinion::SetTopLevelContent(const std::string& content_name) {
1290     if (m_opinionated_species_name)
1291         m_opinionated_species_name->SetTopLevelContent(content_name);
1292     if (m_rated_species_name)
1293         m_rated_species_name->SetTopLevelContent(content_name);
1294     if (m_opinion)
1295         m_opinion->SetTopLevelContent(content_name);
1296 }
1297 
GetCheckSum() const1298 unsigned int SetSpeciesSpeciesOpinion::GetCheckSum() const {
1299     unsigned int retval{0};
1300 
1301     CheckSums::CheckSumCombine(retval, "SetSpeciesSpeciesOpinion");
1302     CheckSums::CheckSumCombine(retval, m_opinionated_species_name);
1303     CheckSums::CheckSumCombine(retval, m_rated_species_name);
1304     CheckSums::CheckSumCombine(retval, m_opinion);
1305 
1306     TraceLogger() << "GetCheckSum(SetSpeciesSpeciesOpinion): retval: " << retval;
1307     return retval;
1308 }
1309 
1310 
1311 ///////////////////////////////////////////////////////////
1312 // CreatePlanet                                          //
1313 ///////////////////////////////////////////////////////////
CreatePlanet(std::unique_ptr<ValueRef::ValueRef<PlanetType>> && type,std::unique_ptr<ValueRef::ValueRef<PlanetSize>> && size,std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1314 CreatePlanet::CreatePlanet(std::unique_ptr<ValueRef::ValueRef<PlanetType>>&& type,
1315                            std::unique_ptr<ValueRef::ValueRef<PlanetSize>>&& size,
1316                            std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
1317                            std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1318     m_type(std::move(type)),
1319     m_size(std::move(size)),
1320     m_name(std::move(name)),
1321     m_effects_to_apply_after(std::move(effects_to_apply_after))
1322 {}
1323 
Execute(ScriptingContext & context) const1324 void CreatePlanet::Execute(ScriptingContext& context) const {
1325     if (!context.effect_target) {
1326         ErrorLogger() << "CreatePlanet::Execute passed no target object";
1327         return;
1328     }
1329     auto system = context.ContextObjects().get<System>(context.effect_target->SystemID());
1330     if (!system) {
1331         ErrorLogger() << "CreatePlanet::Execute couldn't get a System object at which to create the planet";
1332         return;
1333     }
1334 
1335     PlanetSize target_size = INVALID_PLANET_SIZE;
1336     PlanetType target_type = INVALID_PLANET_TYPE;
1337     if (auto location_planet = std::dynamic_pointer_cast<const Planet>(context.effect_target)) {
1338         target_size = location_planet->Size();
1339         target_type = location_planet->Type();
1340     }
1341 
1342     PlanetSize size = m_size->Eval(ScriptingContext(context, target_size));
1343     PlanetType type = m_type->Eval(ScriptingContext(context, target_type));
1344     if (size == INVALID_PLANET_SIZE || type == INVALID_PLANET_TYPE) {
1345         ErrorLogger() << "CreatePlanet::Execute got invalid size or type of planet to create...";
1346         return;
1347     }
1348 
1349     // determine if and which orbits are available
1350     std::set<int> free_orbits = system->FreeOrbits();
1351     if (free_orbits.empty()) {
1352         ErrorLogger() << "CreatePlanet::Execute couldn't find any free orbits in system where planet was to be created";
1353         return;
1354     }
1355 
1356     auto planet = GetUniverse().InsertNew<Planet>(type, size);
1357     if (!planet) {
1358         ErrorLogger() << "CreatePlanet::Execute unable to create new Planet object";
1359         return;
1360     }
1361 
1362     system->Insert(planet);   // let system chose an orbit for planet
1363 
1364     std::string name_str;
1365     if (m_name) {
1366         name_str = m_name->Eval(context);
1367         if (m_name->ConstantExpr() && UserStringExists(name_str))
1368             name_str = UserString(name_str);
1369     } else {
1370         name_str = str(FlexibleFormat(UserString("NEW_PLANET_NAME")) % system->Name() % planet->CardinalSuffix());
1371     }
1372     planet->Rename(name_str);
1373 
1374     // apply after-creation effects
1375     ScriptingContext local_context = context;
1376     local_context.effect_target = planet;
1377     for (auto& effect : m_effects_to_apply_after) {
1378         if (!effect)
1379             continue;
1380         effect->Execute(local_context);
1381     }
1382 }
1383 
Dump(unsigned short ntabs) const1384 std::string CreatePlanet::Dump(unsigned short ntabs) const {
1385     std::string retval = DumpIndent(ntabs) + "CreatePlanet";
1386     if (m_size)
1387         retval += " size = " + m_size->Dump(ntabs);
1388     if (m_type)
1389         retval += " type = " + m_type->Dump(ntabs);
1390     if (m_name)
1391         retval += " name = " + m_name->Dump(ntabs);
1392     return retval + "\n";
1393 }
1394 
SetTopLevelContent(const std::string & content_name)1395 void CreatePlanet::SetTopLevelContent(const std::string& content_name) {
1396     if (m_type)
1397         m_type->SetTopLevelContent(content_name);
1398     if (m_size)
1399         m_size->SetTopLevelContent(content_name);
1400     if (m_name)
1401         m_name->SetTopLevelContent(content_name);
1402     for (auto& effect : m_effects_to_apply_after) {
1403         if (!effect)
1404             continue;
1405         effect->SetTopLevelContent(content_name);
1406     }
1407 }
1408 
GetCheckSum() const1409 unsigned int CreatePlanet::GetCheckSum() const {
1410     unsigned int retval{0};
1411 
1412     CheckSums::CheckSumCombine(retval, "CreatePlanet");
1413     CheckSums::CheckSumCombine(retval, m_type);
1414     CheckSums::CheckSumCombine(retval, m_size);
1415     CheckSums::CheckSumCombine(retval, m_name);
1416     CheckSums::CheckSumCombine(retval, m_effects_to_apply_after);
1417 
1418     TraceLogger() << "GetCheckSum(CreatePlanet): retval: " << retval;
1419     return retval;
1420 }
1421 
1422 
1423 ///////////////////////////////////////////////////////////
1424 // CreateBuilding                                        //
1425 ///////////////////////////////////////////////////////////
CreateBuilding(std::unique_ptr<ValueRef::ValueRef<std::string>> && building_type_name,std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1426 CreateBuilding::CreateBuilding(std::unique_ptr<ValueRef::ValueRef<std::string>>&& building_type_name,
1427                                std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
1428                                std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1429     m_building_type_name(std::move(building_type_name)),
1430     m_name(std::move(name)),
1431     m_effects_to_apply_after(std::move(effects_to_apply_after))
1432 {}
1433 
Execute(ScriptingContext & context) const1434 void CreateBuilding::Execute(ScriptingContext& context) const {
1435     if (!context.effect_target) {
1436         ErrorLogger() << "CreateBuilding::Execute passed no target object";
1437         return;
1438     }
1439     auto location = std::dynamic_pointer_cast<Planet>(context.effect_target);
1440     if (!location)
1441         if (auto location_building = std::dynamic_pointer_cast<Building>(context.effect_target))
1442             location = context.ContextObjects().get<Planet>(location_building->PlanetID());
1443     if (!location) {
1444         ErrorLogger() << "CreateBuilding::Execute couldn't get a Planet object at which to create the building";
1445         return;
1446     }
1447 
1448     if (!m_building_type_name) {
1449         ErrorLogger() << "CreateBuilding::Execute has no building type specified!";
1450         return;
1451     }
1452 
1453     std::string building_type_name = m_building_type_name->Eval(context);
1454     const BuildingType* building_type = GetBuildingType(building_type_name);
1455     if (!building_type) {
1456         ErrorLogger() << "CreateBuilding::Execute couldn't get building type: " << building_type_name;
1457         return;
1458     }
1459 
1460     auto building = GetUniverse().InsertNew<Building>(ALL_EMPIRES, building_type_name, ALL_EMPIRES);
1461     if (!building) {
1462         ErrorLogger() << "CreateBuilding::Execute couldn't create building!";
1463         return;
1464     }
1465 
1466     location->AddBuilding(building->ID());
1467     building->SetPlanetID(location->ID());
1468 
1469     building->SetOwner(location->Owner());
1470 
1471     auto system = context.ContextObjects().get<System>(location->SystemID());
1472     if (system)
1473         system->Insert(building);
1474 
1475     if (m_name) {
1476         std::string name_str = m_name->Eval(context);
1477         if (m_name->ConstantExpr() && UserStringExists(name_str))
1478             name_str = UserString(name_str);
1479         building->Rename(name_str);
1480     }
1481 
1482     // apply after-creation effects
1483     ScriptingContext local_context = context;
1484     local_context.effect_target = building;
1485     for (auto& effect : m_effects_to_apply_after) {
1486         if (!effect)
1487             continue;
1488         effect->Execute(local_context);
1489     }
1490 }
1491 
Dump(unsigned short ntabs) const1492 std::string CreateBuilding::Dump(unsigned short ntabs) const {
1493     std::string retval = DumpIndent(ntabs) + "CreateBuilding";
1494     if (m_building_type_name)
1495         retval += " type = " + m_building_type_name->Dump(ntabs);
1496     if (m_name)
1497         retval += " name = " + m_name->Dump(ntabs);
1498     return retval + "\n";
1499 }
1500 
SetTopLevelContent(const std::string & content_name)1501 void CreateBuilding::SetTopLevelContent(const std::string& content_name) {
1502     if (m_building_type_name)
1503         m_building_type_name->SetTopLevelContent(content_name);
1504     if (m_name)
1505         m_name->SetTopLevelContent(content_name);
1506     for (auto& effect : m_effects_to_apply_after) {
1507         if (!effect)
1508             continue;
1509         effect->SetTopLevelContent(content_name);
1510     }
1511 }
1512 
GetCheckSum() const1513 unsigned int CreateBuilding::GetCheckSum() const {
1514     unsigned int retval{0};
1515 
1516     CheckSums::CheckSumCombine(retval, "CreateBuilding");
1517     CheckSums::CheckSumCombine(retval, m_building_type_name);
1518     CheckSums::CheckSumCombine(retval, m_name);
1519     CheckSums::CheckSumCombine(retval, m_effects_to_apply_after);
1520 
1521     TraceLogger() << "GetCheckSum(CreateBuilding): retval: " << retval;
1522     return retval;
1523 }
1524 
1525 
1526 ///////////////////////////////////////////////////////////
1527 // CreateShip                                            //
1528 ///////////////////////////////////////////////////////////
CreateShip(std::unique_ptr<ValueRef::ValueRef<std::string>> && predefined_ship_design_name,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<std::string>> && species_name,std::unique_ptr<ValueRef::ValueRef<std::string>> && ship_name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1529 CreateShip::CreateShip(std::unique_ptr<ValueRef::ValueRef<std::string>>&& predefined_ship_design_name,
1530                        std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
1531                        std::unique_ptr<ValueRef::ValueRef<std::string>>&& species_name,
1532                        std::unique_ptr<ValueRef::ValueRef<std::string>>&& ship_name,
1533                        std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1534     m_design_name(std::move(predefined_ship_design_name)),
1535     m_empire_id(std::move(empire_id)),
1536     m_species_name(std::move(species_name)),
1537     m_name(std::move(ship_name)),
1538     m_effects_to_apply_after(std::move(effects_to_apply_after))
1539 {}
1540 
CreateShip(std::unique_ptr<ValueRef::ValueRef<int>> && ship_design_id,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<ValueRef::ValueRef<std::string>> && species_name,std::unique_ptr<ValueRef::ValueRef<std::string>> && ship_name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1541 CreateShip::CreateShip(std::unique_ptr<ValueRef::ValueRef<int>>&& ship_design_id,
1542                        std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
1543                        std::unique_ptr<ValueRef::ValueRef<std::string>>&& species_name,
1544                        std::unique_ptr<ValueRef::ValueRef<std::string>>&& ship_name,
1545                        std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1546     m_design_id(std::move(ship_design_id)),
1547     m_empire_id(std::move(empire_id)),
1548     m_species_name(std::move(species_name)),
1549     m_name(std::move(ship_name)),
1550     m_effects_to_apply_after(std::move(effects_to_apply_after))
1551 {}
1552 
Execute(ScriptingContext & context) const1553 void CreateShip::Execute(ScriptingContext& context) const {
1554     if (!context.effect_target) {
1555         ErrorLogger() << "CreateShip::Execute passed null target";
1556         return;
1557     }
1558 
1559     auto system = context.ContextObjects().get<System>(context.effect_target->SystemID());
1560     if (!system) {
1561         ErrorLogger() << "CreateShip::Execute passed a target not in a system";
1562         return;
1563     }
1564 
1565     int design_id = INVALID_DESIGN_ID;
1566     if (m_design_id) {
1567         design_id = m_design_id->Eval(context);
1568         if (!GetShipDesign(design_id)) {
1569             ErrorLogger() << "CreateShip::Execute couldn't get ship design with id: " << design_id;
1570             return;
1571         }
1572     } else if (m_design_name) {
1573         std::string design_name = m_design_name->Eval(context);
1574         const ShipDesign* ship_design = GetPredefinedShipDesign(design_name);
1575         if (!ship_design) {
1576             ErrorLogger() << "CreateShip::Execute couldn't get predefined ship design with name " << m_design_name->Dump();
1577             return;
1578         }
1579         design_id = ship_design->ID();
1580     }
1581     if (design_id == INVALID_DESIGN_ID) {
1582         ErrorLogger() << "CreateShip::Execute got invalid ship design id: -1";
1583         return;
1584     }
1585 
1586     int empire_id = ALL_EMPIRES;
1587     Empire* empire = nullptr;  // not const Empire* so that empire::NewShipName can be called
1588     if (m_empire_id) {
1589         empire_id = m_empire_id->Eval(context);
1590         if (empire_id != ALL_EMPIRES) {
1591             empire = GetEmpire(empire_id);
1592             if (!empire) {
1593                 ErrorLogger() << "CreateShip::Execute couldn't get empire with id " << empire_id;
1594                 return;
1595             }
1596         }
1597     }
1598 
1599     std::string species_name;
1600     if (m_species_name) {
1601         species_name = m_species_name->Eval(context);
1602         if (!species_name.empty() && !GetSpecies(species_name)) {
1603             ErrorLogger() << "CreateShip::Execute couldn't get species with which to create a ship";
1604             return;
1605         }
1606     }
1607 
1608     //// possible future modification: try to put new ship into existing fleet if
1609     //// ownership with target object's fleet works out (if target is a ship)
1610     //// attempt to find a
1611     //auto fleet = std::dynamic_pointer_cast<Fleet>(target);
1612     //if (!fleet)
1613     //    if (auto ship = std::dynamic_pointer_cast<const Ship>(target))
1614     //        fleet = ship->FleetID();
1615     //// etc.
1616 
1617     auto ship = GetUniverse().InsertNew<Ship>(empire_id, design_id, species_name, ALL_EMPIRES);
1618     system->Insert(ship);
1619 
1620     if (m_name) {
1621         std::string name_str = m_name->Eval(context);
1622         if (m_name->ConstantExpr() && UserStringExists(name_str))
1623             name_str = UserString(name_str);
1624         ship->Rename(name_str);
1625     } else if (ship->IsMonster()) {
1626         ship->Rename(NewMonsterName());
1627     } else if (empire) {
1628         ship->Rename(empire->NewShipName());
1629     } else {
1630         ship->Rename(ship->Design()->Name());
1631     }
1632 
1633     ship->ResetTargetMaxUnpairedMeters();
1634     ship->ResetPairedActiveMeters();
1635     ship->SetShipMetersToMax();
1636 
1637     ship->BackPropagateMeters();
1638 
1639     GetUniverse().SetEmpireKnowledgeOfShipDesign(design_id, empire_id);
1640 
1641     CreateNewFleet(system, ship, context.ContextObjects());
1642 
1643     // apply after-creation effects
1644     ScriptingContext local_context = context;
1645     local_context.effect_target = ship;
1646     for (auto& effect : m_effects_to_apply_after) {
1647         if (!effect)
1648             continue;
1649         effect->Execute(local_context);
1650     }
1651 }
1652 
Dump(unsigned short ntabs) const1653 std::string CreateShip::Dump(unsigned short ntabs) const {
1654     std::string retval = DumpIndent(ntabs) + "CreateShip";
1655     if (m_design_id)
1656         retval += " designid = " + m_design_id->Dump(ntabs);
1657     if (m_design_name)
1658         retval += " designname = " + m_design_name->Dump(ntabs);
1659     if (m_empire_id)
1660         retval += " empire = " + m_empire_id->Dump(ntabs);
1661     if (m_species_name)
1662         retval += " species = " + m_species_name->Dump(ntabs);
1663     if (m_name)
1664         retval += " name = " + m_name->Dump(ntabs);
1665 
1666     retval += "\n";
1667     return retval;
1668 }
1669 
SetTopLevelContent(const std::string & content_name)1670 void CreateShip::SetTopLevelContent(const std::string& content_name) {
1671     if (m_design_name)
1672         m_design_name->SetTopLevelContent(content_name);
1673     if (m_design_id)
1674         m_design_id->SetTopLevelContent(content_name);
1675     if (m_empire_id)
1676         m_empire_id->SetTopLevelContent(content_name);
1677     if (m_species_name)
1678         m_species_name->SetTopLevelContent(content_name);
1679     if (m_name)
1680         m_name->SetTopLevelContent(content_name);
1681     for (auto& effect : m_effects_to_apply_after) {
1682         if (!effect)
1683             continue;
1684         effect->SetTopLevelContent(content_name);
1685     }
1686 }
1687 
GetCheckSum() const1688 unsigned int CreateShip::GetCheckSum() const {
1689     unsigned int retval{0};
1690 
1691     CheckSums::CheckSumCombine(retval, "CreateShip");
1692     CheckSums::CheckSumCombine(retval, m_design_name);
1693     CheckSums::CheckSumCombine(retval, m_design_id);
1694     CheckSums::CheckSumCombine(retval, m_empire_id);
1695     CheckSums::CheckSumCombine(retval, m_species_name);
1696     CheckSums::CheckSumCombine(retval, m_name);
1697     CheckSums::CheckSumCombine(retval, m_effects_to_apply_after);
1698 
1699     TraceLogger() << "GetCheckSum(CreateShip): retval: " << retval;
1700     return retval;
1701 }
1702 
1703 
1704 ///////////////////////////////////////////////////////////
1705 // CreateField                                           //
1706 ///////////////////////////////////////////////////////////
CreateField(std::unique_ptr<ValueRef::ValueRef<std::string>> && field_type_name,std::unique_ptr<ValueRef::ValueRef<double>> && size,std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1707 CreateField::CreateField(std::unique_ptr<ValueRef::ValueRef<std::string>>&& field_type_name,
1708                          std::unique_ptr<ValueRef::ValueRef<double>>&& size,
1709                          std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
1710                          std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1711     m_field_type_name(std::move(field_type_name)),
1712     m_size(std::move(size)),
1713     m_name(std::move(name)),
1714     m_effects_to_apply_after(std::move(effects_to_apply_after))
1715 {}
1716 
CreateField(std::unique_ptr<ValueRef::ValueRef<std::string>> && field_type_name,std::unique_ptr<ValueRef::ValueRef<double>> && x,std::unique_ptr<ValueRef::ValueRef<double>> && y,std::unique_ptr<ValueRef::ValueRef<double>> && size,std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1717 CreateField::CreateField(std::unique_ptr<ValueRef::ValueRef<std::string>>&& field_type_name,
1718                          std::unique_ptr<ValueRef::ValueRef<double>>&& x,
1719                          std::unique_ptr<ValueRef::ValueRef<double>>&& y,
1720                          std::unique_ptr<ValueRef::ValueRef<double>>&& size,
1721                          std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
1722                          std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1723     m_field_type_name(std::move(field_type_name)),
1724     m_x(std::move(x)),
1725     m_y(std::move(y)),
1726     m_size(std::move(size)),
1727     m_name(std::move(name)),
1728     m_effects_to_apply_after(std::move(effects_to_apply_after))
1729 {}
1730 
Execute(ScriptingContext & context) const1731 void CreateField::Execute(ScriptingContext& context) const {
1732     if (!context.effect_target) {
1733         ErrorLogger() << "CreateField::Execute passed null target";
1734         return;
1735     }
1736     auto target = context.effect_target;
1737 
1738     if (!m_field_type_name)
1739         return;
1740 
1741     const FieldType* field_type = GetFieldType(m_field_type_name->Eval(context));
1742     if (!field_type) {
1743         ErrorLogger() << "CreateField::Execute couldn't get field type with name: " << m_field_type_name->Dump();
1744         return;
1745     }
1746 
1747     double size = 10.0;
1748     if (m_size)
1749         size = m_size->Eval(context);
1750     if (size < 1.0) {
1751         ErrorLogger() << "CreateField::Execute given very small / negative size: " << size << "  ... so resetting to 1.0";
1752         size = 1.0;
1753     }
1754     if (size > 10000) {
1755         ErrorLogger() << "CreateField::Execute given very large size: " << size << "  ... so resetting to 10000";
1756         size = 10000;
1757     }
1758 
1759     double x = 0.0;
1760     double y = 0.0;
1761     if (m_x)
1762         x = m_x->Eval(context);
1763     else
1764         x = target->X();
1765     if (m_y)
1766         y = m_y->Eval(context);
1767     else
1768         y = target->Y();
1769 
1770     auto field = GetUniverse().InsertNew<Field>(field_type->Name(), x, y, size);
1771     if (!field) {
1772         ErrorLogger() << "CreateField::Execute couldn't create field!";
1773         return;
1774     }
1775 
1776     // if target is a system, and location matches system location, can put
1777     // field into system
1778     auto system = std::dynamic_pointer_cast<System>(target);
1779     if (system && (!m_y || y == system->Y()) && (!m_x || x == system->X()))
1780         system->Insert(field);
1781 
1782     std::string name_str;
1783     if (m_name) {
1784         name_str = m_name->Eval(context);
1785         if (m_name->ConstantExpr() && UserStringExists(name_str))
1786             name_str = UserString(name_str);
1787     } else {
1788         name_str = UserString(field_type->Name());
1789     }
1790     field->Rename(name_str);
1791 
1792     // apply after-creation effects
1793     ScriptingContext local_context = context;
1794     local_context.effect_target = field;
1795     for (auto& effect : m_effects_to_apply_after) {
1796         if (!effect)
1797             continue;
1798         effect->Execute(local_context);
1799     }
1800 }
1801 
Dump(unsigned short ntabs) const1802 std::string CreateField::Dump(unsigned short ntabs) const {
1803     std::string retval = DumpIndent(ntabs) + "CreateField";
1804     if (m_field_type_name)
1805         retval += " type = " + m_field_type_name->Dump(ntabs);
1806     if (m_x)
1807         retval += " x = " + m_x->Dump(ntabs);
1808     if (m_y)
1809         retval += " y = " + m_y->Dump(ntabs);
1810     if (m_size)
1811         retval += " size = " + m_size->Dump(ntabs);
1812     if (m_name)
1813         retval += " name = " + m_name->Dump(ntabs);
1814     retval += "\n";
1815     return retval;
1816 }
1817 
SetTopLevelContent(const std::string & content_name)1818 void CreateField::SetTopLevelContent(const std::string& content_name) {
1819     if (m_field_type_name)
1820         m_field_type_name->SetTopLevelContent(content_name);
1821     if (m_x)
1822         m_x->SetTopLevelContent(content_name);
1823     if (m_y)
1824         m_y->SetTopLevelContent(content_name);
1825     if (m_size)
1826         m_size->SetTopLevelContent(content_name);
1827     if (m_name)
1828         m_name->SetTopLevelContent(content_name);
1829     for (auto& effect : m_effects_to_apply_after) {
1830         if (!effect)
1831             continue;
1832         effect->SetTopLevelContent(content_name);
1833     }
1834 }
1835 
GetCheckSum() const1836 unsigned int CreateField::GetCheckSum() const {
1837     unsigned int retval{0};
1838 
1839     CheckSums::CheckSumCombine(retval, "CreateField");
1840     CheckSums::CheckSumCombine(retval, m_field_type_name);
1841     CheckSums::CheckSumCombine(retval, m_x);
1842     CheckSums::CheckSumCombine(retval, m_y);
1843     CheckSums::CheckSumCombine(retval, m_size);
1844     CheckSums::CheckSumCombine(retval, m_name);
1845     CheckSums::CheckSumCombine(retval, m_effects_to_apply_after);
1846 
1847     TraceLogger() << "GetCheckSum(CreateField): retval: " << retval;
1848     return retval;
1849 }
1850 
1851 
1852 ///////////////////////////////////////////////////////////
1853 // CreateSystem                                          //
1854 ///////////////////////////////////////////////////////////
CreateSystem(std::unique_ptr<ValueRef::ValueRef<::StarType>> && type,std::unique_ptr<ValueRef::ValueRef<double>> && x,std::unique_ptr<ValueRef::ValueRef<double>> && y,std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1855 CreateSystem::CreateSystem(std::unique_ptr<ValueRef::ValueRef< ::StarType>>&& type,
1856                            std::unique_ptr<ValueRef::ValueRef<double>>&& x,
1857                            std::unique_ptr<ValueRef::ValueRef<double>>&& y,
1858                            std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
1859                            std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1860     m_type(std::move(type)),
1861     m_x(std::move(x)),
1862     m_y(std::move(y)),
1863     m_name(std::move(name)),
1864     m_effects_to_apply_after(std::move(effects_to_apply_after))
1865 {
1866     DebugLogger() << "Effect System created 1";
1867 }
1868 
CreateSystem(std::unique_ptr<ValueRef::ValueRef<double>> && x,std::unique_ptr<ValueRef::ValueRef<double>> && y,std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::vector<std::unique_ptr<Effect>> && effects_to_apply_after)1869 CreateSystem::CreateSystem(std::unique_ptr<ValueRef::ValueRef<double>>&& x,
1870                            std::unique_ptr<ValueRef::ValueRef<double>>&& y,
1871                            std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
1872                            std::vector<std::unique_ptr<Effect>>&& effects_to_apply_after) :
1873     m_x(std::move(x)),
1874     m_y(std::move(y)),
1875     m_name(std::move(name)),
1876     m_effects_to_apply_after(std::move(effects_to_apply_after))
1877 {
1878     DebugLogger() << "Effect System created 2";
1879 }
1880 
Execute(ScriptingContext & context) const1881 void CreateSystem::Execute(ScriptingContext& context) const {
1882     // pick a star type
1883     StarType star_type = STAR_NONE;
1884     if (m_type) {
1885         star_type = m_type->Eval(context);
1886     } else {
1887         int max_type_idx = int(NUM_STAR_TYPES) - 1;
1888         int type_idx = RandSmallInt(0, max_type_idx);
1889         star_type = StarType(type_idx);
1890     }
1891 
1892     // pick location
1893     double x = 0.0;
1894     double y = 0.0;
1895     if (m_x)
1896         x = m_x->Eval(context);
1897     if (m_y)
1898         y = m_y->Eval(context);
1899 
1900     std::string name_str;
1901     if (m_name) {
1902         name_str = m_name->Eval(context);
1903         if (m_name->ConstantExpr() && UserStringExists(name_str))
1904             name_str = UserString(name_str);
1905     } else {
1906         name_str = GenerateSystemName(context.ContextObjects());
1907     }
1908 
1909     auto system = GetUniverse().InsertNew<System>(star_type, name_str, x, y);
1910     if (!system) {
1911         ErrorLogger() << "CreateSystem::Execute couldn't create system!";
1912         return;
1913     }
1914 
1915     // apply after-creation effects
1916     ScriptingContext local_context = context;
1917     local_context.effect_target = system;
1918     for (auto& effect : m_effects_to_apply_after) {
1919         if (!effect)
1920             continue;
1921         effect->Execute(local_context);
1922     }
1923 }
1924 
Dump(unsigned short ntabs) const1925 std::string CreateSystem::Dump(unsigned short ntabs) const {
1926     std::string retval = DumpIndent(ntabs) + "CreateSystem";
1927     if (m_type)
1928         retval += " type = " + m_type->Dump(ntabs);
1929     if (m_x)
1930         retval += " x = " + m_x->Dump(ntabs);
1931     if (m_y)
1932         retval += " y = " + m_y->Dump(ntabs);
1933     if (m_name)
1934         retval += " name = " + m_name->Dump(ntabs);
1935     retval += "\n";
1936     return retval;
1937 }
1938 
SetTopLevelContent(const std::string & content_name)1939 void CreateSystem::SetTopLevelContent(const std::string& content_name) {
1940     if (m_x)
1941         m_x->SetTopLevelContent(content_name);
1942     if (m_y)
1943         m_y->SetTopLevelContent(content_name);
1944     if (m_type)
1945         m_type->SetTopLevelContent(content_name);
1946     if (m_name)
1947         m_name->SetTopLevelContent(content_name);
1948     for (auto& effect : m_effects_to_apply_after) {
1949         if (!effect)
1950             continue;
1951         effect->SetTopLevelContent(content_name);
1952     }
1953 }
1954 
GetCheckSum() const1955 unsigned int CreateSystem::GetCheckSum() const {
1956     unsigned int retval{0};
1957 
1958     CheckSums::CheckSumCombine(retval, "CreateSystem");
1959     CheckSums::CheckSumCombine(retval, m_type);
1960     CheckSums::CheckSumCombine(retval, m_x);
1961     CheckSums::CheckSumCombine(retval, m_y);
1962     CheckSums::CheckSumCombine(retval, m_name);
1963     CheckSums::CheckSumCombine(retval, m_effects_to_apply_after);
1964 
1965     TraceLogger() << "GetCheckSum(CreateSystem): retval: " << retval;
1966     return retval;
1967 }
1968 
1969 
1970 ///////////////////////////////////////////////////////////
1971 // Destroy                                               //
1972 ///////////////////////////////////////////////////////////
Destroy()1973 Destroy::Destroy()
1974 {}
1975 
Execute(ScriptingContext & context) const1976 void Destroy::Execute(ScriptingContext& context) const {
1977     if (!context.effect_target) {
1978         ErrorLogger() << "Destroy::Execute passed no target object";
1979         return;
1980     }
1981 
1982     int source_id = INVALID_OBJECT_ID;
1983     if (context.source)
1984         source_id = context.source->ID();
1985 
1986     GetUniverse().EffectDestroy(context.effect_target->ID(), source_id);
1987 }
1988 
Dump(unsigned short ntabs) const1989 std::string Destroy::Dump(unsigned short ntabs) const
1990 { return DumpIndent(ntabs) + "Destroy\n"; }
1991 
GetCheckSum() const1992 unsigned int Destroy::GetCheckSum() const {
1993     unsigned int retval{0};
1994 
1995     CheckSums::CheckSumCombine(retval, "Destroy");
1996 
1997     TraceLogger() << "GetCheckSum(Destroy): retval: " << retval;
1998     return retval;
1999 }
2000 
2001 
2002 ///////////////////////////////////////////////////////////
2003 // AddSpecial                                            //
2004 ///////////////////////////////////////////////////////////
AddSpecial(const std::string & name,float capacity)2005 AddSpecial::AddSpecial(const std::string& name, float capacity) :
2006     m_name(std::make_unique<ValueRef::Constant<std::string>>(name)),
2007     m_capacity(std::make_unique<ValueRef::Constant<double>>(capacity))
2008 {}
2009 
AddSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>> && name,std::unique_ptr<ValueRef::ValueRef<double>> && capacity)2010 AddSpecial::AddSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name,
2011                        std::unique_ptr<ValueRef::ValueRef<double>>&& capacity) :
2012     m_name(std::move(name)),
2013     m_capacity(std::move(capacity))
2014 {}
2015 
Execute(ScriptingContext & context) const2016 void AddSpecial::Execute(ScriptingContext& context) const {
2017     if (!context.effect_target) {
2018         ErrorLogger() << "AddSpecial::Execute passed no target object";
2019         return;
2020     }
2021 
2022     std::string name = (m_name ? m_name->Eval(context) : "");
2023 
2024     float initial_capacity = context.effect_target->SpecialCapacity(name);  // returns 0.0f if no such special yet present
2025     float capacity = (m_capacity ? m_capacity->Eval(ScriptingContext(context, initial_capacity)) : initial_capacity);
2026 
2027     context.effect_target->SetSpecialCapacity(name, capacity);
2028 }
2029 
Dump(unsigned short ntabs) const2030 std::string AddSpecial::Dump(unsigned short ntabs) const {
2031     return DumpIndent(ntabs) + "AddSpecial name = " +  (m_name ? m_name->Dump(ntabs) : "") +
2032         " capacity = " + (m_capacity ? m_capacity->Dump(ntabs) : "0.0") +  "\n";
2033 }
2034 
SetTopLevelContent(const std::string & content_name)2035 void AddSpecial::SetTopLevelContent(const std::string& content_name) {
2036     if (m_name)
2037         m_name->SetTopLevelContent(content_name);
2038     if (m_capacity)
2039         m_capacity->SetTopLevelContent(content_name);
2040 }
2041 
GetCheckSum() const2042 unsigned int AddSpecial::GetCheckSum() const {
2043     unsigned int retval{0};
2044 
2045     CheckSums::CheckSumCombine(retval, "AddSpecial");
2046     CheckSums::CheckSumCombine(retval, m_name);
2047     CheckSums::CheckSumCombine(retval, m_capacity);
2048 
2049     TraceLogger() << "GetCheckSum(AddSpecial): retval: " << retval;
2050     return retval;
2051 }
2052 
2053 
2054 ///////////////////////////////////////////////////////////
2055 // RemoveSpecial                                         //
2056 ///////////////////////////////////////////////////////////
RemoveSpecial(const std::string & name)2057 RemoveSpecial::RemoveSpecial(const std::string& name) :
2058     m_name(std::make_unique<ValueRef::Constant<std::string>>(name))
2059 {}
2060 
RemoveSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>> && name)2061 RemoveSpecial::RemoveSpecial(std::unique_ptr<ValueRef::ValueRef<std::string>>&& name) :
2062     m_name(std::move(name))
2063 {}
2064 
Execute(ScriptingContext & context) const2065 void RemoveSpecial::Execute(ScriptingContext& context) const {
2066     if (!context.effect_target) {
2067         ErrorLogger() << "RemoveSpecial::Execute passed no target object";
2068         return;
2069     }
2070 
2071     std::string name = (m_name ? m_name->Eval(context) : "");
2072     context.effect_target->RemoveSpecial(name);
2073 }
2074 
Dump(unsigned short ntabs) const2075 std::string RemoveSpecial::Dump(unsigned short ntabs) const {
2076     return DumpIndent(ntabs) + "RemoveSpecial name = " +  (m_name ? m_name->Dump(ntabs) : "") + "\n";
2077 }
2078 
SetTopLevelContent(const std::string & content_name)2079 void RemoveSpecial::SetTopLevelContent(const std::string& content_name) {
2080     if (m_name)
2081         m_name->SetTopLevelContent(content_name);
2082 }
2083 
GetCheckSum() const2084 unsigned int RemoveSpecial::GetCheckSum() const {
2085     unsigned int retval{0};
2086 
2087     CheckSums::CheckSumCombine(retval, "RemoveSpecial");
2088     CheckSums::CheckSumCombine(retval, m_name);
2089 
2090     TraceLogger() << "GetCheckSum(RemoveSpecial): retval: " << retval;
2091     return retval;
2092 }
2093 
2094 
2095 ///////////////////////////////////////////////////////////
2096 // AddStarlanes                                          //
2097 ///////////////////////////////////////////////////////////
AddStarlanes(std::unique_ptr<Condition::Condition> && other_lane_endpoint_condition)2098 AddStarlanes::AddStarlanes(std::unique_ptr<Condition::Condition>&& other_lane_endpoint_condition) :
2099     m_other_lane_endpoint_condition(std::move(other_lane_endpoint_condition))
2100 {}
2101 
Execute(ScriptingContext & context) const2102 void AddStarlanes::Execute(ScriptingContext& context) const {
2103     // get target system
2104     if (!context.effect_target) {
2105         ErrorLogger() << "AddStarlanes::Execute passed no target object";
2106         return;
2107     }
2108     auto target_system = std::dynamic_pointer_cast<System>(context.effect_target);
2109     if (!target_system)
2110         target_system = context.ContextObjects().get<System>(context.effect_target->SystemID());
2111     if (!target_system)
2112         return; // nothing to do!
2113 
2114     // get other endpoint systems...
2115     Condition::ObjectSet endpoint_objects;
2116     // apply endpoints condition to determine objects whose systems should be
2117     // connected to the source system
2118     m_other_lane_endpoint_condition->Eval(context, endpoint_objects);
2119 
2120     // early exit if there are no valid locations
2121     if (endpoint_objects.empty())
2122         return; // nothing to do!
2123 
2124     // get systems containing at least one endpoint object
2125     std::set<std::shared_ptr<System>> endpoint_systems;
2126     for (auto& endpoint_object : endpoint_objects) {
2127         auto endpoint_system = std::dynamic_pointer_cast<const System>(endpoint_object);
2128         if (!endpoint_system)
2129             endpoint_system = context.ContextObjects().get<System>(endpoint_object->SystemID());
2130         if (!endpoint_system)
2131             continue;
2132         endpoint_systems.insert(std::const_pointer_cast<System>(endpoint_system));
2133     }
2134 
2135     // add starlanes from target to endpoint systems
2136     for (auto& endpoint_system : endpoint_systems) {
2137         target_system->AddStarlane(endpoint_system->ID());
2138         endpoint_system->AddStarlane(target_system->ID());
2139     }
2140 }
2141 
Dump(unsigned short ntabs) const2142 std::string AddStarlanes::Dump(unsigned short ntabs) const
2143 { return DumpIndent(ntabs) + "AddStarlanes endpoints = " + m_other_lane_endpoint_condition->Dump(ntabs) + "\n"; }
2144 
SetTopLevelContent(const std::string & content_name)2145 void AddStarlanes::SetTopLevelContent(const std::string& content_name) {
2146     if (m_other_lane_endpoint_condition)
2147         m_other_lane_endpoint_condition->SetTopLevelContent(content_name);
2148 }
2149 
GetCheckSum() const2150 unsigned int AddStarlanes::GetCheckSum() const {
2151     unsigned int retval{0};
2152 
2153     CheckSums::CheckSumCombine(retval, "AddStarlanes");
2154     CheckSums::CheckSumCombine(retval, m_other_lane_endpoint_condition);
2155 
2156     TraceLogger() << "GetCheckSum(AddStarlanes): retval: " << retval;
2157     return retval;
2158 }
2159 
2160 
2161 ///////////////////////////////////////////////////////////
2162 // RemoveStarlanes                                       //
2163 ///////////////////////////////////////////////////////////
RemoveStarlanes(std::unique_ptr<Condition::Condition> && other_lane_endpoint_condition)2164 RemoveStarlanes::RemoveStarlanes(std::unique_ptr<Condition::Condition>&& other_lane_endpoint_condition) :
2165     m_other_lane_endpoint_condition(std::move(other_lane_endpoint_condition))
2166 {}
2167 
Execute(ScriptingContext & context) const2168 void RemoveStarlanes::Execute(ScriptingContext& context) const {
2169     // get target system
2170     if (!context.effect_target) {
2171         ErrorLogger() << "AddStarlanes::Execute passed no target object";
2172         return;
2173     }
2174     auto target_system = std::dynamic_pointer_cast<System>(context.effect_target);
2175     if (!target_system)
2176         target_system = context.ContextObjects().get<System>(context.effect_target->SystemID());
2177     if (!target_system)
2178         return; // nothing to do!
2179 
2180     // get other endpoint systems...
2181 
2182     Condition::ObjectSet endpoint_objects;
2183     // apply endpoints condition to determine objects whose systems should be
2184     // connected to the source system
2185     m_other_lane_endpoint_condition->Eval(context, endpoint_objects);
2186 
2187     // early exit if there are no valid locations - can't move anything if there's nowhere to move to
2188     if (endpoint_objects.empty())
2189         return; // nothing to do!
2190 
2191     // get systems containing at least one endpoint object
2192     std::set<std::shared_ptr<System>> endpoint_systems;
2193     for (auto& endpoint_object : endpoint_objects) {
2194         auto endpoint_system = std::dynamic_pointer_cast<const System>(endpoint_object);
2195         if (!endpoint_system)
2196             endpoint_system = context.ContextObjects().get<System>(endpoint_object->SystemID());
2197         if (!endpoint_system)
2198             continue;
2199         endpoint_systems.insert(std::const_pointer_cast<System>(endpoint_system));
2200     }
2201 
2202     // remove starlanes from target to endpoint systems
2203     int target_system_id = target_system->ID();
2204     for (auto& endpoint_system : endpoint_systems) {
2205         target_system->RemoveStarlane(endpoint_system->ID());
2206         endpoint_system->RemoveStarlane(target_system_id);
2207     }
2208 }
2209 
Dump(unsigned short ntabs) const2210 std::string RemoveStarlanes::Dump(unsigned short ntabs) const
2211 { return DumpIndent(ntabs) + "RemoveStarlanes endpoints = " + m_other_lane_endpoint_condition->Dump(ntabs) + "\n"; }
2212 
SetTopLevelContent(const std::string & content_name)2213 void RemoveStarlanes::SetTopLevelContent(const std::string& content_name) {
2214     if (m_other_lane_endpoint_condition)
2215         m_other_lane_endpoint_condition->SetTopLevelContent(content_name);
2216 }
2217 
GetCheckSum() const2218 unsigned int RemoveStarlanes::GetCheckSum() const {
2219     unsigned int retval{0};
2220 
2221     CheckSums::CheckSumCombine(retval, "RemoveStarlanes");
2222     CheckSums::CheckSumCombine(retval, m_other_lane_endpoint_condition);
2223 
2224     TraceLogger() << "GetCheckSum(RemoveStarlanes): retval: " << retval;
2225     return retval;
2226 }
2227 
2228 
2229 ///////////////////////////////////////////////////////////
2230 // SetStarType                                           //
2231 ///////////////////////////////////////////////////////////
SetStarType(std::unique_ptr<ValueRef::ValueRef<StarType>> && type)2232 SetStarType::SetStarType(std::unique_ptr<ValueRef::ValueRef<StarType>>&& type) :
2233     m_type(std::move(type))
2234 {}
2235 
Execute(ScriptingContext & context) const2236 void SetStarType::Execute(ScriptingContext& context) const {
2237     if (!context.effect_target) {
2238         ErrorLogger() << "SetStarType::Execute given no target object";
2239         return;
2240     }
2241     if (auto s = std::dynamic_pointer_cast<System>(context.effect_target))
2242         s->SetStarType(m_type->Eval(ScriptingContext(context, s->GetStarType())));
2243     else
2244         ErrorLogger() << "SetStarType::Execute given a non-system target";
2245 }
2246 
Dump(unsigned short ntabs) const2247 std::string SetStarType::Dump(unsigned short ntabs) const
2248 { return DumpIndent(ntabs) + "SetStarType type = " + m_type->Dump(ntabs) + "\n"; }
2249 
SetTopLevelContent(const std::string & content_name)2250 void SetStarType::SetTopLevelContent(const std::string& content_name) {
2251     if (m_type)
2252         m_type->SetTopLevelContent(content_name);
2253 }
2254 
GetCheckSum() const2255 unsigned int SetStarType::GetCheckSum() const {
2256     unsigned int retval{0};
2257 
2258     CheckSums::CheckSumCombine(retval, "SetStarType");
2259     CheckSums::CheckSumCombine(retval, m_type);
2260 
2261     TraceLogger() << "GetCheckSum(SetStarType): retval: " << retval;
2262     return retval;
2263 }
2264 
2265 
2266 ///////////////////////////////////////////////////////////
2267 // MoveTo                                                //
2268 ///////////////////////////////////////////////////////////
MoveTo(std::unique_ptr<Condition::Condition> && location_condition)2269 MoveTo::MoveTo(std::unique_ptr<Condition::Condition>&& location_condition) :
2270     m_location_condition(std::move(location_condition))
2271 {}
2272 
Execute(ScriptingContext & context) const2273 void MoveTo::Execute(ScriptingContext& context) const {
2274     if (!context.effect_target) {
2275         ErrorLogger() << "MoveTo::Execute given no target object";
2276         return;
2277     }
2278 
2279     Universe& universe = GetUniverse();
2280 
2281     Condition::ObjectSet valid_locations;
2282     // apply location condition to determine valid location to move target to
2283     m_location_condition->Eval(context, valid_locations);
2284 
2285     // early exit if there are no valid locations - can't move anything if there's nowhere to move to
2286     if (valid_locations.empty())
2287         return;
2288 
2289     // "randomly" pick a destination
2290     auto destination = std::const_pointer_cast<UniverseObject>(*valid_locations.begin());
2291 
2292     // get previous system from which to remove object if necessary
2293     auto old_sys = context.ContextObjects().get<System>(context.effect_target->SystemID());
2294 
2295     // do the moving...
2296     if (auto fleet = std::dynamic_pointer_cast<Fleet>(context.effect_target)) {
2297         // fleets can be inserted into the system that contains the destination
2298         // object (or the destination object itself if it is a system)
2299         if (auto dest_system = context.ContextObjects().get<System>(destination->SystemID())) {
2300             if (fleet->SystemID() != dest_system->ID()) {
2301                 // remove fleet from old system, put into new system
2302                 if (old_sys)
2303                     old_sys->Remove(fleet->ID());
2304                 dest_system->Insert(fleet);
2305 
2306                 // also move ships of fleet
2307                 for (auto& ship : context.ContextObjects().find<Ship>(fleet->ShipIDs())) {
2308                     if (old_sys)
2309                         old_sys->Remove(ship->ID());
2310                     dest_system->Insert(ship);
2311                 }
2312 
2313                 ExploreSystem(dest_system->ID(), fleet);
2314                 UpdateFleetRoute(fleet, INVALID_OBJECT_ID, INVALID_OBJECT_ID, context.ContextObjects());  // inserted into dest_system, so next and previous systems are invalid objects
2315             }
2316 
2317             // if old and new systems are the same, and destination is that
2318             // system, don't need to do anything
2319 
2320         } else {
2321             // move fleet to new location
2322             if (old_sys)
2323                 old_sys->Remove(fleet->ID());
2324             fleet->SetSystem(INVALID_OBJECT_ID);
2325             fleet->MoveTo(destination);
2326 
2327             // also move ships of fleet
2328             for (auto& ship : context.ContextObjects().find<Ship>(fleet->ShipIDs())) {
2329                 if (old_sys)
2330                     old_sys->Remove(ship->ID());
2331                 ship->SetSystem(INVALID_OBJECT_ID);
2332                 ship->MoveTo(destination);
2333             }
2334 
2335 
2336             // fleet has been moved to a location that is not a system.
2337             // Presumably this will be located on a starlane between two other
2338             // systems, which may or may not have been explored.  Regardless,
2339             // the fleet needs to be given a new next and previous system so it
2340             // can move into a system, or can be ordered to a new location, and
2341             // so that it won't try to move off of starlanes towards some other
2342             // system from its current location (if it was heading to another
2343             // system) and so it won't be stuck in the middle of a starlane,
2344             // unable to move (if it wasn't previously moving)
2345 
2346             // if destination object is a fleet or is part of a fleet, can use
2347             // that fleet's previous and next systems to get valid next and
2348             // previous systems for the target fleet.
2349             auto dest_fleet = std::dynamic_pointer_cast<const Fleet>(destination);
2350             if (!dest_fleet)
2351                 if (auto dest_ship = std::dynamic_pointer_cast<const Ship>(destination))
2352                     dest_fleet = context.ContextObjects().get<Fleet>(dest_ship->FleetID());
2353             if (dest_fleet) {
2354                 UpdateFleetRoute(fleet, dest_fleet->NextSystemID(), dest_fleet->PreviousSystemID(), context.ContextObjects());
2355 
2356             } else {
2357                 // TODO: need to do something else to get updated previous/next
2358                 // systems if the destination is a field.
2359                 ErrorLogger() << "MoveTo::Execute couldn't find a way to set the previous and next systems for the target fleet!";
2360             }
2361         }
2362 
2363     } else if (auto ship = std::dynamic_pointer_cast<Ship>(context.effect_target)) {
2364         // TODO: make sure colonization doesn't interfere with this effect, and vice versa
2365 
2366         // is destination a ship/fleet ?
2367         auto dest_fleet = std::dynamic_pointer_cast<Fleet>(destination);
2368         if (!dest_fleet) {
2369             auto dest_ship = std::dynamic_pointer_cast<Ship>(destination);
2370             if (dest_ship)
2371                 dest_fleet = context.ContextObjects().get<Fleet>(dest_ship->FleetID());
2372         }
2373         if (dest_fleet && dest_fleet->ID() == ship->FleetID())
2374             return; // already in destination fleet. nothing to do.
2375 
2376         bool same_owners = ship->Owner() == destination->Owner();
2377         int dest_sys_id = destination->SystemID();
2378         int ship_sys_id = ship->SystemID();
2379 
2380 
2381         if (ship_sys_id != dest_sys_id) {
2382             // ship is moving to a different system.
2383 
2384             // remove ship from old system
2385             if (old_sys) {
2386                 old_sys->Remove(ship->ID());
2387                 ship->SetSystem(INVALID_OBJECT_ID);
2388             }
2389 
2390             if (auto new_sys = context.ContextObjects().get<System>(dest_sys_id)) {
2391                 // ship is moving to a new system. insert it.
2392                 new_sys->Insert(ship);
2393             } else {
2394                 // ship is moving to a non-system location. move it there.
2395                 ship->MoveTo(std::dynamic_pointer_cast<UniverseObject>(dest_fleet));
2396             }
2397 
2398             // may create a fleet for ship below...
2399         }
2400 
2401         auto old_fleet = context.ContextObjects().get<Fleet>(ship->FleetID());
2402 
2403         if (dest_fleet && same_owners) {
2404             // ship is moving to a different fleet owned by the same empire, so
2405             // can be inserted into it.
2406             old_fleet->RemoveShips({ship->ID()});
2407             dest_fleet->AddShips({ship->ID()});
2408             ship->SetFleetID(dest_fleet->ID());
2409 
2410         } else if (dest_sys_id == ship_sys_id && dest_sys_id != INVALID_OBJECT_ID) {
2411             // ship is moving to the system it is already in, but isn't being or
2412             // can't be moved into a specific fleet, so the ship can be left in
2413             // its current fleet and at its current location
2414 
2415         } else if (destination->X() == ship->X() && destination->Y() == ship->Y()) {
2416             // ship is moving to the same location it's already at, but isn't
2417             // being or can't be moved to a specific fleet, so the ship can be
2418             // left in its current fleet and at its current location
2419 
2420         } else {
2421             // need to create a new fleet for ship
2422             if (auto dest_system = context.ContextObjects().get<System>(dest_sys_id)) {
2423                 CreateNewFleet(dest_system, ship, context.ContextObjects());         // creates new fleet, inserts fleet into system and ship into fleet
2424                 ExploreSystem(dest_system->ID(), ship);
2425 
2426             } else {
2427                 CreateNewFleet(destination->X(), destination->Y(), ship);   // creates new fleet and inserts ship into fleet
2428             }
2429         }
2430 
2431         if (old_fleet && old_fleet->Empty()) {
2432             old_sys->Remove(old_fleet->ID());
2433             universe.EffectDestroy(old_fleet->ID(), INVALID_OBJECT_ID); // no particular object destroyed this fleet
2434         }
2435 
2436     } else if (auto planet = std::dynamic_pointer_cast<Planet>(context.effect_target)) {
2437         // planets need to be located in systems, so get system that contains destination object
2438 
2439         auto dest_system = context.ContextObjects().get<System>(destination->SystemID());
2440         if (!dest_system)
2441             return; // can't move a planet to a non-system
2442 
2443         if (planet->SystemID() == dest_system->ID())
2444             return; // planet already at destination
2445 
2446         if (dest_system->FreeOrbits().empty())
2447             return; // no room for planet at destination
2448 
2449         if (old_sys)
2450             old_sys->Remove(planet->ID());
2451         dest_system->Insert(planet);  // let system pick an orbit
2452 
2453         // also insert buildings of planet into system.
2454         for (auto& building : context.ContextObjects().find<Building>(planet->BuildingIDs())) {
2455             if (old_sys)
2456                 old_sys->Remove(building->ID());
2457             dest_system->Insert(building);
2458         }
2459 
2460         // buildings planet should be unchanged by move, as should planet's
2461         // records of its buildings
2462 
2463         ExploreSystem(dest_system->ID(), planet);
2464 
2465 
2466     } else if (auto building = std::dynamic_pointer_cast<Building>(context.effect_target)) {
2467         // buildings need to be located on planets, so if destination is a
2468         // planet, insert building into it, or attempt to get the planet on
2469         // which the destination object is located and insert target building
2470         // into that
2471         auto dest_planet = std::dynamic_pointer_cast<Planet>(destination);
2472         if (!dest_planet) {
2473             auto dest_building = std::dynamic_pointer_cast<Building>(destination);
2474             if (dest_building) {
2475                 dest_planet = context.ContextObjects().get<Planet>(dest_building->PlanetID());
2476             }
2477         }
2478         if (!dest_planet)
2479             return;
2480 
2481         if (dest_planet->ID() == building->PlanetID())
2482             return; // nothing to do
2483 
2484         auto dest_system = context.ContextObjects().get<System>(destination->SystemID());
2485         if (!dest_system)
2486             return;
2487 
2488         // remove building from old planet / system, add to new planet / system
2489         if (old_sys)
2490             old_sys->Remove(building->ID());
2491         building->SetSystem(INVALID_OBJECT_ID);
2492 
2493         if (auto old_planet = context.ContextObjects().get<Planet>(building->PlanetID()))
2494             old_planet->RemoveBuilding(building->ID());
2495 
2496         dest_planet->AddBuilding(building->ID());
2497         building->SetPlanetID(dest_planet->ID());
2498 
2499         dest_system->Insert(building);
2500         ExploreSystem(dest_system->ID(), building);
2501 
2502 
2503     } else if (auto system = std::dynamic_pointer_cast<System>(context.effect_target)) {
2504         if (destination->SystemID() != INVALID_OBJECT_ID) {
2505             // TODO: merge systems
2506             return;
2507         }
2508 
2509         // move target system to new destination, and insert destination object
2510         // and related objects into system
2511         system->MoveTo(destination);
2512 
2513         if (destination->ObjectType() == OBJ_FIELD)
2514             system->Insert(destination);
2515 
2516         // find fleets / ships at destination location and insert into system
2517         for (auto& obj : context.ContextObjects().all<Fleet>()) {
2518             if (obj->X() == system->X() && obj->Y() == system->Y())
2519                 system->Insert(obj);
2520         }
2521 
2522         for (auto& obj : context.ContextObjects().all<Ship>()) {
2523             if (obj->X() == system->X() && obj->Y() == system->Y())
2524                 system->Insert(obj);
2525         }
2526 
2527 
2528     } else if (auto field = std::dynamic_pointer_cast<Field>(context.effect_target)) {
2529         if (old_sys)
2530             old_sys->Remove(field->ID());
2531         field->SetSystem(INVALID_OBJECT_ID);
2532         field->MoveTo(destination);
2533         if (auto dest_system = std::dynamic_pointer_cast<System>(destination))
2534             dest_system->Insert(field);
2535     }
2536 }
2537 
Dump(unsigned short ntabs) const2538 std::string MoveTo::Dump(unsigned short ntabs) const
2539 { return DumpIndent(ntabs) + "MoveTo destination = " + m_location_condition->Dump(ntabs) + "\n"; }
2540 
SetTopLevelContent(const std::string & content_name)2541 void MoveTo::SetTopLevelContent(const std::string& content_name) {
2542     if (m_location_condition)
2543         m_location_condition->SetTopLevelContent(content_name);
2544 }
2545 
GetCheckSum() const2546 unsigned int MoveTo::GetCheckSum() const {
2547     unsigned int retval{0};
2548 
2549     CheckSums::CheckSumCombine(retval, "MoveTo");
2550     CheckSums::CheckSumCombine(retval, m_location_condition);
2551 
2552     TraceLogger() << "GetCheckSum(MoveTo): retval: " << retval;
2553     return retval;
2554 }
2555 
2556 
2557 ///////////////////////////////////////////////////////////
2558 // MoveInOrbit                                           //
2559 ///////////////////////////////////////////////////////////
MoveInOrbit(std::unique_ptr<ValueRef::ValueRef<double>> && speed,std::unique_ptr<Condition::Condition> && focal_point_condition)2560 MoveInOrbit::MoveInOrbit(std::unique_ptr<ValueRef::ValueRef<double>>&& speed,
2561                          std::unique_ptr<Condition::Condition>&& focal_point_condition) :
2562     m_speed(std::move(speed)),
2563     m_focal_point_condition(std::move(focal_point_condition))
2564 {}
2565 
MoveInOrbit(std::unique_ptr<ValueRef::ValueRef<double>> && speed,std::unique_ptr<ValueRef::ValueRef<double>> && focus_x,std::unique_ptr<ValueRef::ValueRef<double>> && focus_y)2566 MoveInOrbit::MoveInOrbit(std::unique_ptr<ValueRef::ValueRef<double>>&& speed,
2567                          std::unique_ptr<ValueRef::ValueRef<double>>&& focus_x/* = 0*/,
2568                          std::unique_ptr<ValueRef::ValueRef<double>>&& focus_y/* = 0*/) :
2569     m_speed(std::move(speed)),
2570     m_focus_x(std::move(focus_x)),
2571     m_focus_y(std::move(focus_y))
2572 {}
2573 
Execute(ScriptingContext & context) const2574 void MoveInOrbit::Execute(ScriptingContext& context) const {
2575     if (!context.effect_target) {
2576         ErrorLogger() << "MoveInOrbit::Execute given no target object";
2577         return;
2578     }
2579     auto target = context.effect_target;
2580 
2581     double focus_x = 0.0, focus_y = 0.0, speed = 1.0;
2582     if (m_focus_x)
2583         focus_x = m_focus_x->Eval(ScriptingContext(context, target->X()));
2584     if (m_focus_y)
2585         focus_y = m_focus_y->Eval(ScriptingContext(context, target->Y()));
2586     if (m_speed)
2587         speed = m_speed->Eval(context);
2588     if (speed == 0.0)
2589         return;
2590     if (m_focal_point_condition) {
2591         Condition::ObjectSet matches;
2592         m_focal_point_condition->Eval(context, matches);
2593         if (matches.empty())
2594             return;
2595         std::shared_ptr<const UniverseObject> focus_object = *matches.begin();
2596         focus_x = focus_object->X();
2597         focus_y = focus_object->Y();
2598     }
2599 
2600     double focus_to_target_x = target->X() - focus_x;
2601     double focus_to_target_y = target->Y() - focus_y;
2602     double focus_to_target_radius = std::sqrt(focus_to_target_x * focus_to_target_x +
2603                                               focus_to_target_y * focus_to_target_y);
2604     if (focus_to_target_radius < 1.0)
2605         return;    // don't move objects that are too close to focus
2606 
2607     double angle_radians = atan2(focus_to_target_y, focus_to_target_x);
2608     double angle_increment_radians = speed / focus_to_target_radius;
2609     double new_angle_radians = angle_radians + angle_increment_radians;
2610 
2611     double new_x = focus_x + focus_to_target_radius * cos(new_angle_radians);
2612     double new_y = focus_y + focus_to_target_radius * sin(new_angle_radians);
2613 
2614     if (target->X() == new_x && target->Y() == new_y)
2615         return;
2616 
2617     auto old_sys = context.ContextObjects().get<System>(target->SystemID());
2618 
2619     if (auto system = std::dynamic_pointer_cast<System>(target)) {
2620         system->MoveTo(new_x, new_y);
2621         return;
2622 
2623     } else if (auto fleet = std::dynamic_pointer_cast<Fleet>(target)) {
2624         if (old_sys)
2625             old_sys->Remove(fleet->ID());
2626         fleet->SetSystem(INVALID_OBJECT_ID);
2627         fleet->MoveTo(new_x, new_y);
2628         UpdateFleetRoute(fleet, INVALID_OBJECT_ID, INVALID_OBJECT_ID, context.ContextObjects());
2629 
2630         for (auto& ship : context.ContextObjects().find<Ship>(fleet->ShipIDs())) {
2631             if (old_sys)
2632                 old_sys->Remove(ship->ID());
2633             ship->SetSystem(INVALID_OBJECT_ID);
2634             ship->MoveTo(new_x, new_y);
2635         }
2636         return;
2637 
2638     } else if (auto ship = std::dynamic_pointer_cast<Ship>(target)) {
2639         if (old_sys)
2640             old_sys->Remove(ship->ID());
2641         ship->SetSystem(INVALID_OBJECT_ID);
2642 
2643         auto old_fleet = context.ContextObjects().get<Fleet>(ship->FleetID());
2644         if (old_fleet) {
2645             old_fleet->RemoveShips({ship->ID()});
2646             if (old_fleet->Empty()) {
2647                 old_sys->Remove(old_fleet->ID());
2648                 GetUniverse().EffectDestroy(old_fleet->ID(), INVALID_OBJECT_ID);    // no object in particular destroyed this fleet
2649             }
2650         }
2651 
2652         ship->SetFleetID(INVALID_OBJECT_ID);
2653         ship->MoveTo(new_x, new_y);
2654 
2655         CreateNewFleet(new_x, new_y, ship); // creates new fleet and inserts ship into fleet
2656         return;
2657 
2658     } else if (auto field = std::dynamic_pointer_cast<Field>(target)) {
2659         if (old_sys)
2660             old_sys->Remove(field->ID());
2661         field->SetSystem(INVALID_OBJECT_ID);
2662         field->MoveTo(new_x, new_y);
2663     }
2664     // don't move planets or buildings, as these can't exist outside of systems
2665 }
2666 
Dump(unsigned short ntabs) const2667 std::string MoveInOrbit::Dump(unsigned short ntabs) const {
2668     if (m_focal_point_condition)
2669         return DumpIndent(ntabs) + "MoveInOrbit around = " + m_focal_point_condition->Dump(ntabs) + "\n";
2670     else if (m_focus_x && m_focus_y)
2671         return DumpIndent(ntabs) + "MoveInOrbit x = " + m_focus_x->Dump(ntabs) + " y = " + m_focus_y->Dump(ntabs) + "\n";
2672     else
2673         return DumpIndent(ntabs) + "MoveInOrbit";
2674 }
2675 
SetTopLevelContent(const std::string & content_name)2676 void MoveInOrbit::SetTopLevelContent(const std::string& content_name) {
2677     if (m_speed)
2678         m_speed->SetTopLevelContent(content_name);
2679     if (m_focal_point_condition)
2680         m_focal_point_condition->SetTopLevelContent(content_name);
2681     if (m_focus_x)
2682         m_focus_x->SetTopLevelContent(content_name);
2683     if (m_focus_y)
2684         m_focus_y->SetTopLevelContent(content_name);
2685 }
2686 
GetCheckSum() const2687 unsigned int MoveInOrbit::GetCheckSum() const {
2688     unsigned int retval{0};
2689 
2690     CheckSums::CheckSumCombine(retval, "MoveInOrbit");
2691     CheckSums::CheckSumCombine(retval, m_speed);
2692     CheckSums::CheckSumCombine(retval, m_focal_point_condition);
2693     CheckSums::CheckSumCombine(retval, m_focus_x);
2694     CheckSums::CheckSumCombine(retval, m_focus_y);
2695 
2696     TraceLogger() << "GetCheckSum(MoveInOrbit): retval: " << retval;
2697     return retval;
2698 }
2699 
2700 
2701 ///////////////////////////////////////////////////////////
2702 // MoveTowards                                           //
2703 ///////////////////////////////////////////////////////////
MoveTowards(std::unique_ptr<ValueRef::ValueRef<double>> && speed,std::unique_ptr<Condition::Condition> && dest_condition)2704 MoveTowards::MoveTowards(std::unique_ptr<ValueRef::ValueRef<double>>&& speed,
2705                          std::unique_ptr<Condition::Condition>&& dest_condition) :
2706     m_speed(std::move(speed)),
2707     m_dest_condition(std::move(dest_condition))
2708 {}
2709 
MoveTowards(std::unique_ptr<ValueRef::ValueRef<double>> && speed,std::unique_ptr<ValueRef::ValueRef<double>> && dest_x,std::unique_ptr<ValueRef::ValueRef<double>> && dest_y)2710 MoveTowards::MoveTowards(std::unique_ptr<ValueRef::ValueRef<double>>&& speed,
2711                          std::unique_ptr<ValueRef::ValueRef<double>>&& dest_x/* = 0*/,
2712                          std::unique_ptr<ValueRef::ValueRef<double>>&& dest_y/* = 0*/) :
2713     m_speed(std::move(speed)),
2714     m_dest_x(std::move(dest_x)),
2715     m_dest_y(std::move(dest_y))
2716 {}
2717 
Execute(ScriptingContext & context) const2718 void MoveTowards::Execute(ScriptingContext& context) const {
2719     if (!context.effect_target) {
2720         ErrorLogger() << "MoveTowards::Execute given no target object";
2721         return;
2722     }
2723     auto target = context.effect_target;
2724 
2725     double dest_x = 0.0, dest_y = 0.0, speed = 1.0;
2726     if (m_dest_x)
2727         dest_x = m_dest_x->Eval(ScriptingContext(context, target->X()));
2728     if (m_dest_y)
2729         dest_y = m_dest_y->Eval(ScriptingContext(context, target->Y()));
2730     if (m_speed)
2731         speed = m_speed->Eval(context);
2732     if (speed == 0.0)
2733         return;
2734     if (m_dest_condition) {
2735         Condition::ObjectSet matches;
2736         m_dest_condition->Eval(context, matches);
2737         if (matches.empty())
2738             return;
2739         std::shared_ptr<const UniverseObject> focus_object = *matches.begin();
2740         dest_x = focus_object->X();
2741         dest_y = focus_object->Y();
2742     }
2743 
2744     double dest_to_target_x = dest_x - target->X();
2745     double dest_to_target_y = dest_y - target->Y();
2746     double dest_to_target_dist = std::sqrt(dest_to_target_x * dest_to_target_x +
2747                                            dest_to_target_y * dest_to_target_y);
2748     double new_x, new_y;
2749 
2750     if (dest_to_target_dist < speed) {
2751         new_x = dest_x;
2752         new_y = dest_y;
2753     } else {
2754         // ensure no divide by zero issues
2755         if (dest_to_target_dist < 1.0)
2756             dest_to_target_dist = 1.0;
2757         // avoid stalling when right on top of object and attempting to move away from it
2758         if (dest_to_target_x == 0.0 && dest_to_target_y == 0.0)
2759             dest_to_target_x = 1.0;
2760         // move in direction of target
2761         new_x = target->X() + dest_to_target_x / dest_to_target_dist * speed;
2762         new_y = target->Y() + dest_to_target_y / dest_to_target_dist * speed;
2763     }
2764     if (target->X() == new_x && target->Y() == new_y)
2765         return; // nothing to do
2766 
2767     if (auto system = std::dynamic_pointer_cast<System>(target)) {
2768         system->MoveTo(new_x, new_y);
2769         for (auto& obj : context.ContextObjects().find<UniverseObject>(system->ObjectIDs())) {
2770             obj->MoveTo(new_x, new_y);
2771         }
2772         // don't need to remove objects from system or insert into it, as all
2773         // contained objects in system are moved with it, maintaining their
2774         // containment situation
2775 
2776     } else if (auto fleet = std::dynamic_pointer_cast<Fleet>(target)) {
2777         auto old_sys = context.ContextObjects().get<System>(fleet->SystemID());
2778         if (old_sys)
2779             old_sys->Remove(fleet->ID());
2780         fleet->SetSystem(INVALID_OBJECT_ID);
2781         fleet->MoveTo(new_x, new_y);
2782         for (auto& ship : context.ContextObjects().find<Ship>(fleet->ShipIDs())) {
2783             if (old_sys)
2784                 old_sys->Remove(ship->ID());
2785             ship->SetSystem(INVALID_OBJECT_ID);
2786             ship->MoveTo(new_x, new_y);
2787         }
2788 
2789         // todo: is fleet now close enough to fall into a system?
2790         UpdateFleetRoute(fleet, INVALID_OBJECT_ID, INVALID_OBJECT_ID, context.ContextObjects());
2791 
2792     } else if (auto ship = std::dynamic_pointer_cast<Ship>(target)) {
2793         auto old_sys = context.ContextObjects().get<System>(ship->SystemID());
2794         if (old_sys)
2795             old_sys->Remove(ship->ID());
2796         ship->SetSystem(INVALID_OBJECT_ID);
2797 
2798         auto old_fleet = context.ContextObjects().get<Fleet>(ship->FleetID());
2799         if (old_fleet)
2800             old_fleet->RemoveShips({ship->ID()});
2801         ship->SetFleetID(INVALID_OBJECT_ID);
2802 
2803         CreateNewFleet(new_x, new_y, ship); // creates new fleet and inserts ship into fleet
2804         if (old_fleet && old_fleet->Empty()) {
2805             if (old_sys)
2806                 old_sys->Remove(old_fleet->ID());
2807             GetUniverse().EffectDestroy(old_fleet->ID(), INVALID_OBJECT_ID);    // no object in particular destroyed this fleet
2808         }
2809 
2810     } else if (auto field = std::dynamic_pointer_cast<Field>(target)) {
2811         auto old_sys = context.ContextObjects().get<System>(field->SystemID());
2812         if (old_sys)
2813             old_sys->Remove(field->ID());
2814         field->SetSystem(INVALID_OBJECT_ID);
2815         field->MoveTo(new_x, new_y);
2816 
2817     }
2818     // don't move planets or buildings, as these can't exist outside of systems
2819 }
2820 
Dump(unsigned short ntabs) const2821 std::string MoveTowards::Dump(unsigned short ntabs) const {
2822     if (m_dest_condition)
2823         return DumpIndent(ntabs) + "MoveTowards destination = " + m_dest_condition->Dump(ntabs) + "\n";
2824     else if (m_dest_x && m_dest_y)
2825         return DumpIndent(ntabs) + "MoveTowards x = " + m_dest_x->Dump(ntabs) + " y = " + m_dest_y->Dump(ntabs) + "\n";
2826     else
2827         return DumpIndent(ntabs) + "MoveTowards";
2828 }
2829 
SetTopLevelContent(const std::string & content_name)2830 void MoveTowards::SetTopLevelContent(const std::string& content_name) {
2831     if (m_speed)
2832         m_speed->SetTopLevelContent(content_name);
2833     if (m_dest_condition)
2834         m_dest_condition->SetTopLevelContent(content_name);
2835     if (m_dest_x)
2836         m_dest_x->SetTopLevelContent(content_name);
2837     if (m_dest_y)
2838         m_dest_y->SetTopLevelContent(content_name);
2839 }
2840 
GetCheckSum() const2841 unsigned int MoveTowards::GetCheckSum() const {
2842     unsigned int retval{0};
2843 
2844     CheckSums::CheckSumCombine(retval, "MoveTowards");
2845     CheckSums::CheckSumCombine(retval, m_speed);
2846     CheckSums::CheckSumCombine(retval, m_dest_condition);
2847     CheckSums::CheckSumCombine(retval, m_dest_x);
2848     CheckSums::CheckSumCombine(retval, m_dest_y);
2849 
2850     TraceLogger() << "GetCheckSum(MoveTowards): retval: " << retval;
2851     return retval;
2852 }
2853 
2854 
2855 ///////////////////////////////////////////////////////////
2856 // SetDestination                                        //
2857 ///////////////////////////////////////////////////////////
SetDestination(std::unique_ptr<Condition::Condition> && location_condition)2858 SetDestination::SetDestination(std::unique_ptr<Condition::Condition>&& location_condition) :
2859     m_location_condition(std::move(location_condition))
2860 {}
2861 
Execute(ScriptingContext & context) const2862 void SetDestination::Execute(ScriptingContext& context) const {
2863     if (!context.effect_target) {
2864         ErrorLogger() << "SetDestination::Execute given no target object";
2865         return;
2866     }
2867 
2868     auto target_fleet = std::dynamic_pointer_cast<Fleet>(context.effect_target);
2869     if (!target_fleet) {
2870         ErrorLogger() << "SetDestination::Execute acting on non-fleet target:";
2871         context.effect_target->Dump();
2872         return;
2873     }
2874 
2875     Universe& universe = GetUniverse();
2876 
2877     Condition::ObjectSet valid_locations;
2878     // apply location condition to determine valid location to move target to
2879     m_location_condition->Eval(context, valid_locations);
2880 
2881     // early exit if there are no valid locations - can't move anything if there's nowhere to move to
2882     if (valid_locations.empty())
2883         return;
2884 
2885     // "randomly" pick a destination
2886     int destination_idx = RandSmallInt(0, valid_locations.size() - 1);
2887     auto destination = std::const_pointer_cast<UniverseObject>(
2888         *std::next(valid_locations.begin(), destination_idx));
2889     int destination_system_id = destination->SystemID();
2890 
2891     // early exit if destination is not / in a system
2892     if (destination_system_id == INVALID_OBJECT_ID)
2893         return;
2894 
2895     int start_system_id = target_fleet->SystemID();
2896     if (start_system_id == INVALID_OBJECT_ID)
2897         start_system_id = target_fleet->NextSystemID();
2898     // abort if no valid starting system
2899     if (start_system_id == INVALID_OBJECT_ID)
2900         return;
2901 
2902     // find shortest path for fleet's owner
2903     std::pair<std::list<int>, double> short_path = universe.GetPathfinder()->ShortestPath(start_system_id, destination_system_id, target_fleet->Owner());
2904     const std::list<int>& route_list = short_path.first;
2905 
2906     // reject empty move paths (no path exists).
2907     if (route_list.empty())
2908         return;
2909 
2910     // check destination validity: disallow movement that's out of range
2911     std::pair<int, int> eta = target_fleet->ETA(target_fleet->MovePath(route_list));
2912     if (eta.first == Fleet::ETA_NEVER || eta.first == Fleet::ETA_OUT_OF_RANGE)
2913         return;
2914 
2915     try {
2916         target_fleet->SetRoute(route_list);
2917     } catch (const std::exception& e) {
2918         ErrorLogger() << "Caught exception in Effect::SetDestination setting fleet route: " << e.what();
2919     }
2920 }
2921 
Dump(unsigned short ntabs) const2922 std::string SetDestination::Dump(unsigned short ntabs) const
2923 { return DumpIndent(ntabs) + "SetDestination destination = " + m_location_condition->Dump(ntabs) + "\n"; }
2924 
SetTopLevelContent(const std::string & content_name)2925 void SetDestination::SetTopLevelContent(const std::string& content_name) {
2926     if (m_location_condition)
2927         m_location_condition->SetTopLevelContent(content_name);
2928 }
2929 
GetCheckSum() const2930 unsigned int SetDestination::GetCheckSum() const {
2931     unsigned int retval{0};
2932 
2933     CheckSums::CheckSumCombine(retval, "SetDestination");
2934     CheckSums::CheckSumCombine(retval, m_location_condition);
2935 
2936     TraceLogger() << "GetCheckSum(SetDestination): retval: " << retval;
2937     return retval;
2938 }
2939 
2940 
2941 ///////////////////////////////////////////////////////////
2942 // SetAggression                                         //
2943 ///////////////////////////////////////////////////////////
SetAggression(bool aggressive)2944 SetAggression::SetAggression(bool aggressive) :
2945     m_aggressive(aggressive)
2946 {}
2947 
Execute(ScriptingContext & context) const2948 void SetAggression::Execute(ScriptingContext& context) const {
2949     if (!context.effect_target) {
2950         ErrorLogger() << "SetAggression::Execute given no target object";
2951         return;
2952     }
2953 
2954     auto target_fleet = std::dynamic_pointer_cast<Fleet>(context.effect_target);
2955     if (!target_fleet) {
2956         ErrorLogger() << "SetAggression::Execute acting on non-fleet target:";
2957         context.effect_target->Dump();
2958         return;
2959     }
2960 
2961     target_fleet->SetAggressive(m_aggressive);
2962 }
2963 
Dump(unsigned short ntabs) const2964 std::string SetAggression::Dump(unsigned short ntabs) const
2965 { return DumpIndent(ntabs) + (m_aggressive ? "SetAggressive" : "SetPassive") + "\n"; }
2966 
GetCheckSum() const2967 unsigned int SetAggression::GetCheckSum() const {
2968     unsigned int retval{0};
2969 
2970     CheckSums::CheckSumCombine(retval, "SetAggression");
2971     CheckSums::CheckSumCombine(retval, m_aggressive);
2972 
2973     TraceLogger() << "GetCheckSum(SetAggression): retval: " << retval;
2974     return retval;
2975 }
2976 
2977 
2978 ///////////////////////////////////////////////////////////
2979 // Victory                                               //
2980 ///////////////////////////////////////////////////////////
Victory(const std::string & reason_string)2981 Victory::Victory(const std::string& reason_string) :
2982     m_reason_string(reason_string)
2983 {}
2984 
Execute(ScriptingContext & context) const2985 void Victory::Execute(ScriptingContext& context) const {
2986     if (!context.effect_target) {
2987         ErrorLogger() << "Victory::Execute given no target object";
2988         return;
2989     }
2990     if (Empire* empire = GetEmpire(context.effect_target->Owner()))
2991         empire->Win(m_reason_string);
2992     else
2993         ErrorLogger() << "Trying to grant victory to a missing empire!";
2994 }
2995 
Dump(unsigned short ntabs) const2996 std::string Victory::Dump(unsigned short ntabs) const
2997 { return DumpIndent(ntabs) + "Victory reason = \"" + m_reason_string + "\"\n"; }
2998 
GetCheckSum() const2999 unsigned int Victory::GetCheckSum() const {
3000     unsigned int retval{0};
3001 
3002     CheckSums::CheckSumCombine(retval, "Victory");
3003     CheckSums::CheckSumCombine(retval, m_reason_string);
3004 
3005     TraceLogger() << "GetCheckSum(Victory): retval: " << retval;
3006     return retval;
3007 }
3008 
3009 
3010 ///////////////////////////////////////////////////////////
3011 // SetEmpireTechProgress                                 //
3012 ///////////////////////////////////////////////////////////
SetEmpireTechProgress(std::unique_ptr<ValueRef::ValueRef<std::string>> && tech_name,std::unique_ptr<ValueRef::ValueRef<double>> && research_progress,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)3013 SetEmpireTechProgress::SetEmpireTechProgress(std::unique_ptr<ValueRef::ValueRef<std::string>>&& tech_name,
3014                                              std::unique_ptr<ValueRef::ValueRef<double>>&& research_progress,
3015                                              std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id /*= nullptr*/) :
3016     m_tech_name(std::move(tech_name)),
3017     m_research_progress(std::move(research_progress)),
3018     m_empire_id(
3019         empire_id
3020         ? std::move(empire_id)
3021         : std::make_unique<ValueRef::Variable<int>>(ValueRef::EFFECT_TARGET_REFERENCE, std::vector<std::string>(1, "Owner")))
3022 {}
3023 
Execute(ScriptingContext & context) const3024 void SetEmpireTechProgress::Execute(ScriptingContext& context) const {
3025     if (!m_empire_id) return;
3026     Empire* empire = GetEmpire(m_empire_id->Eval(context));
3027     if (!empire) return;
3028 
3029     if (!m_tech_name) {
3030         ErrorLogger() << "SetEmpireTechProgress::Execute has not tech name to evaluate";
3031         return;
3032     }
3033     std::string tech_name = m_tech_name->Eval(context);
3034     if (tech_name.empty())
3035         return;
3036 
3037     const Tech* tech = GetTech(tech_name);
3038     if (!tech) {
3039         ErrorLogger() << "SetEmpireTechProgress::Execute couldn't get tech with name " << tech_name;
3040         return;
3041     }
3042 
3043     float initial_progress = empire->ResearchProgress(tech_name);
3044     double value = m_research_progress->Eval(ScriptingContext(context, initial_progress));
3045     empire->SetTechResearchProgress(tech_name, value);
3046 }
3047 
Dump(unsigned short ntabs) const3048 std::string SetEmpireTechProgress::Dump(unsigned short ntabs) const {
3049     std::string retval = "SetEmpireTechProgress name = ";
3050     if (m_tech_name)
3051         retval += m_tech_name->Dump(ntabs);
3052     if (m_research_progress)
3053         retval += " progress = " + m_research_progress->Dump(ntabs);
3054     if (m_empire_id)
3055         retval += " empire = " + m_empire_id->Dump(ntabs) + "\n";
3056     return retval;
3057 }
3058 
SetTopLevelContent(const std::string & content_name)3059 void SetEmpireTechProgress::SetTopLevelContent(const std::string& content_name) {
3060     if (m_tech_name)
3061         m_tech_name->SetTopLevelContent(content_name);
3062     if (m_research_progress)
3063         m_research_progress->SetTopLevelContent(content_name);
3064     if (m_empire_id)
3065         m_empire_id->SetTopLevelContent(content_name);
3066 }
3067 
GetCheckSum() const3068 unsigned int SetEmpireTechProgress::GetCheckSum() const {
3069     unsigned int retval{0};
3070 
3071     CheckSums::CheckSumCombine(retval, "SetEmpireTechProgress");
3072     CheckSums::CheckSumCombine(retval, m_tech_name);
3073     CheckSums::CheckSumCombine(retval, m_research_progress);
3074     CheckSums::CheckSumCombine(retval, m_empire_id);
3075 
3076     TraceLogger() << "GetCheckSum(SetEmpireTechProgress): retval: " << retval;
3077     return retval;
3078 }
3079 
3080 
3081 ///////////////////////////////////////////////////////////
3082 // GiveEmpireTech                                        //
3083 ///////////////////////////////////////////////////////////
GiveEmpireTech(std::unique_ptr<ValueRef::ValueRef<std::string>> && tech_name,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id)3084 GiveEmpireTech::GiveEmpireTech(std::unique_ptr<ValueRef::ValueRef<std::string>>&& tech_name,
3085                                std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id) :
3086     m_tech_name(std::move(tech_name)),
3087     m_empire_id(std::move(empire_id))
3088 {
3089     if (!m_empire_id)
3090         m_empire_id.reset(new ValueRef::Variable<int>(ValueRef::EFFECT_TARGET_REFERENCE, std::vector<std::string>(1, "Owner")));
3091 }
3092 
Execute(ScriptingContext & context) const3093 void GiveEmpireTech::Execute(ScriptingContext& context) const {
3094     if (!m_empire_id) return;
3095     Empire* empire = GetEmpire(m_empire_id->Eval(context));
3096     if (!empire) return;
3097 
3098     if (!m_tech_name)
3099         return;
3100 
3101     std::string tech_name = m_tech_name->Eval(context);
3102 
3103     const Tech* tech = GetTech(tech_name);
3104     if (!tech) {
3105         ErrorLogger() << "GiveEmpireTech::Execute couldn't get tech with name: " << tech_name;
3106         return;
3107     }
3108 
3109     empire->AddNewlyResearchedTechToGrantAtStartOfNextTurn(tech_name);
3110 }
3111 
Dump(unsigned short ntabs) const3112 std::string GiveEmpireTech::Dump(unsigned short ntabs) const {
3113     std::string retval = DumpIndent(ntabs) + "GiveEmpireTech";
3114 
3115     if (m_tech_name)
3116         retval += " name = " + m_tech_name->Dump(ntabs);
3117 
3118     if (m_empire_id)
3119         retval += " empire = " + m_empire_id->Dump(ntabs);
3120 
3121     retval += "\n";
3122     return retval;
3123 }
3124 
SetTopLevelContent(const std::string & content_name)3125 void GiveEmpireTech::SetTopLevelContent(const std::string& content_name) {
3126     if (m_empire_id)
3127         m_empire_id->SetTopLevelContent(content_name);
3128     if (m_tech_name)
3129         m_tech_name->SetTopLevelContent(content_name);
3130 }
3131 
GetCheckSum() const3132 unsigned int GiveEmpireTech::GetCheckSum() const {
3133     unsigned int retval{0};
3134 
3135     CheckSums::CheckSumCombine(retval, "GiveEmpireTech");
3136     CheckSums::CheckSumCombine(retval, m_tech_name);
3137     CheckSums::CheckSumCombine(retval, m_empire_id);
3138 
3139     TraceLogger() << "GetCheckSum(GiveEmpireTech): retval: " << retval;
3140     return retval;
3141 }
3142 
3143 
3144 ///////////////////////////////////////////////////////////
3145 // GenerateSitRepMessage                                 //
3146 ///////////////////////////////////////////////////////////
GenerateSitRepMessage(const std::string & message_string,const std::string & icon,MessageParams && message_parameters,std::unique_ptr<ValueRef::ValueRef<int>> && recipient_empire_id,EmpireAffiliationType affiliation,const std::string label,bool stringtable_lookup)3147 GenerateSitRepMessage::GenerateSitRepMessage(const std::string& message_string,
3148                                              const std::string& icon,
3149                                              MessageParams&& message_parameters,
3150                                              std::unique_ptr<ValueRef::ValueRef<int>>&& recipient_empire_id,
3151                                              EmpireAffiliationType affiliation,
3152                                              const std::string label,
3153                                              bool stringtable_lookup) :
3154     m_message_string(message_string),
3155     m_icon(icon),
3156     m_message_parameters(std::move(message_parameters)),
3157     m_recipient_empire_id(std::move(recipient_empire_id)),
3158     m_affiliation(affiliation),
3159     m_label(label),
3160     m_stringtable_lookup(stringtable_lookup)
3161 {}
3162 
GenerateSitRepMessage(const std::string & message_string,const std::string & icon,MessageParams && message_parameters,EmpireAffiliationType affiliation,std::unique_ptr<Condition::Condition> && condition,const std::string label,bool stringtable_lookup)3163 GenerateSitRepMessage::GenerateSitRepMessage(const std::string& message_string,
3164                                              const std::string& icon,
3165                                              MessageParams&& message_parameters,
3166                                              EmpireAffiliationType affiliation,
3167                                              std::unique_ptr<Condition::Condition>&& condition,
3168                                              const std::string label,
3169                                              bool stringtable_lookup) :
3170     m_message_string(message_string),
3171     m_icon(icon),
3172     m_message_parameters(std::move(message_parameters)),
3173     m_condition(std::move(condition)),
3174     m_affiliation(affiliation),
3175     m_label(label),
3176     m_stringtable_lookup(stringtable_lookup)
3177 {}
3178 
GenerateSitRepMessage(const std::string & message_string,const std::string & icon,MessageParams && message_parameters,EmpireAffiliationType affiliation,const std::string & label,bool stringtable_lookup)3179 GenerateSitRepMessage::GenerateSitRepMessage(const std::string& message_string, const std::string& icon,
3180                                              MessageParams&& message_parameters,
3181                                              EmpireAffiliationType affiliation,
3182                                              const std::string& label,
3183                                              bool stringtable_lookup):
3184     m_message_string(message_string),
3185     m_icon(icon),
3186     m_message_parameters(std::move(message_parameters)),
3187     m_affiliation(affiliation),
3188     m_label(label),
3189     m_stringtable_lookup(stringtable_lookup)
3190 {}
3191 
Execute(ScriptingContext & context) const3192 void GenerateSitRepMessage::Execute(ScriptingContext& context) const {
3193     int recipient_id = ALL_EMPIRES;
3194     if (m_recipient_empire_id)
3195         recipient_id = m_recipient_empire_id->Eval(context);
3196 
3197     // track any ship designs used in message, which any recipients must be
3198     // made aware of so sitrep won't have errors
3199     std::set<int> ship_design_ids_to_inform_receipits_of;
3200 
3201     // TODO: should any referenced object IDs being made known at basic visibility?
3202 
3203 
3204     // evaluate all parameter valuerefs so they can be substituted into sitrep template
3205     std::vector<std::pair<std::string, std::string>> parameter_tag_values;
3206     for (const auto& entry : m_message_parameters) {
3207         parameter_tag_values.push_back({entry.first, entry.second->Eval(context)});
3208 
3209         // special case for ship designs: make sure sitrep recipient knows about the design
3210         // so the sitrep won't have errors about unknown designs being referenced
3211         if (entry.first == VarText::PREDEFINED_DESIGN_TAG) {
3212             if (const ShipDesign* design = GetPredefinedShipDesign(entry.second->Eval(context))) {
3213                 ship_design_ids_to_inform_receipits_of.insert(design->ID());
3214             }
3215         }
3216     }
3217 
3218     // whom to send to?
3219     std::set<int> recipient_empire_ids;
3220     switch (m_affiliation) {
3221     case AFFIL_SELF: {
3222         // add just specified empire
3223         if (recipient_id != ALL_EMPIRES)
3224             recipient_empire_ids.insert(recipient_id);
3225         break;
3226     }
3227 
3228     case AFFIL_ALLY: {
3229         // add allies of specified empire
3230         for (auto& empire_id : Empires()) {
3231             if (empire_id.first == recipient_id || recipient_id == ALL_EMPIRES)
3232                 continue;
3233 
3234             DiplomaticStatus status = Empires().GetDiplomaticStatus(recipient_id, empire_id.first);
3235             if (status >= DIPLO_ALLIED)
3236                 recipient_empire_ids.insert(empire_id.first);
3237         }
3238         break;
3239     }
3240 
3241     case AFFIL_PEACE: {
3242         // add empires at peace with the specified empire
3243         for (auto& empire_id : Empires()) {
3244             if (empire_id.first == recipient_id || recipient_id == ALL_EMPIRES)
3245                 continue;
3246 
3247             DiplomaticStatus status = Empires().GetDiplomaticStatus(recipient_id, empire_id.first);
3248             if (status == DIPLO_PEACE)
3249                 recipient_empire_ids.insert(empire_id.first);
3250         }
3251         break;
3252     }
3253 
3254     case AFFIL_ENEMY: {
3255         // add enemies of specified empire
3256         for (auto& empire_id : Empires()) {
3257             if (empire_id.first == recipient_id || recipient_id == ALL_EMPIRES)
3258                 continue;
3259 
3260             DiplomaticStatus status = Empires().GetDiplomaticStatus(recipient_id, empire_id.first);
3261             if (status == DIPLO_WAR)
3262                 recipient_empire_ids.insert(empire_id.first);
3263         }
3264         break;
3265     }
3266 
3267     case AFFIL_CAN_SEE: {
3268         // evaluate condition
3269         Condition::ObjectSet condition_matches;
3270         if (m_condition)
3271             m_condition->Eval(context, condition_matches);
3272 
3273         // add empires that can see any condition-matching object
3274         for (auto& empire_entry : Empires()) {
3275             int empire_id = empire_entry.first;
3276             for (auto& object : condition_matches) {
3277                 if (object->GetVisibility(empire_id) >= VIS_BASIC_VISIBILITY) {
3278                     recipient_empire_ids.insert(empire_id);
3279                     break;
3280                 }
3281             }
3282         }
3283         break;
3284     }
3285 
3286     case AFFIL_NONE:
3287         // add no empires
3288         break;
3289 
3290     case AFFIL_HUMAN:
3291         // todo: implement this separately, though not high priority since it
3292         // probably doesn't matter if AIs get an extra sitrep message meant for
3293         // human eyes
3294     case AFFIL_ANY:
3295     default: {
3296         // add all empires
3297         for (auto& empire_entry : Empires())
3298             recipient_empire_ids.insert(empire_entry.first);
3299         break;
3300     }
3301     }
3302 
3303     int sitrep_turn = CurrentTurn() + 1;
3304 
3305     // send to recipient empires
3306     for (int empire_id : recipient_empire_ids) {
3307         Empire* empire = GetEmpire(empire_id);
3308         if (!empire)
3309             continue;
3310         empire->AddSitRepEntry(CreateSitRep(m_message_string, sitrep_turn, m_icon,
3311                                             parameter_tag_values, m_label, m_stringtable_lookup));
3312 
3313         // also inform of any ship designs recipients should know about
3314         for (int design_id : ship_design_ids_to_inform_receipits_of) {
3315             GetUniverse().SetEmpireKnowledgeOfShipDesign(design_id, empire_id);
3316         }
3317     }
3318 }
3319 
Dump(unsigned short ntabs) const3320 std::string GenerateSitRepMessage::Dump(unsigned short ntabs) const {
3321     std::string retval = DumpIndent(ntabs);
3322     retval += "GenerateSitRepMessage\n";
3323     retval += DumpIndent(ntabs+1) + "message = \"" + m_message_string + "\"" + " icon = " + m_icon + "\n";
3324 
3325     if (m_message_parameters.size() == 1) {
3326         retval += DumpIndent(ntabs+1) + "parameters = tag = " + m_message_parameters[0].first + " data = " + m_message_parameters[0].second->Dump(ntabs+1) + "\n";
3327     } else if (!m_message_parameters.empty()) {
3328         retval += DumpIndent(ntabs+1) + "parameters = [ ";
3329         for (const auto& entry : m_message_parameters) {
3330             retval += " tag = " + entry.first
3331                    + " data = " + entry.second->Dump(ntabs+1)
3332                    + " ";
3333         }
3334         retval += "]\n";
3335     }
3336 
3337     retval += DumpIndent(ntabs+1) + "affiliation = ";
3338     switch (m_affiliation) {
3339     case AFFIL_SELF:    retval += "TheEmpire";  break;
3340     case AFFIL_ENEMY:   retval += "EnemyOf";    break;
3341     case AFFIL_PEACE:   retval += "PeaceWith";  break;
3342     case AFFIL_ALLY:    retval += "AllyOf";     break;
3343     case AFFIL_ANY:     retval += "AnyEmpire";  break;
3344     case AFFIL_CAN_SEE: retval += "CanSee";     break;
3345     case AFFIL_HUMAN:   retval += "Human";      break;
3346     default:            retval += "?";          break;
3347     }
3348 
3349     if (m_recipient_empire_id)
3350         retval += "\n" + DumpIndent(ntabs+1) + "empire = " + m_recipient_empire_id->Dump(ntabs+1) + "\n";
3351     if (m_condition)
3352         retval += "\n" + DumpIndent(ntabs+1) + "condition = " + m_condition->Dump(ntabs+1) + "\n";
3353 
3354     return retval;
3355 }
3356 
SetTopLevelContent(const std::string & content_name)3357 void GenerateSitRepMessage::SetTopLevelContent(const std::string& content_name) {
3358     for (auto& entry : m_message_parameters) {
3359         entry.second->SetTopLevelContent(content_name);
3360     }
3361     if (m_recipient_empire_id)
3362         m_recipient_empire_id->SetTopLevelContent(content_name);
3363     if (m_condition)
3364         m_condition->SetTopLevelContent(content_name);
3365 }
3366 
GetCheckSum() const3367 unsigned int GenerateSitRepMessage::GetCheckSum() const {
3368     unsigned int retval{0};
3369 
3370     CheckSums::CheckSumCombine(retval, "GenerateSitRepMessage");
3371     CheckSums::CheckSumCombine(retval, m_message_string);
3372     CheckSums::CheckSumCombine(retval, m_icon);
3373     CheckSums::CheckSumCombine(retval, m_message_parameters);
3374     CheckSums::CheckSumCombine(retval, m_recipient_empire_id);
3375     CheckSums::CheckSumCombine(retval, m_condition);
3376     CheckSums::CheckSumCombine(retval, m_affiliation);
3377     CheckSums::CheckSumCombine(retval, m_label);
3378     CheckSums::CheckSumCombine(retval, m_stringtable_lookup);
3379 
3380     TraceLogger() << "GetCheckSum(GenerateSitRepMessage): retval: " << retval;
3381     return retval;
3382 }
3383 
3384 std::vector<std::pair<std::string, ValueRef::ValueRef<std::string>*>>
MessageParameters() const3385 GenerateSitRepMessage::MessageParameters() const {
3386     std::vector<std::pair<std::string, ValueRef::ValueRef<std::string>*>> retval(m_message_parameters.size());
3387     std::transform(m_message_parameters.begin(), m_message_parameters.end(), retval.begin(),
3388                    [](const std::pair<std::string, std::unique_ptr<ValueRef::ValueRef<std::string>>>& xx) {
3389                        return std::make_pair(xx.first, xx.second.get());
3390                    });
3391     return retval;
3392 }
3393 
3394 ///////////////////////////////////////////////////////////
3395 // SetOverlayTexture                                     //
3396 ///////////////////////////////////////////////////////////
SetOverlayTexture(const std::string & texture,std::unique_ptr<ValueRef::ValueRef<double>> && size)3397 SetOverlayTexture::SetOverlayTexture(const std::string& texture,
3398                                      std::unique_ptr<ValueRef::ValueRef<double>>&& size) :
3399     m_texture(texture),
3400     m_size(std::move(size))
3401 {}
3402 
SetOverlayTexture(const std::string & texture,ValueRef::ValueRef<double> * size)3403 SetOverlayTexture::SetOverlayTexture(const std::string& texture, ValueRef::ValueRef<double>* size) :
3404     m_texture(texture),
3405     m_size(size)
3406 {}
3407 
Execute(ScriptingContext & context) const3408 void SetOverlayTexture::Execute(ScriptingContext& context) const {
3409     if (!context.effect_target)
3410         return;
3411     double size = 1.0;
3412     if (m_size)
3413         size = m_size->Eval(context);
3414 
3415     if (auto system = std::dynamic_pointer_cast<System>(context.effect_target))
3416         system->SetOverlayTexture(m_texture, size);
3417 }
3418 
Dump(unsigned short ntabs) const3419 std::string SetOverlayTexture::Dump(unsigned short ntabs) const {
3420     std::string retval = DumpIndent(ntabs) + "SetOverlayTexture texture = " + m_texture;
3421     if (m_size)
3422         retval += " size = " + m_size->Dump(ntabs);
3423     retval += "\n";
3424     return retval;
3425 }
3426 
SetTopLevelContent(const std::string & content_name)3427 void SetOverlayTexture::SetTopLevelContent(const std::string& content_name) {
3428     if (m_size)
3429         m_size->SetTopLevelContent(content_name);
3430 }
3431 
GetCheckSum() const3432 unsigned int SetOverlayTexture::GetCheckSum() const {
3433     unsigned int retval{0};
3434 
3435     CheckSums::CheckSumCombine(retval, "SetOverlayTexture");
3436     CheckSums::CheckSumCombine(retval, m_texture);
3437     CheckSums::CheckSumCombine(retval, m_size);
3438 
3439     TraceLogger() << "GetCheckSum(SetOverlayTexture): retval: " << retval;
3440     return retval;
3441 }
3442 
3443 
3444 ///////////////////////////////////////////////////////////
3445 // SetTexture                                            //
3446 ///////////////////////////////////////////////////////////
SetTexture(const std::string & texture)3447 SetTexture::SetTexture(const std::string& texture) :
3448     m_texture(texture)
3449 {}
3450 
Execute(ScriptingContext & context) const3451 void SetTexture::Execute(ScriptingContext& context) const {
3452     if (!context.effect_target)
3453         return;
3454     if (auto planet = std::dynamic_pointer_cast<Planet>(context.effect_target))
3455         planet->SetSurfaceTexture(m_texture);
3456 }
3457 
Dump(unsigned short ntabs) const3458 std::string SetTexture::Dump(unsigned short ntabs) const
3459 { return DumpIndent(ntabs) + "SetTexture texture = " + m_texture + "\n"; }
3460 
GetCheckSum() const3461 unsigned int SetTexture::GetCheckSum() const {
3462     unsigned int retval{0};
3463 
3464     CheckSums::CheckSumCombine(retval, "SetTexture");
3465     CheckSums::CheckSumCombine(retval, m_texture);
3466 
3467     TraceLogger() << "GetCheckSum(SetTexture): retval: " << retval;
3468     return retval;
3469 }
3470 
3471 
3472 ///////////////////////////////////////////////////////////
3473 // SetVisibility                                         //
3474 ///////////////////////////////////////////////////////////
SetVisibility(std::unique_ptr<ValueRef::ValueRef<Visibility>> vis,EmpireAffiliationType affiliation,std::unique_ptr<ValueRef::ValueRef<int>> && empire_id,std::unique_ptr<Condition::Condition> && of_objects)3475 SetVisibility::SetVisibility(std::unique_ptr<ValueRef::ValueRef<Visibility>> vis,
3476                              EmpireAffiliationType affiliation,
3477                              std::unique_ptr<ValueRef::ValueRef<int>>&& empire_id,
3478                              std::unique_ptr<Condition::Condition>&& of_objects) :
3479     m_vis(std::move(vis)),
3480     m_empire_id(std::move(empire_id)),
3481     m_affiliation(affiliation),
3482     m_condition(std::move(of_objects))
3483 {}
3484 
Execute(ScriptingContext & context) const3485 void SetVisibility::Execute(ScriptingContext& context) const {
3486     if (!context.effect_target)
3487         return;
3488 
3489     // Note: currently ignoring upgrade-only flag
3490 
3491     if (!m_vis)
3492         return; // nothing to evaluate!
3493 
3494     int empire_id = ALL_EMPIRES;
3495     if (m_empire_id)
3496         empire_id = m_empire_id->Eval(context);
3497 
3498     // whom to set visbility for?
3499     std::set<int> empire_ids;
3500     switch (m_affiliation) {
3501     case AFFIL_SELF: {
3502         // add just specified empire
3503         if (empire_id != ALL_EMPIRES)
3504             empire_ids.insert(empire_id);
3505         break;
3506     }
3507 
3508     case AFFIL_ALLY: {
3509         // add allies of specified empire
3510         for (const auto& empire_entry : Empires()) {
3511             if (empire_entry.first == empire_id || empire_id == ALL_EMPIRES)
3512                 continue;
3513 
3514             DiplomaticStatus status = Empires().GetDiplomaticStatus(empire_id, empire_entry.first);
3515             if (status >= DIPLO_ALLIED)
3516                 empire_ids.insert(empire_entry.first);
3517         }
3518         break;
3519     }
3520 
3521     case AFFIL_PEACE: {
3522         // add empires at peace with the specified empire
3523         for (const auto& empire_entry : Empires()) {
3524             if (empire_entry.first == empire_id || empire_id == ALL_EMPIRES)
3525                 continue;
3526 
3527             DiplomaticStatus status = Empires().GetDiplomaticStatus(empire_id, empire_entry.first);
3528             if (status == DIPLO_PEACE)
3529                 empire_ids.insert(empire_entry.first);
3530         }
3531         break;
3532     }
3533 
3534     case AFFIL_ENEMY: {
3535         // add enemies of specified empire
3536         for (const auto& empire_entry : Empires()) {
3537             if (empire_entry.first == empire_id || empire_id == ALL_EMPIRES)
3538                 continue;
3539 
3540             DiplomaticStatus status = Empires().GetDiplomaticStatus(empire_id, empire_entry.first);
3541             if (status == DIPLO_WAR)
3542                 empire_ids.insert(empire_entry.first);
3543         }
3544         break;
3545     }
3546 
3547     case AFFIL_CAN_SEE:
3548         // unsupported so far...
3549     case AFFIL_HUMAN:
3550         // unsupported so far...
3551     case AFFIL_NONE:
3552         // add no empires
3553         break;
3554 
3555     case AFFIL_ANY:
3556     default: {
3557         // add all empires
3558         for (const auto& empire_entry : Empires())
3559             empire_ids.insert(empire_entry.first);
3560         break;
3561     }
3562     }
3563 
3564     // what to set visibility of?
3565     std::set<int> object_ids;
3566     if (!m_condition) {
3567         object_ids.insert(context.effect_target->ID());
3568     } else {
3569         Condition::ObjectSet condition_matches;
3570         m_condition->Eval(context, condition_matches);
3571         for (auto& object : condition_matches) {
3572             object_ids.insert(object->ID());
3573         }
3574     }
3575 
3576     int source_id = INVALID_OBJECT_ID;
3577     if (context.source)
3578         source_id = context.source->ID();
3579 
3580     for (int emp_id : empire_ids) {
3581         if (!GetEmpire(emp_id))
3582             continue;
3583         for (int obj_id : object_ids) {
3584             // store source object id and ValueRef to evaluate to determine
3585             // what visibility level to set at time of application
3586             GetUniverse().SetEffectDerivedVisibility(emp_id, obj_id, source_id, m_vis.get());
3587         }
3588     }
3589 }
3590 
Dump(unsigned short ntabs) const3591 std::string SetVisibility::Dump(unsigned short ntabs) const {
3592     std::string retval = DumpIndent(ntabs);
3593 
3594     retval += DumpIndent(ntabs) + "SetVisibility affiliation = ";
3595     switch (m_affiliation) {
3596     case AFFIL_SELF:    retval += "TheEmpire";  break;
3597     case AFFIL_ENEMY:   retval += "EnemyOf";    break;
3598     case AFFIL_PEACE:   retval += "PeaceWith";  break;
3599     case AFFIL_ALLY:    retval += "AllyOf";     break;
3600     case AFFIL_ANY:     retval += "AnyEmpire";  break;
3601     case AFFIL_CAN_SEE: retval += "CanSee";     break;
3602     case AFFIL_HUMAN:   retval += "Human";      break;
3603     default:            retval += "?";          break;
3604     }
3605 
3606     if (m_empire_id)
3607         retval += " empire = " + m_empire_id->Dump(ntabs);
3608 
3609     if (m_vis)
3610         retval += " visibility = " + m_vis->Dump(ntabs);
3611 
3612     if (m_condition)
3613         retval += " condition = " + m_condition->Dump(ntabs);
3614 
3615     retval += "\n";
3616     return retval;
3617 }
3618 
SetTopLevelContent(const std::string & content_name)3619 void SetVisibility::SetTopLevelContent(const std::string& content_name) {
3620     if (m_vis)
3621         m_vis->SetTopLevelContent(content_name);
3622     if (m_empire_id)
3623         m_empire_id->SetTopLevelContent(content_name);
3624     if (m_condition)
3625         m_condition->SetTopLevelContent(content_name);
3626 }
3627 
GetCheckSum() const3628 unsigned int SetVisibility::GetCheckSum() const {
3629     unsigned int retval{0};
3630 
3631     CheckSums::CheckSumCombine(retval, "SetVisibility");
3632     CheckSums::CheckSumCombine(retval, m_vis.get());
3633     CheckSums::CheckSumCombine(retval, m_empire_id);
3634     CheckSums::CheckSumCombine(retval, m_affiliation);
3635     CheckSums::CheckSumCombine(retval, m_condition);
3636 
3637     TraceLogger() << "GetCheckSum(SetVisibility): retval: " << retval;
3638     return retval;
3639 }
3640 
3641 
3642 ///////////////////////////////////////////////////////////
3643 // Conditional                                           //
3644 ///////////////////////////////////////////////////////////
Conditional(std::unique_ptr<Condition::Condition> && target_condition,std::vector<std::unique_ptr<Effect>> && true_effects,std::vector<std::unique_ptr<Effect>> && false_effects)3645 Conditional::Conditional(std::unique_ptr<Condition::Condition>&& target_condition,
3646                          std::vector<std::unique_ptr<Effect>>&& true_effects,
3647                          std::vector<std::unique_ptr<Effect>>&& false_effects) :
3648     m_target_condition(std::move(target_condition)),
3649     m_true_effects(std::move(true_effects)),
3650     m_false_effects(std::move(false_effects))
3651 {
3652     if (m_target_condition && !m_target_condition->TargetInvariant()) {
3653         ErrorLogger() << "Conditional effect has a target condition that depends on the target object. The condition is evaluated once to pick the targets, so when evaluating it, there is no defined target object.";
3654         DebugLogger() << "Condition effect is: " << Dump();
3655     }
3656 }
3657 
Execute(ScriptingContext & context) const3658 void Conditional::Execute(ScriptingContext& context) const {
3659     if (!context.effect_target)
3660         return;
3661 
3662     if (!m_target_condition || m_target_condition->Eval(context, context.effect_target)) {
3663         for (auto& effect : m_true_effects) {
3664             if (effect)
3665                 effect->Execute(context);
3666         }
3667     } else {
3668         for (auto& effect : m_false_effects) {
3669             if (effect)
3670                 effect->Execute(context);
3671         }
3672     }
3673 }
3674 
Execute(ScriptingContext & context,const TargetSet & targets) const3675 void Conditional::Execute(ScriptingContext& context, const TargetSet& targets) const {
3676     if (targets.empty())
3677         return;
3678 
3679     // apply sub-condition to target set to pick which to act on with which of sub-effects
3680     TargetSet matches{targets.begin(), targets.end()};
3681     TargetSet non_matches;
3682     non_matches.reserve(matches.size());
3683     if (m_target_condition)
3684         m_target_condition->Eval(context, matches, non_matches, Condition::MATCHES);
3685 
3686     if (!matches.empty() && !m_true_effects.empty()) {
3687         for (auto& effect : m_true_effects) {
3688             if (effect)
3689                 effect->Execute(context, matches);
3690         }
3691     }
3692     if (!non_matches.empty() && !m_false_effects.empty()) {
3693         for (auto& effect : m_false_effects) {
3694             if (effect)
3695                 effect->Execute(context, non_matches);
3696         }
3697     }
3698 }
3699 
Execute(ScriptingContext & context,const TargetSet & targets,AccountingMap * accounting_map,const EffectCause & effect_cause,bool only_meter_effects,bool only_appearance_effects,bool include_empire_meter_effects,bool only_generate_sitrep_effects) const3700 void Conditional::Execute(ScriptingContext& context,
3701                           const TargetSet& targets,
3702                           AccountingMap* accounting_map,
3703                           const EffectCause& effect_cause,
3704                           bool only_meter_effects,
3705                           bool only_appearance_effects,
3706                           bool include_empire_meter_effects,
3707                           bool only_generate_sitrep_effects) const
3708 {
3709     TraceLogger(effects) << "\n\nExecute Conditional effect: \n" << Dump();
3710 
3711     // apply sub-condition to target set to pick which to act on with which of sub-effects
3712     TargetSet matches{targets.begin(), targets.end()};
3713     TargetSet non_matches;
3714     non_matches.reserve(matches.size());
3715 
3716     if (m_target_condition)
3717         m_target_condition->Eval(context, matches, non_matches, Condition::MATCHES);
3718 
3719 
3720     // execute true and false effects to target matches and non-matches respectively
3721     if (!matches.empty() && !m_true_effects.empty()) {
3722         for (const auto& effect : m_true_effects) {
3723             effect->Execute(context, matches, accounting_map,
3724                             effect_cause,
3725                             only_meter_effects, only_appearance_effects,
3726                             include_empire_meter_effects,
3727                             only_generate_sitrep_effects);
3728         }
3729     }
3730     if (!non_matches.empty() && !m_false_effects.empty()) {
3731         for (const auto& effect : m_false_effects) {
3732             effect->Execute(context, non_matches, accounting_map,
3733                             effect_cause,
3734                             only_meter_effects, only_appearance_effects,
3735                             include_empire_meter_effects,
3736                             only_generate_sitrep_effects);
3737         }
3738     }
3739 }
3740 
Dump(unsigned short ntabs) const3741 std::string Conditional::Dump(unsigned short ntabs) const {
3742     std::string retval = DumpIndent(ntabs) + "If\n";
3743     if (m_target_condition) {
3744         retval += DumpIndent(ntabs+1) + "condition =\n";
3745         retval += m_target_condition->Dump(ntabs+2);
3746     }
3747 
3748     if (m_true_effects.size() == 1) {
3749         retval += DumpIndent(ntabs+1) + "effects =\n";
3750         retval += m_true_effects[0]->Dump(ntabs+2);
3751     } else {
3752         retval += DumpIndent(ntabs+1) + "effects = [\n";
3753         for (auto& effect : m_true_effects) {
3754             retval += effect->Dump(ntabs+2);
3755         }
3756         retval += DumpIndent(ntabs+1) + "]\n";
3757     }
3758 
3759     if (m_false_effects.empty()) {
3760     } else if (m_false_effects.size() == 1) {
3761         retval += DumpIndent(ntabs+1) + "else =\n";
3762         retval += m_false_effects[0]->Dump(ntabs+2);
3763     } else {
3764         retval += DumpIndent(ntabs+1) + "else = [\n";
3765         for (auto& effect : m_false_effects) {
3766             retval += effect->Dump(ntabs+2);
3767         }
3768         retval += DumpIndent(ntabs+1) + "]\n";
3769     }
3770 
3771     return retval;
3772 }
3773 
IsMeterEffect() const3774 bool Conditional::IsMeterEffect() const {
3775     for (auto& effect : m_true_effects) {
3776         if (effect->IsMeterEffect())
3777             return true;
3778     }
3779     for (auto& effect : m_false_effects) {
3780         if (effect->IsMeterEffect())
3781             return true;
3782     }
3783     return false;
3784 }
3785 
IsAppearanceEffect() const3786 bool Conditional::IsAppearanceEffect() const {
3787     for (auto& effect : m_true_effects) {
3788         if (effect->IsAppearanceEffect())
3789             return true;
3790     }
3791     for (auto& effect : m_false_effects) {
3792         if (effect->IsAppearanceEffect())
3793             return true;
3794     }
3795     return false;
3796 }
3797 
IsSitrepEffect() const3798 bool Conditional::IsSitrepEffect() const {
3799     for (auto& effect : m_true_effects) {
3800         if (effect->IsSitrepEffect())
3801             return true;
3802     }
3803     for (auto& effect : m_false_effects) {
3804         if (effect->IsSitrepEffect())
3805             return true;
3806     }
3807     return false;
3808 }
3809 
SetTopLevelContent(const std::string & content_name)3810 void Conditional::SetTopLevelContent(const std::string& content_name) {
3811     if (m_target_condition)
3812         m_target_condition->SetTopLevelContent(content_name);
3813     for (auto& effect : m_true_effects)
3814         if (effect)
3815             (effect)->SetTopLevelContent(content_name);
3816     for (auto& effect : m_false_effects)
3817         if (effect)
3818             (effect)->SetTopLevelContent(content_name);
3819 }
3820 
GetCheckSum() const3821 unsigned int Conditional::GetCheckSum() const {
3822     unsigned int retval{0};
3823 
3824     CheckSums::CheckSumCombine(retval, "Conditional");
3825     CheckSums::CheckSumCombine(retval, m_target_condition);
3826     CheckSums::CheckSumCombine(retval, m_true_effects);
3827     CheckSums::CheckSumCombine(retval, m_false_effects);
3828 
3829     TraceLogger() << "GetCheckSum(Conditional): retval: " << retval;
3830     return retval;
3831 }
3832 
3833 } // namespace Effect
3834