The Battle for Wesnoth  1.19.27+dev
mp_create_game.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2025
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 "formatter.hpp"
21 #include "formula/string_utils.hpp"
22 #include "game_config.hpp"
23 #include "game_config_manager.hpp"
24 #include "gettext.hpp"
25 #include "gui/auxiliary/field.hpp"
28 #include "gui/widgets/button.hpp"
29 #include "gui/widgets/image.hpp"
30 #include "gui/widgets/listbox.hpp"
32 #include "gui/widgets/minimap.hpp"
33 #include "gui/widgets/slider.hpp"
36 #include "gui/widgets/text_box.hpp"
39 #include "log.hpp"
40 #include "map_settings.hpp"
42 #include "save_index.hpp"
43 #include "savegame.hpp"
44 #include "tod_manager.hpp"
45 
46 #include <boost/algorithm/string.hpp>
47 
48 static lg::log_domain log_mp_create("mp/create");
49 
50 #define DBG_MP LOG_STREAM(debug, log_mp_create)
51 #define WRN_MP LOG_STREAM(warn, log_mp_create)
52 #define ERR_MP LOG_STREAM(err, log_mp_create)
53 
54 namespace gui2::dialogs
55 {
56 
57 // Special retval value for loading a game
58 static const int LOAD_GAME = 100;
59 
61 
62 mp_create_game::mp_create_game(saved_game& state, bool local_mode)
63  : modal_dialog(window_id())
64  , create_engine_(state)
65  , options_manager_()
66  , selected_game_index_(-1)
67  , selected_rfm_index_(-1)
68  , use_map_settings_(register_bool("use_map_settings", true,
69  []() {return prefs::get().mp_use_map_settings();},
70  [](bool v) {prefs::get().set_mp_use_map_settings(v);},
71  std::bind(&mp_create_game::update_map_settings, this)))
72  , fog_(register_bool("fog", true,
73  []() {return prefs::get().mp_fog();},
74  [](bool v) {prefs::get().set_mp_fog(v);}))
75  , shroud_(register_bool("shroud", true,
76  []() {return prefs::get().mp_shroud();},
77  [](bool v) {prefs::get().set_mp_shroud(v);}))
78  , start_time_(register_bool("random_start_time", true,
79  []() {return prefs::get().mp_random_start_time();},
80  [](bool v) {prefs::get().set_mp_random_start_time(v);}))
81  , time_limit_(register_bool("time_limit", true,
82  []() {return prefs::get().mp_countdown();},
83  [](bool v) {prefs::get().set_mp_countdown(v);},
84  std::bind(&mp_create_game::update_map_settings, this)))
85  , shuffle_sides_(register_bool("shuffle_sides", true,
86  []() {return prefs::get().shuffle_sides();},
87  [](bool v) {prefs::get().set_shuffle_sides(v);}))
88  , observers_(register_bool("observers", true,
89  []() {return prefs::get().allow_observers();},
90  [](bool v) {prefs::get().set_allow_observers(v);}))
91  , strict_sync_(register_bool("strict_sync", true))
92  , private_replay_(register_bool("private_replay", true))
93  , turns_(register_integer("turn_count", true,
94  []() {return prefs::get().mp_turns();},
95  [](int v) {prefs::get().set_mp_turns(v);}))
96  , gold_(register_integer("village_gold", true,
97  []() {return prefs::get().village_gold();},
98  [](int v) {prefs::get().set_village_gold(v);}))
99  , support_(register_integer("village_support", true,
100  []() {return prefs::get().village_support();},
101  [](int v) {prefs::get().set_village_support(v);}))
102  , experience_(register_integer("experience_modifier", true,
103  []() {return prefs::get().xp_modifier();},
104  [](int v) {prefs::get().set_xp_modifier(v);}))
105  , init_turn_limit_(register_integer("init_turn_limit", true,
106  []() {return prefs::get().countdown_init_time().count();},
107  [](int v) {prefs::get().set_countdown_init_time(std::chrono::seconds{v});}))
108  , turn_bonus_(register_integer("turn_bonus", true,
109  []() {return prefs::get().countdown_turn_bonus().count();},
110  [](int v) {prefs::get().set_countdown_turn_bonus(std::chrono::seconds{v});}))
111  , reservoir_(register_integer("reservoir", true,
112  []() {return prefs::get().countdown_reservoir_time().count();},
113  [](int v) {prefs::get().set_countdown_reservoir_time(std::chrono::seconds{v});}))
114  , action_bonus_(register_integer("action_bonus", true,
115  []() {return prefs::get().countdown_action_bonus().count();},
116  [](int v) {prefs::get().set_countdown_action_bonus(std::chrono::seconds{v});}))
117  , mod_list_()
118  , eras_menu_button_()
119  , local_mode_(local_mode)
120  , previous_settings_()
121 {
122  level_types_ = {
123  {level_type::type::scenario, _("Scenarios")},
124  {level_type::type::campaign, _("Multiplayer Campaigns")},
125  {level_type::type::sp_campaign, _("Singleplayer Campaigns")},
126  {level_type::type::user_map, _("Custom Maps")},
127  {level_type::type::user_scenario, _("Custom Scenarios")},
128  {level_type::type::random_map, _("Random Maps")},
129  {level_type::type::preset, _("Presets")},
130  };
131 
132  utils::erase_if(level_types_, [this](level_type_info& type_info) {
133  return create_engine_.get_levels_by_type_unfiltered(type_info.first).empty();
134  });
135 
136  set_show_even_without_video(true);
137 
138  create_engine_.init_active_mods();
139 
140  create_engine_.get_state().clear();
141  create_engine_.get_state().classification().type = campaign_type::type::multiplayer;
142 
143  // Need to set this in the constructor, pre_show() is too late
144  set_allow_plugin_skip(false);
145 }
146 
147 // NOLINTNEXTLINE(performance-unnecessary-value-param)
149 {
150  // from constructor
151  ng::create_engine create(state);
152  create.init_active_mods();
153  create.get_state().clear();
154  create.get_state().classification().type = campaign_type::type::multiplayer;
155 
156  // from pre_show
157  create.set_current_level_type(level_type::type::scenario);
158  const auto& levels = create.get_levels_by_type(level_type::type::scenario);
159  bool found_scenario = false;
160  for(std::size_t i = 0; i < levels.size(); i++) {
161  if(levels[i]->id() == presets["scenario"].str()) {
162  create.set_current_level(i);
163  found_scenario = true;
164  }
165  }
166  if(!found_scenario) {
167  throw config::error(_("Scenario not found: ")+presets["scenario"].str());
168  }
169 
170  create.set_current_era_index(presets["era"]);
171 
172  // from post_show
173  create.prepare_for_era_and_mods();
174  create.prepare_for_scenario();
175  create.get_parameters();
176  create.prepare_for_new_level();
177 
178  mp_game_settings& params = create.get_state().mp_settings();
179  params.mp_scenario = presets["scenario"].str();
180  params.use_map_settings = true;
181  params.num_turns = presets["turn_count"].to_int(-1);
182  params.village_gold = presets["village_gold"].to_int();
183  params.village_support = presets["village_support"].to_int();
184  params.xp_modifier = presets["experience_modifier"].to_int();
185  params.random_start_time = presets["random_start_time"].to_bool();
186  params.fog_game = presets["fog"].to_bool();
187  params.shroud_game = presets["shroud"].to_bool();
188 
189  // write to scenario
190  // queue games are supposed to all use the same settings, not be modified by the user
191  // can be removed later if we jump straight from the lobby into a game instead of going to the staging screen to wait for other players to join
192  config& scenario = create.get_state().get_starting_point();
193 
194  if(params.random_start_time) {
195  if(!tod_manager::is_start_ToD(scenario["random_start_time"])) {
196  scenario["random_start_time"] = true;
197  }
198  } else {
199  scenario["random_start_time"] = false;
200  }
201 
202  scenario["experience_modifier"] = params.xp_modifier;
203  scenario["turns"] = params.num_turns;
204 
205  for(config& side : scenario.child_range("side")) {
206  side["controller_lock"] = false;
207  side["team_lock"] = true;
208  side["gold_lock"] = true;
209  side["income_lock"] = true;
210 
211  side["fog"] = params.fog_game;
212  side["shroud"] = params.shroud_game;
213  side["village_gold"] = params.village_gold;
214  side["village_support"] = params.village_support;
215  }
216 
217  params.mp_countdown = presets["countdown"].to_bool();
218  params.mp_countdown_init_time = std::chrono::seconds{presets["countdown_init_time"].to_int()};
219  params.mp_countdown_turn_bonus = std::chrono::seconds{presets["countdown_turn_bonus"].to_int()};
220  params.mp_countdown_reservoir_time = std::chrono::seconds{presets["countdown_reservoir_time"].to_int()};
221  params.mp_countdown_action_bonus = std::chrono::seconds{presets["countdown_action_bonus"].to_int()};
222 
223  params.allow_observers = true;
224  params.private_replay = false;
225  create.get_state().classification().oos_debug = false;
226  params.shuffle_sides = presets["shuffle_sides"].to_bool();
227 
228  params.mode = random_faction_mode::get_enum(presets["random_faction_mode"].str()).value_or(random_faction_mode::type::independent);
230 
231  for(const std::string& mod : utils::split(presets["modifications"].str())) {
232  std::vector<std::string> missing_mods;
233  if(!game_config_manager::get()->game_config().find_child("modification", "id", mod)) {
234  missing_mods.emplace_back(mod);
235  }
236  if(!missing_mods.empty()) {
237  throw config::error(_("Modification(s) not found: ")+utils::join(missing_mods));
238  }
239  create.active_mods().push_back(mod);
240  }
241 
242  if(presets.has_child("options")) {
243  params.options = presets.mandatory_child("options");
244  }
245 }
246 
248 {
249  find_widget<text_box>("game_name").set_value(local_mode_ ? "" : settings::game_name_default());
250 
252  find_widget<button>("random_map_regenerate"),
253  std::bind(&mp_create_game::regenerate_random_map, this));
254 
256  find_widget<button>("random_map_settings"),
257  std::bind(&mp_create_game::show_generator_settings, this));
258 
260  find_widget<button>("load_game"),
261  std::bind(&mp_create_game::load_game_callback, this));
262 
264  find_widget<button>("save_preset"),
265  std::bind(&mp_create_game::save_preset, this));
266 
267  // Custom dialog close hook
269 
270  //
271  // Set up the options manager. Needs to be done before selecting an initial tab
272  //
274 
275  //
276  // Set up filtering
277  //
278  connect_signal_notify_modified(find_widget<slider>("num_players"),
279  std::bind(&mp_create_game::on_filter_change<slider>, this, "num_players", true));
280 
281  text_box& filter = find_widget<text_box>("game_filter");
282 
283  filter.on_modified([this](const auto&) { on_filter_change<text_box>("game_filter", true); });
284 
285  //
286  // Set up game types menu_button
287  //
288  std::vector<config> game_types;
289  for(level_type_info& type_info : level_types_) {
290  game_types.emplace_back("label", type_info.second);
291  }
292 
293  if(game_types.empty()) {
294  gui2::show_transient_message("", _("No games found."));
295  throw game::error(_("No games found."));
296  }
297 
298  menu_button& game_menu_button = find_widget<menu_button>("game_types");
299 
300  // Helper to make sure the initially selected level type is valid
301  auto get_initial_type_index = [this]()->int {
303  level_type::get_enum(prefs::get().mp_level_type()).value(), &level_type_info::first);
304 
305  if(index != level_types_.end()) {
306  return std::distance(level_types_.begin(), index);
307  }
308 
309  return 0;
310  };
311 
312  int initial_type = get_initial_type_index();
313  game_menu_button.set_values(game_types, initial_type);
314  find_widget<button>("save_preset").set_active(level_type::get_enum(initial_type) == level_type::type::scenario);
315 
316  connect_signal_notify_modified(game_menu_button,
317  std::bind(&mp_create_game::update_games_list, this));
318 
319  //
320  // Set up mods list
321  //
322  mod_list_ = &find_widget<listbox>("mod_list");
323 
324  const auto& activemods = prefs::get().modifications();
326  grid* row_grid = &mod_list_->add_row(widget_data{{ "mod_name", {{ "label", mod->name }}}});
327 
328  row_grid->find_widget<toggle_panel>("panel").set_tooltip(mod->description);
329 
330  toggle_button& mog_toggle = row_grid->find_widget<toggle_button>("mod_active_state");
331 
332  if(utils::contains(activemods, mod->id)) {
333  create_engine_.active_mods().push_back(mod->id);
334  mog_toggle.set_value_bool(true);
335  }
336 
337  connect_signal_notify_modified(mog_toggle, std::bind(&mp_create_game::on_mod_toggle, this, mod->id, &mog_toggle));
338  }
339 
340  // No mods, hide the header
341  if(mod_list_->get_item_count() <= 0) {
342  find_widget<styled_widget>("mods_header").set_visible(widget::visibility::invisible);
343  }
344 
345  //
346  // Set up eras menu_button
347  //
348  eras_menu_button_ = &find_widget<menu_button>("eras");
349 
350  std::vector<config> era_names;
352  era_names.emplace_back("label", era->name, "tooltip", era->description);
353  }
354 
355  if(era_names.empty()) {
356  gui2::show_transient_message("", _("No eras found."));
357  throw config::error(_("No eras found"));
358  }
359 
360  eras_menu_button_->set_values(era_names);
361 
363  std::bind(&mp_create_game::on_era_select, this));
364 
365  const int era_selection = create_engine_.find_extra_by_id(ng::create_engine::ERA, prefs::get().mp_era());
366  if(era_selection >= 0) {
367  eras_menu_button_->set_selected(era_selection);
368  }
369 
370  on_era_select();
371 
372  //
373  // Set up random faction mode menu_button
374  //
375  const int initial_index = static_cast<int>(random_faction_mode::get_enum(prefs::get().random_faction_mode()).value_or(random_faction_mode::type::independent));
376 
377  menu_button& rfm_menu_button = find_widget<menu_button>("random_faction_mode");
378  rfm_menu_button.set_selected(initial_index);
379 
380  connect_signal_notify_modified(rfm_menu_button,
382 
384 
385  //
386  // Set up the setting status labels
387  //
389  bind_default_status_label(static_cast<slider&>(*gold_->get_widget()));
396 
397  //
398  // Timer reset button
399  //
401  find_widget<button>("reset_timer_defaults"),
402  std::bind(&mp_create_game::reset_timer_settings, this));
403 
404  //
405  // Disable certain settings if we're playing a local game.
406  //
407  if(local_mode_) {
408  find_widget<text_box>("game_name").set_active(false);
409  find_widget<text_box>("game_password").set_active(false);
410 
411  observers_->widget_set_enabled(false, false);
412  strict_sync_->widget_set_enabled(false, false);
413  private_replay_->widget_set_enabled(false, false);
414  }
415 
416  //
417  // Set up tab control
418  //
419  listbox& tab_bar = find_widget<listbox>("tab_bar");
420 
422  std::bind(&mp_create_game::on_tab_select, this));
423 
424  // Allow the settings stack to find widgets in all pages, regardless of which is selected.
425  // This ensures settings (especially game settings) widgets are appropriately updated when
426  // a new game is selected, regardless of which settings tab is active at the time.
427  find_widget<stacked_widget>("pager").set_find_in_all_layers(true);
428 
429  // We call on_tab_select farther down.
430 
431  //
432  // Main games list
433  //
434  listbox& list = find_widget<listbox>("games_list");
435 
437  std::bind(&mp_create_game::on_game_select, this));
438 
439 #ifdef __IPHONEOS__
440  // On iOS, opening this browser-style dialog should not immediately summon
441  // the software keyboard just because the optional filter field exists.
442  keyboard_capture(&list);
443 #else
444  // Note this cannot be in the keyboard chain or it will capture focus from other text boxes
446 #endif
447  add_to_keyboard_chain(&list);
448 
449  // This handles the initial game selection as well
450  display_games_of_type(level_types_[get_initial_type_index()].first, prefs::get().mp_level());
451 
452  // Initial tab selection must be done after game selection so the field widgets are set to their correct active state.
453  on_tab_select();
454 
455  //
456  // Set up the Lua plugin context
457  //
458  plugins_context_.reset(new plugins_context("Multiplayer Create"));
459 
460  plugins_context_->set_callback("create", [this](const config&) { set_retval(retval::OK); }, false);
461  plugins_context_->set_callback("quit", [this](const config&) { set_retval(retval::CANCEL); }, false);
462  plugins_context_->set_callback("load", [this](const config&) { load_game_callback(); }, false);
463 
464 #define UPDATE_ATTRIBUTE(field, convert) \
465  do { if(cfg.has_attribute(#field)) { field##_->set_widget_value(cfg[#field].convert()); } } while(false) \
466 
467  plugins_context_->set_callback("update_settings", [this](const config& cfg) {
468  UPDATE_ATTRIBUTE(turns, to_int);
469  UPDATE_ATTRIBUTE(gold, to_int);
470  UPDATE_ATTRIBUTE(support, to_int);
471  UPDATE_ATTRIBUTE(experience, to_int);
472  UPDATE_ATTRIBUTE(start_time, to_bool);
473  UPDATE_ATTRIBUTE(fog, to_bool);
474  UPDATE_ATTRIBUTE(shroud, to_bool);
475  UPDATE_ATTRIBUTE(time_limit, to_bool);
476  UPDATE_ATTRIBUTE(init_turn_limit, to_int);
477  UPDATE_ATTRIBUTE(turn_bonus, to_int);
478  UPDATE_ATTRIBUTE(reservoir, to_int);
479  UPDATE_ATTRIBUTE(action_bonus, to_int);
480  UPDATE_ATTRIBUTE(observers, to_bool);
481  UPDATE_ATTRIBUTE(strict_sync, to_bool);
482  UPDATE_ATTRIBUTE(private_replay, to_bool);
483  UPDATE_ATTRIBUTE(shuffle_sides, to_bool);
484  }, true);
485 
486 #undef UPDATE_ATTRIBUTE
487 
488  plugins_context_->set_callback("set_name", [this](const config& cfg) {
490  }, true);
491 
492  plugins_context_->set_callback("set_password", [this](const config& cfg) {
493  create_engine_.get_state().mp_settings().password = cfg["password"];
494  }, true);
495 
496  plugins_context_->set_callback("select_level", [this](const config& cfg) {
499  }, true);
500 
501  plugins_context_->set_callback("select_type", [this](const config& cfg) {
502  create_engine_.set_current_level_type(level_type::get_enum(cfg["type"].str()).value_or(level_type::type::scenario)); }, true);
503 
504  plugins_context_->set_callback("select_era", [this](const config& cfg) {
505  create_engine_.set_current_era_index(cfg["index"].to_int()); }, true);
506 
507  plugins_context_->set_callback("select_mod", [this](const config& cfg) {
508  on_mod_toggle(cfg["id"].str(), nullptr);
509  }, true);
510 
511  plugins_context_->set_accessor("get_selected", [this](const config&) {
512  const ng::level& current_level = create_engine_.current_level();
513  return config {
514  "id", current_level.id(),
515  "name", current_level.name(),
516  "icon", current_level.icon(),
517  "description", current_level.description(),
518  "allow_era_choice", current_level.allow_era_choice(),
520  };
521  });
522 
523  plugins_context_->set_accessor("find_level", [this](const config& cfg) {
524  const std::string id = cfg["id"].str();
525  auto result = create_engine_.find_level_by_id(id);
526  return config {
527  "index", result.second,
528  "type", level_type::get_string(result.first),
529  };
530  });
531 
532  plugins_context_->set_accessor_int("find_era", [this](const config& cfg) {
534  });
535 
536  plugins_context_->set_accessor_int("find_mod", [this](const config& cfg) {
538  });
539 }
540 
542 {
543  DBG_MP << "sync_with_depcheck: start";
544 
546  DBG_MP << "sync_with_depcheck: correcting era";
547  const int new_era_index = create_engine_.dependency_manager().get_era_index();
548 
549  create_engine_.set_current_era_index(new_era_index, true);
550  eras_menu_button_->set_value(new_era_index);
551  }
552 
554  DBG_MP << "sync_with_depcheck: correcting scenario";
555 
556  // Match scenario and scenario type
557  const auto [new_level_type, new_level_index] = create_engine_.find_level_by_id(create_engine_.dependency_manager().get_scenario());
558  const bool different_type = new_level_type != create_engine_.current_level_type();
559 
560  if(new_level_index != -1) {
561  create_engine_.set_current_level_type(new_level_type);
562  create_engine_.set_current_level(new_level_index);
563  selected_game_index_ = new_level_index;
564 
565  auto iter = utils::ranges::find(level_types_, new_level_type, &level_type_info::first);
566  find_widget<menu_button>("game_types").set_value(std::distance(level_types_.begin(), iter));
567 
568  if(different_type) {
570  } else {
571  // This function (or rather on_game_select) might be triggered by a listbox callback, in
572  // which case we cannot use display_games_of_type since it destroys the list (and its
573  // elements) which might result in segfaults. Instead, we assume that a listbox-triggered
574  // sync_with_depcheck call never changes the game type and goes to this branch instead.
575  find_widget<listbox>("games_list").select_row(new_level_index);
576 
577  // Override the last selection so on_game_select selects the new level
579 
580  on_game_select();
581  }
582  }
583  }
584 
586  DBG_MP << "sync_with_depcheck: correcting modifications";
588  }
589 
591  DBG_MP << "sync_with_depcheck: end";
592 }
593 
594 template<typename T>
595 void mp_create_game::on_filter_change(const std::string& id, bool do_select)
596 {
597  create_engine_.apply_level_filter(find_widget<T>(id).get_value());
598 
599  listbox& game_list = find_widget<listbox>("games_list");
600 
601  boost::dynamic_bitset<> filtered(game_list.get_item_count());
603  filtered[i] = true;
604  }
605 
606  game_list.set_row_shown(filtered);
607 
608  if(do_select) {
609  on_game_select();
610  }
611 }
612 
614 {
615  const int selected_game = find_widget<listbox>("games_list").get_selected_row();
616 
617  if(selected_game == selected_game_index_) {
618  return;
619  }
620 
621  // Convert the absolute-index get_selected_row to a relative index for the create_engine to handle
623 
625 
627 
628  update_details();
629 
630  // General settings
631  const bool can_select_era = create_engine_.current_level().allow_era_choice();
632 
633  if(!can_select_era) {
634  eras_menu_button_->set_label(_("No eras available for this game."));
635  } else {
637  }
638 
639  eras_menu_button_->set_active(can_select_era);
640 
641  // Custom options
642  options_manager_->update_game_options();
643 
644  // Game settings
645  if(level_types_[find_widget<menu_button>("game_types").get_value()].first != level_type::type::preset) {
647  } else {
648  if(auto preset = prefs::get().get_game_preset(create_engine_.preset_id_by_index(selected_game))) {
649  use_map_settings_->set_widget_value(preset["use_map_settings"].to_bool());
650  fog_->set_widget_value(preset["fog"].to_bool());
651  shroud_->set_widget_value(preset["shroud"].to_bool());
652  start_time_->set_widget_value(preset["random_start_time"].to_bool());
653  shuffle_sides_->set_widget_value(preset["shuffle_sides"].to_bool());
654  observers_->set_widget_value(preset["observer"].to_bool());
655  turns_->set_widget_value(preset["turns"].to_int());
656  gold_->set_widget_value(preset["village_gold"].to_int());
657  support_->set_widget_value(preset["village_support"].to_int());
658  experience_->set_widget_value(preset["experience_modifier"].to_int());
659  time_limit_->set_widget_value(preset["countdown"].to_bool());
660  init_turn_limit_->set_widget_value(preset["countdown_turn_limit"].to_int());
661  turn_bonus_->set_widget_value(preset["countdown_turn_bonus"].to_int());
662  reservoir_->set_widget_value(preset["countdown_reservoir"].to_int());
663  action_bonus_->set_widget_value(preset["countdown_action_bonus"].to_int());
664 
665  random_faction_mode::type mode = random_faction_mode::get_enum(preset["random_faction_mode"].str()).value_or(random_faction_mode::type::independent);
666  find_widget<menu_button>("random_faction_mode").set_selected(static_cast<int>(mode));
667 
668  const int era_selection = create_engine_.find_extra_by_id(ng::create_engine::ERA, preset["era"].str());
669  if(era_selection >= 0) {
670  eras_menu_button_->set_selected(era_selection);
671  }
672  on_era_select();
673 
674  int i = 0;
675  const auto& activemods = utils::split(preset["modifications"]);
677  toggle_button& mod_button = mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state");
678  mod_button.set_value_bool(utils::contains(activemods, mod->id));
679  on_mod_toggle(mod->id, &mod_button);
680  i++;
681  }
682 
683  if(preset->has_child("options")) {
684  options_manager_->set_options(preset->mandatory_child("options"));
685  }
686  }
687  }
688 }
689 
691 {
692  const int i = find_widget<listbox>("tab_bar").get_selected_row();
693  find_widget<stacked_widget>("pager").select_layer(i);
694 }
695 
696 void mp_create_game::on_mod_toggle(const std::string& id, toggle_button* sender)
697 {
698  if(sender && (sender->get_value_bool() == create_engine_.dependency_manager().is_modification_active(id))) {
699  ERR_MP << "ignoring on_mod_toggle that is already set";
700  return;
701  }
702 
704 
706 
707  options_manager_->update_mod_options();
708 }
709 
711 {
713 
715 
717 
718  options_manager_->update_era_options();
719 }
720 
722 {
723  selected_rfm_index_ = find_widget<menu_button>("random_faction_mode").get_value();
724 }
725 
726 void mp_create_game::show_description(const std::string& new_description)
727 {
728  styled_widget& description = find_widget<styled_widget>("description");
729 
730  description.set_label(!new_description.empty() ? new_description : _("No description available."));
731  description.set_use_markup(true);
732 }
733 
735 {
736  const int index = find_widget<menu_button>("game_types").get_value();
737 
739  find_widget<button>("save_preset").set_active(level_types_[index].first == level_type::type::scenario);
740 
741  if(level_types_[index].first != level_type::type::preset) {
742  backup_settings();
743  }
744 }
745 
747 {
749 
750  listbox& list = find_widget<listbox>("games_list");
751 
752  list.clear();
753 
756  widget_item item;
757 
758  if(type == level_type::type::campaign || type == level_type::type::sp_campaign) {
759  item["label"] = game->icon();
760  data.emplace("game_icon", item);
761  }
762 
763  item["label"] = game->name();
764  data.emplace("game_name", item);
765 
766  grid& rg = list.add_row(data);
767 
768  auto& icon = rg.find_widget<image>("game_icon");
769  if(icon.get_label().empty()) {
770  icon.set_visible(gui2::widget::visibility::invisible);
771  }
772  }
773 
774  if(!level.empty() && !list.get_rows_shown().empty()) {
775  // Recalculate which rows should be visible
776  on_filter_change<slider>("num_players", false);
777  on_filter_change<text_box>("game_filter", false);
778 
779  int level_index = create_engine_.find_level_by_id(level).second;
780  if(level_index >= 0 && std::size_t(level_index) < list.get_item_count()) {
781  list.select_row(level_index);
782  }
783  }
784 
785  const bool is_random_map = type == level_type::type::random_map;
786 
787  find_widget<button>("random_map_regenerate").set_active(is_random_map);
788  find_widget<button>("random_map_settings").set_active(is_random_map);
789 
790  // Override the last selection so on_game_select selects the new level
792 
793  on_game_select();
794 }
795 
797 {
799 
801 }
802 
804 {
806 
807  update_details();
808 }
809 
810 int mp_create_game::convert_to_game_filtered_index(const unsigned int initial_index)
811 {
812  const std::vector<std::size_t>& filtered_indices = create_engine_.get_filtered_level_indices(create_engine_.current_level_type());
813  return std::distance(filtered_indices.begin(), std::find(filtered_indices.begin(), filtered_indices.end(), initial_index));
814 }
815 
817 {
818  styled_widget& players = find_widget<styled_widget>("map_num_players");
819  styled_widget& map_size = find_widget<styled_widget>("map_size");
820 
821  if(create_engine_.current_level_type() == level_type::type::random_map) {
822  // If the current random map doesn't have data, generate it
824  create_engine_.current_level().data()["map_data"].empty() &&
825  create_engine_.current_level().data()["map_file"].empty()) {
827  }
828 
829  find_widget<button>("random_map_settings").set_active(create_engine_.generator_has_settings());
830  }
831 
833 
834  // Reset the mp_parameters with the defaults
836 
837  // Set the title, with newlines replaced. Newlines are sometimes found in SP Campaign names
838  std::string title = create_engine_.current_level().name();
839  boost::replace_all(title, "\n", " " + font::unicode_em_dash + " ");
840  find_widget<styled_widget>("game_title").set_label(title);
841 
842 
844  case level_type::type::preset:
845  case level_type::type::scenario:
846  case level_type::type::user_map:
847  case level_type::type::user_scenario:
848  case level_type::type::random_map: {
849  ng::scenario* current_scenario = dynamic_cast<ng::scenario*>(&create_engine_.current_level());
850 
851  assert(current_scenario);
852 
854 
855  find_widget<stacked_widget>("minimap_stack").select_layer(0);
856 
857  if(current_scenario->data()["map_data"].empty()) {
858  saved_game::expand_map_file(current_scenario->data());
859  current_scenario->set_metadata();
860  }
861 
862  find_widget<minimap>("minimap").set_map_data(current_scenario->data()["map_data"]);
863 
864  players.set_label(std::to_string(current_scenario->num_players()));
865  map_size.set_label(current_scenario->map_size());
866 
867  break;
868  }
869  case level_type::type::campaign:
870  case level_type::type::sp_campaign: {
871  ng::campaign* current_campaign = dynamic_cast<ng::campaign*>(&create_engine_.current_level());
872 
873  assert(current_campaign);
874 
875  create_engine_.get_state().classification().campaign = current_campaign->data()["id"].str();
876 
877  const std::string img = formatter() << current_campaign->data()["image"] << "~SCALE_INTO(265,265)";
878 
879  find_widget<stacked_widget>("minimap_stack").select_layer(1);
880  find_widget<image>("campaign_image").set_image(img);
881 
882  const int p_min = current_campaign->min_players();
883  const int p_max = current_campaign->max_players();
884 
885  if(p_max > p_min) {
886  players.set_label(VGETTEXT("number of players^$min to $max", {{"min", std::to_string(p_min)}, {"max", std::to_string(p_max)}}));
887  } else {
888  players.set_label(std::to_string(p_min));
889  }
890 
892 
893  break;
894  }
895  }
896 
897  // This needs to be at the end, since errors found expanding the map data are put into the description
899 }
900 
902 {
904  if(level["force_lock_settings"].to_bool(!create_engine_.get_state().classification().is_normal_mp_game())) {
905  use_map_settings_->widget_set_enabled(false, false);
907  } else {
909  }
910 
912 
913  const bool use_map_settings = create_engine_.get_state().mp_settings().use_map_settings;
914 
915  fog_ ->widget_set_enabled(!use_map_settings, false);
916  shroud_ ->widget_set_enabled(!use_map_settings, false);
917  start_time_ ->widget_set_enabled(!use_map_settings, false);
918 
919  turns_ ->widget_set_enabled(!use_map_settings, false);
920  gold_ ->widget_set_enabled(!use_map_settings, false);
921  support_ ->widget_set_enabled(!use_map_settings, false);
922  experience_ ->widget_set_enabled(!use_map_settings, false);
923 
924  const bool time_limit = time_limit_->get_widget_value();
925 
926  init_turn_limit_->widget_set_enabled(time_limit, false);
927  turn_bonus_ ->widget_set_enabled(time_limit, false);
928  reservoir_ ->widget_set_enabled(time_limit, false);
929  action_bonus_ ->widget_set_enabled(time_limit, false);
930 
931  find_widget<button>("reset_timer_defaults").set_active(time_limit);
932 
936 
941 }
942 
944 {
946  if(!load_data) {
947  return;
948  }
949 
951 
952  if(load_data->cancel_orders) {
954  }
955 
957 }
958 
960 {
962 
963  settings["scenario"] = create_engine_.current_level().id();
965  settings["fog"] = fog_->get_widget_value();
966  settings["shroud"] = shroud_->get_widget_value();
967  settings["village_gold"] = gold_->get_widget_value();
968  settings["village_support"] = support_->get_widget_value();
969  settings["experience_modifier"] = experience_->get_widget_value();
970  settings["countdown"] = time_limit_->get_widget_value();
971  settings["countdown_turn_limit"] = init_turn_limit_->get_widget_value();
972  settings["countdown_action_bonus"] = action_bonus_->get_widget_value();
973  settings["countdown_turn_bonus"] = turn_bonus_->get_widget_value();
974  settings["countdown_reservoir"] = reservoir_->get_widget_value();
975  settings["random_start_time"] = start_time_->get_widget_value();
976  settings["shuffle_sides"] = shuffle_sides_->get_widget_value();
977  settings["turns"] = turns_->get_widget_value();
978  settings["observer"] = observers_->get_widget_value();
979  settings["use_map_settings"] = use_map_settings_->get_widget_value();
980  settings["modifications"] = utils::join(create_engine_.active_mods(), ",");
981  settings.add_child("options", options_manager_->get_options_config());
982  random_faction_mode::type type = random_faction_mode::get_enum(selected_rfm_index_).value_or(random_faction_mode::type::independent);
983  settings["random_faction_mode"] = random_faction_mode::get_string(type);
984 
985  return settings;
986 }
987 
989 {
991 }
992 
994 {
996 }
997 
998 std::vector<std::string> mp_create_game::get_active_mods()
999 {
1000  int i = 0;
1001  std::set<std::string> res;
1002  for(const auto& mod : create_engine_.get_extras_by_type(ng::create_engine::MOD)) {
1003  if(mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state").get_value_bool()) {
1004  res.insert(mod->id);
1005  }
1006  ++i;
1007  }
1008  return std::vector<std::string>(res.begin(), res.end());
1009 }
1010 
1011 void mp_create_game::set_active_mods(const std::vector<std::string>& val)
1012 {
1013  std::set<std::string> val2(val.begin(), val.end());
1014  int i = 0;
1015  std::set<std::string> res;
1016  for(const auto& mod : create_engine_.get_extras_by_type(ng::create_engine::MOD)) {
1017  mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state").set_value_bool(val2.find(mod->id) != val2.end());
1018  ++i;
1019  }
1020 }
1021 
1023 {
1024  // This allows the defaults to be returned by the pref getters below
1029 
1030  init_turn_limit_->set_widget_value(prefs::get().countdown_init_time().count());
1031  turn_bonus_->set_widget_value(prefs::get().countdown_turn_bonus().count());
1032  reservoir_->set_widget_value(prefs::get().countdown_reservoir_time().count());
1033  action_bonus_->set_widget_value(prefs::get().countdown_action_bonus().count());
1034 }
1035 
1037 {
1039  gui2::show_transient_error_message(_("The selected game has no sides!"));
1040  return false;
1041  }
1042 
1044  std::stringstream msg;
1045  // 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
1046  msg << _("The selected game cannot be created.");
1047  msg << "\n\n";
1050  return false;
1051  }
1052 
1053  if(!create_engine_.is_campaign()) {
1054  return true;
1055  }
1056 
1057  return create_engine_.select_campaign_difficulty() != "CANCEL";
1058 }
1059 
1061 {
1062  plugins_context_.reset();
1063 
1064  // Show all tabs so that find_widget works correctly
1065  find_widget<stacked_widget>("pager").select_layer(-1);
1066 
1067  if(get_retval() == LOAD_GAME) {
1069 
1070  // 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.
1072  return;
1073  }
1074 
1075  if(get_retval() == retval::OK) {
1077  prefs::get().set_mp_level_type(static_cast<int>(create_engine_.current_level_type()));
1078  prefs::get().set_mp_level(create_engine_.current_level().id());
1079  prefs::get().set_mp_era(create_engine_.current_era().id);
1080 
1082 
1083  if(create_engine_.current_level_type() == level_type::type::campaign ||
1084  create_engine_.current_level_type() == level_type::type::sp_campaign) {
1086  } else if(create_engine_.current_level_type() == level_type::type::scenario) {
1088  } else {
1089  // This means define= doesn't work for randomly generated scenarios
1091  }
1092 
1094 
1096 
1097  std::vector<const config*> entry_points;
1098  std::vector<std::string> entry_point_titles;
1099 
1100  const auto& tagname = create_engine_.get_state().classification().get_tagname();
1101 
1102  if(tagname == "scenario") {
1103  const std::string first_scenario = create_engine_.current_level().data()["first_scenario"];
1104  for(const config& scenario : game_config_manager::get()->game_config().child_range(tagname)) {
1105  const bool is_first = scenario["id"] == first_scenario;
1106  if(scenario["allow_new_game"].to_bool(false) || is_first || game_config::debug ) {
1107  const std::string& title = !scenario["new_game_title"].empty()
1108  ? scenario["new_game_title"]
1109  : scenario["name"];
1110 
1111  entry_points.insert(is_first ? entry_points.begin() : entry_points.end(), &scenario);
1112  entry_point_titles.insert(is_first ? entry_point_titles.begin() : entry_point_titles.end(), title);
1113  }
1114  }
1115  }
1116 
1118  if(entry_points.size() > 1) {
1119  gui2::dialogs::simple_item_selector dlg(_("Choose Starting Scenario"), _("Select at which point to begin this campaign."), entry_point_titles);
1120 
1121  dlg.set_single_button(true);
1122  dlg.show();
1123 
1124  const config& scenario = *entry_points[dlg.selected_index()];
1125 
1126  params.hash = scenario.hash();
1128  }
1129 
1131 
1132  if(!create_engine_.current_level().data()["force_lock_settings"].to_bool(!create_engine_.get_state().classification().is_normal_mp_game())) {
1133  // Max slider value (in this case, 100) means 'unlimited turns', so pass the value -1
1134  const int num_turns = turns_->get_widget_value();
1135  params.num_turns = num_turns < ::settings::turns_max ? num_turns : - 1;
1136  params.village_gold = gold_->get_widget_value();
1140  params.fog_game = fog_->get_widget_value();
1141  params.shroud_game = shroud_->get_widget_value();
1142 
1143  // write to scenario
1145 
1146  if(params.random_start_time) {
1147  if(!tod_manager::is_start_ToD(scenario["random_start_time"])) {
1148  scenario["random_start_time"] = true;
1149  }
1150  } else {
1151  scenario["random_start_time"] = false;
1152  }
1153 
1154  scenario["experience_modifier"] = params.xp_modifier;
1155  scenario["turns"] = params.num_turns;
1156 
1157  for(config& side : scenario.child_range("side")) {
1158  if(!params.use_map_settings) {
1159  side["fog"] = params.fog_game;
1160  side["shroud"] = params.shroud_game;
1161  side["village_gold"] = params.village_gold;
1162  side["village_support"] = params.village_support;
1163  } else {
1164  if(side["fog"].empty()) {
1165  side["fog"] = params.fog_game;
1166  }
1167 
1168  if(side["shroud"].empty()) {
1169  side["shroud"] = params.shroud_game;
1170  }
1171 
1172  if(side["village_gold"].empty()) {
1173  side["village_gold"] = params.village_gold;
1174  }
1175 
1176  if(side["village_support"].empty()) {
1177  side["village_support"] = params.village_support;
1178  }
1179  }
1180  }
1181  }
1182 
1184  params.mp_countdown_init_time = std::chrono::seconds{init_turn_limit_->get_widget_value()};
1185  params.mp_countdown_turn_bonus = std::chrono::seconds{turn_bonus_->get_widget_value()};
1186  params.mp_countdown_reservoir_time = std::chrono::seconds{reservoir_->get_widget_value()};
1187  params.mp_countdown_action_bonus = std::chrono::seconds{action_bonus_->get_widget_value()};
1188 
1193 
1194  random_faction_mode::type type = random_faction_mode::get_enum(selected_rfm_index_).value_or(random_faction_mode::type::independent);
1195  params.mode = type;
1196 
1197  // Since we don't have a field handling this option, we need to save the value manually
1198  prefs::get().set_random_faction_mode(random_faction_mode::get_string(type));
1199 
1200  // Save custom option settings
1201  params.options = options_manager_->get_options_config();
1202  prefs::get().set_options(options_manager_->get_options_config());
1203 
1204  // Set game name
1205  const std::string name = find_widget<text_box>("game_name").get_value();
1206  if(!name.empty() && (name != settings::game_name_default())) {
1207  params.name = name;
1208  }
1209 
1210  // Set game password
1211  const std::string password = find_widget<text_box>("game_password").get_value();
1212  if(!password.empty()) {
1213  params.password = password;
1214  }
1215  }
1216 }
1217 
1218 } // namespace dialogs
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
child_itors child_range(std::string_view key)
Definition: config.cpp:268
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
bool empty() const
Definition: config.cpp:823
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:362
std::string hash() const
Definition: config.cpp:1227
std::ostringstream wrapper.
Definition: formatter.hpp:40
campaign_type::type type
std::string get_tagname() const
std::string campaign
The id of the campaign being played.
static game_config_manager * get()
Abstract base class for all modal dialogs.
bool show(const unsigned auto_close_time=0)
Shows the window.
std::pair< level_type::type, std::string > level_type_info
void display_games_of_type(level_type::type type, const std::string &level)
virtual void post_show() override
Actions to be taken after the window has been shown.
void set_active_mods(const std::vector< std::string > &val)
void show_description(const std::string &new_description)
virtual void pre_show() override
Actions to be taken before showing the window.
void on_mod_toggle(const std::string &id, toggle_button *sender)
std::unique_ptr< mp_options_helper > options_manager_
std::vector< level_type_info > level_types_
ng::create_engine create_engine_
void on_filter_change(const std::string &id, bool do_select)
static void quick_mp_setup(saved_game &state, const config presets)
presets needs to be a copy! Otherwise you'll get segfaults when clicking the Join button since it res...
std::vector< std::string > get_active_mods()
bool dialog_exit_hook()
Dialog exit hook to bring up the difficulty dialog when starting a campaign.
int convert_to_game_filtered_index(const unsigned int initial_index)
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...
std::unique_ptr< plugins_context > plugins_context_
void set_single_button(bool value)
Sets whether the Cancel button should be hidden or not.
int selected_index() const
Returns the selected item index after displaying.
styled_widget * get_widget()
Definition: field.hpp:192
void widget_set_enabled(const bool enable, const bool sync)
Enables a widget.
Definition: field.hpp:158
void set_widget_value(CT value)
Sets the value of the field.
Definition: field.hpp:343
T get_widget_value()
Gets the value of the field.
Definition: field.hpp:378
Base container class.
Definition: grid.hpp:32
The listbox class.
Definition: listbox.hpp:41
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:163
grid & add_row(const widget_item &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:92
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:256
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:267
boost::dynamic_bitset get_rows_shown() const
Returns a list of visible rows.
Definition: listbox.cpp:251
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:147
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:153
void set_selected(unsigned selected, bool fire_event=true)
virtual void set_value(unsigned value, bool fire_event=false) override
Inherited from selectable_item.
Definition: menu_button.hpp:58
void set_values(const std::vector<::config > &values, unsigned selected=0)
virtual unsigned get_value() const override
Inherited from selectable_item.
Definition: menu_button.hpp:55
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:74
void set_value_bool(bool value, bool fire_event=false)
void set_tooltip(const t_string &tooltip)
virtual void set_label(const t_string &text)
virtual void set_use_markup(bool use_markup)
A widget that allows the user to input text in single line.
Definition: text_box.hpp:125
@ invisible
The user set the widget invisible, that means:
T * find_widget(const std::string_view id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition: widget.hpp:747
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:387
show_mode mode() const
Returns the dialog mode for this window.
Definition: window.hpp:669
void keyboard_capture(widget *widget)
Definition: window.cpp:1197
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1211
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
void set_exit_hook(exit_hook mode, const Func &hook)
Sets the window's exit hook.
Definition: window.hpp:435
int get_retval()
Definition: window.hpp:394
int max_players() const
int min_players() const
int find_extra_by_id(const MP_EXTRA extra_type, const std::string &id) const
std::vector< std::size_t > get_filtered_level_indices(level_type::type type) const
bool toggle_mod(const std::string &id, bool force=false)
void set_current_era_index(const std::size_t index, bool force=false)
std::string select_campaign_difficulty(int set_value=-1)
select_campaign_difficulty
std::vector< level_ptr > get_levels_by_type(level_type::type type) const
bool generator_has_settings() const
std::vector< std::string > & active_mods()
bool current_level_has_side_data()
Returns true if the current level has one or more [side] tags.
bool generator_assigned() const
level_type::type current_level_type() const
int preset_id_by_index(int index) const
std::vector< extras_metadata_ptr > & get_extras_by_type(const MP_EXTRA extra_type)
const std::vector< extras_metadata_ptr > & get_const_extras_by_type(const MP_EXTRA extra_type) const
void apply_level_filter(const std::string &name)
const depcheck::manager & dependency_manager() const
const extras_metadata & current_era() const
void set_current_level_type(const level_type::type type)
std::pair< level_type::type, int > find_level_by_id(const std::string &id) const
saved_game & get_state()
bool is_campaign() const
Wrapper to simplify the is-type-campaign-or-sp-campaign check.
void prepare_for_campaign(const std::string &difficulty="")
std::vector< level_ptr > get_levels_by_type_unfiltered(level_type::type type) const
const mp_game_settings & get_parameters()
void set_current_level(const std::size_t index)
void prepare_for_era_and_mods()
std::size_t current_era_index() const
void init_generated_level_data()
level & current_level() const
const std::string & get_scenario() const
Returns the selected scenario.
Definition: depcheck.hpp:116
int get_era_index() const
Returns the selected era.
Definition: depcheck.cpp:398
bool is_modification_active(int index) const
Tells whether a certain mod is activated.
Definition: depcheck.cpp:432
const std::vector< std::string > & get_modifications() const
Returns the enabled modifications.
Definition: depcheck.hpp:123
Base class for all level type classes.
const config & data() const
virtual bool can_launch_game() const =0
virtual std::string id() const
virtual std::string name() const
virtual void set_metadata()=0
virtual bool allow_era_choice() const
virtual std::string icon() const
virtual std::string description() const
int num_players() const
void set_metadata()
std::string map_size() const
int village_support()
std::chrono::seconds countdown_turn_bonus()
void set_countdown_reservoir_time(const std::chrono::seconds &value)
void set_village_gold(int value)
void clear_countdown_init_time()
static prefs & get()
void set_xp_modifier(int value)
std::chrono::seconds countdown_init_time()
void set_countdown_turn_bonus(const std::chrono::seconds &value)
void set_options(const config &values)
void set_village_support(int value)
std::chrono::seconds countdown_action_bonus()
int village_gold()
void clear_countdown_turn_bonus()
void clear_countdown_reservoir_time()
const std::vector< std::string > & modifications(bool mp=true)
std::chrono::seconds countdown_reservoir_time()
void add_game_preset(config &&preset_data)
void set_modifications(const std::vector< std::string > &value, bool mp=true)
void set_countdown_init_time(const std::chrono::seconds &value)
int xp_modifier()
void set_countdown_action_bonus(const std::chrono::seconds &value)
void clear_countdown_action_bonus()
game_classification & classification()
Definition: saved_game.hpp:55
void clear()
Definition: saved_game.cpp:832
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:59
void set_scenario(config scenario)
Definition: saved_game.cpp:615
static void expand_map_file(config &scenario)
reads scenario["map_file"]
Definition: saved_game.cpp:499
config & get_starting_point()
Definition: saved_game.cpp:631
void cancel_orders()
Definition: saved_game.cpp:735
static bool is_start_ToD(const std::string &)
const config * cfg
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1031
static std::string _(const char *str)
Definition: gettext.hpp:100
Standard logging facilities (interface).
General settings and defaults for scenarios.
#define ERR_MP
#define DBG_MP
static lg::log_domain log_mp_create("mp/create")
#define UPDATE_ATTRIBUTE(field, convert)
const std::string unicode_em_dash
Definition: constants.cpp:44
Game configuration data as global variables.
Definition: build_info.cpp:68
const bool & debug
Definition: game_config.cpp:96
REGISTER_DIALOG(editor_edit_unit)
static const int LOAD_GAME
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:189
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:163
void bind_default_status_label(W &source)
Binds a status label using the default value getter and default target ID.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
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.
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
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)
Shows a transient message to the user.
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
@ CANCEL
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
Functions to load and save images from/to disk.
std::string img(const std::string &src, const std::string &align, bool floating)
Generates a Help markup tag corresponding to an image.
Definition: markup.cpp:37
utils::optional< load_game_metadata > load_interactive_for_multiplayer()
Definition: savegame.cpp:216
void set_gamestate(saved_game &gamestate, load_game_metadata &load_data)
Definition: savegame.cpp:257
Contains the general settings which have a default.
bool shroud_game_default(ng::create_engine &create)
int village_support_default(ng::create_engine &create)
int village_gold_default(ng::create_engine &create)
int num_turns_default(ng::create_engine &create)
std::string game_name_default()
const int turns_max
maximum number of turns
bool fog_game_default(ng::create_engine &create)
bool random_start_time_default(ng::create_engine &create)
int xp_modifier_default(ng::create_engine &create)
void set_default_values(ng::create_engine &create)
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
auto find(Container &container, const Value &value, const Projection &projection={})
Definition: general.hpp:213
constexpr auto filter
Definition: ranges.hpp:42
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:88
std::string join(const Range &v, const std::string &s=",")
Generates a new string joining container items in a list.
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
Definition: general.hpp:108
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:142
std::string to_string(const Range &range, const Func &op)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::string_view data
Definition: picture.cpp:188
string_enums::enum_base< random_faction_mode_defines > random_faction_mode
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition: sound.cpp:139
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
random_faction_mode::type mode
std::chrono::seconds mp_countdown_reservoir_time
std::chrono::seconds mp_countdown_action_bonus
std::chrono::seconds mp_countdown_turn_bonus
std::chrono::seconds mp_countdown_init_time
std::string mp_scenario
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57