1 #include "SystemResourceSummaryBrowseWnd.h"
2
3 #include "../util/i18n.h"
4 #include "../util/Logger.h"
5 #include "../universe/ResourceCenter.h"
6 #include "../universe/System.h"
7 #include "../universe/Enums.h"
8 #include "../Empire/Empire.h"
9 #include "../client/human/HumanClientApp.h"
10 #include "CUIControls.h"
11
12 namespace {
13 /** Returns how much of specified \a resource_type is being consumed by the
14 * empire with id \a empire_id at the location of the specified
15 * object \a obj. */
ObjectResourceConsumption(std::shared_ptr<const UniverseObject> obj,ResourceType resource_type,int empire_id=ALL_EMPIRES)16 double ObjectResourceConsumption(std::shared_ptr<const UniverseObject> obj,
17 ResourceType resource_type,
18 int empire_id = ALL_EMPIRES)
19 {
20 if (!obj) {
21 ErrorLogger() << "ObjectResourceConsumption passed a null object";
22 return 0.0;
23 }
24 if (resource_type == INVALID_RESOURCE_TYPE) {
25 ErrorLogger() << "ObjectResourceConsumption passed a INVALID_RESOURCE_TYPE";
26 return 0.0;
27 }
28
29
30 const Empire* empire = nullptr;
31
32 if (empire_id != ALL_EMPIRES) {
33 empire = GetEmpire(empire_id);
34
35 if (!empire) {
36 ErrorLogger() << "ObjectResourceConsumption requested consumption for empire " << empire_id << " but this empire was not found";
37 return 0.0; // requested a specific empire, but didn't find it in this client, so production is 0.0
38 }
39
40 if (!obj->OwnedBy(empire_id)) {
41 DebugLogger() << "ObjectResourceConsumption requested consumption for empire " << empire_id << " but this empire doesn't own the object";
42 return 0.0; // if the empire doesn't own the object, assuming it can't be consuming any of the empire's resources. May need to revisit this assumption later.
43 }
44 }
45
46
47 //std::shared_ptr<const PopCenter> pc;
48 double prod_queue_allocation_sum = 0.0;
49 std::shared_ptr<const Building> building;
50
51 switch (resource_type) {
52 case RE_INDUSTRY:
53 // PP (equal to mineral and industry) cost of objects on production queue at this object's location
54 if (empire) {
55 // add allocated PP for all production items at this location for this empire
56 for (const auto& elem : empire->GetProductionQueue())
57 if (elem.location == obj->ID())
58 prod_queue_allocation_sum += elem.allocated_pp;
59
60 } else {
61 // add allocated PP for all production items at this location for all empires
62 for (auto& entry : Empires()) {
63 empire = entry.second;
64 for (const ProductionQueue::Element& elem : empire->GetProductionQueue())
65 if (elem.location == obj->ID())
66 prod_queue_allocation_sum += elem.allocated_pp;
67 }
68 }
69 return prod_queue_allocation_sum;
70 break;
71
72 case RE_TRADE:
73 case RE_RESEARCH:
74 case RE_STOCKPILE:
75 // research/stockpile aren't consumed at a particular location, so none is consumed at any location
76 default:
77 // for INVALID_RESOURCE_TYPE just return 0.0. Could throw an exception, I suppose...
78 break;
79 }
80 return 0.0;
81 }
82
83 const int EDGE_PAD(3);
LabelWidth()84 GG::X LabelWidth()
85 { return GG::X(ClientUI::Pts()*18); }
86
ValueWidth()87 GG::X ValueWidth()
88 { return GG::X(ClientUI::Pts()*4); }
89 }
90
SystemResourceSummaryBrowseWnd(ResourceType resource_type,int system_id,int empire_id)91 SystemResourceSummaryBrowseWnd::SystemResourceSummaryBrowseWnd(ResourceType resource_type,
92 int system_id, int empire_id) :
93 GG::BrowseInfoWnd(GG::X0, GG::Y0, LabelWidth() + ValueWidth(), GG::Y1),
94 m_resource_type(resource_type),
95 m_system_id(system_id),
96 m_empire_id(empire_id),
97 row_height(1),
98 production_label_top(0),
99 allocation_label_top(0),
100 import_export_label_top(0)
101 {}
102
WndHasBrowseInfo(const GG::Wnd * wnd,std::size_t mode) const103 bool SystemResourceSummaryBrowseWnd::WndHasBrowseInfo(const GG::Wnd* wnd, std::size_t mode) const {
104 assert(mode <= wnd->BrowseModes().size());
105 return true;
106 }
107
Render()108 void SystemResourceSummaryBrowseWnd::Render() {
109 GG::Pt ul = UpperLeft();
110 GG::Pt lr = LowerRight();
111 GG::FlatRectangle(ul, lr, OpaqueColor(ClientUI::WndColor()), ClientUI::WndOuterBorderColor(), 1); // main background
112 GG::FlatRectangle(GG::Pt(ul.x, ul.y + production_label_top), GG::Pt(lr.x, ul.y + production_label_top + row_height),
113 ClientUI::WndOuterBorderColor(), ClientUI::WndOuterBorderColor(), 0); // production label background
114 GG::FlatRectangle(GG::Pt(ul.x, ul.y + allocation_label_top), GG::Pt(lr.x, ul.y + allocation_label_top + row_height),
115 ClientUI::WndOuterBorderColor(), ClientUI::WndOuterBorderColor(), 0); // allocation label background
116 GG::FlatRectangle(GG::Pt(ul.x, ul.y + import_export_label_top), GG::Pt(lr.x, ul.y + import_export_label_top + row_height),
117 ClientUI::WndOuterBorderColor(), ClientUI::WndOuterBorderColor(), 0); // import or export label background
118 }
119
UpdateImpl(std::size_t mode,const GG::Wnd * target)120 void SystemResourceSummaryBrowseWnd::UpdateImpl(std::size_t mode, const GG::Wnd* target) {
121 // fully recreate browse wnd for each viewing. finding all the queues, resourcepools and (maybe?) individual
122 // UniverseObject that would have ChangedSignals that would need to be connected to the object that creates
123 // this BrowseWnd seems like more trouble than it's worth to avoid recreating the BrowseWnd every time it's shown
124 // (the alternative is to only reinitialize when something changes that would affect what's displayed in the
125 // BrowseWnd, which is how MeterBrowseWnd works)
126 Clear();
127 Initialize();
128 }
129
Initialize()130 void SystemResourceSummaryBrowseWnd::Initialize() {
131 row_height = GG::Y(ClientUI::Pts() * 3/2);
132 const GG::X TOTAL_WIDTH = LabelWidth() + ValueWidth();
133
134 GG::Y top = GG::Y0;
135
136
137 production_label_top = top;
138 m_production_label = GG::Wnd::Create<CUILabel>("", GG::FORMAT_RIGHT);
139 m_production_label->MoveTo(GG::Pt(GG::X0, production_label_top));
140 m_production_label->Resize(GG::Pt(TOTAL_WIDTH - EDGE_PAD, row_height));
141 m_production_label->SetFont(ClientUI::GetBoldFont());
142 AttachChild(m_production_label);
143 top += row_height;
144 UpdateProduction(top);
145
146
147 allocation_label_top = top;
148 m_allocation_label = GG::Wnd::Create<CUILabel>("", GG::FORMAT_RIGHT);
149 m_allocation_label->MoveTo(GG::Pt(GG::X0, allocation_label_top));
150 m_allocation_label->Resize(GG::Pt(TOTAL_WIDTH - EDGE_PAD, row_height));
151 m_allocation_label->SetFont(ClientUI::GetBoldFont());
152 AttachChild(m_allocation_label);
153 top += row_height;
154 UpdateAllocation(top);
155
156
157 import_export_label_top = top;
158 m_import_export_label = GG::Wnd::Create<CUILabel>("", GG::FORMAT_RIGHT);
159 m_import_export_label->MoveTo(GG::Pt(GG::X0, import_export_label_top));
160 m_import_export_label->Resize(GG::Pt(TOTAL_WIDTH - EDGE_PAD, row_height));
161 m_import_export_label->SetFont(ClientUI::GetBoldFont());
162 AttachChild(m_import_export_label);
163 top += row_height;
164 UpdateImportExport(top);
165
166
167 Resize(GG::Pt(LabelWidth() + ValueWidth(), top));
168 }
169
UpdateProduction(GG::Y & top)170 void SystemResourceSummaryBrowseWnd::UpdateProduction(GG::Y& top) {
171 // adds pairs of labels for ResourceCenter name and production of resource starting at vertical position \a top
172 // and updates \a top to the vertical position after the last entry
173 for (const auto& label_pair : m_production_labels_and_amounts) {
174 DetachChild(label_pair.first);
175 DetachChild(label_pair.second);
176 }
177 m_production_labels_and_amounts.clear();
178
179 auto system = Objects().get<System>(m_system_id);
180 if (!system || m_resource_type == INVALID_RESOURCE_TYPE)
181 return;
182
183
184 m_production = 0.0;
185
186
187 // add label-value pair for each resource-producing object in system to indicate amount of resource produced
188 auto objects = Objects().find<const UniverseObject>(system->ContainedObjectIDs());
189
190 for (auto& obj : objects) {
191 // display information only for the requested player
192 if (m_empire_id != ALL_EMPIRES && !obj->OwnedBy(m_empire_id))
193 continue; // if m_empire_id == -1, display resource production for all empires. otherwise, skip this resource production if it's not owned by the requested player
194
195 auto rc = std::dynamic_pointer_cast<const ResourceCenter>(obj);
196 if (!rc) continue;
197
198 std::string name = obj->Name();
199 double production = rc->GetMeter(ResourceToMeter(m_resource_type))->Initial();
200 m_production += production;
201
202 std::string amount_text = DoubleToString(production, 3, false);
203
204
205 auto label = GG::Wnd::Create<CUILabel>(name, GG::FORMAT_RIGHT);
206 label->MoveTo(GG::Pt(GG::X0, top));
207 label->Resize(GG::Pt(LabelWidth(), row_height));
208 AttachChild(label);
209
210 auto value = GG::Wnd::Create<CUILabel>(amount_text);
211 value->MoveTo(GG::Pt(LabelWidth(), top));
212 value->Resize(GG::Pt(ValueWidth(), row_height));
213 AttachChild(value);
214
215 m_production_labels_and_amounts.push_back({label, value});
216
217 top += row_height;
218 }
219
220
221 if (m_production_labels_and_amounts.empty()) {
222 // add "blank" line to indicate no production
223 auto label = GG::Wnd::Create<CUILabel>(UserString("NOT_APPLICABLE"));
224 label->MoveTo(GG::Pt(GG::X0, top));
225 label->Resize(GG::Pt(LabelWidth(), row_height));
226 AttachChild(label);
227
228 auto value = GG::Wnd::Create<CUILabel>("");
229 value->MoveTo(GG::Pt(LabelWidth(), top));
230 value->Resize(GG::Pt(ValueWidth(), row_height));
231 AttachChild(value);
232
233 m_production_labels_and_amounts.push_back({label, value});
234
235 top += row_height;
236 }
237
238
239 // set production label
240 std::string resource_text = "";
241 switch (m_resource_type) {
242 case RE_INDUSTRY:
243 resource_text = UserString("INDUSTRY_PRODUCTION"); break;
244 case RE_RESEARCH:
245 resource_text = UserString("RESEARCH_PRODUCTION"); break;
246 case RE_TRADE:
247 resource_text = UserString("TRADE_PRODUCTION"); break;
248 case RE_STOCKPILE:
249 resource_text = UserString("STOCKPILE_GENERATION"); break;
250 default:
251 resource_text = UserString("UNKNOWN_VALUE_SYMBOL"); break;
252 }
253
254 m_production_label->SetText(boost::io::str(FlexibleFormat(UserString("RESOURCE_PRODUCTION_TOOLTIP")) %
255 resource_text %
256 DoubleToString(m_production, 3, false)));
257
258 // height of label already added to top outside this function
259 }
260
UpdateAllocation(GG::Y & top)261 void SystemResourceSummaryBrowseWnd::UpdateAllocation(GG::Y& top) {
262 // adds pairs of labels for allocation of resources in system, starting at vertical position \a top and
263 // updates \a top to be the vertical position after the last entry
264 for (const auto& label_pair : m_allocation_labels_and_amounts) {
265 DetachChild(label_pair.first);
266 DetachChild(label_pair.second);
267 }
268 m_allocation_labels_and_amounts.clear();
269
270 auto system = Objects().get<System>(m_system_id);
271 if (!system || m_resource_type == INVALID_RESOURCE_TYPE)
272 return;
273
274
275 m_allocation = 0.0;
276
277
278 // add label-value pair for each resource-consuming object in system to indicate amount of resource consumed
279 for (auto& obj : Objects().find<const UniverseObject>(system->ContainedObjectIDs())) {
280 // display information only for the requested player
281 if (m_empire_id != ALL_EMPIRES && !obj->OwnedBy(m_empire_id))
282 continue; // if m_empire_id == ALL_EMPIRES, display resource production for all empires. otherwise, skip this resource production if it's not owned by the requested player
283
284
285 std::string name = obj->Name();
286
287
288 double allocation = ObjectResourceConsumption(obj, m_resource_type, m_empire_id);
289
290
291 // don't add summary entries for objects that consume no resource. (otherwise there would be a loooong pointless list of 0's
292 if (allocation <= 0.0) {
293 if (allocation < 0.0)
294 ErrorLogger() << "object " << obj->Name() << " is reported having negative " << m_resource_type << " consumption";
295 continue;
296 }
297
298
299 m_allocation += allocation;
300
301 std::string amount_text = DoubleToString(allocation, 3, false);
302
303
304 auto label = GG::Wnd::Create<CUILabel>(name, GG::FORMAT_RIGHT);
305 label->MoveTo(GG::Pt(GG::X0, top));
306 label->Resize(GG::Pt(LabelWidth(), row_height));
307 AttachChild(label);
308
309
310 auto value = GG::Wnd::Create<CUILabel>(amount_text);
311 value->MoveTo(GG::Pt(LabelWidth(), top));
312 value->Resize(GG::Pt(ValueWidth(), row_height));
313 AttachChild(value);
314
315 m_allocation_labels_and_amounts.push_back({label, value});
316
317 top += row_height;
318 }
319
320
321 if (m_allocation_labels_and_amounts.empty()) {
322 // add "blank" line to indicate no allocation
323 auto label = GG::Wnd::Create<CUILabel>(UserString("NOT_APPLICABLE"), GG::FORMAT_RIGHT);
324 label->MoveTo(GG::Pt(GG::X0, top));
325 label->Resize(GG::Pt(LabelWidth(), row_height));
326 AttachChild(label);
327
328 auto value = GG::Wnd::Create<CUILabel>("");
329 value->MoveTo(GG::Pt(LabelWidth(), top));
330 value->Resize(GG::Pt(ValueWidth(), row_height));
331 AttachChild(value);
332
333 m_allocation_labels_and_amounts.push_back({label, value});
334
335 top += row_height;
336 }
337
338
339 // set consumption / allocation label
340 std::string resource_text = "";
341 switch (m_resource_type) {
342 case RE_INDUSTRY:
343 resource_text = UserString("INDUSTRY_CONSUMPTION"); break;
344 case RE_RESEARCH:
345 resource_text = UserString("RESEARCH_CONSUMPTION"); break;
346 case RE_TRADE:
347 resource_text = UserString("TRADE_CONSUMPTION"); break;
348 case RE_STOCKPILE:
349 resource_text = UserString("STOCKPILE_USE"); break;
350 default:
351 resource_text = UserString("UNKNOWN_VALUE_SYMBOL"); break;
352 }
353
354 std::string system_allocation_text = DoubleToString(m_allocation, 3, false);
355
356 // for research and stockpiling, local allocation makes no sense
357 if (m_resource_type == RE_RESEARCH && m_allocation == 0.0)
358 system_allocation_text = UserString("NOT_APPLICABLE");
359 if (m_resource_type == RE_STOCKPILE && m_allocation == 0.0)
360 system_allocation_text = UserString("NOT_APPLICABLE");
361
362
363 m_allocation_label->SetText(boost::io::str(FlexibleFormat(UserString("RESOURCE_ALLOCATION_TOOLTIP")) %
364 resource_text %
365 system_allocation_text));
366
367 // height of label already added to top outside this function
368 }
369
UpdateImportExport(GG::Y & top)370 void SystemResourceSummaryBrowseWnd::UpdateImportExport(GG::Y& top) {
371 m_import_export_label->SetText(UserString("IMPORT_EXPORT_TOOLTIP"));
372
373 const Empire* empire = nullptr;
374
375 // check for early exit cases...
376 bool abort = false;
377 if (m_empire_id == ALL_EMPIRES ||
378 m_resource_type == RE_RESEARCH ||
379 m_resource_type == RE_STOCKPILE)
380 {
381 // multiple empires have complicated stockpiling which don't make sense to try to display.
382 // Research use is nonlocalized, so importing / exporting doesn't make sense to display
383 abort = true;
384 } else {
385 empire = GetEmpire(m_empire_id);
386 if (!empire)
387 abort = true;
388 }
389
390
391 std::string label_text = "", amount_text = "";
392
393
394 if (!abort) {
395 double difference = m_production - m_allocation;
396
397 switch (m_resource_type) {
398 case RE_TRADE:
399 case RE_INDUSTRY:
400 if (difference > 0.0) {
401 // show surplus
402 label_text = UserString("RESOURCE_EXPORT");
403 amount_text = DoubleToString(difference, 3, false);
404 } else if (difference < 0.0) {
405 // show amount being imported
406 label_text = UserString("RESOURCE_IMPORT");
407 amount_text = DoubleToString(std::abs(difference), 3, false);
408 } else {
409 // show self-sufficiency
410 label_text = UserString("RESOURCE_SELF_SUFFICIENT");
411 amount_text = "";
412 }
413 break;
414 case RE_RESEARCH:
415 case RE_STOCKPILE:
416 default:
417 // show nothing
418 abort = true;
419 break;
420 }
421 }
422
423
424 if (abort) {
425 label_text = UserString("NOT_APPLICABLE");
426 amount_text = ""; // no change
427 }
428
429
430 // add label and amount. may be "NOT APPLIABLE" and nothing if aborted above
431 auto label = GG::Wnd::Create<CUILabel>(label_text, GG::FORMAT_RIGHT);
432 label->MoveTo(GG::Pt(GG::X0, top));
433 label->Resize(GG::Pt(LabelWidth(), row_height));
434 AttachChild(label);
435
436 auto value = GG::Wnd::Create<CUILabel>(amount_text);
437 value->MoveTo(GG::Pt(LabelWidth(), top));
438 value->Resize(GG::Pt(ValueWidth(), row_height));
439 AttachChild(value);
440
441 m_import_export_labels_and_amounts.push_back({label, value});
442
443 top += row_height;
444 }
445
Clear()446 void SystemResourceSummaryBrowseWnd::Clear() {
447 DetachChildAndReset(m_production_label);
448 DetachChildAndReset(m_allocation_label);
449 DetachChildAndReset(m_import_export_label);
450
451 for (const auto& label_pair : m_production_labels_and_amounts) {
452 DetachChild(label_pair.first);
453 DetachChild(label_pair.second);
454 }
455 m_production_labels_and_amounts.clear();
456
457 for (const auto& label_pair : m_allocation_labels_and_amounts) {
458 DetachChild(label_pair.first);
459 DetachChild(label_pair.second);
460 }
461 m_allocation_labels_and_amounts.clear();
462
463 for (const auto& label_pair : m_import_export_labels_and_amounts) {
464 DetachChild(label_pair.first);
465 DetachChild(label_pair.second);
466 }
467 m_import_export_labels_and_amounts.clear();
468 }
469