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