Merge lp:~widelands-dev/widelands/smaller_building_statistics into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 8656
Proposed branch: lp:~widelands-dev/widelands/smaller_building_statistics
Merge into: lp:widelands
Diff against target: 596 lines (+247/-158)
7 files modified
data/tribes/buildings/warehouses/atlanteans/port/init.lua (+1/-0)
data/tribes/buildings/warehouses/barbarians/port/init.lua (+1/-0)
data/tribes/buildings/warehouses/empire/port/init.lua (+1/-0)
data/tribes/buildings/warehouses/frisians/port/init.lua (+1/-0)
src/ui_basic/tabpanel.cc (+3/-0)
src/wui/building_statistics_menu.cc (+216/-154)
src/wui/building_statistics_menu.h (+24/-4)
To merge this branch: bzr merge lp:~widelands-dev/widelands/smaller_building_statistics
Reviewer Review Type Date Requested Status
Klaus Halfmann compile, revied, debug for coverage, testplay Approve
GunChleoc Needs Resubmitting
Review via email: mp+342828@code.launchpad.net

Commit message

Building Statistics now only show relevant buildings
- Military sites not belonging to the tribe are omitted, unless the payer currently owns one
- Seafaring and allowed buildings are dynamically checked
- Show "under construction" navigation for buildings being enhanced
- Added "needs_seafaring" to port buildings

Description of the change

Fixes the attached bug.

Since the Frisians were added, the building statistics got too big due to all the military sites - the navigation became impossible to reach on 800x600 resolution without moving the window around. So, I have implemented a dynamic approach that will only show allowed and currently owned buildings.

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 3357. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/363552376.
Appveyor build 3163. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_smaller_building_statistics-3163.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

One question inline, will take a closer look later.

just brached this and will have a look.

Not sure who will be the payer for the other tribes military sites ;-) ?

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Some more commetns inline, I think this deserves some time in the debugger.
Mostly for me to better understand the widelands internal structures.

Maybe this will make things slower, Not sure about this, lets seee.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks for the review :)

And you were right about the efficiency concerns, the seafaring check is causing a slowdown. I have reduced it to be checked once every 2 minutes.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have changed my mind about the seafaring check - since this is so expensive, we should generally only recalculate it on map changes. I'll create another branch for it, to be merged before this branch.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Mhh, scripts might want to add seafaring later in some scenario.
Hopefully this will not break such scripts.

Anyway, I do not have that much time today for a debugging session,
lets see what I can do perhaps next weekend.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I know - I have taken care of it. Branch is ready; I'll put it up for review as soon as Launchpad has finished parsing it.

https://code.launchpad.net/~widelands-dev/widelands/allows_seafaring_performance

review: Needs Resubmitting
Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

fetched it again, no idea if I have time at the weekend.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

I did some testplaying with a debugger now and gained some coverage of that code:
 * Everything worked as expected so far.
 * The Handling of the forward / backward buttons has room for improvement:
  * no need so use large switch blocks -> call the repective function directly.
  * Maybe I can do some refactoring there, later.
 * I could not trigger foreign_tribe_building_is_valid, yet, no Enemy t conquer found so far :-)
 * I need a another map _with_ seafaring, my current one does not allow this ....
 * I was unable to trigger BuildingStatisticsMenu::reset()
   no idea why this was never called. Gun: any idea?

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

I testplayed this now on a seafaring Map:
 * I was unable to reach the following lies of code:
  * BuildingStatisticsMenu::foreign_tribe_building_is_valid()
    ...
    if (descr.type() == MapObjectType::CONSTRUCTIONSITE ||
 descr.type() == MapObjectType::DISMANTLESITE) {
 return false;
    // I think this can never be reached I can not build a forign building
    // A dismantled site cannot be reached
  * BuildingStatisticsMenu::reset()
     ...
     if (building_buttons_[current_building_type_] != nullptr) {
 set_current_building_type(current_building_type_); // no idea what this shall be
     } else {
 * I found that some missing soldiers Arrows where incorrect for a foreign military building.

 * I played on a lot of different resolutions

Despite these Issues this can go in:

@bunnybot merge

review: Approve (compile, revied, debug for coverage, testplay)
Revision history for this message
GunChleoc (gunchleoc) wrote :

You trigger those by conquering a foreign military site and then again by destroying it. I ran a setup on Golden Peninsula where I gave the opponent the "Village" starting condition. so that I could easily conquer something.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/tribes/buildings/warehouses/atlanteans/port/init.lua'
2--- data/tribes/buildings/warehouses/atlanteans/port/init.lua 2017-09-02 19:53:03 +0000
3+++ data/tribes/buildings/warehouses/atlanteans/port/init.lua 2018-04-12 05:33:14 +0000
4@@ -8,6 +8,7 @@
5 helptext_script = dirname .. "helptexts.lua",
6 icon = dirname .. "menu.png",
7 size = "port",
8+ needs_seafaring = true,
9
10 buildcost = {
11 log = 3,
12
13=== modified file 'data/tribes/buildings/warehouses/barbarians/port/init.lua'
14--- data/tribes/buildings/warehouses/barbarians/port/init.lua 2017-09-02 19:53:03 +0000
15+++ data/tribes/buildings/warehouses/barbarians/port/init.lua 2018-04-12 05:33:14 +0000
16@@ -8,6 +8,7 @@
17 helptext_script = dirname .. "helptexts.lua",
18 icon = dirname .. "menu.png",
19 size = "port",
20+ needs_seafaring = true,
21
22 buildcost = {
23 log = 3,
24
25=== modified file 'data/tribes/buildings/warehouses/empire/port/init.lua'
26--- data/tribes/buildings/warehouses/empire/port/init.lua 2017-09-02 19:53:03 +0000
27+++ data/tribes/buildings/warehouses/empire/port/init.lua 2018-04-12 05:33:14 +0000
28@@ -8,6 +8,7 @@
29 helptext_script = dirname .. "helptexts.lua",
30 icon = dirname .. "menu.png",
31 size = "port",
32+ needs_seafaring = true,
33
34 buildcost = {
35 log = 3,
36
37=== modified file 'data/tribes/buildings/warehouses/frisians/port/init.lua'
38--- data/tribes/buildings/warehouses/frisians/port/init.lua 2018-02-16 14:53:04 +0000
39+++ data/tribes/buildings/warehouses/frisians/port/init.lua 2018-04-12 05:33:14 +0000
40@@ -8,6 +8,7 @@
41 helptext_script = dirname .. "helptexts.lua",
42 icon = dirname .. "menu.png",
43 size = "port",
44+ needs_seafaring = true,
45
46 buildcost = {
47 brick = 6,
48
49=== modified file 'src/ui_basic/tabpanel.cc'
50--- src/ui_basic/tabpanel.cc 2018-04-07 16:59:00 +0000
51+++ src/ui_basic/tabpanel.cc 2018-04-12 05:33:14 +0000
52@@ -225,6 +225,9 @@
53 }
54
55 bool TabPanel::remove_last_tab(const std::string& tabname) {
56+ if (tabs_.empty()) {
57+ return false;
58+ }
59 if (tabs_.back()->get_name() == tabname) {
60 tabs_.pop_back();
61 if (active_ > tabs_.size() - 1) {
62
63=== modified file 'src/wui/building_statistics_menu.cc'
64--- src/wui/building_statistics_menu.cc 2018-04-07 16:59:00 +0000
65+++ src/wui/building_statistics_menu.cc 2018-04-12 05:33:14 +0000
66@@ -39,9 +39,7 @@
67 constexpr int kButtonRowHeight = kButtonHeight + kMargin;
68 constexpr int kLabelHeight = 18;
69 constexpr int kLabelFontSize = 12;
70-constexpr int kTabHeight = 35 + 5 * (kBuildGridCellHeight + kLabelHeight + kLabelHeight);
71 constexpr int32_t kWindowWidth = kColumns * kBuildGridCellWidth;
72-constexpr int32_t kWindowHeight = kTabHeight + kMargin + 4 * kButtonRowHeight;
73
74 constexpr int32_t kUpdateTimeInGametimeMs = 1000; // 1 second, gametime
75
76@@ -63,7 +61,7 @@
77 "building_statistics",
78 &registry,
79 kWindowWidth,
80- kWindowHeight,
81+ 100,
82 _("Building Statistics")),
83 tab_panel_(this, g_gr->images().get("images/ui_basic/but1.png")),
84 navigation_panel_(this, 0, 0, kWindowWidth, 4 * kButtonRowHeight),
85@@ -118,119 +116,12 @@
86 "",
87 UI::Align::kRight),
88 low_production_(33),
89- has_selection_(false) {
90-
91- for (int i = 0; i < kNoOfBuildingTabs; ++i) {
92- row_counters_[i] = 0;
93- tabs_[i] = new UI::Box(&tab_panel_, 0, 0, UI::Box::Vertical);
94- }
95-
96- tab_panel_.add("building_stats_small",
97- g_gr->images().get("images/wui/fieldaction/menu_tab_buildsmall.png"),
98- tabs_[BuildingTab::Small], _("Small buildings"));
99- tab_panel_.add("building_stats_medium",
100- g_gr->images().get("images/wui/fieldaction/menu_tab_buildmedium.png"),
101- tabs_[BuildingTab::Medium], _("Medium buildings"));
102- tab_panel_.add("building_stats_big",
103- g_gr->images().get("images/wui/fieldaction/menu_tab_buildbig.png"),
104- tabs_[BuildingTab::Big], _("Big buildings"));
105- tab_panel_.add("building_stats_mines",
106- g_gr->images().get("images/wui/fieldaction/menu_tab_buildmine.png"),
107- tabs_[BuildingTab::Mines], _("Mines"));
108-
109- // Only show the ports tab for seafaring maps
110- if (iplayer().game().map().allows_seafaring()) {
111- tab_panel_.add("building_stats_ports",
112- g_gr->images().get("images/wui/fieldaction/menu_tab_buildport.png"),
113- tabs_[BuildingTab::Ports], _("Ports"));
114- }
115-
116- const DescriptionIndex nr_buildings = parent.egbase().tribes().nrbuildings();
117- building_buttons_ = std::vector<UI::Button*>(nr_buildings);
118- owned_labels_ = std::vector<UI::Textarea*>(nr_buildings);
119- productivity_labels_ = std::vector<UI::Textarea*>(nr_buildings);
120-
121- // Column counters
122- int columns[kNoOfBuildingTabs] = {0, 0, 0, 0, 0};
123-
124- // Row containers
125- UI::Box* rows[kNoOfBuildingTabs];
126- for (int i = 0; i < kNoOfBuildingTabs; ++i) {
127- rows[i] = new UI::Box(tabs_[i], 0, 0, UI::Box::Horizontal);
128- }
129-
130- // We want to add player tribe's buildings in correct order
131- const TribeDescr& tribe = iplayer().player().tribe();
132- std::vector<DescriptionIndex> buildings_to_add;
133- for (DescriptionIndex index : tribe.buildings()) {
134- // Only add headquarter types that are owned by player.
135- const BuildingDescr& descr = *tribe.get_building_descr(index);
136- const Widelands::Player& player = iplayer().player();
137- if (descr.is_buildable() || descr.is_enhanced() ||
138- !player.get_building_statistics(index).empty()) {
139- buildings_to_add.push_back(index);
140- }
141- }
142-
143- // We want to add other tribes' militarysites on the bottom
144- for (DescriptionIndex index = 0; index < nr_buildings; ++index) {
145- const BuildingDescr& descr = *parent.egbase().tribes().get_building_descr(index);
146- if (descr.type() == MapObjectType::MILITARYSITE && !tribe.has_building(index)) {
147- buildings_to_add.push_back(index);
148- }
149- }
150-
151- for (DescriptionIndex id : buildings_to_add) {
152- const BuildingDescr& descr = *tribe.get_building_descr(id);
153-
154- if (descr.type() != MapObjectType::CONSTRUCTIONSITE &&
155- descr.type() != MapObjectType::DISMANTLESITE) {
156- if (descr.get_ismine()) {
157- if (add_button(id, descr, BuildingTab::Mines, *rows[BuildingTab::Mines],
158- &columns[BuildingTab::Mines])) {
159- rows[BuildingTab::Mines] =
160- new UI::Box(tabs_[BuildingTab::Mines], 0, 0, UI::Box::Horizontal);
161- }
162- } else if (descr.get_isport()) {
163- if (add_button(id, descr, BuildingTab::Ports, *rows[BuildingTab::Ports],
164- &columns[BuildingTab::Ports])) {
165- rows[BuildingTab::Ports] =
166- new UI::Box(tabs_[BuildingTab::Ports], 0, 0, UI::Box::Horizontal);
167- }
168- } else {
169- switch (descr.get_size()) {
170- case BaseImmovable::SMALL:
171- if (add_button(id, descr, BuildingTab::Small, *rows[BuildingTab::Small],
172- &columns[BuildingTab::Small])) {
173- rows[BuildingTab::Small] =
174- new UI::Box(tabs_[BuildingTab::Small], 0, 0, UI::Box::Horizontal);
175- }
176- break;
177- case BaseImmovable::MEDIUM:
178- if (add_button(id, descr, BuildingTab::Medium, *rows[BuildingTab::Medium],
179- &columns[BuildingTab::Medium])) {
180- rows[BuildingTab::Medium] =
181- new UI::Box(tabs_[BuildingTab::Medium], 0, 0, UI::Box::Horizontal);
182- }
183- break;
184- case BaseImmovable::BIG:
185- if (add_button(id, descr, BuildingTab::Big, *rows[BuildingTab::Big],
186- &columns[BuildingTab::Big])) {
187- rows[BuildingTab::Big] =
188- new UI::Box(tabs_[BuildingTab::Big], 0, 0, UI::Box::Horizontal);
189- }
190- break;
191- default:
192- throw wexception(
193- "Building statictics: Found building without a size: %s", descr.name().c_str());
194- }
195- }
196- }
197- }
198-
199- for (int i = 0; i < kNoOfBuildingTabs; ++i) {
200- tabs_[i]->add(rows[i]);
201- }
202+ has_selection_(false),
203+ nr_building_types_(parent.egbase().tribes().nrbuildings()) {
204+
205+ building_buttons_ = std::vector<UI::Button*>(nr_building_types_);
206+ owned_labels_ = std::vector<UI::Textarea*>(nr_building_types_);
207+ productivity_labels_ = std::vector<UI::Textarea*>(nr_building_types_);
208
209 set_label_font(&owned_label_);
210 set_label_font(&construction_label_);
211@@ -306,7 +197,7 @@
212 unproductive_percent_.cancel.connect(
213 boost::bind(&BuildingStatisticsMenu::low_production_reset_focus, boost::ref(*this)));
214
215- update();
216+ init();
217 }
218
219 BuildingStatisticsMenu::~BuildingStatisticsMenu() {
220@@ -315,6 +206,189 @@
221 productivity_labels_.clear();
222 }
223
224+void BuildingStatisticsMenu::reset() {
225+ update(); // In case a building got removed, make sure to deselect it first
226+
227+ const int last_selected_tab = tab_assignments_[tab_panel_.active()];
228+
229+ tab_panel_.remove_last_tab("building_stats_ports");
230+ tab_panel_.remove_last_tab("building_stats_mines");
231+ tab_panel_.remove_last_tab("building_stats_big");
232+ tab_panel_.remove_last_tab("building_stats_medium");
233+ tab_panel_.remove_last_tab("building_stats_small");
234+
235+ // Clean state if buildings disappear from list
236+ building_buttons_.clear();
237+ building_buttons_.resize(nr_building_types_);
238+ owned_labels_.clear();
239+ owned_labels_.resize(nr_building_types_);
240+ productivity_labels_.clear();
241+ productivity_labels_.resize(nr_building_types_);
242+
243+ // Ensure that defunct buttons disappear
244+ for (int tab_index = 0; tab_index < kNoOfBuildingTabs; ++tab_index) {
245+ if (tabs_[tab_index] != nullptr) {
246+ tabs_[tab_index]->die();
247+ }
248+ }
249+
250+ init(last_selected_tab);
251+
252+ // Reset navigator
253+ building_name_.set_text("");
254+ if (has_selection_) {
255+ if (building_buttons_[current_building_type_] != nullptr) {
256+ set_current_building_type(current_building_type_);
257+ } else {
258+ has_selection_ = false;
259+ }
260+ }
261+}
262+
263+void BuildingStatisticsMenu::init(int last_selected_tab) {
264+ // We want to add player tribe's buildings in correct order
265+ const Widelands::Player& player = iplayer().player();
266+ const TribeDescr& tribe = player.tribe();
267+ const bool map_allows_seafaring = iplayer().game().map().allows_seafaring();
268+ std::vector<DescriptionIndex> buildings_to_add[kNoOfBuildingTabs];
269+ // Add the player's own tribe's buildings.
270+ for (DescriptionIndex index : tribe.buildings()) {
271+ if (own_building_is_valid(player, index, map_allows_seafaring)) {
272+ buildings_to_add[find_tab_for_building(*tribe.get_building_descr(index))].push_back(index);
273+ }
274+ }
275+
276+ // We want to add other tribes' buildings on the bottom. Only add the ones that the player owns.
277+ for (DescriptionIndex index = 0; index < nr_building_types_; ++index) {
278+ if (foreign_tribe_building_is_valid(player, index)) {
279+ buildings_to_add[find_tab_for_building(*tribe.get_building_descr(index))].push_back(index);
280+ }
281+ }
282+
283+ // Now create the tab contents and add the building buttons
284+ int row_counters[kNoOfBuildingTabs];
285+ for (int tab_index = 0; tab_index < kNoOfBuildingTabs; ++tab_index) {
286+ int current_column = 0;
287+ tabs_[tab_index] = new UI::Box(&tab_panel_, 0, 0, UI::Box::Vertical);
288+ UI::Box* row = new UI::Box(tabs_[tab_index], 0, 0, UI::Box::Horizontal);
289+ row_counters[tab_index] = 0;
290+
291+ for (const Widelands::DescriptionIndex id : buildings_to_add[tab_index]) {
292+ const BuildingDescr& descr = *iplayer().egbase().tribes().get_building_descr(id);
293+ add_button(id, descr, row);
294+ ++current_column;
295+ if (current_column == 1) {
296+ ++row_counters[tab_index];
297+ } else if (current_column == kColumns) {
298+ tabs_[tab_index]->add(row, UI::Box::Resizing::kFullSize);
299+ tabs_[tab_index]->add_space(6);
300+ row = new UI::Box(tabs_[tab_index], 0, 0, UI::Box::Horizontal);
301+ current_column = 0;
302+ }
303+ }
304+ // Add final row
305+ if (current_column != 0) {
306+ tabs_[tab_index]->add(row, UI::Box::Resizing::kFullSize);
307+ }
308+ }
309+
310+ // Show the tabs that have buttons on them
311+ int tab_counter = 0;
312+ auto add_tab = [this, row_counters, &tab_counter, last_selected_tab](
313+ int tab_index, const std::string& name, const std::string& image, const std::string& descr) {
314+ if (row_counters[tab_index] > 0) {
315+ tab_panel_.add(name, g_gr->images().get(image), tabs_[tab_index], descr);
316+ if (last_selected_tab == tab_index) {
317+ tab_panel_.activate(tab_counter);
318+ }
319+ tab_assignments_[tab_counter] = tab_index;
320+ row_counters_[tab_counter] = row_counters[tab_index];
321+ ++tab_counter;
322+ }
323+ };
324+ add_tab(BuildingTab::Small, "building_stats_small",
325+ "images/wui/fieldaction/menu_tab_buildsmall.png", _("Small buildings"));
326+ add_tab(BuildingTab::Medium, "building_stats_medium",
327+ "images/wui/fieldaction/menu_tab_buildmedium.png", _("Medium buildings"));
328+ add_tab(BuildingTab::Big, "building_stats_big", "images/wui/fieldaction/menu_tab_buildbig.png",
329+ _("Big buildings"));
330+ add_tab(BuildingTab::Mines, "building_stats_mines",
331+ "images/wui/fieldaction/menu_tab_buildmine.png", _("Mines"));
332+ add_tab(BuildingTab::Ports, "building_stats_ports",
333+ "images/wui/fieldaction/menu_tab_buildport.png", _("Ports"));
334+
335+ update();
336+}
337+
338+bool BuildingStatisticsMenu::own_building_is_valid(const Widelands::Player& player, Widelands::DescriptionIndex index, bool map_allows_seafaring) const {
339+ const BuildingDescr& descr = *player.tribe().get_building_descr(index);
340+ // Skip seafaring buildings if not needed
341+ if (descr.needs_seafaring() && !map_allows_seafaring &&
342+ player.get_building_statistics(index).empty()) {
343+ return false;
344+ }
345+ if (descr.type() == MapObjectType::CONSTRUCTIONSITE ||
346+ descr.type() == MapObjectType::DISMANTLESITE) {
347+ return false;
348+ }
349+ // Only add allowed buildings or buildings that are owned by the player.
350+ if ((player.is_building_type_allowed(index) && (descr.is_buildable() || descr.is_enhanced())) ||
351+ !player.get_building_statistics(index).empty()) {
352+ return true;
353+ }
354+ return false;
355+}
356+
357+bool BuildingStatisticsMenu::foreign_tribe_building_is_valid(
358+ const Widelands::Player& player, Widelands::DescriptionIndex index) const {
359+ if (!player.tribe().has_building(index) && !player.get_building_statistics(index).empty()) {
360+ const BuildingDescr& descr = *iplayer().egbase().tribes().get_building_descr(index);
361+ if (descr.type() == MapObjectType::CONSTRUCTIONSITE ||
362+ descr.type() == MapObjectType::DISMANTLESITE) {
363+ return false;
364+ }
365+ return true;
366+ }
367+ return false;
368+}
369+
370+int BuildingStatisticsMenu::find_tab_for_building(const Widelands::BuildingDescr& descr) const {
371+ assert(descr.type() != MapObjectType::CONSTRUCTIONSITE);
372+ assert(descr.type() != MapObjectType::DISMANTLESITE);
373+ if (descr.get_ismine()) {
374+ return BuildingTab::Mines;
375+ } else if (descr.get_isport()) {
376+ return BuildingTab::Ports;
377+ } else {
378+ switch (descr.get_size()) {
379+ case BaseImmovable::SMALL:
380+ return BuildingTab::Small;
381+ case BaseImmovable::MEDIUM:
382+ return BuildingTab::Medium;
383+ case BaseImmovable::BIG:
384+ return BuildingTab::Big;
385+ default:
386+ throw wexception(
387+ "Building statictics: Found building without a size: %s", descr.name().c_str());
388+ }
389+ }
390+ NEVER_HERE();
391+}
392+
393+void BuildingStatisticsMenu::update_building_list() {
394+ const Widelands::Player& player = iplayer().player();
395+ const bool map_allows_seafaring = iplayer().game().map().allows_seafaring();
396+ for (DescriptionIndex index = 0; index < nr_building_types_; ++index) {
397+ const bool should_have_this_building =
398+ own_building_is_valid(player, index, map_allows_seafaring) || foreign_tribe_building_is_valid(player, index);
399+ const bool has_this_building = building_buttons_[index] != nullptr;
400+ if (should_have_this_building != has_this_building) {
401+ reset();
402+ return;
403+ }
404+ }
405+}
406+
407 /**
408 * Adds 3 buttons per building type.
409 *
410@@ -322,10 +396,10 @@
411 * - Buildings owned, steps through constructionsites
412 * - Productivity, steps though buildings with low productivity and stopped buildings
413 */
414-bool BuildingStatisticsMenu::add_button(
415- DescriptionIndex id, const BuildingDescr& descr, int tab_index, UI::Box& row, int* column) {
416-
417- UI::Box* button_box = new UI::Box(&row, 0, 0, UI::Box::Vertical);
418+void BuildingStatisticsMenu::add_button(DescriptionIndex id,
419+ const BuildingDescr& descr,
420+ UI::Box* row) {
421+ UI::Box* button_box = new UI::Box(row, 0, 0, UI::Box::Vertical);
422 building_buttons_[id] = new UI::Button(
423 button_box, (boost::format("building_button%s") % id).str(), 0, 0, kBuildGridCellWidth,
424 kBuildGridCellHeight, g_gr->images().get("images/ui_basic/but1.png"),
425@@ -347,25 +421,10 @@
426 productivity_labels_[id]->set_fixed_width(kBuildGridCellWidth);
427 button_box->add(productivity_labels_[id]);
428
429- row.add(button_box);
430+ row->add(button_box);
431
432 building_buttons_[id]->sigclicked.connect(
433 boost::bind(&BuildingStatisticsMenu::set_current_building_type, boost::ref(*this), id));
434-
435- // For dynamic window height
436- if (*column == 0) {
437- ++row_counters_[tab_index];
438- }
439-
440- // Check if the row is full
441- ++*column;
442- if (*column == kColumns) {
443- tabs_[tab_index]->add(&row);
444- tabs_[tab_index]->add_space(6);
445- *column = 0;
446- return true;
447- }
448- return false;
449 }
450
451 void BuildingStatisticsMenu::jump_building(JumpTarget target, bool reverse) {
452@@ -496,28 +555,30 @@
453 * Update this statistic
454 */
455 void BuildingStatisticsMenu::think() {
456+ // Update statistics
457+ const int32_t gametime = iplayer().game().get_gametime();
458+
459+ if (was_minimized_ || (gametime - lastupdate_) > kUpdateTimeInGametimeMs) {
460+ update_building_list();
461+ update();
462+ lastupdate_ = gametime;
463+ }
464+ // Make sure we don't have a delay with displaying labels when we restore the window.
465+ was_minimized_ = is_minimal();
466+
467 // Adjust height to current tab
468 if (is_minimal()) {
469 tab_panel_.set_size(0, 0);
470 } else {
471- int tab_height =
472+ const int tab_height =
473 35 +
474- row_counters_[tab_panel_.active()] * (kBuildGridCellHeight + kLabelHeight + kLabelHeight);
475+ row_counters_[tab_panel_.active()] * (kBuildGridCellHeight + kLabelHeight + kLabelHeight) +
476+ kMargin;
477 tab_panel_.set_size(kWindowWidth, tab_height);
478 set_size(
479- get_w(), tab_height + kMargin + 4 * kButtonRowHeight + get_tborder() + get_bborder());
480+ get_w(), tab_height + kMargin + navigation_panel_.get_h() + get_tborder() + get_bborder());
481 navigation_panel_.set_pos(Vector2i(0, tab_height + kMargin));
482 }
483-
484- // Update statistics
485- const int32_t gametime = iplayer().game().get_gametime();
486-
487- if (was_minimized_ || (gametime - lastupdate_) > kUpdateTimeInGametimeMs) {
488- update();
489- lastupdate_ = gametime;
490- }
491- // Make sure we don't have a delay with displaying labels when we restore the window.
492- was_minimized_ = is_minimal();
493 }
494
495 /*
496@@ -540,7 +601,6 @@
497 void BuildingStatisticsMenu::update() {
498 const Player& player = iplayer().player();
499 const TribeDescr& tribe = player.tribe();
500- const DescriptionIndex nr_buildings = iplayer().egbase().tribes().nrbuildings();
501
502 owned_label_.set_visible(false);
503 no_owned_label_.set_visible(false);
504@@ -558,7 +618,7 @@
505 navigation_buttons_[NavigationButton::NextUnproductive]->set_visible(false);
506 navigation_buttons_[NavigationButton::PrevUnproductive]->set_visible(false);
507
508- for (DescriptionIndex id = 0; id < nr_buildings; ++id) {
509+ for (DescriptionIndex id = 0; id < nr_building_types_; ++id) {
510 const BuildingDescr& building = *tribe.get_building_descr(id);
511 if (building_buttons_[id] == nullptr) {
512 continue;
513@@ -676,7 +736,9 @@
514 }
515
516 std::string owned_text;
517- if (player.tribe().has_building(id) && (building.is_buildable() || building.is_enhanced())) {
518+ const bool can_construct_this_building = player.tribe().has_building(id) &&
519+ (building.is_buildable() || building.is_enhanced());
520+ if (can_construct_this_building) {
521 /** TRANSLATORS: Buildings: owned / under construction */
522 owned_text = (boost::format(_("%1%/%2%")) % nr_owned % nr_build).str();
523 } else {
524@@ -694,7 +756,7 @@
525 no_owned_label_.set_visible(true);
526 navigation_buttons_[NavigationButton::NextOwned]->set_visible(true);
527 navigation_buttons_[NavigationButton::PrevOwned]->set_visible(true);
528- if (player.tribe().has_building(id) && building.is_buildable()) {
529+ if (can_construct_this_building) {
530 no_construction_label_.set_text(nr_build > 0 ? std::to_string(nr_build) : "");
531 navigation_buttons_[NavigationButton::NextConstruction]->set_enabled(nr_build > 0);
532 navigation_buttons_[NavigationButton::PrevConstruction]->set_enabled(nr_build > 0);
533
534=== modified file 'src/wui/building_statistics_menu.h'
535--- src/wui/building_statistics_menu.h 2018-04-07 16:59:00 +0000
536+++ src/wui/building_statistics_menu.h 2018-04-12 05:33:14 +0000
537@@ -46,6 +46,8 @@
538 ~BuildingStatisticsMenu() override;
539
540 void think() override;
541+
542+ /// Update state of current building buttons
543 void update();
544
545 private:
546@@ -65,13 +67,25 @@
547 NextUnproductive
548 };
549
550+ /// Initialize the buttons
551+ void reset();
552+ void init(int last_selected_tab = 0);
553+
554+ /// Whether a building that is used by the player's tribe should be added
555+ bool own_building_is_valid(const Widelands::Player& player, Widelands::DescriptionIndex index, bool map_allows_seafaring) const;
556+ /// Whether a building that isn't used by the player's tribe should be added
557+ bool foreign_tribe_building_is_valid(const Widelands::Player& player, Widelands::DescriptionIndex index) const;
558+ /// Determine which tab a building button should end up on, according to building size etc.
559+ int find_tab_for_building(const Widelands::BuildingDescr& descr) const;
560+
561+ /// If the buildings that should be shown have changes, update the list and reinitialize
562+ void update_building_list();
563+
564 /// Adds a button for the building type belonging to the id and descr to the tab.
565 /// Returns true when a new row needs to be created.
566- bool add_button(Widelands::DescriptionIndex id,
567+ void add_button(Widelands::DescriptionIndex id,
568 const Widelands::BuildingDescr& descr,
569- int tab_index,
570- UI::Box& row,
571- int* column);
572+ UI::Box* row);
573
574 /// Jumps to the next / previous appropriate building
575 void jump_building(JumpTarget target, bool reverse);
576@@ -95,7 +109,10 @@
577 /// UI tabs
578 UI::TabPanel tab_panel_;
579 UI::Box* tabs_[kNoOfBuildingTabs];
580+ /// How many button rows each tab has
581 int row_counters_[kNoOfBuildingTabs];
582+ /// We can have gaps in the tab sequence, so we need to map the indices for remembering the last selected tab
583+ int tab_assignments_[kNoOfBuildingTabs];
584
585 /// Button with building icon
586 std::vector<UI::Button*> building_buttons_;
587@@ -134,6 +151,9 @@
588
589 /// Whether a building has been selected
590 bool has_selection_;
591+
592+ /// The total number of building types available for all the tribes
593+ const Widelands::DescriptionIndex nr_building_types_;
594 };
595
596 #endif // end of include guard: WL_WUI_BUILDING_STATISTICS_MENU_H

Subscribers

People subscribed via source and target branches

to status/vote changes: