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