The Battle for Wesnoth  1.17.4+dev
mp_create_game.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2022
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "filesystem.hpp"
21 #include "formatter.hpp"
22 #include "formula/string_utils.hpp"
23 #include "game_config.hpp"
24 #include "game_config_manager.hpp"
26 #include "gettext.hpp"
27 #include "gui/auxiliary/field.hpp"
28 #include "gui/dialogs/message.hpp"
31 #include "gui/widgets/button.hpp"
32 #include "gui/widgets/image.hpp"
34 #include "gui/widgets/listbox.hpp"
36 #include "gui/widgets/minimap.hpp"
37 #include "gui/widgets/slider.hpp"
40 #include "gui/widgets/text_box.hpp"
43 #include "log.hpp"
44 #include "map_settings.hpp"
45 #include "preferences/game.hpp"
46 #include "save_index.hpp"
47 #include "savegame.hpp"
48 
49 #include <boost/algorithm/string.hpp>
50 
51 static lg::log_domain log_mp_create("mp/create");
52 
53 #define DBG_MP LOG_STREAM(debug, log_mp_create)
54 #define WRN_MP LOG_STREAM(warn, log_mp_create)
55 #define ERR_MP LOG_STREAM(err, log_mp_create)
56 
57 namespace gui2::dialogs
58 {
59 
60 // Special retval value for loading a game
61 static const int LOAD_GAME = 100;
62 
63 // Shorthand
64 namespace prefs = preferences;
65 
67 
68 mp_create_game::mp_create_game(saved_game& state, bool local_mode)
69  : create_engine_(state)
70  , config_engine_()
71  , options_manager_()
72  , selected_game_index_(-1)
73  , selected_rfm_index_(-1)
74  , use_map_settings_(register_bool( "use_map_settings", true, prefs::use_map_settings, prefs::set_use_map_settings,
75  std::bind(&mp_create_game::update_map_settings, this)))
76  , fog_(register_bool("fog", true, prefs::fog, prefs::set_fog))
77  , shroud_(register_bool("shroud", true, prefs::shroud, prefs::set_shroud))
78  , start_time_(register_bool("random_start_time", true, prefs::random_start_time, prefs::set_random_start_time))
79  , time_limit_(register_bool("time_limit", true, prefs::countdown, prefs::set_countdown,
80  std::bind(&mp_create_game::update_map_settings, this)))
81  , shuffle_sides_(register_bool("shuffle_sides", true, prefs::shuffle_sides, prefs::set_shuffle_sides))
82  , observers_(register_bool("observers", true, prefs::allow_observers, prefs::set_allow_observers))
83  , strict_sync_(register_bool("strict_sync", true))
84  , private_replay_(register_bool("private_replay", true))
85  , turns_(register_integer("turn_count", true, prefs::turns, prefs::set_turns))
86  , gold_(register_integer("village_gold", true, prefs::village_gold, prefs::set_village_gold))
87  , support_(register_integer("village_support", true, prefs::village_support, prefs::set_village_support))
88  , experience_(register_integer("experience_modifier", true, prefs::xp_modifier, prefs::set_xp_modifier))
89  , init_turn_limit_(register_integer("init_turn_limit", true, prefs::countdown_init_time, prefs::set_countdown_init_time))
90  , turn_bonus_(register_integer("turn_bonus", true, prefs::countdown_turn_bonus, prefs::set_countdown_turn_bonus))
91  , reservoir_(register_integer("reservoir", true, prefs::countdown_reservoir_time, prefs::set_countdown_reservoir_time))
92  , action_bonus_(register_integer("action_bonus", true, prefs::countdown_action_bonus, prefs::set_countdown_action_bonus))
93  , mod_list_()
94  , eras_menu_button_()
95  , local_mode_(local_mode)
96 {
97  level_types_ = {
98  {level_type::type::scenario, _("Scenarios")},
99  {level_type::type::campaign, _("Multiplayer Campaigns")},
100  {level_type::type::sp_campaign, _("Singleplayer Campaigns")},
101  {level_type::type::user_map, _("Custom Maps")},
102  {level_type::type::user_scenario, _("Custom Scenarios")},
103  {level_type::type::random_map, _("Random Maps")},
104  };
105 
106  level_types_.erase(std::remove_if(level_types_.begin(), level_types_.end(),
107  [this](level_type_info& type_info) {
108  return create_engine_.get_levels_by_type_unfiltered(type_info.first).empty();
109  }), level_types_.end());
110 
111  set_show_even_without_video(true);
112 
113  create_engine_.init_active_mods();
114 
115  create_engine_.get_state().clear();
116  create_engine_.get_state().classification().type = campaign_type::type::multiplayer;
117 
118  // Need to set this in the constructor, pre_show() is too late
119  set_allow_plugin_skip(false);
120 }
121 
123 {
124  find_widget<text_box>(&win, "game_name", false).set_value(local_mode_ ? "" : ng::configure_engine::game_name_default());
125 
127  find_widget<button>(&win, "random_map_regenerate", false),
128  std::bind(&mp_create_game::regenerate_random_map, this));
129 
131  find_widget<button>(&win, "random_map_settings", false),
132  std::bind(&mp_create_game::show_generator_settings, this));
133 
135  find_widget<button>(&win, "load_game", false),
136  std::bind(&mp_create_game::load_game_callback, this));
137 
138  // Custom dialog close hook
139  win.set_exit_hook_ok_only([this](window& w)->bool { return dialog_exit_hook(w); });
140 
141  //
142  // Set up the options manager. Needs to be done before selecting an initial tab
143  //
145 
146  //
147  // Set up filtering
148  //
149  connect_signal_notify_modified(find_widget<slider>(&win, "num_players", false),
150  std::bind(&mp_create_game::on_filter_change<slider>, this, "num_players", true));
151 
152  text_box& filter = find_widget<text_box>(&win, "game_filter", false);
153 
155  std::bind(&mp_create_game::on_filter_change<text_box>, this, "game_filter", true));
156 
157  // Note this cannot be in the keyboard chain or it will capture focus from other text boxes
158  win.keyboard_capture(&filter);
159 
160  //
161  // Set up game types menu_button
162  //
163  std::vector<config> game_types;
164  for(level_type_info& type_info : level_types_) {
165  game_types.emplace_back("label", type_info.second);
166  }
167 
168  if(game_types.empty()) {
169  gui2::show_transient_message("", _("No games found."));
170  throw game::error(_("No games found."));
171  }
172 
173  menu_button& game_menu_button = find_widget<menu_button>(&win, "game_types", false);
174 
175  // Helper to make sure the initially selected level type is valid
176  auto get_initial_type_index = [this]()->int {
177  const auto index = std::find_if(level_types_.begin(), level_types_.end(), [](level_type_info& info) {
179  });
180 
181  if(index != level_types_.end()) {
182  return std::distance(level_types_.begin(), index);
183  }
184 
185  return 0;
186  };
187 
188  game_menu_button.set_values(game_types, get_initial_type_index());
189 
190  connect_signal_notify_modified(game_menu_button,
191  std::bind(&mp_create_game::update_games_list, this));
192 
193  //
194  // Set up mods list
195  //
196  mod_list_ = &find_widget<listbox>(&win, "mod_list", false);
197 
198  const auto& activemods = preferences::modifications();
200  std::map<std::string, string_map> data;
202 
203  item["label"] = mod->name;
204  data.emplace("mod_name", item);
205 
206  grid* row_grid = &mod_list_->add_row(data);
207 
208  find_widget<toggle_panel>(row_grid, "panel", false).set_tooltip(mod->description);
209 
210  toggle_button& mog_toggle = find_widget<toggle_button>(row_grid, "mod_active_state", false);
211 
212  const int i = mod_list_->get_item_count() - 1;
213  if(std::find(activemods.begin(), activemods.end(), mod->id) != activemods.end()) {
214  create_engine_.active_mods().push_back(mod->id);
215  mog_toggle.set_value_bool(true);
216  }
217 
218  connect_signal_notify_modified(mog_toggle, std::bind(&mp_create_game::on_mod_toggle, this, i, &mog_toggle));
219  }
220 
221  // No mods, hide the header
222  if(mod_list_->get_item_count() <= 0) {
223  find_widget<styled_widget>(&win, "mods_header", false).set_visible(widget::visibility::invisible);
224  }
225 
226  //
227  // Set up eras menu_button
228  //
229  eras_menu_button_ = &find_widget<menu_button>(&win, "eras", false);
230 
231  std::vector<config> era_names;
233  era_names.emplace_back("label", era->name, "tooltip", era->description);
234  }
235 
236  if(era_names.empty()) {
237  gui2::show_transient_message("", _("No eras found."));
238  throw config::error(_("No eras found"));
239  }
240 
241  eras_menu_button_->set_values(era_names);
242 
244  std::bind(&mp_create_game::on_era_select, this));
245 
247  if(era_selection >= 0) {
248  eras_menu_button_->set_selected(era_selection);
249  }
250 
251  on_era_select();
252 
253  //
254  // Set up random faction mode menu_button
255  //
256  static const random_faction_mode::sized_array<t_string> names {_("Independent"), _("No Mirror"), _("No Ally Mirror")};
257  static const random_faction_mode::sized_array<t_string> tooltips {
258  _("Independent: Random factions assigned independently"),
259  _("No Mirror: No two players will get the same faction"),
260  _("No Ally Mirror: No two allied players will get the same faction")
261  };
262  std::vector<config> rfm_options;
263  for(std::size_t i = 0; i < random_faction_mode::size(); i++) {
264  rfm_options.emplace_back("label", names[i]);
265  rfm_options[i]["tooltip"] = tooltips[i];
266  };
267 
268  const int initial_index = static_cast<int>(random_faction_mode::get_enum(prefs::random_faction_mode()).value_or(random_faction_mode::type::independent));
269 
270  menu_button& rfm_menu_button = find_widget<menu_button>(&win, "random_faction_mode", false);
271 
272  rfm_menu_button.set_values(rfm_options, initial_index);
273 
274  connect_signal_notify_modified(rfm_menu_button,
276 
278 
279  //
280  // Set up the setting status labels
281  //
282  bind_status_label<slider>(&win, turns_->id());
283  bind_status_label<slider>(&win, gold_->id());
284  bind_status_label<slider>(&win, support_->id());
285  bind_status_label<slider>(&win, experience_->id());
286 
287  bind_status_label<slider>(&win, init_turn_limit_->id());
288  bind_status_label<slider>(&win, turn_bonus_->id());
289  bind_status_label<slider>(&win, reservoir_->id());
290  bind_status_label<slider>(&win, action_bonus_->id());
291 
292  //
293  // Timer reset button
294  //
296  find_widget<button>(&win, "reset_timer_defaults", false),
297  std::bind(&mp_create_game::reset_timer_settings, this));
298 
299  //
300  // Disable certain settings if we're playing a local game.
301  //
302  if(local_mode_) {
303  find_widget<text_box>(&win, "game_name", false).set_active(false);
304  find_widget<text_box>(&win, "game_password", false).set_active(false);
305 
306  observers_->widget_set_enabled(false, false);
307  strict_sync_->widget_set_enabled(false, false);
308  private_replay_->widget_set_enabled(false, false);
309  }
310 
311  //
312  // Set up tab control
313  //
314  listbox& tab_bar = find_widget<listbox>(&win, "tab_bar", false);
315 
317  std::bind(&mp_create_game::on_tab_select, this));
318 
319  // Allow the settings stack to find widgets in all pages, regardless of which is selected.
320  // This ensures settings (especially game settings) widgets are appropriately updated when
321  // a new game is selected, regardless of which settings tab is active at the time.
322  find_widget<stacked_widget>(&win, "pager", false).set_find_in_all_layers(true);
323 
324  // We call on_tab_select farther down.
325 
326  //
327  // Main games list
328  //
329  listbox& list = find_widget<listbox>(&win, "games_list", false);
330 
332  std::bind(&mp_create_game::on_game_select, this));
333 
334  win.add_to_keyboard_chain(&list);
335 
336  // This handles the initial game selection as well
337  display_games_of_type(level_types_[get_initial_type_index()].first, preferences::level());
338 
339  // Initial tab selection must be done after game selection so the field widgets are set to their correct active state.
340  on_tab_select();
341 
342  //
343  // Set up the Lua plugin context
344  //
345  plugins_context_.reset(new plugins_context("Multiplayer Create"));
346 
347  plugins_context_->set_callback("create", [&win](const config&) { win.set_retval(retval::OK); }, false);
348  plugins_context_->set_callback("quit", [&win](const config&) { win.set_retval(retval::CANCEL); }, false);
349  plugins_context_->set_callback("load", [this](const config&) { load_game_callback(); }, false);
350 
351 #define UPDATE_ATTRIBUTE(field, convert) \
352  do { if(cfg.has_attribute(#field)) { field##_->set_widget_value(cfg[#field].convert()); } } while(false) \
353 
354  plugins_context_->set_callback("update_settings", [this, &win](const config& cfg) {
355  UPDATE_ATTRIBUTE(turns, to_int);
356  UPDATE_ATTRIBUTE(gold, to_int);
357  UPDATE_ATTRIBUTE(support, to_int);
358  UPDATE_ATTRIBUTE(experience, to_int);
359  UPDATE_ATTRIBUTE(start_time, to_bool);
360  UPDATE_ATTRIBUTE(fog, to_bool);
361  UPDATE_ATTRIBUTE(shroud, to_bool);
362  UPDATE_ATTRIBUTE(time_limit, to_bool);
363  UPDATE_ATTRIBUTE(init_turn_limit, to_int);
364  UPDATE_ATTRIBUTE(turn_bonus, to_int);
365  UPDATE_ATTRIBUTE(reservoir, to_int);
366  UPDATE_ATTRIBUTE(action_bonus, to_int);
367  UPDATE_ATTRIBUTE(observers, to_bool);
368  UPDATE_ATTRIBUTE(strict_sync, to_bool);
369  UPDATE_ATTRIBUTE(private_replay, to_bool);
371  }, true);
372 
373 #undef UPDATE_ATTRIBUTE
374 
375  plugins_context_->set_callback("set_name", [this](const config& cfg) {
376  config_engine_->set_game_name(cfg["name"]); }, true);
377 
378  plugins_context_->set_callback("set_password", [this](const config& cfg) {
379  config_engine_->set_game_password(cfg["password"]); }, true);
380 
381  plugins_context_->set_callback("select_level", [this](const config& cfg) {
382  selected_game_index_ = convert_to_game_filtered_index(cfg["index"].to_int());
384  }, true);
385 
386  plugins_context_->set_callback("select_type", [this](const config& cfg) {
387  create_engine_.set_current_level_type(level_type::get_enum(cfg["type"].str()).value_or(level_type::type::scenario)); }, true);
388 
389  plugins_context_->set_callback("select_era", [this](const config& cfg) {
390  create_engine_.set_current_era_index(cfg["index"].to_int()); }, true);
391 
392  plugins_context_->set_callback("select_mod", [this](const config& cfg) {
393  on_mod_toggle(cfg["index"].to_int(), nullptr);
394  }, true);
395 
396  plugins_context_->set_accessor("get_selected", [this](const config&) {
397  const ng::level& current_level = create_engine_.current_level();
398  return config {
399  "id", current_level.id(),
400  "name", current_level.name(),
401  "icon", current_level.icon(),
402  "description", current_level.description(),
403  "allow_era_choice", current_level.allow_era_choice(),
405  };
406  });
407 
408  plugins_context_->set_accessor("find_level", [this](const config& cfg) {
409  const std::string id = cfg["id"].str();
410  return config {
411  "index", create_engine_.find_level_by_id(id).second,
413  };
414  });
415 
416  plugins_context_->set_accessor_int("find_era", [this](const config& cfg) {
418  });
419 
420  plugins_context_->set_accessor_int("find_mod", [this](const config& cfg) {
422  });
423 }
424 
426 {
427  DBG_MP << "sync_with_depcheck: start\n";
428 
430  DBG_MP << "sync_with_depcheck: correcting era\n";
431  const int new_era_index = create_engine_.dependency_manager().get_era_index();
432 
433  create_engine_.set_current_era_index(new_era_index, true);
434  eras_menu_button_->set_value(new_era_index);
435  }
436 
438  DBG_MP << "sync_with_depcheck: correcting scenario\n";
439 
440  // Match scenario and scenario type
442  const bool different_type = new_level_index.first != create_engine_.current_level_type();
443 
444  if(new_level_index.second != -1) {
445  create_engine_.set_current_level_type(new_level_index.first);
446  create_engine_.set_current_level(new_level_index.second);
447  selected_game_index_ = new_level_index.second;
448 
449  auto& game_types_list = find_widget<menu_button>(get_window(), "game_types", false);
450  game_types_list.set_value(std::distance(level_types_.begin(), std::find_if(level_types_.begin(), level_types_.begin(), [&](const level_type_info& info){ return info.first == new_level_index.first; })));
451 
452  if(different_type) {
453  display_games_of_type(new_level_index.first, create_engine_.current_level().id());
454  } else {
455  // This function (or rather on_game_select) might be triggered by a listbox callback, in
456  // which case we cannot use display_games_of_type since it destroys the list (and its
457  // elements) which might result in segfaults. Instead, we assume that a listbox-triggered
458  // sync_with_depcheck call never changes the game type and goes to this branch instead.
459  find_widget<listbox>(get_window(), "games_list", false).select_row(new_level_index.second);
460 
461  // Override the last selection so on_game_select selects the new level
463 
464  on_game_select();
465  }
466  }
467  }
468 
470  DBG_MP << "sync_with_depcheck: correcting modifications\n";
472  }
473 
475  DBG_MP << "sync_with_depcheck: end\n";
476 }
477 
478 template<typename widget>
479 void mp_create_game::on_filter_change(const std::string& id, bool do_select)
480 {
481  create_engine_.apply_level_filter(find_widget<widget>(get_window(), id, false).get_value());
482 
483  listbox& game_list = find_widget<listbox>(get_window(), "games_list", false);
484 
485  boost::dynamic_bitset<> filtered(game_list.get_item_count());
487  filtered[i] = true;
488  }
489 
490  game_list.set_row_shown(filtered);
491 
492  if(do_select) {
493  on_game_select();
494  }
495 }
496 
498 {
499  const int selected_game = find_widget<listbox>(get_window(), "games_list", false).get_selected_row();
500 
501  if(selected_game == selected_game_index_) {
502  return;
503  }
504 
505  // Convert the absolute-index get_selected_row to a relative index for the create_engine to handle
507 
509 
511 
512  update_details();
513 
514  // General settings
515  const bool can_select_era = create_engine_.current_level().allow_era_choice();
516 
517  if(!can_select_era) {
518  eras_menu_button_->set_label(_("No eras available for this game."));
519  } else {
521  }
522 
523  eras_menu_button_->set_active(can_select_era);
524 
525  // Custom options
526  options_manager_->update_game_options();
527 
528  // Game settings
530 }
531 
533 {
534  const int i = find_widget<listbox>(get_window(), "tab_bar", false).get_selected_row();
535  find_widget<stacked_widget>(get_window(), "pager", false).select_layer(i);
536 }
537 
539 {
540  if(sender && (sender->get_value_bool() == create_engine_.dependency_manager().is_modification_active(index))) {
541  ERR_MP << "ignoring on_mod_toggle that is already set\n";
542  return;
543  }
544 
545  create_engine_.toggle_mod(index);
546 
548 
549  options_manager_->update_mod_options();
550 }
551 
553 {
555 
557 
559 
560  options_manager_->update_era_options();
561 }
562 
564 {
565  selected_rfm_index_ = find_widget<menu_button>(get_window(), "random_faction_mode", false).get_value();
566 }
567 
568 void mp_create_game::show_description(const std::string& new_description)
569 {
570  styled_widget& description = find_widget<styled_widget>(get_window(), "description", false);
571 
572  description.set_label(!new_description.empty() ? new_description : _("No description available."));
573  description.set_use_markup(true);
574 }
575 
577 {
578  const int index = find_widget<menu_button>(get_window(), "game_types", false).get_value();
579 
581 }
582 
584 {
586 
587  listbox& list = find_widget<listbox>(get_window(), "games_list", false);
588 
589  list.clear();
590 
591  for(const auto& game : create_engine_.get_levels_by_type_unfiltered(type)) {
592  std::map<std::string, string_map> data;
594 
595  if(type == level_type::type::campaign || type == level_type::type::sp_campaign) {
596  item["label"] = game->icon();
597  data.emplace("game_icon", item);
598  }
599 
600  item["label"] = game->name();
601  data.emplace("game_name", item);
602 
603  list.add_row(data);
604  }
605 
606  if(!level.empty() && !list.get_rows_shown().empty()) {
607  // Recalculate which rows should be visible
608  on_filter_change<slider>("num_players", false);
609  on_filter_change<text_box>("game_filter", false);
610 
611  int level_index = create_engine_.find_level_by_id(level).second;
612  if(level_index >= 0 && std::size_t(level_index) < list.get_item_count()) {
613  list.select_row(level_index);
614  }
615  }
616 
617  const bool is_random_map = type == level_type::type::random_map;
618 
619  find_widget<button>(get_window(), "random_map_regenerate", false).set_active(is_random_map);
620  find_widget<button>(get_window(), "random_map_settings", false).set_active(is_random_map);
621 
622  // Override the last selection so on_game_select selects the new level
624 
625  on_game_select();
626 }
627 
629 {
631 
633 }
634 
636 {
638 
639  update_details();
640 }
641 
642 int mp_create_game::convert_to_game_filtered_index(const unsigned int initial_index)
643 {
644  const std::vector<std::size_t>& filtered_indices = create_engine_.get_filtered_level_indices(create_engine_.current_level_type());
645  return std::distance(filtered_indices.begin(), std::find(filtered_indices.begin(), filtered_indices.end(), initial_index));
646 }
647 
649 {
650  styled_widget& players = find_widget<styled_widget>(get_window(), "map_num_players", false);
651  styled_widget& map_size = find_widget<styled_widget>(get_window(), "map_size", false);
652 
653  if(create_engine_.current_level_type() == level_type::type::random_map) {
654  // If the current random map doesn't have data, generate it
656  create_engine_.current_level().data()["map_data"].empty() &&
657  create_engine_.current_level().data()["map_file"].empty()) {
659  }
660 
661  find_widget<button>(get_window(), "random_map_settings", false).set_active(create_engine_.generator_has_settings());
662  }
663 
665 
666  // Reset the config_engine with new values
668  config_engine_->update_initial_cfg(create_engine_.current_level().data());
669  config_engine_->set_default_values();
670 
671  // Set the title, with newlines replaced. Newlines are sometimes found in SP Campaign names
672  std::string title = create_engine_.current_level().name();
673  boost::replace_all(title, "\n", " " + font::unicode_em_dash + " ");
674  find_widget<styled_widget>(get_window(), "game_title", false).set_label(title);
675 
677 
679  case level_type::type::scenario:
680  case level_type::type::user_map:
681  case level_type::type::user_scenario:
682  case level_type::type::random_map: {
683  ng::scenario* current_scenario = dynamic_cast<ng::scenario*>(&create_engine_.current_level());
684 
685  assert(current_scenario);
686 
688 
689  find_widget<stacked_widget>(get_window(), "minimap_stack", false).select_layer(0);
690 
691  if(current_scenario->data()["map_data"].empty()) {
692  saved_game::expand_map_file(current_scenario->data());
693  current_scenario->set_metadata();
694  }
695 
696  find_widget<minimap>(get_window(), "minimap", false).set_map_data(current_scenario->data()["map_data"]);
697 
698  players.set_label(std::to_string(current_scenario->num_players()));
699  map_size.set_label(current_scenario->map_size());
700 
701  break;
702  }
703  case level_type::type::campaign:
704  case level_type::type::sp_campaign: {
705  ng::campaign* current_campaign = dynamic_cast<ng::campaign*>(&create_engine_.current_level());
706 
707  assert(current_campaign);
708 
709  create_engine_.get_state().classification().campaign = current_campaign->data()["id"].str();
710 
711  const std::string img = formatter() << current_campaign->data()["image"] << "~SCALE_INTO(265,265)";
712 
713  find_widget<stacked_widget>(get_window(), "minimap_stack", false).select_layer(1);
714  find_widget<image>(get_window(), "campaign_image", false).set_image(img);
715 
716  const int p_min = current_campaign->min_players();
717  const int p_max = current_campaign->max_players();
718 
719  if(p_max > p_min) {
720  players.set_label(VGETTEXT("number of players^$min to $max", {{"min", std::to_string(p_min)}, {"max", std::to_string(p_max)}}));
721  } else {
722  players.set_label(std::to_string(p_min));
723  }
724 
725  map_size.set_label(font::unicode_em_dash);
726 
727  break;
728  }
729  }
730 }
731 
733 {
734  if(config_engine_->force_lock_settings()) {
735  use_map_settings_->widget_set_enabled(false, false);
737  } else {
739  }
740 
742 
743  config_engine_->set_use_map_settings(use_map_settings);
744 
745  fog_ ->widget_set_enabled(!use_map_settings, false);
746  shroud_ ->widget_set_enabled(!use_map_settings, false);
747  start_time_ ->widget_set_enabled(!use_map_settings, false);
748 
749  turns_ ->widget_set_enabled(!use_map_settings, false);
750  gold_ ->widget_set_enabled(!use_map_settings, false);
751  support_ ->widget_set_enabled(!use_map_settings, false);
752  experience_ ->widget_set_enabled(!use_map_settings, false);
753 
754  const bool time_limit = time_limit_->get_widget_value();
755 
756  init_turn_limit_->widget_set_enabled(time_limit, false);
757  turn_bonus_ ->widget_set_enabled(time_limit, false);
758  reservoir_ ->widget_set_enabled(time_limit, false);
759  action_bonus_ ->widget_set_enabled(time_limit, false);
760 
761  find_widget<button>(get_window(), "reset_timer_defaults", false).set_active(time_limit);
762 
763  if(use_map_settings) {
764  fog_ ->set_widget_value(config_engine_->fog_game_default());
765  shroud_ ->set_widget_value(config_engine_->shroud_game_default());
766  start_time_->set_widget_value(config_engine_->random_start_time_default());
767 
768  turns_ ->set_widget_value(config_engine_->num_turns_default());
769  gold_ ->set_widget_value(config_engine_->village_gold_default());
770  support_ ->set_widget_value(config_engine_->village_support_default());
771  experience_->set_widget_value(config_engine_->xp_modifier_default());
772  }
773 }
774 
776 {
778 
779  if(!load.load_multiplayer_game()) {
780  return;
781  }
782 
783  if(load.data().cancel_orders) {
785  }
786 
787  set_retval(LOAD_GAME);
788 }
789 
790 std::vector<std::string> mp_create_game::get_active_mods()
791 {
792  int i = 0;
793  std::set<std::string> res;
795  if(find_widget<toggle_button>(mod_list_->get_row_grid(i), "mod_active_state", false).get_value_bool()) {
796  res.insert(mod->id);
797  }
798  ++i;
799  }
800  return std::vector<std::string>(res.begin(), res.end());
801 }
802 
803 void mp_create_game::set_active_mods(const std::vector<std::string>& val)
804 {
805  std::set<std::string> val2(val.begin(), val.end());
806  int i = 0;
807  std::set<std::string> res;
809  find_widget<toggle_button>(mod_list_->get_row_grid(i), "mod_active_state", false).set_value_bool(val2.find(mod->id) != val2.end());
810  ++i;
811  }
812 }
813 
815 {
816  // This allows the defaults to be returned by the pref getters below
817  preferences::erase("mp_countdown_init_time");
818  preferences::erase("mp_countdown_reservoir_time");
819  preferences::erase("mp_countdown_turn_bonus");
820  preferences::erase("mp_countdown_action_bonus");
821 
826 }
827 
829 {
831  gui2::show_transient_error_message(_("The selected game has no sides!"));
832  return false;
833  }
834 
836  std::stringstream msg;
837  // TRANSLATORS: This sentence will be followed by some details of the error, most likely the "Map could not be loaded" message from create_engine.cpp
838  msg << _("The selected game cannot be created.");
839  msg << "\n\n";
842  return false;
843  }
844 
845  if(!create_engine_.is_campaign()) {
846  return true;
847  }
848 
849  return create_engine_.select_campaign_difficulty() != "CANCEL";
850 }
851 
853 {
854  plugins_context_.reset();
855 
856  // Show all tabs so that find_widget works correctly
857  find_widget<stacked_widget>(&window, "pager", false).select_layer(-1);
858 
859  if(get_retval() == LOAD_GAME) {
861 
862  // We don't need the LOAD_GAME retval past this point. For convenience, reset it to OK so we can use the execute wrapper, then exit.
864  return;
865  }
866 
867  if(get_retval() == retval::OK) {
872 
874 
875  if(create_engine_.current_level_type() == level_type::type::campaign ||
876  create_engine_.current_level_type() == level_type::type::sp_campaign) {
878  } else if(create_engine_.current_level_type() == level_type::type::scenario) {
880  } else {
881  // This means define= doesn't work for randomly generated scenarios
883  }
884 
886 
888 
889  std::vector<const config*> entry_points;
890  std::vector<std::string> entry_point_titles;
891 
892  const auto& tagname = create_engine_.get_state().classification().get_tagname();
893 
894  if(tagname == "scenario") {
895  const std::string first_scenario = create_engine_.current_level().data()["first_scenario"];
896  for(const config& scenario : game_config_manager::get()->game_config().child_range(tagname)) {
897  const bool is_first = scenario["id"] == first_scenario;
898  if(scenario["allow_new_game"].to_bool(false) || is_first || game_config::debug ) {
899  const std::string& title = !scenario["new_game_title"].empty()
900  ? scenario["new_game_title"]
901  : scenario["name"];
902 
903  entry_points.insert(is_first ? entry_points.begin() : entry_points.end(), &scenario);
904  entry_point_titles.insert(is_first ? entry_point_titles.begin() : entry_point_titles.end(), title);
905  }
906  }
907  }
908 
909  if(entry_points.size() > 1) {
910  gui2::dialogs::simple_item_selector dlg(_("Choose Starting Scenario"), _("Select at which point to begin this campaign."), entry_point_titles);
911 
912  dlg.set_single_button(true);
913  dlg.show();
914 
915  const config& scenario = *entry_points[dlg.selected_index()];
916 
917  create_engine_.get_state().mp_settings().hash = scenario.hash();
919  }
920 
921  config_engine_->set_use_map_settings(use_map_settings_->get_widget_value());
922 
923  if(!config_engine_->force_lock_settings()) {
924  // Max slider value (in this case, 100) means 'unlimited turns', so pass the value -1
925  const int num_turns = turns_->get_widget_value();
926  config_engine_->set_num_turns(num_turns < ::settings::turns_max ? num_turns : - 1);
927  config_engine_->set_village_gold(gold_->get_widget_value());
928  config_engine_->set_village_support(support_->get_widget_value());
929  config_engine_->set_xp_modifier(experience_->get_widget_value());
930  config_engine_->set_random_start_time(start_time_->get_widget_value());
931  config_engine_->set_fog_game(fog_->get_widget_value());
932  config_engine_->set_shroud_game(shroud_->get_widget_value());
933 
934  config_engine_->write_parameters();
935  }
936 
937  config_engine_->set_mp_countdown(time_limit_->get_widget_value());
938  config_engine_->set_mp_countdown_init_time(init_turn_limit_->get_widget_value());
939  config_engine_->set_mp_countdown_turn_bonus(turn_bonus_->get_widget_value());
940  config_engine_->set_mp_countdown_reservoir_time(reservoir_->get_widget_value());
941  config_engine_->set_mp_countdown_action_bonus(action_bonus_->get_widget_value());
942 
943  config_engine_->set_allow_observers(observers_->get_widget_value());
944  config_engine_->set_private_replay(private_replay_->get_widget_value());
945  config_engine_->set_oos_debug(strict_sync_->get_widget_value());
946  config_engine_->set_shuffle_sides(shuffle_sides_->get_widget_value());
947 
948  random_faction_mode::type type = random_faction_mode::get_enum(selected_rfm_index_).value_or(random_faction_mode::type::independent);
949  config_engine_->set_random_faction_mode(type);
950 
951  // Since we don't have a field handling this option, we need to save the value manually
953 
954  // Save custom option settings
955  config_engine_->set_options(options_manager_->get_options_config());
956 
957  // Set game name
958  const std::string name = find_widget<text_box>(&window, "game_name", false).get_value();
959  if(!name.empty() && (name != ng::configure_engine::game_name_default())) {
960  config_engine_->set_game_name(name);
961  }
962 
963  // Set game password
964  const std::string password = find_widget<text_box>(&window, "game_password", false).get_value();
965  if(!password.empty()) {
966  config_engine_->set_game_password(password);
967  }
968  }
969 }
970 
971 } // namespace dialogs
const std::string & get_scenario() const
Returns the selected scenario.
Definition: depcheck.hpp:116
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
std::string map_size() const
void set_value_bool(bool value, bool fire_event=false)
void set_text_changed_callback(std::function< void(text_box_base *textbox, const std::string text)> cb)
Set the text_changed callback.
void set_selected(unsigned selected, bool fire_event=true)
void set_village_support(int value)
Definition: game.cpp:666
configure_engine
saved_game & get_state()
const int turns_max
maximum number of turns
void set_current_era_index(const std::size_t index, bool force=false)
The class for loading a savefile.
Definition: savegame.hpp:99
int village_support
Definition: game_config.cpp:56
void set_countdown_action_bonus(int value)
Definition: game.cpp:646
void set_countdown_turn_bonus(int value)
Definition: game.cpp:636
int min_players() const
std::string era()
Definition: game.cpp:681
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:61
void set_shroud(bool value)
Definition: game.cpp:540
void set_countdown_init_time(int value)
Definition: game.cpp:616
bool generator_assigned() const
int xp_modifier()
Definition: game.cpp:671
std::vector< extras_metadata_ptr > & get_extras_by_type(const MP_EXTRA extra_type)
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:172
int get_era_index() const
Returns the selected era.
Definition: depcheck.cpp:399
string_enums::enum_base< random_faction_mode_defines > random_faction_mode
int find_extra_by_id(const MP_EXTRA extra_type, const std::string &id) const
void set_scenario(config scenario)
Definition: saved_game.cpp:567
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
std::pair< level_type::type, std::string > level_type_info
logger & info()
Definition: log.cpp:89
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:210
void set_current_level_type(const level_type::type type)
bool generator_has_settings() const
bool shuffle_sides()
Definition: game.cpp:461
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
void set_widget_value(CT value)
Sets the value of the field.
Definition: field.hpp:344
int max_players() const
std::vector< level_type_info > level_types_
void set_current_level(const std::size_t index)
std::vector< std::size_t > get_filtered_level_indices(level_type::type type) const
virtual void set_value(unsigned value, bool fire_event=false) override
Inherited from selectable_item.
Definition: menu_button.hpp:87
bool is_modification_active(int index) const
Tells whether a certain mod is activated.
Definition: depcheck.cpp:428
void on_mod_toggle(const int index, toggle_button *sender)
void set_random_faction_mode(const std::string &value)
Definition: game.cpp:476
std::vector< level_ptr > get_levels_by_type_unfiltered(level_type::type type) const
STL namespace.
window * get_window() const
Returns a pointer to the dialog&#39;s window.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed...
static std::string _(const char *str)
Definition: gettext.hpp:93
bool show(const unsigned auto_close_time=0)
Shows the window.
void set_countdown(bool value)
Definition: game.cpp:606
const mp_game_settings & get_parameters()
void prepare_for_era_and_mods()
bool is_campaign() const
Wrapper to simplify the is-type-campaign-or-sp-campaign check.
int countdown_init_time()
Definition: game.cpp:611
bool current_level_has_side_data()
Returns true if the current level has one or more [side] tags.
static constexpr std::size_t size()
Definition: enum_base.hpp:79
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:246
Class for a single line text area.
Definition: text_box.hpp:141
std::string select_campaign_difficulty(int set_value=-1)
select_campaign_difficulty
virtual std::string id() const
const config & data() const
std::vector< std::string > get_active_mods()
bool fog()
Definition: game.cpp:525
virtual void set_label(const t_string &label)
The listbox class.
Definition: listbox.hpp:45
Base container class.
Definition: grid.hpp:31
static game_config_manager * get()
virtual unsigned get_value() const override
Inherited from selectable_item.
Definition: menu_button.hpp:84
field_bool * use_map_settings_
All fields are also in the normal field vector, but they need to be manually controlled as well so ad...
int countdown_turn_bonus()
Definition: game.cpp:631
bool toggle_mod(int index, bool force=false)
void set_level_type(int value)
Definition: game.cpp:706
static void expand_map_file(config &scenario)
reads scenario["map_file"]
Definition: saved_game.cpp:461
A simple one-column listbox with OK and Cancel buttons.
void set_tooltip(const t_string &tooltip)
std::string level()
Definition: game.cpp:691
#define DBG_MP
int num_players() const
virtual std::string name() const
void set_metadata()
const std::string & id() const
Definition: field.hpp:183
void prepare_for_campaign(const std::string &difficulty="")
std::ostringstream wrapper.
Definition: formatter.hpp:39
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:121
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
std::unique_ptr< plugins_context > plugins_context_
int countdown_reservoir_time()
Definition: game.cpp:621
void erase(const std::string &key)
Definition: general.cpp:207
void set_random_start_time(bool value)
Definition: game.cpp:520
virtual void set_metadata()=0
static lg::log_domain log_mp_create("mp/create")
void set_values(const std::vector<::config > &values, unsigned selected=0)
Modify, read and display user preferences.
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:127
virtual void set_use_markup(bool use_markup)
int village_gold()
Definition: game.cpp:651
std::string campaign
The id of the campaign being played.
#define ERR_MP
ng::create_engine create_engine_
General settings and defaults for scenarios.
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
void set_active_mods(const std::vector< std::string > &val)
std::vector< std::string > & active_mods()
void apply_level_filter(const std::string &name)
void init_generated_level_data()
static const int LOAD_GAME
void set_level(const std::string &value)
Definition: game.cpp:696
#define UPDATE_ATTRIBUTE(field, convert)
static std::string game_name_default()
virtual std::string icon() const
void set_allow_observers(bool value)
Definition: game.cpp:456
bool countdown()
Definition: game.cpp:601
bool shroud()
Definition: game.cpp:535
tooltips.
std::size_t i
Definition: function.cpp:967
level_type::type current_level_type() const
void on_filter_change(const std::string &id, bool do_select)
int selected_index() const
Returns the selected item index after displaying.
level & current_level() const
std::unique_ptr< mp_options_helper > options_manager_
int convert_to_game_filtered_index(const unsigned int initial_index)
Game configuration data as global variables.
Definition: build_info.cpp:60
The user set the widget invisible, that means:
static std::string get_string(typename T::type key)
Uses the int value of the provided enum to get the associated index of the values array in the implem...
Definition: enum_base.hpp:41
std::vector< std::string > names
Definition: build_info.cpp:67
std::string password(const std::string &server, const std::string &login)
bool use_map_settings()
Definition: game.cpp:481
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
std::pair< level_type::type, int > find_level_by_id(const std::string &id) const
T get_widget_value()
Gets the value of the field.
Definition: field.hpp:379
grid & add_row(const string_map &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:62
Declarations for File-IO.
int w
const bool & debug
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
Base class for all visible items.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
virtual std::string description() const
boost::dynamic_bitset get_rows_shown() const
Returns a list of visible rows.
Definition: listbox.cpp:217
std::size_t current_era_index() const
void set_turns(int value)
Definition: game.cpp:550
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:233
bool allow_observers()
Definition: game.cpp:451
void cancel_orders()
Definition: saved_game.cpp:686
void show_transient_error_message(const std::string &message, const std::string &image, const bool message_use_markup)
Shows a transient error message to the user.
int turns()
Definition: game.cpp:545
int get_retval() const
Returns the cached window exit code.
const std::vector< std::string > & modifications(bool mp)
Definition: game.cpp:711
const extras_metadata & current_era() const
const depcheck::manager & dependency_manager() const
static std::optional< typename T::type > get_enum(const std::string value)
Convert a string into its enum equivalent.
Definition: enum_base.hpp:52
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:28
void widget_set_enabled(const bool enable, const bool sync)
Enables a widget.
Definition: field.hpp:159
game_classification & classification()
Definition: saved_game.hpp:55
void set_modifications(const std::vector< std::string > &value, bool mp)
Definition: game.cpp:720
const std::string unicode_em_dash
Definition: constants.cpp:44
const std::vector< std::string > & get_modifications() const
Returns the enabled modifications.
Definition: depcheck.hpp:123
Standard logging facilities (interface).
Base class for all level type classes.
std::unique_ptr< ng::configure_engine > config_engine_
const std::vector< extras_metadata_ptr > & get_const_extras_by_type(const MP_EXTRA extra_type) const
int countdown_action_bonus()
Definition: game.cpp:641
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:75
void set_era(const std::string &value)
Definition: game.cpp:686
void set_shuffle_sides(bool value)
Definition: game.cpp:466
void set_fog(bool value)
Definition: game.cpp:530
Dialog was closed with the OK button.
Definition: retval.hpp:35
bool dialog_exit_hook(window &)
Dialog exit hook to bring up the difficulty dialog when starting a campaign.
void display_games_of_type(level_type::type type, const std::string &level)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
void show_description(const std::string &new_description)
void set_countdown_reservoir_time(int value)
Definition: game.cpp:626
bool random_start_time()
Definition: game.cpp:515
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:59
void set_xp_modifier(int value)
Definition: game.cpp:676
void set_retval(int retval)
Convenience wrapper to set the window&#39;s exit code.
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:66
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:139
std::string hash() const
Definition: config.cpp:1392
void set_village_gold(int value)
Definition: game.cpp:656
void set_use_map_settings(bool value)
Definition: game.cpp:486
bool empty() const
Definition: config.cpp:941
std::string get_tagname() const
Class for a toggle button.
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:198
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410
virtual bool allow_era_choice() const
virtual bool can_launch_game() const =0
void set_single_button(bool value)
Sets whether the Cancel button should be hidden or not.
int level_type()
Definition: game.cpp:701