The Battle for Wesnoth  1.15.5+dev
mp_create_game.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
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 "filesystem.hpp"
20 #include "formatter.hpp"
21 #include "formula/string_utils.hpp"
22 #include "game_config.hpp"
23 #include "game_config_manager.hpp"
25 #include "gettext.hpp"
26 #include "gui/auxiliary/field.hpp"
27 #include "gui/dialogs/message.hpp"
30 #include "gui/widgets/button.hpp"
31 #include "gui/widgets/image.hpp"
33 #include "gui/widgets/listbox.hpp"
35 #include "gui/widgets/minimap.hpp"
36 #include "gui/widgets/settings.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 #include "game_config_view.hpp"
49 
50 #include <boost/algorithm/string.hpp>
51 
52 static lg::log_domain log_mp_create("mp/create");
53 
54 #define DBG_MP LOG_STREAM(debug, log_mp_create)
55 #define WRN_MP LOG_STREAM(warn, log_mp_create)
56 #define ERR_MP LOG_STREAM(err, log_mp_create)
57 
58 namespace gui2
59 {
60 namespace dialogs
61 {
62 
63 // Special retval value for loading a game
64 static const int LOAD_GAME = 100;
65 
66 // Shorthand
67 namespace prefs = preferences;
68 
70 
71 mp_create_game::mp_create_game(const game_config_view& cfg, saved_game& state, bool local_mode, mp::user_info* host_info)
72  : cfg_(cfg)
73  , create_engine_(state)
74  , config_engine_()
75  , options_manager_()
76  , selected_game_index_(-1)
77  , selected_rfm_index_(-1)
78  , use_map_settings_(register_bool( "use_map_settings", true, prefs::use_map_settings, prefs::set_use_map_settings,
79  std::bind(&mp_create_game::update_map_settings, this)))
80  , fog_(register_bool("fog", true, prefs::fog, prefs::set_fog))
81  , shroud_(register_bool("shroud", true, prefs::shroud, prefs::set_shroud))
82  , start_time_(register_bool("random_start_time", true, prefs::random_start_time, prefs::set_random_start_time))
83  , time_limit_(register_bool("time_limit", true, prefs::countdown, prefs::set_countdown,
84  std::bind(&mp_create_game::update_map_settings, this)))
85  , shuffle_sides_(register_bool("shuffle_sides", true, prefs::shuffle_sides, prefs::set_shuffle_sides))
86  , observers_(register_bool("observers", true, prefs::allow_observers, prefs::set_allow_observers))
87  , registered_users_(register_bool("registered_users", true, prefs::registered_users_only, prefs::set_registered_users_only))
88  , strict_sync_(register_bool("strict_sync", true))
89  , private_replay_(register_bool("private_replay", true))
90  , turns_(register_integer("turn_count", true, prefs::turns, prefs::set_turns))
91  , gold_(register_integer("village_gold", true, prefs::village_gold, prefs::set_village_gold))
92  , support_(register_integer("village_support", true, prefs::village_support, prefs::set_village_support))
93  , experience_(register_integer("experience_modifier", true, prefs::xp_modifier, prefs::set_xp_modifier))
94  , init_turn_limit_(register_integer("init_turn_limit", true, prefs::countdown_init_time, prefs::set_countdown_init_time))
95  , turn_bonus_(register_integer("turn_bonus", true, prefs::countdown_turn_bonus, prefs::set_countdown_turn_bonus))
96  , reservoir_(register_integer("reservoir", true, prefs::countdown_reservoir_time, prefs::set_countdown_reservoir_time))
97  , action_bonus_(register_integer("action_bonus", true, prefs::countdown_action_bonus, prefs::set_countdown_action_bonus))
98  , mod_list_()
99  , eras_menu_button_()
100  , local_mode_(local_mode)
101  , host_info_(host_info)
102 {
103  level_types_ = {
104  {ng::level::TYPE::SCENARIO, _("Scenarios")},
105  {ng::level::TYPE::CAMPAIGN, _("Multiplayer Campaigns")},
106  {ng::level::TYPE::SP_CAMPAIGN, _("Singleplayer Campaigns")},
107  {ng::level::TYPE::USER_MAP, _("Custom Maps")},
108  {ng::level::TYPE::USER_SCENARIO, _("Custom Scenarios")},
109  {ng::level::TYPE::RANDOM_MAP, _("Random Maps")},
110  };
111 
112  level_types_.erase(std::remove_if(level_types_.begin(), level_types_.end(),
113  [this](level_type_info& type_info) {
114  return create_engine_.get_levels_by_type_unfiltered(type_info.first).empty();
115  }), level_types_.end());
116 
117  rfm_types_ = {
118  mp_game_settings::RANDOM_FACTION_MODE::DEFAULT,
119  mp_game_settings::RANDOM_FACTION_MODE::NO_MIRROR,
120  mp_game_settings::RANDOM_FACTION_MODE::NO_ALLY_MIRROR,
121  };
122 
123  set_show_even_without_video(true);
124 
125  create_engine_.init_active_mods();
126 
127  create_engine_.get_state().clear();
128  create_engine_.get_state().classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
129 
130  // Need to set this in the constructor, pre_show() is too late
131  set_allow_plugin_skip(false);
132 }
133 
135 {
136  find_widget<minimap>(&win, "minimap", false).set_config(&cfg_);
137 
138  find_widget<text_box>(&win, "game_name", false).set_value(local_mode_ ? "" : ng::configure_engine::game_name_default());
139 
141  find_widget<button>(&win, "random_map_regenerate", false),
142  std::bind(&mp_create_game::regenerate_random_map, this, std::ref(win)));
143 
145  find_widget<button>(&win, "random_map_settings", false),
146  std::bind(&mp_create_game::show_generator_settings, this, std::ref(win)));
147 
149  find_widget<button>(&win, "load_game", false),
150  std::bind(&mp_create_game::load_game_callback, this, std::ref(win)));
151 
152  // Custom dialog close hook
153  win.set_exit_hook_ok_only([this](window& w)->bool { return dialog_exit_hook(w); });
154 
155  //
156  // Set up the options manager. Needs to be done before selecting an initial tab
157  //
159 
160  //
161  // Set up filtering
162  //
163  connect_signal_notify_modified(find_widget<slider>(&win, "num_players", false),
164  std::bind(&mp_create_game::on_filter_change<slider>, this, std::ref(win), "num_players", true));
165 
166  text_box& filter = find_widget<text_box>(&win, "game_filter", false);
167 
169  std::bind(&mp_create_game::on_filter_change<text_box>, this, std::ref(win), "game_filter", true));
170 
171  // Note this cannot be in the keyboard chain or it will capture focus from other text boxes
172  win.keyboard_capture(&filter);
173 
174  //
175  // Set up game types menu_button
176  //
177  std::vector<config> game_types;
178  for(level_type_info& type_info : level_types_) {
179  game_types.emplace_back("label", type_info.second);
180  }
181 
182  if(game_types.empty()) {
183  gui2::show_transient_message("", _("No games found."));
184  throw game::error(_("No games found."));
185  }
186 
187  menu_button& game_menu_button = find_widget<menu_button>(&win, "game_types", false);
188 
189  // Helper to make sure the initially selected level type is valid
190  auto get_initial_type_index = [this]()->int {
191  const auto index = std::find_if(level_types_.begin(), level_types_.end(), [](level_type_info& info) {
192  return info.first == ng::level::TYPE::from_int(preferences::level_type());
193  });
194 
195  if(index != level_types_.end()) {
196  return std::distance(level_types_.begin(), index);
197  }
198 
199  return 0;
200  };
201 
202  game_menu_button.set_values(game_types, get_initial_type_index());
203 
204  connect_signal_notify_modified(game_menu_button,
205  std::bind(&mp_create_game::update_games_list, this, std::ref(win)));
206 
207  //
208  // Set up mods list
209  //
210  mod_list_ = &find_widget<listbox>(&win, "mod_list", false);
211 
212  const auto& activemods = preferences::modifications();
214  std::map<std::string, string_map> data;
216 
217  item["label"] = mod->name;
218  data.emplace("mod_name", item);
219 
220  grid* row_grid = &mod_list_->add_row(data);
221 
222  find_widget<toggle_panel>(row_grid, "panel", false).set_tooltip(mod->description);
223 
224  toggle_button& mog_toggle = find_widget<toggle_button>(row_grid, "mod_active_state", false);
225 
226  const int i = mod_list_->get_item_count() - 1;
227  if(std::find(activemods.begin(), activemods.end(), mod->id) != activemods.end()) {
228  create_engine_.active_mods().push_back(mod->id);
229  mog_toggle.set_value_bool(true);
230  }
231 
232  connect_signal_notify_modified(mog_toggle, std::bind(&mp_create_game::on_mod_toggle, this, std::ref(win), i, &mog_toggle));
233  }
234 
235  // No mods, hide the header
236  if(mod_list_->get_item_count() <= 0) {
237  find_widget<styled_widget>(&win, "mods_header", false).set_visible(widget::visibility::invisible);
238  }
239 
240  //
241  // Set up eras menu_button
242  //
243  eras_menu_button_ = &find_widget<menu_button>(&win, "eras", false);
244 
245  std::vector<config> era_names;
247  era_names.emplace_back("label", era->name, "tooltip", era->description);
248  }
249 
250  if(era_names.empty()) {
251  gui2::show_transient_message("", _("No eras found."));
252  throw config::error(_("No eras found"));
253  }
254 
255  eras_menu_button_->set_values(era_names);
256 
258  std::bind(&mp_create_game::on_era_select, this, std::ref(win)));
259 
261  if(era_selection >= 0) {
262  eras_menu_button_->set_selected(era_selection);
263  }
264 
265  on_era_select(win);
266 
267  //
268  // Set up random faction mode menu_button
269  //
270  std::vector<config> rfm_options;
271  for(const auto& type : rfm_types_) {
272  // HACK: The labels are defined for the wesnoth textdomain in a header,
273  // see mp_game_settings::RANDOM_FACTION_MODE in src/mp_game_settings.hpp
274  rfm_options.emplace_back("label",
275  translation::dsgettext("wesnoth", mp_game_settings::RANDOM_FACTION_MODE::enum_to_string(type).c_str())
276  );
277  };
278 
279  // Manually insert tooltips. Need to find a better way to do this
280  rfm_options[0]["tooltip"] = _("Independent: Random factions assigned independently");
281  rfm_options[1]["tooltip"] = _("No Mirror: No two players will get the same faction");
282  rfm_options[2]["tooltip"] = _("No Ally Mirror: No two allied players will get the same faction");
283 
284  const int initial_index = std::distance(rfm_types_.begin(), std::find(rfm_types_.begin(), rfm_types_.end(),
285  mp_game_settings::RANDOM_FACTION_MODE::string_to_enum(prefs::random_faction_mode(), mp_game_settings::RANDOM_FACTION_MODE::DEFAULT))
286  );
287 
288  menu_button& rfm_menu_button = find_widget<menu_button>(&win, "random_faction_mode", false);
289 
290  rfm_menu_button.set_values(rfm_options, initial_index);
291 
292  connect_signal_notify_modified(rfm_menu_button,
293  std::bind(&mp_create_game::on_random_faction_mode_select, this, std::ref(win)));
294 
296 
297  //
298  // Set up the setting status labels
299  //
300  bind_status_label<slider>(&win, turns_->id());
301  bind_status_label<slider>(&win, gold_->id());
302  bind_status_label<slider>(&win, support_->id());
303  bind_status_label<slider>(&win, experience_->id());
304 
305  bind_status_label<slider>(&win, init_turn_limit_->id());
306  bind_status_label<slider>(&win, turn_bonus_->id());
307  bind_status_label<slider>(&win, reservoir_->id());
308  bind_status_label<slider>(&win, action_bonus_->id());
309 
310  //
311  // Disable certain settings if we're playing a local game.
312  //
313 
314  // Don't allow a 'registered users only' game if the host themselves isn't registered.
316  registered_users_->widget_set_enabled(win, false, false);
317  }
318 
319  if(local_mode_) {
320  find_widget<text_box>(&win, "game_name", false).set_active(false);
321  find_widget<text_box>(&win, "game_password", false).set_active(false);
322 
323  observers_->widget_set_enabled(win, false, false);
324  strict_sync_->widget_set_enabled(win, false, false);
325  private_replay_->widget_set_enabled(win, false, false);
326  }
327 
328  //
329  // Set up tab control
330  //
331  listbox& tab_bar = find_widget<listbox>(&win, "tab_bar", false);
332 
334  std::bind(&mp_create_game::on_tab_select, this, std::ref(win)));
335 
336  // Allow the settings stack to find widgets in all pages, regardless of which is selected.
337  // This ensures settings (especially game settings) widgets are appropriately updated when
338  // a new game is selected, regardless of which settings tab is active at the time.
339  find_widget<stacked_widget>(&win, "pager", false).set_find_in_all_layers(true);
340 
341  // We call on_tab_select farther down.
342 
343  //
344  // Main games list
345  //
346  listbox& list = find_widget<listbox>(&win, "games_list", false);
347 
349  std::bind(&mp_create_game::on_game_select, this, std::ref(win)));
350 
351  win.add_to_keyboard_chain(&list);
352 
353  // This handles the initial game selection as well
354  display_games_of_type(win, level_types_[get_initial_type_index()].first, preferences::level());
355 
356  // Initial tab selection must be done after game selection so the field widgets are set to their correct active state.
357  on_tab_select(win);
358 
359  //
360  // Set up the Lua plugin context
361  //
362  plugins_context_.reset(new plugins_context("Multiplayer Create"));
363 
364  plugins_context_->set_callback("create", [&win](const config&) { win.set_retval(retval::OK); }, false);
365  plugins_context_->set_callback("quit", [&win](const config&) { win.set_retval(retval::CANCEL); }, false);
366  plugins_context_->set_callback("load", [this, &win](const config&) { load_game_callback(win); }, false);
367 
368 #define UPDATE_ATTRIBUTE(field, convert) \
369  do { if(cfg.has_attribute(#field)) { field##_->set_widget_value(win, cfg[#field].convert()); } } while(false) \
370 
371  plugins_context_->set_callback("update_settings", [this, &win](const config& cfg) {
372  UPDATE_ATTRIBUTE(turns, to_int);
373  UPDATE_ATTRIBUTE(gold, to_int);
374  UPDATE_ATTRIBUTE(support, to_int);
375  UPDATE_ATTRIBUTE(experience, to_int);
376  UPDATE_ATTRIBUTE(start_time, to_bool);
377  UPDATE_ATTRIBUTE(fog, to_bool);
378  UPDATE_ATTRIBUTE(shroud, to_bool);
379  UPDATE_ATTRIBUTE(time_limit, to_bool);
380  UPDATE_ATTRIBUTE(init_turn_limit, to_int);
381  UPDATE_ATTRIBUTE(turn_bonus, to_int);
382  UPDATE_ATTRIBUTE(reservoir, to_int);
383  UPDATE_ATTRIBUTE(action_bonus, to_int);
384  UPDATE_ATTRIBUTE(observers, to_bool);
385  UPDATE_ATTRIBUTE(registered_users, to_bool);
386  UPDATE_ATTRIBUTE(strict_sync, to_bool);
387  UPDATE_ATTRIBUTE(private_replay, to_bool);
389  }, true);
390 
391 #undef UPDATE_ATTRIBUTE
392 
393  plugins_context_->set_callback("set_name", [this](const config& cfg) {
394  config_engine_->set_game_name(cfg["name"]); }, true);
395 
396  plugins_context_->set_callback("set_password", [this](const config& cfg) {
397  config_engine_->set_game_password(cfg["password"]); }, true);
398 
399  plugins_context_->set_callback("select_level", [this](const config& cfg) {
400  selected_game_index_ = convert_to_game_filtered_index(cfg["index"].to_int());
402  }, true);
403 
404  plugins_context_->set_callback("select_type", [this](const config& cfg) {
405  create_engine_.set_current_level_type(ng::level::TYPE::string_to_enum(cfg["type"], ng::level::TYPE::SCENARIO)); }, true);
406 
407  plugins_context_->set_callback("select_era", [this](const config& cfg) {
408  create_engine_.set_current_era_index(cfg["index"].to_int()); }, true);
409 
410  plugins_context_->set_callback("select_mod", [this, &win](const config& cfg) {
411  on_mod_toggle(win, cfg["index"].to_int(), nullptr);
412  }, true);
413 
414  //plugins_context_->set_accessor("game_config", [this](const config&) {return cfg_; });
415  plugins_context_->set_accessor("get_selected", [this](const config&) {
416  const ng::level& current_level = create_engine_.current_level();
417  return config {
418  "id", current_level.id(),
419  "name", current_level.name(),
420  "icon", current_level.icon(),
421  "description", current_level.description(),
422  "allow_era_choice", current_level.allow_era_choice(),
424  };
425  });
426 
427  plugins_context_->set_accessor("find_level", [this](const config& cfg) {
428  const std::string id = cfg["id"].str();
429  return config {
430  "index", create_engine_.find_level_by_id(id).second,
431  "type", create_engine_.find_level_by_id(id).first,
432  };
433  });
434 
435  plugins_context_->set_accessor_int("find_era", [this](const config& cfg) {
437  });
438 
439  plugins_context_->set_accessor_int("find_mod", [this](const config& cfg) {
441  });
442 }
443 
445 {
446  DBG_MP << "sync_with_depcheck: start\n";
447 
448  if(static_cast<int>(create_engine_.current_era_index()) != create_engine_.dependency_manager().get_era_index()) {
449  DBG_MP << "sync_with_depcheck: correcting era\n";
450  const int new_era_index = create_engine_.dependency_manager().get_era_index();
451 
452  create_engine_.set_current_era_index(new_era_index, true);
453  eras_menu_button_->set_value(new_era_index);
454  }
455 
456  if(create_engine_.current_level().id() != create_engine_.dependency_manager().get_scenario()) {
457  DBG_MP << "sync_with_depcheck: correcting scenario\n";
458 
459  // Match scenario and scenario type
460  const auto new_level_index = create_engine_.find_level_by_id(create_engine_.dependency_manager().get_scenario());
461  const bool different_type = new_level_index.first != create_engine_.current_level_type();
462 
463  if(new_level_index.second != -1) {
464  create_engine_.set_current_level_type(new_level_index.first);
465  create_engine_.set_current_level(new_level_index.second);
466  selected_game_index_ = new_level_index.second;
467 
468  auto& game_types_list = find_widget<menu_button>(&window, "game_types", false);
469  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; })));
470 
471  if(different_type) {
472  display_games_of_type(window, new_level_index.first, create_engine_.current_level().id());
473  } else {
474  // This function (or rather on_game_select) might be triggered by a listbox callback, in
475  // which case we cannot use display_games_of_type since it destroys the list (and its
476  // elements) which might result in segfaults. Instead, we assume that a listbox-triggered
477  // sync_with_depcheck call never changes the game type and goes to this branch instead.
478  find_widget<listbox>(&window, "games_list", false).select_row(new_level_index.second);
479 
480  // Override the last selection so on_game_select selects the new level
482 
483  on_game_select(window);
484  }
485  }
486  }
487 
488  if(get_active_mods() != create_engine_.dependency_manager().get_modifications()) {
489  DBG_MP << "sync_with_depcheck: correcting modifications\n";
490  set_active_mods(create_engine_.dependency_manager().get_modifications());
491  }
492 
494  DBG_MP << "sync_with_depcheck: end\n";
495 }
496 
497 template<typename widget>
499 {
500  create_engine_.apply_level_filter(find_widget<widget>(&window, id, false).get_value());
501 
502  listbox& game_list = find_widget<listbox>(&window, "games_list", false);
503 
504  boost::dynamic_bitset<> filtered(game_list.get_item_count());
506  filtered[i] = true;
507  }
508 
509  game_list.set_row_shown(filtered);
510 
511  if(do_select) {
512  on_game_select(window);
513  }
514 }
515 
517 {
518  const int selected_game = find_widget<listbox>(&window, "games_list", false).get_selected_row();
519 
520  if(selected_game == selected_game_index_) {
521  return;
522  }
523 
524  // Convert the absolute-index get_selected_row to a relative index for the create_engine to handle
526 
528 
529  sync_with_depcheck(window);
530 
531  update_details(window);
532 
533  // General settings
534  const bool can_select_era = create_engine_.current_level().allow_era_choice();
535 
536  if(!can_select_era) {
537  eras_menu_button_->set_label(_("No eras available for this game."));
538  } else {
540  }
541 
542  eras_menu_button_->set_active(can_select_era);
543 
544  // Custom options
545  options_manager_->update_game_options();
546 
547  // Game settings
549 }
550 
552 {
553  const int i = find_widget<listbox>(&window, "tab_bar", false).get_selected_row();
554  find_widget<stacked_widget>(&window, "pager", false).select_layer(i);
555 }
556 
558 {
559  if(sender && (sender->get_value_bool() == create_engine_.dependency_manager().is_modification_active(index))) {
560  ERR_MP << "ignoring on_mod_toggle that is already set\n";
561  return;
562  }
563 
564  create_engine_.toggle_mod(index);
565 
566  sync_with_depcheck(window);
567 
568  options_manager_->update_mod_options();
569 }
570 
572 {
574 
576 
577  sync_with_depcheck(window);
578 
579  options_manager_->update_era_options();
580 }
581 
583 {
584  selected_rfm_index_ = find_widget<menu_button>(&window, "random_faction_mode", false).get_value();
585 }
586 
588 {
589  styled_widget& description = find_widget<styled_widget>(&window, "description", false);
590 
591  description.set_label(!new_description.empty() ? new_description : _("No description available."));
592  description.set_use_markup(true);
593 }
594 
596 {
597  const int index = find_widget<menu_button>(&window, "game_types", false).get_value();
598 
600 }
601 
603 {
605 
606  listbox& list = find_widget<listbox>(&window, "games_list", false);
607 
608  list.clear();
609 
610  for(const auto& game : create_engine_.get_levels_by_type_unfiltered(type)) {
611  std::map<std::string, string_map> data;
613 
614  if(type == ng::level::TYPE::CAMPAIGN || type == ng::level::TYPE::SP_CAMPAIGN) {
615  item["label"] = game->icon();
616  data.emplace("game_icon", item);
617  }
618 
619  item["label"] = game->name();
620  data.emplace("game_name", item);
621 
622  list.add_row(data);
623  }
624 
625  if(!level.empty() && !list.get_rows_shown().empty()) {
626  // Recalculate which rows should be visible
627  on_filter_change<slider>(window, "num_players", false);
628  on_filter_change<text_box>(window, "game_filter", false);
629 
630  int level_index = create_engine_.find_level_by_id(level).second;
631  if(level_index >= 0 && std::size_t(level_index) < list.get_item_count()) {
632  list.select_row(level_index);
633  }
634  }
635 
636  const bool is_random_map = type == ng::level::TYPE::RANDOM_MAP;
637 
638  find_widget<button>(&window, "random_map_regenerate", false).set_active(is_random_map);
639  find_widget<button>(&window, "random_map_settings", false).set_active(is_random_map);
640 
641  // Override the last selection so on_game_select selects the new level
643 
644  on_game_select(window);
645 }
646 
648 {
650 
651  regenerate_random_map(window);
652 }
653 
655 {
657 
658  update_details(window);
659 }
660 
661 int mp_create_game::convert_to_game_filtered_index(const unsigned int initial_index)
662 {
663  const std::vector<std::size_t>& filtered_indices = create_engine_.get_filtered_level_indices(create_engine_.current_level_type());
664  return std::distance(filtered_indices.begin(), std::find(filtered_indices.begin(), filtered_indices.end(), initial_index));
665 }
666 
668 {
669  styled_widget& players = find_widget<styled_widget>(&win, "map_num_players", false);
670  styled_widget& map_size = find_widget<styled_widget>(&win, "map_size", false);
671 
672  if(create_engine_.current_level_type() == ng::level::TYPE::RANDOM_MAP) {
673  // If the current random map doesn't have data, generate it
675  create_engine_.current_level().data()["map_data"].empty() &&
676  create_engine_.current_level().data()["map_file"].empty()) {
678  }
679 
680  find_widget<button>(&win, "random_map_settings", false).set_active(create_engine_.generator_has_settings());
681  }
682 
683  create_engine_.current_level().set_metadata();
684 
685  // Reset the config_engine with new values
687  config_engine_->update_initial_cfg(create_engine_.current_level().data());
688  config_engine_->set_default_values();
689 
690  // Set the title, with newlines replaced. Newlines are sometimes found in SP Campaign names
692  boost::replace_all(title, "\n", " " + font::unicode_em_dash + " ");
693  find_widget<styled_widget>(&win, "game_title", false).set_label(title);
694 
696 
697  switch(create_engine_.current_level_type().v) {
699  case ng::level::TYPE::USER_MAP:
700  case ng::level::TYPE::USER_SCENARIO:
701  case ng::level::TYPE::RANDOM_MAP: {
702  ng::scenario* current_scenario = dynamic_cast<ng::scenario*>(&create_engine_.current_level());
703 
704  assert(current_scenario);
705 
707 
708  find_widget<stacked_widget>(&win, "minimap_stack", false).select_layer(0);
709 
710  if(current_scenario->data()["map_data"].empty()) {
711  saved_game::expand_map_file(current_scenario->data());
712  current_scenario->set_metadata();
713  }
714 
715  find_widget<minimap>(&win, "minimap", false).set_map_data(current_scenario->data()["map_data"]);
716 
717  players.set_label(std::to_string(current_scenario->num_players()));
718  map_size.set_label(current_scenario->map_size());
719 
720  break;
721  }
722  case ng::level::TYPE::CAMPAIGN:
723  case ng::level::TYPE::SP_CAMPAIGN: {
724  ng::campaign* current_campaign = dynamic_cast<ng::campaign*>(&create_engine_.current_level());
725 
726  assert(current_campaign);
727 
728  create_engine_.get_state().classification().campaign = current_campaign->data()["id"].str();
729 
730  const std::string img = formatter() << current_campaign->data()["image"] << "~SCALE_INTO(265,265)";
731 
732  find_widget<stacked_widget>(&win, "minimap_stack", false).select_layer(1);
733  find_widget<image>(&win, "campaign_image", false).set_image(img);
734 
735  const int p_min = current_campaign->min_players();
736  const int p_max = current_campaign->max_players();
737 
738  if(p_max > p_min) {
739  players.set_label(VGETTEXT("number of players^$min to $max", {{"min", std::to_string(p_min)}, {"max", std::to_string(p_max)}}));
740  } else {
741  players.set_label(std::to_string(p_min));
742  }
743 
744  map_size.set_label(font::unicode_em_dash);
745 
746  break;
747  }
748  }
749 }
750 
752 {
753  window& window = *get_window();
754 
755  if(config_engine_->force_lock_settings()) {
756  use_map_settings_->widget_set_enabled(window, false, false);
757  use_map_settings_->set_widget_value(window, true);
758  } else {
759  use_map_settings_->widget_set_enabled(window, true, false);
760  }
761 
763 
764  config_engine_->set_use_map_settings(use_map_settings);
765 
766  fog_ ->widget_set_enabled(window, !use_map_settings, false);
767  shroud_ ->widget_set_enabled(window, !use_map_settings, false);
768  start_time_ ->widget_set_enabled(window, !use_map_settings, false);
769 
770  turns_ ->widget_set_enabled(window, !use_map_settings, false);
771  gold_ ->widget_set_enabled(window, !use_map_settings, false);
772  support_ ->widget_set_enabled(window, !use_map_settings, false);
773  experience_ ->widget_set_enabled(window, !use_map_settings, false);
774 
775  const bool time_limit = time_limit_->get_widget_value(window);
776 
777  init_turn_limit_->widget_set_enabled(window, time_limit, false);
778  turn_bonus_ ->widget_set_enabled(window, time_limit, false);
779  reservoir_ ->widget_set_enabled(window, time_limit, false);
780  action_bonus_ ->widget_set_enabled(window, time_limit, false);
781 
782  if(use_map_settings) {
783  fog_ ->set_widget_value(window, config_engine_->fog_game_default());
784  shroud_ ->set_widget_value(window, config_engine_->shroud_game_default());
785  start_time_->set_widget_value(window, config_engine_->random_start_time_default());
786 
787  turns_ ->set_widget_value(window, config_engine_->num_turns_default());
788  gold_ ->set_widget_value(window, config_engine_->village_gold_default());
789  support_ ->set_widget_value(window, config_engine_->village_support_default());
790  experience_->set_widget_value(window, config_engine_->xp_modifier_default());
791  }
792 }
793 
795 {
797 
798  if(!load.load_multiplayer_game()) {
799  return;
800  }
801 
802  if(load.data().cancel_orders) {
804  }
805 
806  window.set_retval(LOAD_GAME);
807 }
808 
809 std::vector<std::string> mp_create_game::get_active_mods()
810 {
811  int i = 0;
812  std::set<std::string> res;
814  if(find_widget<toggle_button>(mod_list_->get_row_grid(i), "mod_active_state", false).get_value_bool()) {
815  res.insert(mod->id);
816  }
817  ++i;
818  }
819  return std::vector<std::string>(res.begin(), res.end());
820 }
821 
822 void mp_create_game::set_active_mods(const std::vector<std::string>& val)
823 {
824  std::set<std::string> val2(val.begin(), val.end());
825  int i = 0;
826  std::set<std::string> res;
828  find_widget<toggle_button>(mod_list_->get_row_grid(i), "mod_active_state", false).set_value_bool(val2.find(mod->id) != val2.end());
829  ++i;
830  }
831 }
832 
834 {
836  gui2::show_transient_error_message(_("The selected game has no sides!"));
837  return false;
838  }
839 
841  std::stringstream msg;
842  // 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
843  msg << _("The selected game cannot be created.");
844  msg << "\n\n";
847  return false;
848  }
849 
850  if(!create_engine_.is_campaign()) {
851  return true;
852  }
853 
854  return create_engine_.select_campaign_difficulty() != "CANCEL";
855 }
856 
858 {
859  plugins_context_.reset();
860 
861  // Show all tabs so that find_widget works correctly
862  find_widget<stacked_widget>(&window, "pager", false).select_layer(-1);
863 
864  if(get_retval() == LOAD_GAME) {
866  return;
867  }
868 
869  if(get_retval() == retval::OK) {
874 
876 
877  if(create_engine_.current_level_type() == ng::level::TYPE::CAMPAIGN ||
878  create_engine_.current_level_type() == ng::level::TYPE::SP_CAMPAIGN) {
882  } else {
883  // This means define= doesn't work for randomly generated scenarios
885  }
886 
888 
890 
891  std::vector<const config*> entry_points;
892  std::vector<std::string> entry_point_titles;
893 
894  const auto& tagname = create_engine_.get_state().classification().get_tagname();
895 
896  if(tagname == "scenario") {
897  const std::string first_scenario = create_engine_.current_level().data()["first_scenario"];
898  for(const config& scenario : game_config_manager::get()->game_config().child_range(tagname)) {
899  const bool is_first = scenario["id"] == first_scenario;
900  if(scenario["allow_new_game"].to_bool(false) || is_first || game_config::debug ) {
901  const std::string& title = !scenario["new_game_title"].empty()
902  ? scenario["new_game_title"]
903  : scenario["name"];
904 
905  entry_points.insert(is_first ? entry_points.begin() : entry_points.end(), &scenario);
906  entry_point_titles.insert(is_first ? entry_point_titles.begin() : entry_point_titles.end(), title);
907  }
908  }
909  }
910 
911  if(entry_points.size() > 1) {
912  gui2::dialogs::simple_item_selector dlg(_("Choose Starting Scenario"), _("Select at which point to begin this campaign."), entry_point_titles);
913 
914  dlg.set_single_button(true);
915  dlg.show();
916 
917  const config& scenario = *entry_points[dlg.selected_index()];
918 
919  create_engine_.get_state().mp_settings().hash = scenario.hash();
921  }
922 
923  config_engine_->set_use_map_settings(use_map_settings_->get_widget_value(window));
924 
925  if(!config_engine_->force_lock_settings()) {
926  // Max slider value (in this case, 100) means 'unlimited turns', so pass the value -1
927  const int num_turns = turns_->get_widget_value(window);
928  config_engine_->set_num_turns(num_turns < ::settings::turns_max ? num_turns : - 1);
929  config_engine_->set_village_gold(gold_->get_widget_value(window));
930  config_engine_->set_village_support(support_->get_widget_value(window));
931  config_engine_->set_xp_modifier(experience_->get_widget_value(window));
932  config_engine_->set_random_start_time(start_time_->get_widget_value(window));
933  config_engine_->set_fog_game(fog_->get_widget_value(window));
934  config_engine_->set_shroud_game(shroud_->get_widget_value(window));
935 
936  config_engine_->write_parameters();
937  }
938 
939  config_engine_->set_mp_countdown(time_limit_->get_widget_value(window));
940  config_engine_->set_mp_countdown_init_time(init_turn_limit_->get_widget_value(window));
941  config_engine_->set_mp_countdown_turn_bonus(turn_bonus_->get_widget_value(window));
942  config_engine_->set_mp_countdown_reservoir_time(reservoir_->get_widget_value(window));
943  config_engine_->set_mp_countdown_action_bonus(action_bonus_->get_widget_value(window));
944 
945  config_engine_->set_allow_observers(observers_->get_widget_value(window));
946  config_engine_->set_registered_users_only(registered_users_->get_widget_value(window));
947  config_engine_->set_private_replay(private_replay_->get_widget_value(window));
948  config_engine_->set_oos_debug(strict_sync_->get_widget_value(window));
949  config_engine_->set_shuffle_sides(shuffle_sides_->get_widget_value(window));
950 
951  config_engine_->set_random_faction_mode(rfm_types_[selected_rfm_index_]);
952 
953  // Since we don't have a field handling this option, we need to save the value manually
954  prefs::set_random_faction_mode(mp_game_settings::RANDOM_FACTION_MODE::enum_to_string(rfm_types_[selected_rfm_index_]));
955 
956  // Save custom option settings
957  config_engine_->set_options(options_manager_->get_options_config());
958 
959  // Set game name
960  const std::string name = find_widget<text_box>(&window, "game_name", false).get_value();
961  if(!name.empty() && (name != ng::configure_engine::game_name_default())) {
962  config_engine_->set_game_name(name);
963  }
964 
965  // Set game password
966  const std::string password = find_widget<text_box>(&window, "game_password", false).get_value();
967  if(!password.empty()) {
968  config_engine_->set_game_password(password);
969  }
970  }
971 }
972 
973 } // namespace dialogs
974 } // namespace gui2
TYPE
UNSCALED : image will be drawn "as is" without changing size, even in case of redraw SCALED_TO_ZOOM :...
Definition: picture.hpp:185
Dialog was closed with the CANCEL button.
Definition: retval.hpp:37
std::string map_size() const
level::TYPE current_level_type() 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 on_tab_select(window &window)
void on_random_faction_mode_select(window &window)
void set_village_support(int value)
Definition: game.cpp:707
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)
std::string random_faction_mode()
Definition: game.cpp:507
The class for loading a savefile.
Definition: savegame.hpp:109
int village_support
Definition: game_config.cpp:55
void set_countdown_action_bonus(int value)
Definition: game.cpp:687
void set_countdown_turn_bonus(int value)
Definition: game.cpp:676
int min_players() const
std::string era()
Definition: game.cpp:722
Simple push button.
Definition: menu_button.hpp:41
void update_games_list(window &window)
void set_shroud(bool value)
Definition: game.cpp:577
void show_description(window &window, const std::string &new_description)
std::vector< mp_game_settings::RANDOM_FACTION_MODE > rfm_types_
void set_countdown_init_time(int value)
Definition: game.cpp:654
bool generator_assigned() const
int xp_modifier()
Definition: game.cpp:712
std::vector< extras_metadata_ptr > & get_extras_by_type(const MP_EXTRA extra_type)
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:35
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
logger & info()
Definition: log.cpp:90
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:188
bool generator_has_settings() const
bool shuffle_sides()
Definition: game.cpp:497
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.
int max_players() const
std::vector< level_type_info > level_types_
void set_current_level(const std::size_t index)
virtual void set_value(unsigned value, bool fire_event=false) override
Inherited from selectable_item.
Definition: menu_button.hpp:67
void on_mod_toggle(window &window, const int index, toggle_button *sender)
void set_random_faction_mode(const std::string &value)
Definition: game.cpp:511
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:109
void load_game_callback(window &window)
T get_widget_value(window &window)
Gets the value of the field.
Definition: field.hpp:395
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed...
bool show(const unsigned auto_close_time=0)
Shows the window.
void set_countdown(bool value)
Definition: game.cpp:643
void sync_with_depcheck(window &window)
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:648
bool current_level_has_side_data()
Returns true if the current level has one or more [side] tags.
void set_registered_users_only(bool value)
Definition: game.cpp:492
void on_era_select(window &window)
std::string dsgettext(const char *domainname, const char *msgid)
Definition: gettext.cpp:403
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:250
void show_generator_settings(window &window)
Class for a single line text area.
Definition: text_box.hpp:121
Generic file dialog.
Definition: field-fwd.hpp:22
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()
void display_games_of_type(window &window, ng::level::TYPE type, const std::string &level)
bool fog()
Definition: game.cpp:562
Pubic entry points for the MP workflow.
Definition: lobby_data.cpp:51
virtual void set_label(const t_string &label)
The listbox class.
Definition: listbox.hpp:40
void set_current_level_type(const level::TYPE type)
Base container class.
Definition: grid.hpp:30
static game_config_manager * get()
virtual unsigned get_value() const override
Inherited from selectable_item.
Definition: menu_button.hpp:64
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:100
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:670
bool toggle_mod(int index, bool force=false)
void set_level_type(int value)
Definition: game.cpp:747
void set_widget_value(window &window, CT value)
Sets the value of the field.
Definition: field.hpp:358
static void expand_map_file(config &scenario)
reads scenario["map_file"]
Definition: saved_game.cpp:460
void set_tooltip(const t_string &tooltip)
std::string level()
Definition: game.cpp:732
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:248
#define DBG_MP
int num_players() const
virtual std::string name() const
void set_metadata()
const std::string & id() const
Definition: field.hpp:196
This file contains the settings handling of the widget library.
void prepare_for_campaign(const std::string &difficulty="")
std::ostringstream wrapper.
Definition: formatter.hpp:38
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:125
virtual void pre_show(window &window) override
Inherited from modal_dialog.
std::unique_ptr< plugins_context > plugins_context_
std::vector< level_ptr > get_levels_by_type_unfiltered(level::TYPE type) const
void on_filter_change(window &window, const std::string &id, bool do_select)
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:233
int countdown_reservoir_time()
Definition: game.cpp:659
void regenerate_random_map(window &window)
void set_random_start_time(bool value)
Definition: game.cpp:557
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:131
virtual void set_use_markup(bool use_markup)
bool registered_users_only()
Definition: game.cpp:487
int village_gold()
Definition: game.cpp:692
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
Inherited from modal_dialog.
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
Various uncategorised dialogs.
std::pair< level::TYPE, int > find_level_by_id(const std::string &id) const
void set_level(const std::string &value)
Definition: game.cpp:737
#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:482
std::pair< ng::level::TYPE, std::string > level_type_info
bool countdown()
Definition: game.cpp:638
bool shroud()
Definition: game.cpp:572
void widget_set_enabled(window &window, const bool enable, const bool sync)
Enables a widget.
Definition: field.hpp:170
std::size_t i
Definition: function.cpp:933
int selected_index() const
Returns the selected item index after displaying.
level & current_level() const
void update_details(window &window)
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:55
The user set the widget invisible, that means:
std::string password(const std::string &server, const std::string &login)
bool use_map_settings()
Definition: game.cpp:515
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
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:66
Declarations for File-IO.
int w
const bool & debug
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:363
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:71
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:221
std::size_t current_era_index() const
void set_turns(int value)
Definition: game.cpp:587
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:237
bool allow_observers()
Definition: game.cpp:477
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:582
const std::vector< std::string > & modifications(bool mp)
Definition: game.cpp:752
const extras_metadata & current_era() const
const depcheck::manager & dependency_manager() const
bool find(E event, F functor)
Tests whether an event handler is available.
game_classification & classification()
Definition: saved_game.hpp:55
void on_game_select(window &window)
void set_modifications(const std::vector< std::string > &value, bool mp)
Definition: game.cpp:760
const std::string unicode_em_dash
Definition: constants.cpp:40
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:681
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:74
void set_era(const std::string &value)
Definition: game.cpp:727
void set_shuffle_sides(bool value)
Definition: game.cpp:502
void set_fog(bool value)
Definition: game.cpp:567
std::vector< std::size_t > get_filtered_level_indices(level::TYPE type) const
Dialog was closed with the OK button.
Definition: retval.hpp:34
bool dialog_exit_hook(window &)
Dialog exit hook to bring up the difficulty dialog when starting a campaign.
const game_config_view & cfg_
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
void set_countdown_reservoir_time(int value)
Definition: game.cpp:665
bool random_start_time()
Definition: game.cpp:552
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:59
void set_xp_modifier(int value)
Definition: game.cpp:717
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:143
std::string hash() const
Definition: config.cpp:1335
void set_village_gold(int value)
Definition: game.cpp:697
void set_use_map_settings(bool value)
Definition: game.cpp:520
bool empty() const
Definition: config.cpp:884
std::string get_tagname() const
Class for a toggle button.
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:384
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:742