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