The Battle for Wesnoth  1.17.17+dev
mp_staging.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2023
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 "ai/configuration.hpp"
20 #include "chat_log.hpp"
21 #include "font/text_formatting.hpp"
22 #include "formatter.hpp"
23 #include "game_config.hpp"
24 #include "gettext.hpp"
28 #include "gui/widgets/button.hpp"
29 #include "gui/widgets/chatbox.hpp"
30 #include "gui/widgets/drawing.hpp"
31 #include "gui/widgets/image.hpp"
32 #include "gui/widgets/label.hpp"
33 #include "gui/widgets/listbox.hpp"
35 #include "gui/widgets/settings.hpp"
36 #include "gui/widgets/slider.hpp"
41 #include "mp_ui_alerts.hpp"
42 #include "units/types.hpp"
43 #include "wesnothd_connection.hpp"
44 
45 namespace gui2::dialogs
46 {
47 
48 REGISTER_DIALOG(mp_staging)
49 
50 mp_staging::mp_staging(ng::connect_engine& connect_engine, wesnothd_connection* connection)
51  : modal_dialog(window_id())
52  , connect_engine_(connect_engine)
53  , ai_algorithms_(ai::configuration::get_available_ais())
54  , network_connection_(connection)
55  , update_timer_(0)
56  , state_changed_(false)
57  , team_tree_map_()
58  , side_tree_map_()
59  , player_list_(nullptr)
60 {
61  set_show_even_without_video(true);
62 
63  assert(!ai_algorithms_.empty());
64 }
65 
67 {
68  if(update_timer_ != 0) {
70  update_timer_ = 0;
71  }
72 }
73 
75 {
78 
79  //
80  // Set title and status widget states
81  //
82  label& title = find_widget<label>(&window, "title", false);
83  title.set_label((formatter() << connect_engine_.params().name << " " << font::unicode_em_dash << " " << connect_engine_.scenario()["name"].t_str()).str());
84 
86 
87  //
88  // Set up sides list
89  //
90  for(const auto& side : connect_engine_.side_engines()) {
91  if(side->allow_player() || game_config::debug) {
92  add_side_node(side);;
93  }
94  }
95 
96  //
97  // Initialize chatbox and game rooms
98  //
99  chatbox& chat = find_widget<chatbox>(&window, "chat", false);
100 
101  chat.room_window_open(N_("this game"), true, false);
102  chat.active_window_changed();
103  chat.load_log(default_chat_log, false);
104 
105  //
106  // Set up player list
107  //
109 
110  //
111  // Set up the network handling
112  //
114 
115  //
116  // Set up the Lua plugin context
117  //
118  plugins_context_.reset(new plugins_context("Multiplayer Staging"));
119 
120  plugins_context_->set_callback("launch", [&window](const config&) { window.set_retval(retval::OK); }, false);
121  plugins_context_->set_callback("quit", [&window](const config&) { window.set_retval(retval::CANCEL); }, false);
122  plugins_context_->set_callback("chat", [&chat](const config& cfg) { chat.send_chat_message(cfg["message"], false); }, true);
123 }
124 
126 {
127  int position = 0;
128  for(const auto& side_engine : connect_engine_.side_engines()) {
129  if(side->team() == side_engine->team() && side->index() > side_engine->index()) {
130  ++position;
131  }
132  }
133 
134  return position;
135 }
136 
137 template<typename... T>
139 {
140  static const widget_data empty_map;
141 
142  // If there is no team node in the map, this will return nullptr
143  tree_view_node* team_node = team_tree_map_[side->team_name()];
144 
145  // Add a team node if none exists
146  if(team_node == nullptr) {
147  tree_view& tree = find_widget<tree_view>(get_window(), "side_list", false);
148 
149  widget_data tree_data;
150  widget_item tree_item;
151 
152  tree_item["label"] = side->user_team_name();
153  tree_data.emplace("tree_view_node_label", tree_item);
154 
155  team_node = &tree.add_node("team_header", tree_data);
156  team_node->add_sibling("side_spacer", empty_map);
157 
158  team_tree_map_[side->team_name()] = team_node;
159  }
160 
161  assert(team_node && "No team node found!");
162  return team_node->add_child(std::forward<T>(params)...);
163 }
164 
166 {
169 
170  item["label"] = std::to_string(side->index() + 1);
171  data.emplace("side_number", item);
172 
173  // TODO: don't hardcode magenta?
174  item["label"] = "units/unknown-unit.png~RC(magenta>" + side->color_id() + ")";
175  data.emplace("leader_image", item);
176 
177  item["label"] = "icons/icon-random.png";
178  data.emplace("leader_gender", item);
179 
180  tree_view_node& node = add_side_to_team_node(side, "side_panel", data, get_side_node_position(side));
181 
182  side_tree_map_[side] = &node;
183 
184  grid& row_grid = node.get_grid();
185 
186  update_leader_display(side, row_grid);
187 
188  // Status variables
189  const bool fls = connect_engine_.force_lock_settings();
190  const bool ums = connect_engine_.params().use_map_settings;
191 
192  const bool lock_gold = side->cfg()["gold_lock"].to_bool(fls);
193  const bool lock_income = side->cfg()["income_lock"].to_bool(fls);
194  const bool lock_team = side->cfg()["team_lock"].to_bool(fls);
195  const bool lock_color = side->cfg()["color_lock"].to_bool(fls);
196 
197  const bool saved_game = connect_engine_.params().saved_game == saved_game_mode::type::midgame;
198 
199  //
200  // AI Algorithm
201  //
202  int selection = 0;
203 
204  // We use an index-based loop in order to get the index of the selected option
205  std::vector<config> ai_options;
206  // If this is loading a saved game, we add an option at the beginning of the menu.
207  // This results in a mismatch between the indices of ai_options and ai_algorithms_
208  // that we need to account for later.
209  if(saved_game) {
210  ai_options.emplace_back("label", "Keep saved AI");
211  }
212  for(unsigned i = 0; i < ai_algorithms_.size(); ++i) {
213  ai_options.emplace_back("label", ai_algorithms_[i]->text);
214 
215  if(ai_algorithms_[i]->id == side->ai_algorithm()) {
216  selection = i;
217  }
218  }
219 
220  menu_button& ai_selection = find_widget<menu_button>(&row_grid, "ai_controller", false);
221 
222  ai_selection.set_values(ai_options, selection);
223 
224  connect_signal_notify_modified(ai_selection,
225  std::bind(&mp_staging::on_ai_select, this, side, std::ref(ai_selection), saved_game));
226 
227  on_ai_select(side, ai_selection, saved_game);
228  //
229  // Controller
230  //
231  std::vector<config> controller_names;
232  for(const auto& controller : side->controller_options()) {
233  controller_names.emplace_back("label", controller.second);
234  }
235 
236  menu_button& controller_selection = find_widget<menu_button>(&row_grid, "controller", false);
237 
238  controller_selection.set_values(controller_names, side->current_controller_index());
239  controller_selection.set_active(controller_names.size() > 1);
240 
241  connect_signal_notify_modified(controller_selection,
242  std::bind(&mp_staging::on_controller_select, this, side, std::ref(row_grid)));
243 
244  on_controller_select(side, row_grid);
245 
246  //
247  // Leader controls
248  //
249  button& leader_select = find_widget<button>(&row_grid, "select_leader", false);
250 
251  //todo: shouldn't this also be disabled when the flg settings are locked.
252  leader_select.set_active(!saved_game);
253 
254  connect_signal_mouse_left_click(leader_select,
255  std::bind(&mp_staging::select_leader_callback, this, side, std::ref(row_grid)));
256 
257  //
258  // Team
259  //
260  std::vector<config> team_names;
261  unsigned initial_team_selection = 0;
262 
263  for(unsigned i = 0; i < connect_engine_.team_data().size(); ++i) {
265 
266  if(!tdata.is_player_team && !game_config::debug) {
267  continue;
268  }
269 
270  config entry;
271  entry["label"] = t_string::from_serialized(tdata.user_team_name);
272 
273  // Since we're not necessarily displaying every every team, we need to store the
274  // index a displayed team has in the connect_engine's team_data vector. This is
275  // then utilized in the click callback.
276  entry["team_index"] = i;
277 
278  team_names.push_back(std::move(entry));
279 
280  // Since, again, every team might not be displayed, and side_engine::team() returns
281  // an index into the team_data vector, get an initial selection index for the menu
282  // adjusted for the displayed named.
283  if(side->team() == i) {
284  initial_team_selection = team_names.size() - 1;
285  }
286  }
287 
288  menu_button& team_selection = find_widget<menu_button>(&row_grid, "side_team", false);
289 
290  team_selection.set_values(team_names, initial_team_selection);
291  //todo: shouldn't this also be disabled when team settings are locked.
292  team_selection.set_active(!saved_game);
293 
294  connect_signal_notify_modified(team_selection,
295  std::bind(&mp_staging::on_team_select, this, side, std::ref(team_selection)));
296 
297  //
298  // Colors
299  //
300  std::vector<config> color_options;
301  for(const auto& color : side->color_options()) {
302  color_options.emplace_back(
303  "label", font::get_color_string_pango(color),
304  "icon", (formatter() << "misc/status.png~RC(magenta>" << color << ")").str()
305  );
306  }
307 
308  menu_button& color_selection = find_widget<menu_button>(&row_grid, "side_color", false);
309 
310  color_selection.set_values(color_options, side->color());
311  color_selection.set_active(!saved_game);
312  color_selection.set_use_markup(true);
313 
314  connect_signal_notify_modified(color_selection,
315  std::bind(&mp_staging::on_color_select, this, side, std::ref(row_grid)));
316 
317  //
318  // Gold and Income
319  //
320  const auto slider_setup_helper = [](slider& slider, const int value) {
321  // For the gold and income sliders, the usual min and max values are set in
322  // the dialog WML. However, if a side specifies a value out of that range,
323  // we adjust the bounds to accommodate it.
325  std::min(value, slider.get_minimum_value()),
326  std::max(value, slider.get_maximum_value())
327  );
328 
329  slider.set_value(value);
330  };
331 
332  slider& slider_gold = find_widget<slider>(&row_grid, "side_gold_slider", false);
333  slider_setup_helper(slider_gold, side->gold());
334 
335  connect_signal_notify_modified(slider_gold, std::bind(
336  &mp_staging::on_side_slider_change<&ng::side_engine::set_gold>, this, side, std::ref(slider_gold)));
337 
338  slider& slider_income = find_widget<slider>(&row_grid, "side_income_slider", false);
339  slider_setup_helper(slider_income, side->income());
340 
341  connect_signal_notify_modified(slider_income, std::bind(
342  &mp_staging::on_side_slider_change<&ng::side_engine::set_income>, this, side, std::ref(slider_income)));
343 
344  // TODO: maybe display the saved values
345  if(saved_game) {
348  }
349 
350  //
351  // Gold, income, team, and color are only suggestions unless explicitly locked
352  //
353  if(!saved_game && ums) {
354  team_selection.set_active(!lock_team);
355  color_selection.set_active(!lock_color);
356 
357  slider_gold.set_active(!lock_gold);
358  slider_income.set_active(!lock_income);
359  }
360 }
361 
363 {
364  menu_button& ai_selection = find_widget<menu_button>(&row_grid, "ai_controller", false);
365  menu_button& controller_selection = find_widget<menu_button>(&row_grid, "controller", false);
366 
367  if(side->controller_changed(controller_selection.get_value())) {
369 
371  }
372 }
373 
375 {
376  // If this is a saved game, we need to reduce the index by one, to account for
377  // the "Keep saved AI" option having been added to the computer player menu
378  int i = ai_menu.get_value();
379  if(saved_game) {
380  i--;
381  }
382 
383  if(i < 0) {
384  side->set_ai_algorithm("use_saved");
385  } else {
386  side->set_ai_algorithm(ai_algorithms_[i]->id);
387  }
388 
390 }
391 
393 {
394  side->set_color(find_widget<menu_button>(&row_grid, "side_color", false).get_value());
395 
396  update_leader_display(side, row_grid);
397 
399 }
400 
402 {
403  // Since we're not necessarily displaying every every team in the menu, we can't just
404  // use the selected index to set a side's team. Instead, we grab the index we stored
405  // in add_side_node from the selected config, which should correspond to the
406  // appropriate entry in the connect_engine's team name vector.
407  const unsigned team_index = team_menu.get_value_config()["team_index"].to_unsigned();
408 
409  if(team_index == side->team()) {
410  return;
411  }
412 
413  // Note the old team so we can remove the node if empty after the side move.
414  // Do this *before* setting the new team!
415  const std::string old_team = side->team_name();
416  side->set_team(team_index);
417 
418  auto& tree = find_widget<tree_view>(get_window(), "side_list", false);
419 
420  // First, remove the node from the tree
421  auto node = tree.remove_node(side_tree_map_[side]);
422 
423  // Then add a new node as a child to the appropriate team's node
424  add_side_to_team_node(side, std::move(node.first), get_side_node_position(side));
425 
426  tree_view_node* old_team_node = team_tree_map_[old_team];
427 
428  // Last, remove the old team node if it's now empty
429  if(old_team_node->empty()) {
430  // Decor node will be immediately after team node. Remove this first!
431  tree.remove_node(old_team_node->get_node_below());
432  tree.remove_node(old_team_node);
433 
434  team_tree_map_[old_team] = nullptr;
435  }
436 
438 }
439 
441 {
442  if(gui2::dialogs::faction_select::execute(side->flg(), side->color_id(), side->index() + 1)) {
443  update_leader_display(side, row_grid);
444 
446  }
447 }
448 
449 template<void(ng::side_engine::*fptr)(int)>
451 {
452  std::invoke(fptr, side, slider.get_value());
453 
455 }
456 
458 {
459  const std::string current_faction = side->flg().current_faction()["name"];
460 
461  // BIG FAT TODO: get rid of this shitty "null" string value in the FLG manager
462  std::string current_leader = side->flg().current_leader() != "null" ? side->flg().current_leader() : font::unicode_em_dash;
463  const std::string current_gender = side->flg().current_gender() != "null" ? side->flg().current_gender() : font::unicode_em_dash;
464 
465  // Sprite
466  std::string new_image;
467 
468  if(side->flg().is_random_faction() || current_leader == "random") {
469  new_image = ng::random_enemy_picture;
470  }
471 
472  if(const unit_type* ut = unit_types.find(current_leader)) {
473  const unit_type& type = ut->get_gender_unit_type(current_gender);
474 
475  new_image = formatter() << type.image() << "~RC(magenta>" << side->color_id() << ")";
476 
477  // We don't need the unit type id anymore, and can now replace this variable with the type name
478  current_leader = type.type_name();
479  }
480 
481  find_widget<drawing>(&row_grid, "leader_image", false).set_label(new_image);
482 
483  // Faction and leader
484  if(!side->cfg()["name"].empty()) {
485  current_leader = formatter() << side->cfg()["name"] << " (<i>" << current_leader << "</i>)";
486  }
487 
488  find_widget<label>(&row_grid, "leader_type", false).set_label(current_leader == "random" ? _("Random") : current_leader);
489  find_widget<label>(&row_grid, "leader_faction", false).set_label("<span color='#a69275'>" + current_faction + "</span>");
490 
491  // Gender
492  if(current_gender != font::unicode_em_dash) {
493  const std::string gender_icon = formatter() << "icons/icon-" << current_gender << ".png";
494 
495  image& icon = find_widget<image>(&row_grid, "leader_gender", false);
496  icon.set_label(gender_icon);
497  }
498 }
499 
501 {
502  find_widget<label>(get_window(), "status_label", false).set_label(
504  ? _("Waiting for players to join...")
505  : _("Waiting for players to choose factions...")
506  );
507 
508  find_widget<button>(get_window(), "ok", false).set_active(connect_engine_.can_start_game());
509 }
510 
512 {
513  // First, send off any changes if they've been accumulated
514  if(state_changed_) {
516  }
517 
518  // Next, check for any incoming changes
519  config data;
521  return;
522  }
523 
524  // Update chat
525  find_widget<chatbox>(get_window(), "chat", false).process_network_data(data);
526 
527  // TODO: why is this needed...
528  const bool was_able_to_start = connect_engine_.can_start_game();
529 
530  bool quit_signal_received;
531  std::tie(quit_signal_received, std::ignore) = connect_engine_.process_network_data(data);
532 
533  if(quit_signal_received) {
535  }
536 
537  // Update side leader displays
538  // This is basically only needed when a new player joins and selects their faction
539  for(auto& tree_entry : side_tree_map_) {
540  ng::side_engine_ptr side = tree_entry.first;
541 
542  grid& row_grid = tree_entry.second->get_grid();
543 
544  update_leader_display(side, row_grid);
545 
546  std::vector<config> controller_names;
547  for(const auto& controller : side->controller_options()) {
548  controller_names.emplace_back("label", controller.second);
549  }
550 
551  menu_button& controller_selection = find_widget<menu_button>(&row_grid, "controller", false);
552 
553  controller_selection.set_values(controller_names, side->current_controller_index());
554  controller_selection.set_active(controller_names.size() > 1);
555  }
556 
557  // Update player list
558  if(data.has_child("user")) {
559  player_list_->update_list(data.child_range("user"));
560  }
561 
562  // Update status label and buttons
564 
565  if(!was_able_to_start && connect_engine_.can_start_game()) {
567  }
568 
569  state_changed_ = false;
570 }
571 
573 {
574  if(update_timer_ != 0) {
576  update_timer_ = 0;
577  }
578 
579  if(window.get_retval() == retval::OK) {
581  } else {
583  }
584 }
585 
586 } // namespace dialogs
std::map< std::string, chatroom_log > default_chat_log
Definition: chat_log.cpp:18
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
std::ostringstream wrapper.
Definition: formatter.hpp:40
Simple push button.
Definition: button.hpp:37
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:65
lobby_chat_window * room_window_open(const std::string &room, const bool open_new, const bool allow_close=true)
Check if a room window for "room" is open, if open_new is true then it will be created if not found.
Definition: chatbox.cpp:380
virtual void send_chat_message(const std::string &message, bool allies_only) override
Inherited form chat_handler.
Definition: chatbox.cpp:248
void load_log(std::map< std::string, chatroom_log > &log, bool show_lobby)
Definition: chatbox.cpp:94
void active_window_changed()
Definition: chatbox.cpp:109
Abstract base class for all modal dialogs.
window * get_window()
Returns a pointer to the dialog's window.
void update_leader_display(ng::side_engine_ptr side, grid &row_grid)
Definition: mp_staging.cpp:457
void on_color_select(ng::side_engine_ptr side, grid &row_grid)
Definition: mp_staging.cpp:392
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: mp_staging.cpp:74
wesnothd_connection * network_connection_
Definition: mp_staging.hpp:92
int get_side_node_position(ng::side_engine_ptr side) const
Find an appropriate position to insert a side node.
Definition: mp_staging.cpp:125
ng::connect_engine & connect_engine_
Definition: mp_staging.hpp:88
void on_controller_select(ng::side_engine_ptr side, grid &row_grid)
Definition: mp_staging.cpp:362
void select_leader_callback(ng::side_engine_ptr side, grid &row_grid)
Definition: mp_staging.cpp:440
void on_ai_select(ng::side_engine_ptr side, menu_button &ai_menu, const bool saved_game)
Definition: mp_staging.cpp:374
void on_team_select(ng::side_engine_ptr side, menu_button &team_menu)
Definition: mp_staging.cpp:401
std::map< ng::side_engine_ptr, tree_view_node * > side_tree_map_
Definition: mp_staging.hpp:99
void on_side_slider_change(ng::side_engine_ptr side, slider &slider)
Definition: mp_staging.cpp:450
std::unique_ptr< player_list_helper > player_list_
Definition: mp_staging.hpp:101
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
Definition: mp_staging.cpp:572
std::vector< ai::description * > ai_algorithms_
Definition: mp_staging.hpp:90
std::map< std::string, tree_view_node * > team_tree_map_
Definition: mp_staging.hpp:98
tree_view_node & add_side_to_team_node(ng::side_engine_ptr side, T &&... params)
Definition: mp_staging.cpp:138
void add_side_node(ng::side_engine_ptr side)
Definition: mp_staging.cpp:165
std::unique_ptr< plugins_context > plugins_context_
Base container class.
Definition: grid.hpp:32
A label displays a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:58
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:62
const ::config & get_value_config() const
Returns the entire config object for the selected row.
Definition: menu_button.hpp:99
void set_values(const std::vector<::config > &values, unsigned selected=0)
virtual unsigned get_value() const override
Inherited from selectable_item.
Definition: menu_button.hpp:84
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:77
virtual void set_active(const bool active) override
See styled_widget::set_active.
A slider is a control that can select a value by moving a grip on a groove.
Definition: slider.hpp:60
virtual int get_minimum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:85
virtual void set_value(int value) override
Inherited from integer_selector.
Definition: slider.cpp:82
virtual int get_maximum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:91
void set_value_range(int min_value, int max_value)
Definition: slider.cpp:251
virtual int get_value() const override
Inherited from integer_selector.
Definition: slider.hpp:79
virtual void set_label(const t_string &label)
virtual void set_use_markup(bool use_markup)
tree_view_node & add_sibling(const std::string &id, const widget_data &data)
Adds a sibbling for a node at the end of the list.
bool empty() const
Does the node have children?
tree_view_node * get_node_below()
tree_view_node & add_child(const std::string &id, const widget_data &data, const int index=-1)
Constructs a new child node.
A tree view is a control that holds several items of the same or different types.
Definition: tree_view.hpp:61
tree_view_node & add_node(const std::string &id, const widget_data &data, const int index=-1)
Definition: tree_view.cpp:57
void set_visible(const visibility visible)
Definition: widget.cpp:456
@ visible
The user sets the widget visible, that means:
@ invisible
The user set the widget invisible, that means:
@ hidden
The user sets the widget hidden, that means:
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:67
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:286
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:358
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:299
int get_retval()
Definition: window.hpp:365
const std::vector< team_data_pod > & team_data() const
std::vector< side_engine_ptr > & side_engines()
std::pair< bool, bool > process_network_data(const config &data)
bool sides_available() const
bool force_lock_settings() const
const mp_game_settings & params() const
bool can_start_game() const
static t_string from_serialized(const std::string &string)
Definition: tstring.hpp:154
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1246
A single unit type that the player may recruit.
Definition: types.hpp:46
A class that represents a TCP/IP connection to the wesnothd server.
bool receive_data(config &result)
Receives the next pending data pack from the server, if available.
Managing the AIs configuration - headers.
std::size_t i
Definition: function.cpp:968
#define N_(String)
Definition: gettext.hpp:101
static std::string _(const char *str)
Definition: gettext.hpp:93
#define REGISTER_DIALOG(window_id)
Wrapper for REGISTER_DIALOG2.
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
const std::string unicode_em_dash
Definition: constants.cpp:44
std::string get_color_string_pango(const std::string &id)
Returns the name of a color range, colored with its own color.
const bool & debug
Definition: game_config.cpp:91
unsigned lobby_network_timer
Definition: game_config.cpp:67
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:205
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:179
std::size_t add_timer(const uint32_t interval, const std::function< void(std::size_t id)> &callback, const bool repeat)
Adds a new timer.
Definition: timer.cpp:127
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
std::map< std::string, t_string > widget_item
Definition: widget.hpp:32
bool remove_timer(const std::size_t id)
Removes a timer.
Definition: timer.cpp:168
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
@ CANCEL
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:414
Functions to load and save images from/to disk.
void ready_for_start()
@ CNTR_COMPUTER
std::shared_ptr< side_engine > side_engine_ptr
const std::string random_enemy_picture("units/random-dice.png")
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
std::string_view data
Definition: picture.cpp:199
This file contains the settings handling of the widget library.
saved_game_mode::type saved_game
unit_type_data unit_types
Definition: types.cpp:1463