The Battle for Wesnoth  1.17.0-dev
mp_create_game.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
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  {ng::level::TYPE::SCENARIO, _("Scenarios")},
99  {ng::level::TYPE::CAMPAIGN, _("Multiplayer Campaigns")},
100  {ng::level::TYPE::SP_CAMPAIGN, _("Singleplayer Campaigns")},
101  {ng::level::TYPE::USER_MAP, _("Custom Maps")},
102  {ng::level::TYPE::USER_SCENARIO, _("Custom Scenarios")},
103  {ng::level::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  rfm_types_ = {
112  mp_game_settings::RANDOM_FACTION_MODE::DEFAULT,
113  mp_game_settings::RANDOM_FACTION_MODE::NO_MIRROR,
114  mp_game_settings::RANDOM_FACTION_MODE::NO_ALLY_MIRROR,
115  };
116 
117  set_show_even_without_video(true);
118 
119  create_engine_.init_active_mods();
120 
121  create_engine_.get_state().clear();
122  create_engine_.get_state().classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
123 
124  // Need to set this in the constructor, pre_show() is too late
125  set_allow_plugin_skip(false);
126 }
127 
129 {
130  find_widget<text_box>(&win, "game_name", false).set_value(local_mode_ ? "" : ng::configure_engine::game_name_default());
131 
133  find_widget<button>(&win, "random_map_regenerate", false),
134  std::bind(&mp_create_game::regenerate_random_map, this));
135 
137  find_widget<button>(&win, "random_map_settings", false),
138  std::bind(&mp_create_game::show_generator_settings, this));
139 
141  find_widget<button>(&win, "load_game", false),
142  std::bind(&mp_create_game::load_game_callback, this));
143 
144  // Custom dialog close hook
145  win.set_exit_hook_ok_only([this](window& w)->bool { return dialog_exit_hook(w); });
146 
147  //
148  // Set up the options manager. Needs to be done before selecting an initial tab
149  //
151 
152  //
153  // Set up filtering
154  //
155  connect_signal_notify_modified(find_widget<slider>(&win, "num_players", false),
156  std::bind(&mp_create_game::on_filter_change<slider>, this, "num_players", true));
157 
158  text_box& filter = find_widget<text_box>(&win, "game_filter", false);
159 
161  std::bind(&mp_create_game::on_filter_change<text_box>, this, "game_filter", true));
162 
163  // Note this cannot be in the keyboard chain or it will capture focus from other text boxes
164  win.keyboard_capture(&filter);
165 
166  //
167  // Set up game types menu_button
168  //
169  std::vector<config> game_types;
170  for(level_type_info& type_info : level_types_) {
171  game_types.emplace_back("label", type_info.second);
172  }
173 
174  if(game_types.empty()) {
175  gui2::show_transient_message("", _("No games found."));
176  throw game::error(_("No games found."));
177  }
178 
179  menu_button& game_menu_button = find_widget<menu_button>(&win, "game_types", false);
180 
181  // Helper to make sure the initially selected level type is valid
182  auto get_initial_type_index = [this]()->int {
183  const auto index = std::find_if(level_types_.begin(), level_types_.end(), [](level_type_info& info) {
184  return info.first == ng::level::TYPE::from_int(preferences::level_type());
185  });
186 
187  if(index != level_types_.end()) {
188  return std::distance(level_types_.begin(), index);
189  }
190 
191  return 0;
192  };
193 
194  game_menu_button.set_values(game_types, get_initial_type_index());
195 
196  connect_signal_notify_modified(game_menu_button,
197  std::bind(&mp_create_game::update_games_list, this));
198 
199  //
200  // Set up mods list
201  //
202  mod_list_ = &find_widget<listbox>(&win, "mod_list", false);
203 
204  const auto& activemods = preferences::modifications();
206  std::map<std::string, string_map> data;
208 
209  item["label"] = mod->name;
210  data.emplace("mod_name", item);
211 
212  grid* row_grid = &mod_list_->add_row(data);
213 
214  find_widget<toggle_panel>(row_grid, "panel", false).set_tooltip(mod->description);
215 
216  toggle_button& mog_toggle = find_widget<toggle_button>(row_grid, "mod_active_state", false);
217 
218  const int i = mod_list_->get_item_count() - 1;
219  if(std::find(activemods.begin(), activemods.end(), mod->id) != activemods.end()) {
220  create_engine_.active_mods().push_back(mod->id);
221  mog_toggle.set_value_bool(true);
222  }
223 
224  connect_signal_notify_modified(mog_toggle, std::bind(&mp_create_game::on_mod_toggle, this, i, &mog_toggle));
225  }
226 
227  // No mods, hide the header
228  if(mod_list_->get_item_count() <= 0) {
229  find_widget<styled_widget>(&win, "mods_header", false).set_visible(widget::visibility::invisible);
230  }
231 
232  //
233  // Set up eras menu_button
234  //
235  eras_menu_button_ = &find_widget<menu_button>(&win, "eras", false);
236 
237  std::vector<config> era_names;
239  era_names.emplace_back("label", era->name, "tooltip", era->description);
240  }
241 
242  if(era_names.empty()) {
243  gui2::show_transient_message("", _("No eras found."));
244  throw config::error(_("No eras found"));
245  }
246 
247  eras_menu_button_->set_values(era_names);
248 
250  std::bind(&mp_create_game::on_era_select, this));
251 
253  if(era_selection >= 0) {
254  eras_menu_button_->set_selected(era_selection);
255  }
256 
257  on_era_select();
258 
259  //
260  // Set up random faction mode menu_button
261  //
262  std::vector<config> rfm_options;
263  for(const auto& type : rfm_types_) {
264  // HACK: The labels are defined for the wesnoth textdomain in a header,
265  // see mp_game_settings::RANDOM_FACTION_MODE in src/mp_game_settings.hpp
266  rfm_options.emplace_back("label",
267  translation::dsgettext("wesnoth", mp_game_settings::RANDOM_FACTION_MODE::enum_to_string(type).c_str())
268  );
269  };
270 
271  // Manually insert tooltips. Need to find a better way to do this
272  rfm_options[0]["tooltip"] = _("Independent: Random factions assigned independently");
273  rfm_options[1]["tooltip"] = _("No Mirror: No two players will get the same faction");
274  rfm_options[2]["tooltip"] = _("No Ally Mirror: No two allied players will get the same faction");
275 
276  const int initial_index = std::distance(rfm_types_.begin(), std::find(rfm_types_.begin(), rfm_types_.end(),
277  mp_game_settings::RANDOM_FACTION_MODE::string_to_enum(prefs::random_faction_mode(), mp_game_settings::RANDOM_FACTION_MODE::DEFAULT))
278  );
279 
280  menu_button& rfm_menu_button = find_widget<menu_button>(&win, "random_faction_mode", false);
281 
282  rfm_menu_button.set_values(rfm_options, initial_index);
283 
284  connect_signal_notify_modified(rfm_menu_button,
286 
288 
289  //
290  // Set up the setting status labels
291  //
292  bind_status_label<slider>(&win, turns_->id());
293  bind_status_label<slider>(&win, gold_->id());
294  bind_status_label<slider>(&win, support_->id());
295  bind_status_label<slider>(&win, experience_->id());
296 
297  bind_status_label<slider>(&win, init_turn_limit_->id());
298  bind_status_label<slider>(&win, turn_bonus_->id());
299  bind_status_label<slider>(&win, reservoir_->id());
300  bind_status_label<slider>(&win, action_bonus_->id());
301 
302  //
303  // Disable certain settings if we're playing a local game.
304  //
305 
306  if(local_mode_) {
307  find_widget<text_box>(&win, "game_name", false).set_active(false);
308  find_widget<text_box>(&win, "game_password", false).set_active(false);
309 
310  observers_->widget_set_enabled(win, false, false);
311  strict_sync_->widget_set_enabled(win, false, false);
312  private_replay_->widget_set_enabled(win, false, false);
313  }
314 
315  //
316  // Set up tab control
317  //
318  listbox& tab_bar = find_widget<listbox>(&win, "tab_bar", false);
319 
321  std::bind(&mp_create_game::on_tab_select, this));
322 
323  // Allow the settings stack to find widgets in all pages, regardless of which is selected.
324  // This ensures settings (especially game settings) widgets are appropriately updated when
325  // a new game is selected, regardless of which settings tab is active at the time.
326  find_widget<stacked_widget>(&win, "pager", false).set_find_in_all_layers(true);
327 
328  // We call on_tab_select farther down.
329 
330  //
331  // Main games list
332  //
333  listbox& list = find_widget<listbox>(&win, "games_list", false);
334 
336  std::bind(&mp_create_game::on_game_select, this));
337 
338  win.add_to_keyboard_chain(&list);
339 
340  // This handles the initial game selection as well
341  display_games_of_type(level_types_[get_initial_type_index()].first, preferences::level());
342 
343  // Initial tab selection must be done after game selection so the field widgets are set to their correct active state.
344  on_tab_select();
345 
346  //
347  // Set up the Lua plugin context
348  //
349  plugins_context_.reset(new plugins_context("Multiplayer Create"));
350 
351  plugins_context_->set_callback("create", [&win](const config&) { win.set_retval(retval::OK); }, false);
352  plugins_context_->set_callback("quit", [&win](const config&) { win.set_retval(retval::CANCEL); }, false);
353  plugins_context_->set_callback("load", [this](const config&) { load_game_callback(); }, false);
354 
355 #define UPDATE_ATTRIBUTE(field, convert) \
356  do { if(cfg.has_attribute(#field)) { field##_->set_widget_value(win, cfg[#field].convert()); } } while(false) \
357 
358  plugins_context_->set_callback("update_settings", [this, &win](const config& cfg) {
359  UPDATE_ATTRIBUTE(turns, to_int);
360  UPDATE_ATTRIBUTE(gold, to_int);
361  UPDATE_ATTRIBUTE(support, to_int);
362  UPDATE_ATTRIBUTE(experience, to_int);
363  UPDATE_ATTRIBUTE(start_time, to_bool);
364  UPDATE_ATTRIBUTE(fog, to_bool);
365  UPDATE_ATTRIBUTE(shroud, to_bool);
366  UPDATE_ATTRIBUTE(time_limit, to_bool);
367  UPDATE_ATTRIBUTE(init_turn_limit, to_int);
368  UPDATE_ATTRIBUTE(turn_bonus, to_int);
369  UPDATE_ATTRIBUTE(reservoir, to_int);
370  UPDATE_ATTRIBUTE(action_bonus, to_int);
371  UPDATE_ATTRIBUTE(observers, to_bool);
372  UPDATE_ATTRIBUTE(strict_sync, to_bool);
373  UPDATE_ATTRIBUTE(private_replay, to_bool);
375  }, true);
376 
377 #undef UPDATE_ATTRIBUTE
378 
379  plugins_context_->set_callback("set_name", [this](const config& cfg) {
380  config_engine_->set_game_name(cfg["name"]); }, true);
381 
382  plugins_context_->set_callback("set_password", [this](const config& cfg) {
383  config_engine_->set_game_password(cfg["password"]); }, true);
384 
385  plugins_context_->set_callback("select_level", [this](const config& cfg) {
386  selected_game_index_ = convert_to_game_filtered_index(cfg["index"].to_int());
388  }, true);
389 
390  plugins_context_->set_callback("select_type", [this](const config& cfg) {
391  create_engine_.set_current_level_type(ng::level::TYPE::string_to_enum(cfg["type"], ng::level::TYPE::SCENARIO)); }, true);
392 
393  plugins_context_->set_callback("select_era", [this](const config& cfg) {
394  create_engine_.set_current_era_index(cfg["index"].to_int()); }, true);
395 
396  plugins_context_->set_callback("select_mod", [this](const config& cfg) {
397  on_mod_toggle(cfg["index"].to_int(), nullptr);
398  }, true);
399 
400  plugins_context_->set_accessor("get_selected", [this](const config&) {
401  const ng::level& current_level = create_engine_.current_level();
402  return config {
403  "id", current_level.id(),
404  "name", current_level.name(),
405  "icon", current_level.icon(),
406  "description", current_level.description(),
407  "allow_era_choice", current_level.allow_era_choice(),
409  };
410  });
411 
412  plugins_context_->set_accessor("find_level", [this](const config& cfg) {
413  const std::string id = cfg["id"].str();
414  return config {
415  "index", create_engine_.find_level_by_id(id).second,
416  "type", create_engine_.find_level_by_id(id).first,
417  };
418  });
419 
420  plugins_context_->set_accessor_int("find_era", [this](const config& cfg) {
422  });
423 
424  plugins_context_->set_accessor_int("find_mod", [this](const config& cfg) {
426  });
427 }
428 
430 {
431  DBG_MP << "sync_with_depcheck: start\n";
432 
434  DBG_MP << "sync_with_depcheck: correcting era\n";
435  const int new_era_index = create_engine_.dependency_manager().get_era_index();
436 
437  create_engine_.set_current_era_index(new_era_index, true);
438  eras_menu_button_->set_value(new_era_index);
439  }
440 
442  DBG_MP << "sync_with_depcheck: correcting scenario\n";
443 
444  // Match scenario and scenario type
446  const bool different_type = new_level_index.first != create_engine_.current_level_type();
447 
448  if(new_level_index.second != -1) {
449  create_engine_.set_current_level_type(new_level_index.first);
450  create_engine_.set_current_level(new_level_index.second);
451  selected_game_index_ = new_level_index.second;
452 
453  auto& game_types_list = find_widget<menu_button>(get_window(), "game_types", false);
454  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; })));
455 
456  if(different_type) {
457  display_games_of_type(new_level_index.first, create_engine_.current_level().id());
458  } else {
459  // This function (or rather on_game_select) might be triggered by a listbox callback, in
460  // which case we cannot use display_games_of_type since it destroys the list (and its
461  // elements) which might result in segfaults. Instead, we assume that a listbox-triggered
462  // sync_with_depcheck call never changes the game type and goes to this branch instead.
463  find_widget<listbox>(get_window(), "games_list", false).select_row(new_level_index.second);
464 
465  // Override the last selection so on_game_select selects the new level
467 
468  on_game_select();
469  }
470  }
471  }
472 
474  DBG_MP << "sync_with_depcheck: correcting modifications\n";
476  }
477 
479  DBG_MP << "sync_with_depcheck: end\n";
480 }
481 
482 template<typename widget>
483 void mp_create_game::on_filter_change(const std::string& id, bool do_select)
484 {
485  create_engine_.apply_level_filter(find_widget<widget>(get_window(), id, false).get_value());
486 
487  listbox& game_list = find_widget<listbox>(get_window(), "games_list", false);
488 
489  boost::dynamic_bitset<> filtered(game_list.get_item_count());
491  filtered[i] = true;
492  }
493 
494  game_list.set_row_shown(filtered);
495 
496  if(do_select) {
497  on_game_select();
498  }
499 }
500 
502 {
503  const int selected_game = find_widget<listbox>(get_window(), "games_list", false).get_selected_row();
504 
505  if(selected_game == selected_game_index_) {
506  return;
507  }
508 
509  // Convert the absolute-index get_selected_row to a relative index for the create_engine to handle
511 
513 
515 
516  update_details();
517 
518  // General settings
519  const bool can_select_era = create_engine_.current_level().allow_era_choice();
520 
521  if(!can_select_era) {
522  eras_menu_button_->set_label(_("No eras available for this game."));
523  } else {
525  }
526 
527  eras_menu_button_->set_active(can_select_era);
528 
529  // Custom options
530  options_manager_->update_game_options();
531 
532  // Game settings
534 }
535 
537 {
538  const int i = find_widget<listbox>(get_window(), "tab_bar", false).get_selected_row();
539  find_widget<stacked_widget>(get_window(), "pager", false).select_layer(i);
540 }
541 
543 {
544  if(sender && (sender->get_value_bool() == create_engine_.dependency_manager().is_modification_active(index))) {
545  ERR_MP << "ignoring on_mod_toggle that is already set\n";
546  return;
547  }
548 
549  create_engine_.toggle_mod(index);
550 
552 
553  options_manager_->update_mod_options();
554 }
555 
557 {
559 
561 
563 
564  options_manager_->update_era_options();
565 }
566 
568 {
569  selected_rfm_index_ = find_widget<menu_button>(get_window(), "random_faction_mode", false).get_value();
570 }
571 
572 void mp_create_game::show_description(const std::string& new_description)
573 {
574  styled_widget& description = find_widget<styled_widget>(get_window(), "description", false);
575 
576  description.set_label(!new_description.empty() ? new_description : _("No description available."));
577  description.set_use_markup(true);
578 }
579 
581 {
582  const int index = find_widget<menu_button>(get_window(), "game_types", false).get_value();
583 
585 }
586 
588 {
590 
591  listbox& list = find_widget<listbox>(get_window(), "games_list", false);
592 
593  list.clear();
594 
595  for(const auto& game : create_engine_.get_levels_by_type_unfiltered(type)) {
596  std::map<std::string, string_map> data;
598 
599  if(type == ng::level::TYPE::CAMPAIGN || type == ng::level::TYPE::SP_CAMPAIGN) {
600  item["label"] = game->icon();
601  data.emplace("game_icon", item);
602  }
603 
604  item["label"] = game->name();
605  data.emplace("game_name", item);
606 
607  list.add_row(data);
608  }
609 
610  if(!level.empty() && !list.get_rows_shown().empty()) {
611  // Recalculate which rows should be visible
612  on_filter_change<slider>("num_players", false);
613  on_filter_change<text_box>("game_filter", false);
614 
615  int level_index = create_engine_.find_level_by_id(level).second;
616  if(level_index >= 0 && std::size_t(level_index) < list.get_item_count()) {
617  list.select_row(level_index);
618  }
619  }
620 
621  const bool is_random_map = type == ng::level::TYPE::RANDOM_MAP;
622 
623  find_widget<button>(get_window(), "random_map_regenerate", false).set_active(is_random_map);
624  find_widget<button>(get_window(), "random_map_settings", false).set_active(is_random_map);
625 
626  // Override the last selection so on_game_select selects the new level
628 
629  on_game_select();
630 }
631 
633 {
635 
637 }
638 
640 {
642 
643  update_details();
644 }
645 
646 int mp_create_game::convert_to_game_filtered_index(const unsigned int initial_index)
647 {
648  const std::vector<std::size_t>& filtered_indices = create_engine_.get_filtered_level_indices(create_engine_.current_level_type());
649  return std::distance(filtered_indices.begin(), std::find(filtered_indices.begin(), filtered_indices.end(), initial_index));
650 }
651 
653 {
654  styled_widget& players = find_widget<styled_widget>(get_window(), "map_num_players", false);
655  styled_widget& map_size = find_widget<styled_widget>(get_window(), "map_size", false);
656 
657  if(create_engine_.current_level_type() == ng::level::TYPE::RANDOM_MAP) {
658  // If the current random map doesn't have data, generate it
660  create_engine_.current_level().data()["map_data"].empty() &&
661  create_engine_.current_level().data()["map_file"].empty()) {
663  }
664 
665  find_widget<button>(get_window(), "random_map_settings", false).set_active(create_engine_.generator_has_settings());
666  }
667 
668  create_engine_.current_level().set_metadata();
669 
670  // Reset the config_engine with new values
672  config_engine_->update_initial_cfg(create_engine_.current_level().data());
673  config_engine_->set_default_values();
674 
675  // Set the title, with newlines replaced. Newlines are sometimes found in SP Campaign names
676  std::string title = create_engine_.current_level().name();
677  boost::replace_all(title, "\n", " " + font::unicode_em_dash + " ");
678  find_widget<styled_widget>(get_window(), "game_title", false).set_label(title);
679 
681 
682  switch(create_engine_.current_level_type().v) {
684  case ng::level::TYPE::USER_MAP:
685  case ng::level::TYPE::USER_SCENARIO:
686  case ng::level::TYPE::RANDOM_MAP: {
687  ng::scenario* current_scenario = dynamic_cast<ng::scenario*>(&create_engine_.current_level());
688 
689  assert(current_scenario);
690 
692 
693  find_widget<stacked_widget>(get_window(), "minimap_stack", false).select_layer(0);
694 
695  if(current_scenario->data()["map_data"].empty()) {
696  saved_game::expand_map_file(current_scenario->data());
697  current_scenario->set_metadata();
698  }
699 
700  find_widget<minimap>(get_window(), "minimap", false).set_map_data(current_scenario->data()["map_data"]);
701 
702  players.set_label(std::to_string(current_scenario->num_players()));
703  map_size.set_label(current_scenario->map_size());
704 
705  break;
706  }
707  case ng::level::TYPE::CAMPAIGN:
708  case ng::level::TYPE::SP_CAMPAIGN: {
709  ng::campaign* current_campaign = dynamic_cast<ng::campaign*>(&create_engine_.current_level());
710 
711  assert(current_campaign);
712 
713  create_engine_.get_state().classification().campaign = current_campaign->data()["id"].str();
714 
715  const std::string img = formatter() << current_campaign->data()["image"] << "~SCALE_INTO(265,265)";
716 
717  find_widget<stacked_widget>(get_window(), "minimap_stack", false).select_layer(1);
718  find_widget<image>(get_window(), "campaign_image", false).set_image(img);
719 
720  const int p_min = current_campaign->min_players();
721  const int p_max = current_campaign->max_players();
722 
723  if(p_max > p_min) {
724  players.set_label(VGETTEXT("number of players^$min to $max", {{"min", std::to_string(p_min)}, {"max", std::to_string(p_max)}}));
725  } else {
726  players.set_label(std::to_string(p_min));
727  }
728 
729  map_size.set_label(font::unicode_em_dash);
730 
731  break;
732  }
733  }
734 }
735 
737 {
738  window& window = *get_window();
739 
740  if(config_engine_->force_lock_settings()) {
741  use_map_settings_->widget_set_enabled(window, false, false);
742  use_map_settings_->set_widget_value(window, true);
743  } else {
744  use_map_settings_->widget_set_enabled(window, true, false);
745  }
746 
748 
749  config_engine_->set_use_map_settings(use_map_settings);
750 
751  fog_ ->widget_set_enabled(window, !use_map_settings, false);
752  shroud_ ->widget_set_enabled(window, !use_map_settings, false);
753  start_time_ ->widget_set_enabled(window, !use_map_settings, false);
754 
755  turns_ ->widget_set_enabled(window, !use_map_settings, false);
756  gold_ ->widget_set_enabled(window, !use_map_settings, false);
757  support_ ->widget_set_enabled(window, !use_map_settings, false);
758  experience_ ->widget_set_enabled(window, !use_map_settings, false);
759 
760  const bool time_limit = time_limit_->get_widget_value(window);
761 
762  init_turn_limit_->widget_set_enabled(window, time_limit, false);
763  turn_bonus_ ->widget_set_enabled(window, time_limit, false);
764  reservoir_ ->widget_set_enabled(window, time_limit, false);
765  action_bonus_ ->widget_set_enabled(window, time_limit, false);
766 
767  if(use_map_settings) {
768  fog_ ->set_widget_value(window, config_engine_->fog_game_default());
769  shroud_ ->set_widget_value(window, config_engine_->shroud_game_default());
770  start_time_->set_widget_value(window, config_engine_->random_start_time_default());
771 
772  turns_ ->set_widget_value(window, config_engine_->num_turns_default());
773  gold_ ->set_widget_value(window, config_engine_->village_gold_default());
774  support_ ->set_widget_value(window, config_engine_->village_support_default());
775  experience_->set_widget_value(window, config_engine_->xp_modifier_default());
776  }
777 }
778 
780 {
782 
783  if(!load.load_multiplayer_game()) {
784  return;
785  }
786 
787  if(load.data().cancel_orders) {
789  }
790 
791  set_retval(LOAD_GAME);
792 }
793 
794 std::vector<std::string> mp_create_game::get_active_mods()
795 {
796  int i = 0;
797  std::set<std::string> res;
799  if(find_widget<toggle_button>(mod_list_->get_row_grid(i), "mod_active_state", false).get_value_bool()) {
800  res.insert(mod->id);
801  }
802  ++i;
803  }
804  return std::vector<std::string>(res.begin(), res.end());
805 }
806 
807 void mp_create_game::set_active_mods(const std::vector<std::string>& val)
808 {
809  std::set<std::string> val2(val.begin(), val.end());
810  int i = 0;
811  std::set<std::string> res;
813  find_widget<toggle_button>(mod_list_->get_row_grid(i), "mod_active_state", false).set_value_bool(val2.find(mod->id) != val2.end());
814  ++i;
815  }
816 }
817 
819 {
821  gui2::show_transient_error_message(_("The selected game has no sides!"));
822  return false;
823  }
824 
826  std::stringstream msg;
827  // 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
828  msg << _("The selected game cannot be created.");
829  msg << "\n\n";
832  return false;
833  }
834 
835  if(!create_engine_.is_campaign()) {
836  return true;
837  }
838 
839  return create_engine_.select_campaign_difficulty() != "CANCEL";
840 }
841 
843 {
844  plugins_context_.reset();
845 
846  // Show all tabs so that find_widget works correctly
847  find_widget<stacked_widget>(&window, "pager", false).select_layer(-1);
848 
849  if(get_retval() == LOAD_GAME) {
851 
852  // 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.
854  return;
855  }
856 
857  if(get_retval() == retval::OK) {
862 
864 
865  if(create_engine_.current_level_type() == ng::level::TYPE::CAMPAIGN ||
866  create_engine_.current_level_type() == ng::level::TYPE::SP_CAMPAIGN) {
870  } else {
871  // This means define= doesn't work for randomly generated scenarios
873  }
874 
876 
878 
879  std::vector<const config*> entry_points;
880  std::vector<std::string> entry_point_titles;
881 
882  const auto& tagname = create_engine_.get_state().classification().get_tagname();
883 
884  if(tagname == "scenario") {
885  const std::string first_scenario = create_engine_.current_level().data()["first_scenario"];
886  for(const config& scenario : game_config_manager::get()->game_config().child_range(tagname)) {
887  const bool is_first = scenario["id"] == first_scenario;
888  if(scenario["allow_new_game"].to_bool(false) || is_first || game_config::debug ) {
889  const std::string& title = !scenario["new_game_title"].empty()
890  ? scenario["new_game_title"]
891  : scenario["name"];
892 
893  entry_points.insert(is_first ? entry_points.begin() : entry_points.end(), &scenario);
894  entry_point_titles.insert(is_first ? entry_point_titles.begin() : entry_point_titles.end(), title);
895  }
896  }
897  }
898 
899  if(entry_points.size() > 1) {
900  gui2::dialogs::simple_item_selector dlg(_("Choose Starting Scenario"), _("Select at which point to begin this campaign."), entry_point_titles);
901 
902  dlg.set_single_button(true);
903  dlg.show();
904 
905  const config& scenario = *entry_points[dlg.selected_index()];
906 
907  create_engine_.get_state().mp_settings().hash = scenario.hash();
909  }
910 
911  config_engine_->set_use_map_settings(use_map_settings_->get_widget_value(window));
912 
913  if(!config_engine_->force_lock_settings()) {
914  // Max slider value (in this case, 100) means 'unlimited turns', so pass the value -1
915  const int num_turns = turns_->get_widget_value(window);
916  config_engine_->set_num_turns(num_turns < ::settings::turns_max ? num_turns : - 1);
917  config_engine_->set_village_gold(gold_->get_widget_value(window));
918  config_engine_->set_village_support(support_->get_widget_value(window));
919  config_engine_->set_xp_modifier(experience_->get_widget_value(window));
920  config_engine_->set_random_start_time(start_time_->get_widget_value(window));
921  config_engine_->set_fog_game(fog_->get_widget_value(window));
922  config_engine_->set_shroud_game(shroud_->get_widget_value(window));
923 
924  config_engine_->write_parameters();
925  }
926 
927  config_engine_->set_mp_countdown(time_limit_->get_widget_value(window));
928  config_engine_->set_mp_countdown_init_time(init_turn_limit_->get_widget_value(window));
929  config_engine_->set_mp_countdown_turn_bonus(turn_bonus_->get_widget_value(window));
930  config_engine_->set_mp_countdown_reservoir_time(reservoir_->get_widget_value(window));
931  config_engine_->set_mp_countdown_action_bonus(action_bonus_->get_widget_value(window));
932 
933  config_engine_->set_allow_observers(observers_->get_widget_value(window));
934  config_engine_->set_private_replay(private_replay_->get_widget_value(window));
935  config_engine_->set_oos_debug(strict_sync_->get_widget_value(window));
936  config_engine_->set_shuffle_sides(shuffle_sides_->get_widget_value(window));
937 
938  config_engine_->set_random_faction_mode(rfm_types_[selected_rfm_index_]);
939 
940  // Since we don't have a field handling this option, we need to save the value manually
941  prefs::set_random_faction_mode(mp_game_settings::RANDOM_FACTION_MODE::enum_to_string(rfm_types_[selected_rfm_index_]));
942 
943  // Save custom option settings
944  config_engine_->set_options(options_manager_->get_options_config());
945 
946  // Set game name
947  const std::string name = find_widget<text_box>(&window, "game_name", false).get_value();
948  if(!name.empty() && (name != ng::configure_engine::game_name_default())) {
949  config_engine_->set_game_name(name);
950  }
951 
952  // Set game password
953  const std::string password = find_widget<text_box>(&window, "game_password", false).get_value();
954  if(!password.empty()) {
955  config_engine_->set_game_password(password);
956  }
957  }
958 }
959 
960 } // namespace dialogs
TYPE
Used to specify the rendering format of images.
Definition: picture.hpp:229
const std::string & get_scenario() const
Returns the selected scenario.
Definition: depcheck.hpp:122
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
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 set_village_support(int value)
Definition: game.cpp:680
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:485
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:660
void set_countdown_turn_bonus(int value)
Definition: game.cpp:650
int min_players() const
std::string era()
Definition: game.cpp:695
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:554
std::vector< mp_game_settings::RANDOM_FACTION_MODE > rfm_types_
void set_countdown_init_time(int value)
Definition: game.cpp:630
bool generator_assigned() const
int xp_modifier()
Definition: game.cpp:685
std::vector< extras_metadata_ptr > & get_extras_by_type(const MP_EXTRA extra_type)
int get_era_index() const
Returns the selected era.
Definition: depcheck.cpp:398
int find_extra_by_id(const MP_EXTRA extra_type, const std::string &id) const
void set_scenario(config scenario)
Definition: saved_game.cpp:566
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
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
bool generator_has_settings() const
bool shuffle_sides()
Definition: game.cpp:475
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 display_games_of_type(ng::level::TYPE type, const std::string &level)
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:87
bool is_modification_active(int index) const
Tells whether a certain mod is activated.
Definition: depcheck.cpp:427
void on_mod_toggle(const int index, toggle_button *sender)
void set_random_faction_mode(const std::string &value)
Definition: game.cpp:490
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
T get_widget_value(window &window)
Gets the value of the field.
Definition: field.hpp:396
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:620
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:625
bool current_level_has_side_data()
Returns true if the current level has one or more [side] tags.
std::string dsgettext(const char *domainname, const char *msgid)
Definition: gettext.cpp:401
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:252
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:539
virtual void set_label(const t_string &label)
The listbox class.
Definition: listbox.hpp:43
void set_current_level_type(const level::TYPE type)
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:645
bool toggle_mod(int index, bool force=false)
void set_level_type(int value)
Definition: game.cpp:720
void set_widget_value(window &window, CT value)
Sets the value of the field.
Definition: field.hpp:359
static void expand_map_file(config &scenario)
reads scenario["map_file"]
Definition: saved_game.cpp:460
A simple one-column listbox with OK and Cancel buttons.
void set_tooltip(const t_string &tooltip)
std::string level()
Definition: game.cpp:705
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:187
#define DBG_MP
int num_players() const
virtual std::string name() const
void set_metadata()
const std::string & id() const
Definition: field.hpp:197
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:127
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
std::unique_ptr< plugins_context > plugins_context_
std::vector< level_ptr > get_levels_by_type_unfiltered(level::TYPE type) const
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:172
int countdown_reservoir_time()
Definition: game.cpp:635
void set_random_start_time(bool value)
Definition: game.cpp:534
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:133
virtual void set_use_markup(bool use_markup)
int village_gold()
Definition: game.cpp:665
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
std::pair< level::TYPE, int > find_level_by_id(const std::string &id) const
void set_level(const std::string &value)
Definition: game.cpp:710
#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:470
std::pair< ng::level::TYPE, std::string > level_type_info
bool countdown()
Definition: game.cpp:615
bool shroud()
Definition: game.cpp:549
void widget_set_enabled(window &window, const bool enable, const bool sync)
Enables a widget.
Definition: field.hpp:171
std::size_t i
Definition: function.cpp:967
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:59
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:495
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:68
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:223
std::size_t current_era_index() const
void set_turns(int value)
Definition: game.cpp:564
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:239
bool allow_observers()
Definition: game.cpp:465
void cancel_orders()
Definition: saved_game.cpp:685
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:559
int get_retval() const
Returns the cached window exit code.
const std::vector< std::string > & modifications(bool mp)
Definition: game.cpp:725
const extras_metadata & current_era() const
const depcheck::manager & dependency_manager() const
game_classification & classification()
Definition: saved_game.hpp:55
void set_modifications(const std::vector< std::string > &value, bool mp)
Definition: game.cpp:734
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:129
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:655
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:700
void set_shuffle_sides(bool value)
Definition: game.cpp:480
void set_fog(bool value)
Definition: game.cpp:544
std::vector< std::size_t > get_filtered_level_indices(level::TYPE type) const
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.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
void show_description(const std::string &new_description)
void set_countdown_reservoir_time(int value)
Definition: game.cpp:640
bool random_start_time()
Definition: game.cpp:529
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:59
void set_xp_modifier(int value)
Definition: game.cpp:690
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:65
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:145
std::string hash() const
Definition: config.cpp:1392
void set_village_gold(int value)
Definition: game.cpp:670
void set_use_map_settings(bool value)
Definition: game.cpp:500
bool empty() const
Definition: config.cpp:941
std::string get_tagname() const
Class for a toggle button.
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:715