1 #include "UniverseObject.h"
2 
3 #include "../util/i18n.h"
4 #include "../util/Logger.h"
5 #include "../util/AppInterface.h"
6 #include "../Empire/EmpireManager.h"
7 #include "System.h"
8 #include "Special.h"
9 #include "Pathfinder.h"
10 #include "Universe.h"
11 #include "Predicates.h"
12 #include "Enums.h"
13 
14 #include <stdexcept>
15 #include <boost/lexical_cast.hpp>
16 
17 // static(s)
18 const int INVALID_OBJECT_ID      = -1;
19 const int TEMPORARY_OBJECT_ID    = -2;
20 
21 const double    UniverseObject::INVALID_POSITION  = -100000.0;
22 const int       UniverseObject::INVALID_OBJECT_AGE = -(1 << 30) - 1;  // using big negative number to allow for potential negative object ages, which might be useful in the event of time travel.
23 const int       UniverseObject::SINCE_BEFORE_TIME_AGE = (1 << 30) + 1;
24 
UniverseObject()25 UniverseObject::UniverseObject() :
26     StateChangedSignal(blocking_combiner<boost::signals2::optional_last_value<void>>(
27         GetUniverse().UniverseObjectSignalsInhibited())),
28     m_x(INVALID_POSITION),
29     m_y(INVALID_POSITION),
30     m_created_on_turn(CurrentTurn() )
31 {}
32 
UniverseObject(const std::string name,double x,double y)33 UniverseObject::UniverseObject(const std::string name, double x, double y) :
34     StateChangedSignal(blocking_combiner<boost::signals2::optional_last_value<void>>(
35         GetUniverse().UniverseObjectSignalsInhibited())),
36     m_name(name),
37     m_x(x),
38     m_y(y),
39     m_created_on_turn(CurrentTurn() )
40 {}
41 
~UniverseObject()42 UniverseObject::~UniverseObject()
43 {}
44 
Copy(std::shared_ptr<const UniverseObject> copied_object,Visibility vis,const std::set<std::string> & visible_specials)45 void UniverseObject::Copy(std::shared_ptr<const UniverseObject> copied_object,
46                           Visibility vis, const std::set<std::string>& visible_specials)
47 {
48     if (copied_object.get() == this)
49         return;
50     if (!copied_object) {
51         ErrorLogger() << "UniverseObject::Copy passed a null object";
52         return;
53     }
54 
55     auto censored_meters = copied_object->CensoredMeters(vis);
56     for (const auto& entry : copied_object->m_meters) {
57         MeterType type = entry.first;
58 
59         // get existing meter in this object, or create a default one
60         auto m_meter_it = m_meters.find(type);
61         bool meter_already_known = (m_meter_it != m_meters.end());
62         if (!meter_already_known)
63             m_meters[type]; // default initialize to (0, 0).  Alternative: = Meter(Meter::INVALID_VALUE, Meter::INVALID_VALUE);*/
64         Meter& this_meter = m_meters[type];
65 
66         // if there is an update to meter from censored meters, update this object's copy
67         auto censored_it = censored_meters.find(type);
68         if (censored_it != censored_meters.end()) {
69             const Meter& copied_object_meter = censored_it->second;
70 
71             if (!meter_already_known) {
72                 // have no previous info, so just use whatever is given
73                 this_meter = copied_object_meter;
74 
75             } else {
76                 // don't want to override legit meter history with sentinel values used for insufficiently visible objects
77                 if (copied_object_meter.Initial() != Meter::LARGE_VALUE ||
78                     copied_object_meter.Current() != Meter::LARGE_VALUE)
79                 {
80                     // some new info available, so can overwrite only meter info
81                     this_meter = copied_object_meter;
82                 }
83             }
84         }
85     }
86 
87     if (vis >= VIS_BASIC_VISIBILITY) {
88         this->m_id =                    copied_object->m_id;
89         this->m_system_id =             copied_object->m_system_id;
90         this->m_x =                     copied_object->m_x;
91         this->m_y =                     copied_object->m_y;
92 
93         this->m_specials.clear();
94         for (const auto& entry_special : copied_object->m_specials) {
95             if (visible_specials.count(entry_special.first))
96                 this->m_specials[entry_special.first] = entry_special.second;
97         }
98 
99         if (vis >= VIS_PARTIAL_VISIBILITY) {
100             this->m_owner_empire_id =   copied_object->m_owner_empire_id;
101             this->m_created_on_turn =   copied_object->m_created_on_turn;
102 
103             if (vis >= VIS_FULL_VISIBILITY) {
104                 this->m_name =          copied_object->m_name;
105             }
106         }
107     }
108 }
109 
Init()110 void UniverseObject::Init()
111 { AddMeter(METER_STEALTH); }
112 
ID() const113 int UniverseObject::ID() const
114 { return m_id; }
115 
Name() const116 const std::string& UniverseObject::Name() const
117 { return m_name; }
118 
X() const119 double UniverseObject::X() const
120 { return m_x; }
121 
Y() const122 double UniverseObject::Y() const
123 { return m_y; }
124 
CreationTurn() const125 int UniverseObject::CreationTurn() const
126 { return m_created_on_turn; }
127 
AgeInTurns() const128 int UniverseObject::AgeInTurns() const {
129     if (m_created_on_turn == BEFORE_FIRST_TURN)
130         return SINCE_BEFORE_TIME_AGE;
131     if ((m_created_on_turn == INVALID_GAME_TURN) || (CurrentTurn() == INVALID_GAME_TURN))
132         return INVALID_OBJECT_AGE;
133     return CurrentTurn() - m_created_on_turn;
134 }
135 
Owner() const136 int UniverseObject::Owner() const
137 { return m_owner_empire_id; }
138 
SystemID() const139 int UniverseObject::SystemID() const
140 { return m_system_id; }
141 
Specials() const142 const std::map<std::string, std::pair<int, float>>& UniverseObject::Specials() const
143 { return m_specials; }
144 
HasSpecial(const std::string & name) const145 bool UniverseObject::HasSpecial(const std::string& name) const
146 { return m_specials.count(name); }
147 
SpecialAddedOnTurn(const std::string & name) const148 int UniverseObject::SpecialAddedOnTurn(const std::string& name) const {
149     auto it = m_specials.find(name);
150     if (it == m_specials.end())
151         return INVALID_GAME_TURN;
152     return it->second.first;
153 }
154 
SpecialCapacity(const std::string & name) const155 float UniverseObject::SpecialCapacity(const std::string& name) const {
156     auto it = m_specials.find(name);
157     if (it == m_specials.end())
158         return 0.0f;
159     return it->second.second;
160 }
161 
Tags() const162 std::set<std::string> UniverseObject::Tags() const
163 { return std::set<std::string>(); }
164 
HasTag(const std::string & name) const165 bool UniverseObject::HasTag(const std::string& name) const
166 { return false; }
167 
ObjectType() const168 UniverseObjectType UniverseObject::ObjectType() const
169 { return INVALID_UNIVERSE_OBJECT_TYPE; }
170 
Dump(unsigned short ntabs) const171 std::string UniverseObject::Dump(unsigned short ntabs) const {
172     auto system = Objects().get<System>(this->SystemID());
173 
174     std::stringstream os;
175 
176     os << this->ObjectType() << " "
177        << this->ID() << ": "
178        << this->Name();
179     if (system) {
180         const std::string& sys_name = system->Name();
181         if (sys_name.empty())
182             os << "  at: (System " << system->ID() << ")";
183         else
184             os << "  at: " << sys_name;
185     } else {
186         os << "  at: (" << this->X() << ", " << this->Y() << ")";
187         int near_id = GetPathfinder()->NearestSystemTo(this->X(), this->Y());
188         auto near_system = Objects().get<System>(near_id);
189         if (near_system) {
190             const std::string& sys_name = near_system->Name();
191             if (sys_name.empty())
192                 os << " nearest (System " << near_system->ID() << ")";
193             else
194                 os << " nearest " << near_system->Name();
195         }
196     }
197     if (Unowned()) {
198         os << " owner: (Unowned) ";
199     } else {
200         const std::string& empire_name = Empires().GetEmpireName(m_owner_empire_id);
201         if (!empire_name.empty())
202             os << " owner: " << empire_name;
203         else
204             os << " owner: (Unknown Empire)";
205     }
206     os << " created on turn: " << m_created_on_turn
207        << " specials: ";
208     for (const auto& entry : m_specials)
209         os << "(" << entry.first << ", " << entry.second.first << ", " << entry.second.second << ") ";
210     os << "  Meters: ";
211     for (const auto& entry : m_meters)
212         os << entry.first
213            << ": " << entry.second.Dump() << "  ";
214     return os.str();
215 }
216 
217 namespace {
218     std::set<int> EMPTY_SET;
219 }
220 
ContainedObjectIDs() const221 const std::set<int>& UniverseObject::ContainedObjectIDs() const
222 { return EMPTY_SET; }
223 
VisibleContainedObjectIDs(int empire_id) const224 std::set<int> UniverseObject::VisibleContainedObjectIDs(int empire_id) const {
225     std::set<int> retval;
226     const Universe& universe = GetUniverse();
227     for (int object_id : ContainedObjectIDs()) {
228         if (universe.GetObjectVisibilityByEmpire(object_id, empire_id) >= VIS_BASIC_VISIBILITY)
229             retval.insert(object_id);
230     }
231     return retval;
232 }
233 
ContainerObjectID() const234 int UniverseObject::ContainerObjectID() const
235 { return INVALID_OBJECT_ID; }
236 
Contains(int object_id) const237 bool UniverseObject::Contains(int object_id) const
238 { return false; }
239 
ContainedBy(int object_id) const240 bool UniverseObject::ContainedBy(int object_id) const
241 { return false; }
242 
GetMeter(MeterType type) const243 const Meter* UniverseObject::GetMeter(MeterType type) const {
244     auto it = m_meters.find(type);
245     if (it != m_meters.end())
246         return &(it->second);
247     return nullptr;
248 }
249 
AddMeter(MeterType meter_type)250 void UniverseObject::AddMeter(MeterType meter_type) {
251     if (INVALID_METER_TYPE == meter_type)
252         ErrorLogger() << "UniverseObject::AddMeter asked to add invalid meter type!";
253     else
254         m_meters[meter_type];
255 }
256 
Unowned() const257 bool UniverseObject::Unowned() const
258 { return Owner() == ALL_EMPIRES; }
259 
OwnedBy(int empire) const260 bool UniverseObject::OwnedBy(int empire) const
261 { return empire != ALL_EMPIRES && empire == Owner(); }
262 
HostileToEmpire(int empire_id) const263 bool UniverseObject::HostileToEmpire(int empire_id) const
264 { return false; }
265 
GetVisibility(int empire_id) const266 Visibility UniverseObject::GetVisibility(int empire_id) const
267 { return GetUniverse().GetObjectVisibilityByEmpire(this->ID(), empire_id); }
268 
PublicName(int empire_id) const269 const std::string& UniverseObject::PublicName(int empire_id) const
270 { return m_name; }
271 
Accept(const UniverseObjectVisitor & visitor) const272 std::shared_ptr<UniverseObject> UniverseObject::Accept(const UniverseObjectVisitor& visitor) const
273 { return visitor.Visit(std::const_pointer_cast<UniverseObject>(shared_from_this())); }
274 
SetID(int id)275 void UniverseObject::SetID(int id) {
276     m_id = id;
277     StateChangedSignal();
278 }
279 
Rename(const std::string & name)280 void UniverseObject::Rename(const std::string& name) {
281     m_name = name;
282     StateChangedSignal();
283 }
284 
Move(double x,double y)285 void UniverseObject::Move(double x, double y)
286 { MoveTo(m_x + x, m_y + y); }
287 
MoveTo(int object_id)288 void UniverseObject::MoveTo(int object_id)
289 { MoveTo(Objects().get(object_id)); }
290 
MoveTo(std::shared_ptr<UniverseObject> object)291 void UniverseObject::MoveTo(std::shared_ptr<UniverseObject> object) {
292     if (!object) {
293         ErrorLogger() << "UniverseObject::MoveTo : attempted to move to a null object.";
294         return;
295     }
296     MoveTo(object->X(), object->Y());
297 }
298 
MoveTo(double x,double y)299 void UniverseObject::MoveTo(double x, double y) {
300     if (m_x == x && m_y == y)
301         return;
302 
303     m_x = x;
304     m_y = y;
305 
306     StateChangedSignal();
307 }
308 
GetMeter(MeterType type)309 Meter* UniverseObject::GetMeter(MeterType type) {
310     auto it = m_meters.find(type);
311     if (it != m_meters.end())
312         return &(it->second);
313     return nullptr;
314 }
315 
BackPropagateMeters()316 void UniverseObject::BackPropagateMeters() {
317     for (auto& m : m_meters)
318         m.second.BackPropagate();
319 }
320 
SetOwner(int id)321 void UniverseObject::SetOwner(int id) {
322     if (m_owner_empire_id != id) {
323         m_owner_empire_id = id;
324         StateChangedSignal();
325     }
326     /* TODO: if changing object ownership gives an the new owner an
327      * observer in, or ownership of a previoiusly unexplored system, then need
328      * to call empire->AddExploredSystem(system_id); */
329 }
330 
SetSystem(int sys)331 void UniverseObject::SetSystem(int sys) {
332     //DebugLogger() << "UniverseObject::SetSystem(int sys)";
333     if (sys != m_system_id) {
334         m_system_id = sys;
335         StateChangedSignal();
336     }
337 }
338 
AddSpecial(const std::string & name,float capacity)339 void UniverseObject::AddSpecial(const std::string& name, float capacity)
340 { m_specials[name] = std::make_pair(CurrentTurn(), capacity); }
341 
SetSpecialCapacity(const std::string & name,float capacity)342 void UniverseObject::SetSpecialCapacity(const std::string& name, float capacity) {
343     if (m_specials.count(name))
344         m_specials[name].second = capacity;
345     else
346         AddSpecial(name, capacity);
347 }
348 
RemoveSpecial(const std::string & name)349 void UniverseObject::RemoveSpecial(const std::string& name)
350 { m_specials.erase(name); }
351 
CensoredMeters(Visibility vis) const352 UniverseObject::MeterMap UniverseObject::CensoredMeters(Visibility vis) const {
353     MeterMap retval;
354     if (vis >= VIS_PARTIAL_VISIBILITY) {
355         retval = m_meters;
356     } else if (vis == VIS_BASIC_VISIBILITY && m_meters.count(METER_STEALTH))
357         retval.emplace(std::make_pair(METER_STEALTH, Meter{Meter::LARGE_VALUE, Meter::LARGE_VALUE}));
358     return retval;
359 }
360 
ResetTargetMaxUnpairedMeters()361 void UniverseObject::ResetTargetMaxUnpairedMeters() {
362     auto it = m_meters.find(METER_STEALTH);
363     if (it != m_meters.end())
364         it->second.ResetCurrent();
365 }
366 
ResetPairedActiveMeters()367 void UniverseObject::ResetPairedActiveMeters() {
368     // iterate over paired active meters (those that have an associated max or
369     // target meter.  if another paired meter type is added to Enums.h, it
370     // should be added here as well.
371     for (auto& m : m_meters) {
372         if (m.first > METER_TROOPS)
373             break;
374         if (m.first >= METER_POPULATION)
375             m.second.SetCurrent(m.second.Initial());
376     }
377 }
378 
ClampMeters()379 void UniverseObject::ClampMeters() {
380     auto it = m_meters.find(METER_STEALTH);
381     if (it != m_meters.end())
382         it->second.ClampCurrentToRange();
383 }
384