The Battle for Wesnoth  1.19.10+dev
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
game_stats.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2025
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "gui/widgets/listbox.hpp"
20 #include "gui/widgets/label.hpp"
22 #include "gui/widgets/window.hpp"
23 #include "formatter.hpp"
24 #include "game_classification.hpp"
25 #include "map/map.hpp"
26 #include "play_controller.hpp"
27 #include "serialization/markup.hpp"
28 #include "resources.hpp"
29 #include "units/map.hpp"
30 #include "units/unit.hpp"
31 
32 #include <functional>
33 
34 static lg::log_domain log_display("display");
35 #define LOG_DP LOG_STREAM(info, log_display)
36 
37 namespace gui2::dialogs
38 {
39 
40 REGISTER_DIALOG(game_stats)
41 
42 game_stats::game_stats(const display_context& board, const team& viewing_team, int& selected_side_number)
43  : modal_dialog(window_id())
44  , board_(board)
45  , viewing_team_(viewing_team)
46  , selected_side_number_(selected_side_number)
47 {
48 }
49 
51 {
53 
54  if(leader != board_.units().end()) {
55  return leader.get_shared_ptr();
56  }
57 
58  return nullptr;
59 }
60 
61 static std::string controller_name(const team& t)
62 {
63  static const side_controller::sized_array<t_string> names {_("controller^Idle"), _("controller^Human"), _("controller^AI"), _("controller^Reserved")};
64  return markup::span_color("#808080", markup::tag("small", names[static_cast<int>(t.controller())]));
65 }
66 
68 {
69  listbox& stats_list = find_widget<listbox>("game_stats_list");
70  listbox& settings_list = find_widget<listbox>("scenario_settings_list");
71 
72  for(const auto& team : board_.teams()) {
73  if(team.hidden()) {
74  continue;
75  }
76 
77  team_data_.emplace_back(board_, team);
78 
79  widget_data row_data_stats;
80  widget_item column_stats;
81 
82  const bool known = viewing_team_.knows_about_team(team.side() - 1);
83  const bool enemy = viewing_team_.is_enemy(team.side());
84 
85  const team_data& data = team_data_.back();
86 
87  unit_const_ptr leader = get_leader(team.side());
88 
89  std::string leader_name;
90  std::string leader_image;
91 
93  if(leader) {
94  const bool visible = leader->is_visible_to_team(leader->get_location(), viewing_team_, see_all);
95 
96  // Add leader image. If it's fogged/[hides], show only a random leader image.
97  if(visible || known) {
98  leader_image = leader->absolute_image() + leader->image_mods();
99  leader_name = leader->name();
100  } else {
101  leader_image = formatter() << "units/unknown-unit.png" << "~RC(magenta>" << team.color() << ")";
102  leader_name = _("Unknown");
103  }
104 
106  if(resources::controller->get_classification().is_multiplayer()) {
107  leader_name = team.side_name();
108  }
109  }
110 
111  leader_name = markup::span_color(team::get_side_highlight_pango(team.side()), leader_name);
112  }
113 
114  //
115  // Status list
116  //
117  column_stats["use_markup"] = "true";
118 
119  column_stats["label"] = leader_image;
120  row_data_stats.emplace("team_leader_image", column_stats);
121 
122  column_stats["label"] = leader_name + "\n" + controller_name(team);
123  column_stats["tooltip"] = team::get_side_color_name_for_UI(team.side());
124  row_data_stats.emplace("team_leader_name", column_stats);
125  column_stats.erase("tooltip");
126 
127  column_stats["label"] = team.user_team_name().empty() ? team.team_name() : team.user_team_name().str();
128  row_data_stats.emplace("team_name", column_stats);
129 
130  // Only fill in the rest of the info if the side is known...
131  if(known || see_all) {
132  std::string gold_str;
133  if(see_all || !enemy || !viewing_team_.uses_fog()) {
134  gold_str = utils::half_signed_value(team.gold());
135  }
136 
137  column_stats["label"] = team.gold() < 0 ? markup::span_color("#ff0000", gold_str) : gold_str;
138  row_data_stats.emplace("team_gold", column_stats);
139 
140  std::string village_count = std::to_string(team.villages().size());
142  village_count += "/" + std::to_string(board_.map().villages().size());
143  }
144 
145  column_stats["label"] = village_count;
146  row_data_stats.emplace("team_villages", column_stats);
147 
148  column_stats["label"] = std::to_string(data.units);
149  row_data_stats.emplace("team_units", column_stats);
150 
151  column_stats["label"] = std::to_string(data.upkeep);
152  row_data_stats.emplace("team_upkeep", column_stats);
153 
154  const std::string income = utils::signed_value(data.net_income);
155  column_stats["label"] = data.net_income < 0 ? markup::span_color("#ff0000", income) : income;
156  row_data_stats.emplace("team_income", column_stats);
157  }
158 
159  stats_list.add_row(row_data_stats);
160 
161  //
162  // Settings list
163  //
164  settings_list.add_row(widget_data{
165  { "team_leader_image", {
166  { "label", leader_image },
167  }},
168  { "team_leader_name", {
169  { "label", leader_name + "\n" + controller_name(team) },
170  { "use_markup", "true" }
171  }},
172  { "team_side", {
173  { "label", std::to_string(team.side()) },
174  }},
175  { "team_start_gold", {
176  { "label", std::to_string(team.start_gold()) },
177  }},
178  { "team_base_income", {
179  { "label", std::to_string(team.base_income()) },
180  }},
181  { "team_village_gold", {
182  { "label", std::to_string(team.village_gold()) },
183  }},
184  { "team_village_support", {
185  { "label", std::to_string(team.village_support()) },
186  }},
187  { "team_fog", {
188  { "label", team.uses_fog() ? _("yes") : _("no") },
189  }},
190  { "team_shroud", {
191  { "label", team.uses_shroud() ? _("yes") : _("no") },
192  }}
193  });
194  }
195 
196  // Sorting options for the status list
197  stats_list.set_sorters(
198  [this](const std::size_t i) {
199  unit_const_ptr leader = get_leader(i + 1);
200  return leader ? leader->name() : t_string();
201  },
202  [this](const std::size_t i) { return board_.teams()[i].user_team_name(); },
203  [this](const std::size_t i) { return board_.teams()[i].gold(); },
204  [this](const std::size_t i) { return board_.teams()[i].villages(); },
205  [this](const std::size_t i) { return team_data_[i].units; },
206  [this](const std::size_t i) { return team_data_[i].upkeep; },
207  [this](const std::size_t i) { return team_data_[i].net_income; }
208  );
209 
210  // Sorting options for the settings list
211  settings_list.set_sorters(
212  [this](const std::size_t i) {
213  unit_const_ptr leader = get_leader(i + 1);
214  return leader ? leader->name() : t_string();
215  },
216  [this](const std::size_t i) { return board_.teams()[i].side(); },
217  [this](const std::size_t i) { return board_.teams()[i].start_gold(); },
218  [this](const std::size_t i) { return board_.teams()[i].base_income(); },
219  [this](const std::size_t i) { return board_.teams()[i].village_gold(); },
220  [this](const std::size_t i) { return board_.teams()[i].village_support(); },
221  [this](const std::size_t i) { return board_.teams()[i].uses_fog(); },
222  [this](const std::size_t i) { return board_.teams()[i].uses_shroud(); }
223  );
224 
225  //
226  // Set up tab control
227  //
228  listbox& tab_bar = find_widget<listbox>("tab_bar");
229 
230  keyboard_capture(&tab_bar);
231 
232  connect_signal_notify_modified(tab_bar, std::bind(&game_stats::on_tab_select, this));
233 
234  on_tab_select();
235 }
236 
238 {
239  const int i = find_widget<listbox>("tab_bar").get_selected_row();
240 
241  find_widget<stacked_widget>("pager").select_layer(i);
242 
243  // There are only two tabs, so this is simple
244  find_widget<label>("title").set_label(
245  i == 0 ? _("Current Status") : _("Scenario Settings")
246  );
247 }
248 
250 {
251  if(get_retval() == retval::OK) {
252  const int selected_tab = find_widget<listbox>("tab_bar").get_selected_row();
253 
254  const std::string list_id = selected_tab == 0 ? "game_stats_list" : "scenario_settings_list";
255  selected_side_number_ = team_data_[find_widget<listbox>(list_id).get_selected_row()].side;
256  }
257 }
258 
259 } // namespace dialogs
double t
Definition: astarsearch.cpp:63
std::vector< std::string > names
Definition: build_info.cpp:67
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
virtual const gamemap & map() const =0
virtual const std::vector< team > & teams() const =0
virtual const unit_map & units() const =0
bool show_everything() const
Definition: display.hpp:112
std::ostringstream wrapper.
Definition: formatter.hpp:40
const std::vector< map_location > & villages() const
Return a list of the locations of villages on the map.
Definition: map.hpp:237
unit_const_ptr get_leader(const int side)
Definition: game_stats.cpp:50
const team & viewing_team_
Definition: game_stats.hpp:49
virtual void post_show() override
Actions to be taken after the window has been shown.
Definition: game_stats.cpp:249
std::vector< team_data > team_data_
Definition: game_stats.hpp:51
const display_context & board_
Definition: game_stats.hpp:47
virtual void pre_show() override
Actions to be taken before showing the window.
Definition: game_stats.cpp:67
Abstract base class for all modal dialogs.
The listbox class.
Definition: listbox.hpp:41
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:92
void set_sorters(Args &&... functors)
Registers sorting controls using magic index IDs.
Definition: listbox.hpp:306
void keyboard_capture(widget *widget)
Definition: window.cpp:1201
int get_retval()
Definition: window.hpp:402
game_display & get_display() override
Get a reference to a display member a derived class uses.
bool empty() const
Definition: tstring.hpp:195
const std::string & str() const
Definition: tstring.hpp:199
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
static std::string get_side_highlight_pango(int side)
Definition: team.cpp:1020
bool uses_shroud() const
Definition: team.hpp:308
const std::string & color() const
Definition: team.hpp:247
const std::string & side_name() const
Definition: team.hpp:298
int side() const
Definition: team.hpp:180
int village_support() const
Definition: team.hpp:191
const std::string & team_name() const
Definition: team.hpp:287
int village_gold() const
Definition: team.hpp:184
bool is_enemy(int n) const
Definition: team.hpp:234
const std::set< map_location > & villages() const
Definition: team.hpp:176
bool knows_about_team(std::size_t index) const
Definition: team.cpp:693
int gold() const
Definition: team.hpp:181
static const t_string get_side_color_name_for_UI(unsigned side)
Definition: team.cpp:988
int start_gold() const
Definition: team.hpp:182
int base_income() const
Definition: team.hpp:183
bool uses_fog() const
Definition: team.hpp:309
bool hidden() const
Definition: team.hpp:338
const t_string & user_team_name() const
Definition: team.hpp:288
unit_iterator end()
Definition: map.hpp:428
unit_iterator find_leader(int side)
Definition: map.cpp:320
std::size_t i
Definition: function.cpp:1030
static lg::log_domain log_display("display")
static std::string _(const char *str)
Definition: gettext.hpp:97
This file contains the window object, this object is a top level container which has the event manage...
const bool & debug
Definition: game_config.cpp:95
static std::string controller_name(const team &t)
Definition: game_stats.cpp:61
REGISTER_DIALOG(editor_edit_unit)
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:189
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
Definition: markup.hpp:110
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
Definition: markup.hpp:45
play_controller * controller
Definition: resources.cpp:21
std::string half_signed_value(int val)
Sign with Unicode "−" if negative.
std::string signed_value(int val)
Convert into a signed value (using the Unicode "−" and +0 convention.
@ enemy
Belongs to a non-friendly side; normally visualised by not displaying an orb.
std::string_view data
Definition: picture.cpp:178
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
std::array< T, size()> sized_array
Provide a alias template for an array of matching size.
Definition: enum_base.hpp:92
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:217