The Battle for Wesnoth  1.19.22+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  for(std::size_t i = 0; i < levels.size(); i++) {
160  if(levels[i]->id() == presets["scenario"].str()) {
161  create.set_current_level(i);
162  }
163  }
164 
165  create.set_current_era_id(presets["era"]);
166 
167  // from post_show
168  create.prepare_for_era_and_mods();
169  create.prepare_for_scenario();
170  create.get_parameters();
171  create.prepare_for_new_level();
172 
173  mp_game_settings& params = create.get_state().mp_settings();
174  params.mp_scenario = presets["scenario"].str();
175  params.use_map_settings = true;
176  params.num_turns = presets["turn_count"].to_int(-1);
177  params.village_gold = presets["village_gold"].to_int();
178  params.village_support = presets["village_support"].to_int();
179  params.xp_modifier = presets["experience_modifier"].to_int();
180  params.random_start_time = presets["random_start_time"].to_bool();
181  params.fog_game = presets["fog"].to_bool();
182  params.shroud_game = presets["shroud"].to_bool();
183 
184  // write to scenario
185  // queue games are supposed to all use the same settings, not be modified by the user
186  // 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
187  config& scenario = create.get_state().get_starting_point();
188 
189  if(params.random_start_time) {
190  if(!tod_manager::is_start_ToD(scenario["random_start_time"])) {
191  scenario["random_start_time"] = true;
192  }
193  } else {
194  scenario["random_start_time"] = false;
195  }
196 
197  scenario["experience_modifier"] = params.xp_modifier;
198  scenario["turns"] = params.num_turns;
199 
200  for(config& side : scenario.child_range("side")) {
201  side["controller_lock"] = false;
202  side["team_lock"] = true;
203  side["gold_lock"] = true;
204  side["income_lock"] = true;
205 
206  side["fog"] = params.fog_game;
207  side["shroud"] = params.shroud_game;
208  side["village_gold"] = params.village_gold;
209  side["village_support"] = params.village_support;
210  }
211 
212  params.mp_countdown = presets["countdown"].to_bool();
213  params.mp_countdown_init_time = std::chrono::seconds{presets["countdown_init_time"].to_int()};
214  params.mp_countdown_turn_bonus = std::chrono::seconds{presets["countdown_turn_bonus"].to_int()};
215  params.mp_countdown_reservoir_time = std::chrono::seconds{presets["countdown_reservoir_time"].to_int()};
216  params.mp_countdown_action_bonus = std::chrono::seconds{presets["countdown_action_bonus"].to_int()};
217 
218  params.allow_observers = true;
219  params.private_replay = false;
220  create.get_state().classification().oos_debug = false;
221  params.shuffle_sides = presets["shuffle_sides"].to_bool();
222 
223  params.mode = random_faction_mode::get_enum(presets["random_faction_mode"].str()).value_or(random_faction_mode::type::independent);
225 
226  for(const std::string& mod : utils::split(presets["modifications"].str())) {
227  create.active_mods().push_back(mod);
228  }
229 
230  if(presets.has_child("options")) {
231  params.options = presets.mandatory_child("options");
232  }
233 }
234 
236 {
237  find_widget<text_box>("game_name").set_value(local_mode_ ? "" : settings::game_name_default());
238 
240  find_widget<button>("random_map_regenerate"),
241  std::bind(&mp_create_game::regenerate_random_map, this));
242 
244  find_widget<button>("random_map_settings"),
245  std::bind(&mp_create_game::show_generator_settings, this));
246 
248  find_widget<button>("load_game"),
249  std::bind(&mp_create_game::load_game_callback, this));
250 
252  find_widget<button>("save_preset"),
253  std::bind(&mp_create_game::save_preset, this));
254 
255  // Custom dialog close hook
257 
258  //
259  // Set up the options manager. Needs to be done before selecting an initial tab
260  //
262 
263  //
264  // Set up filtering
265  //
266  connect_signal_notify_modified(find_widget<slider>("num_players"),
267  std::bind(&mp_create_game::on_filter_change<slider>, this, "num_players", true));
268 
269  text_box& filter = find_widget<text_box>("game_filter");
270 
271  filter.on_modified([this](const auto&) { on_filter_change<text_box>("game_filter", true); });
272 
273  //
274  // Set up game types menu_button
275  //
276  std::vector<config> game_types;
277  for(level_type_info& type_info : level_types_) {
278  game_types.emplace_back("label", type_info.second);
279  }
280 
281  if(game_types.empty()) {
282  gui2::show_transient_message("", _("No games found."));
283  throw game::error(_("No games found."));
284  }
285 
286  menu_button& game_menu_button = find_widget<menu_button>("game_types");
287 
288  // Helper to make sure the initially selected level type is valid
289  auto get_initial_type_index = [this]()->int {
291  level_type::get_enum(prefs::get().mp_level_type()).value(), &level_type_info::first);
292 
293  if(index != level_types_.end()) {
294  return std::distance(level_types_.begin(), index);
295  }
296 
297  return 0;
298  };
299 
300  int initial_type = get_initial_type_index();
301  game_menu_button.set_values(game_types, initial_type);
302  find_widget<button>("save_preset").set_active(level_type::get_enum(initial_type) == level_type::type::scenario);
303 
304  connect_signal_notify_modified(game_menu_button,
305  std::bind(&mp_create_game::update_games_list, this));
306 
307  //
308  // Set up mods list
309  //
310  mod_list_ = &find_widget<listbox>("mod_list");
311 
312  const auto& activemods = prefs::get().modifications();
314  grid* row_grid = &mod_list_->add_row(widget_data{{ "mod_name", {{ "label", mod->name }}}});
315 
316  row_grid->find_widget<toggle_panel>("panel").set_tooltip(mod->description);
317 
318  toggle_button& mog_toggle = row_grid->find_widget<toggle_button>("mod_active_state");
319 
320  if(utils::contains(activemods, mod->id)) {
321  create_engine_.active_mods().push_back(mod->id);
322  mog_toggle.set_value_bool(true);
323  }
324 
325  connect_signal_notify_modified(mog_toggle, std::bind(&mp_create_game::on_mod_toggle, this, mod->id, &mog_toggle));
326  }
327 
328  // No mods, hide the header
329  if(mod_list_->get_item_count() <= 0) {
330  find_widget<styled_widget>("mods_header").set_visible(widget::visibility::invisible);
331  }
332 
333  //
334  // Set up eras menu_button
335  //
336  eras_menu_button_ = &find_widget<menu_button>("eras");
337 
338  std::vector<config> era_names;
340  era_names.emplace_back("label", era->name, "tooltip", era->description);
341  }
342 
343  if(era_names.empty()) {
344  gui2::show_transient_message("", _("No eras found."));
345  throw config::error(_("No eras found"));
346  }
347 
348  eras_menu_button_->set_values(era_names);
349 
351  std::bind(&mp_create_game::on_era_select, this));
352 
353  const int era_selection = create_engine_.find_extra_by_id(ng::create_engine::ERA, prefs::get().mp_era());
354  if(era_selection >= 0) {
355  eras_menu_button_->set_selected(era_selection);
356  }
357 
358  on_era_select();
359 
360  //
361  // Set up random faction mode menu_button
362  //
363  const int initial_index = static_cast<int>(random_faction_mode::get_enum(prefs::get().random_faction_mode()).value_or(random_faction_mode::type::independent));
364 
365  menu_button& rfm_menu_button = find_widget<menu_button>("random_faction_mode");
366  rfm_menu_button.set_selected(initial_index);
367 
368  connect_signal_notify_modified(rfm_menu_button,
370 
372 
373  //
374  // Set up the setting status labels
375  //
377  bind_default_status_label(static_cast<slider&>(*gold_->get_widget()));
384 
385  //
386  // Timer reset button
387  //
389  find_widget<button>("reset_timer_defaults"),
390  std::bind(&mp_create_game::reset_timer_settings, this));
391 
392  //
393  // Disable certain settings if we're playing a local game.
394  //
395  if(local_mode_) {
396  find_widget<text_box>("game_name").set_active(false);
397  find_widget<text_box>("game_password").set_active(false);
398 
399  observers_->widget_set_enabled(false, false);
400  strict_sync_->widget_set_enabled(false, false);
401  private_replay_->widget_set_enabled(false, false);
402  }
403 
404  //
405  // Set up tab control
406  //
407  listbox& tab_bar = find_widget<listbox>("tab_bar");
408 
410  std::bind(&mp_create_game::on_tab_select, this));
411 
412  // Allow the settings stack to find widgets in all pages, regardless of which is selected.
413  // This ensures settings (especially game settings) widgets are appropriately updated when
414  // a new game is selected, regardless of which settings tab is active at the time.
415  find_widget<stacked_widget>("pager").set_find_in_all_layers(true);
416 
417  // We call on_tab_select farther down.
418 
419  //
420  // Main games list
421  //
422  listbox& list = find_widget<listbox>("games_list");
423 
425  std::bind(&mp_create_game::on_game_select, this));
426 
427 #ifdef __IPHONEOS__
428  // On iOS, opening this browser-style dialog should not immediately summon
429  // the software keyboard just because the optional filter field exists.
430  keyboard_capture(&list);
431 #else
432  // Note this cannot be in the keyboard chain or it will capture focus from other text boxes
434 #endif
435  add_to_keyboard_chain(&list);
436 
437  // This handles the initial game selection as well
438  display_games_of_type(level_types_[get_initial_type_index()].first, prefs::get().mp_level());
439 
440  // Initial tab selection must be done after game selection so the field widgets are set to their correct active state.
441  on_tab_select();
442 
443  //
444  // Set up the Lua plugin context
445  //
446  plugins_context_.reset(new plugins_context("Multiplayer Create"));
447 
448  plugins_context_->set_callback("create", [this](const config&) { set_retval(retval::OK); }, false);
449  plugins_context_->set_callback("quit", [this](const config&) { set_retval(retval::CANCEL); }, false);
450  plugins_context_->set_callback("load", [this](const config&) { load_game_callback(); }, false);
451 
452 #define UPDATE_ATTRIBUTE(field, convert) \
453  do { if(cfg.has_attribute(#field)) { field##_->set_widget_value(cfg[#field].convert()); } } while(false) \
454 
455  plugins_context_->set_callback("update_settings", [this](const config& cfg) {
456  UPDATE_ATTRIBUTE(turns, to_int);
457  UPDATE_ATTRIBUTE(gold, to_int);
458  UPDATE_ATTRIBUTE(support, to_int);
459  UPDATE_ATTRIBUTE(experience, to_int);
460  UPDATE_ATTRIBUTE(start_time, to_bool);
461  UPDATE_ATTRIBUTE(fog, to_bool);
462  UPDATE_ATTRIBUTE(shroud, to_bool);
463  UPDATE_ATTRIBUTE(time_limit, to_bool);
464  UPDATE_ATTRIBUTE(init_turn_limit, to_int);
465  UPDATE_ATTRIBUTE(turn_bonus, to_int);
466  UPDATE_ATTRIBUTE(reservoir, to_int);
467  UPDATE_ATTRIBUTE(action_bonus, to_int);
468  UPDATE_ATTRIBUTE(observers, to_bool);
469  UPDATE_ATTRIBUTE(strict_sync, to_bool);
470  UPDATE_ATTRIBUTE(private_replay, to_bool);
471  UPDATE_ATTRIBUTE(shuffle_sides, to_bool);
472  }, true);
473 
474 #undef UPDATE_ATTRIBUTE
475 
476  plugins_context_->set_callback("set_name", [this](const config& cfg) {
478  }, true);
479 
480  plugins_context_->set_callback("set_password", [this](const config& cfg) {
481  create_engine_.get_state().mp_settings().password = cfg["password"];
482  }, true);
483 
484  plugins_context_->set_callback("select_level", [this](const config& cfg) {
487  }, true);
488 
489  plugins_context_->set_callback("select_type", [this](const config& cfg) {
490  create_engine_.set_current_level_type(level_type::get_enum(cfg["type"].str()).value_or(level_type::type::scenario)); }, true);
491 
492  plugins_context_->set_callback("select_era", [this](const config& cfg) {
493  create_engine_.set_current_era_index(cfg["index"].to_int()); }, true);
494 
495  plugins_context_->set_callback("select_mod", [this](const config& cfg) {
496  on_mod_toggle(cfg["id"].str(), nullptr);
497  }, true);
498 
499  plugins_context_->set_accessor("get_selected", [this](const config&) {
500  const ng::level& current_level = create_engine_.current_level();
501  return config {
502  "id", current_level.id(),
503  "name", current_level.name(),
504  "icon", current_level.icon(),
505  "description", current_level.description(),
506  "allow_era_choice", current_level.allow_era_choice(),
508  };
509  });
510 
511  plugins_context_->set_accessor("find_level", [this](const config& cfg) {
512  const std::string id = cfg["id"].str();
513  auto result = create_engine_.find_level_by_id(id);
514  return config {
515  "index", result.second,
516  "type", level_type::get_string(result.first),
517  };
518  });
519 
520  plugins_context_->set_accessor_int("find_era", [this](const config& cfg) {
522  });
523 
524  plugins_context_->set_accessor_int("find_mod", [this](const config& cfg) {
526  });
527 }
528 
530 {
531  DBG_MP << "sync_with_depcheck: start";
532 
534  DBG_MP << "sync_with_depcheck: correcting era";
535  const int new_era_index = create_engine_.dependency_manager().get_era_index();
536 
537  create_engine_.set_current_era_index(new_era_index, true);
538  eras_menu_button_->set_value(new_era_index);
539  }
540 
542  DBG_MP << "sync_with_depcheck: correcting scenario";
543 
544  // Match scenario and scenario type
545  const auto [new_level_type, new_level_index] = create_engine_.find_level_by_id(create_engine_.dependency_manager().get_scenario());
546  const bool different_type = new_level_type != create_engine_.current_level_type();
547 
548  if(new_level_index != -1) {
549  create_engine_.set_current_level_type(new_level_type);
550  create_engine_.set_current_level(new_level_index);
551  selected_game_index_ = new_level_index;
552 
553  auto iter = utils::ranges::find(level_types_, new_level_type, &level_type_info::first);
554  find_widget<menu_button>("game_types").set_value(std::distance(level_types_.begin(), iter));
555 
556  if(different_type) {
558  } else {
559  // This function (or rather on_game_select) might be triggered by a listbox callback, in
560  // which case we cannot use display_games_of_type since it destroys the list (and its
561  // elements) which might result in segfaults. Instead, we assume that a listbox-triggered
562  // sync_with_depcheck call never changes the game type and goes to this branch instead.
563  find_widget<listbox>("games_list").select_row(new_level_index);
564 
565  // Override the last selection so on_game_select selects the new level
567 
568  on_game_select();
569  }
570  }
571  }
572 
574  DBG_MP << "sync_with_depcheck: correcting modifications";
576  }
577 
579  DBG_MP << "sync_with_depcheck: end";
580 }
581 
582 template<typename T>
583 void mp_create_game::on_filter_change(const std::string& id, bool do_select)
584 {
585  create_engine_.apply_level_filter(find_widget<T>(id).get_value());
586 
587  listbox& game_list = find_widget<listbox>("games_list");
588 
589  boost::dynamic_bitset<> filtered(game_list.get_item_count());
591  filtered[i] = true;
592  }
593 
594  game_list.set_row_shown(filtered);
595 
596  if(do_select) {
597  on_game_select();
598  }
599 }
600 
602 {
603  const int selected_game = find_widget<listbox>("games_list").get_selected_row();
604 
605  if(selected_game == selected_game_index_) {
606  return;
607  }
608 
609  // Convert the absolute-index get_selected_row to a relative index for the create_engine to handle
611 
613 
615 
616  update_details();
617 
618  // General settings
619  const bool can_select_era = create_engine_.current_level().allow_era_choice();
620 
621  if(!can_select_era) {
622  eras_menu_button_->set_label(_("No eras available for this game."));
623  } else {
625  }
626 
627  eras_menu_button_->set_active(can_select_era);
628 
629  // Custom options
630  options_manager_->update_game_options();
631 
632  // Game settings
633  if(level_types_[find_widget<menu_button>("game_types").get_value()].first != level_type::type::preset) {
635  } else {
636  if(auto preset = prefs::get().get_game_preset(create_engine_.preset_id_by_index(selected_game))) {
637  use_map_settings_->set_widget_value(preset["use_map_settings"].to_bool());
638  fog_->set_widget_value(preset["fog"].to_bool());
639  shroud_->set_widget_value(preset["shroud"].to_bool());
640  start_time_->set_widget_value(preset["random_start_time"].to_bool());
641  shuffle_sides_->set_widget_value(preset["shuffle_sides"].to_bool());
642  observers_->set_widget_value(preset["observer"].to_bool());
643  turns_->set_widget_value(preset["turns"].to_int());
644  gold_->set_widget_value(preset["village_gold"].to_int());
645  support_->set_widget_value(preset["village_support"].to_int());
646  experience_->set_widget_value(preset["experience_modifier"].to_int());
647  time_limit_->set_widget_value(preset["countdown"].to_bool());
648  init_turn_limit_->set_widget_value(preset["countdown_turn_limit"].to_int());
649  turn_bonus_->set_widget_value(preset["countdown_turn_bonus"].to_int());
650  reservoir_->set_widget_value(preset["countdown_reservoir"].to_int());
651  action_bonus_->set_widget_value(preset["countdown_action_bonus"].to_int());
652 
653  random_faction_mode::type mode = random_faction_mode::get_enum(preset["random_faction_mode"].str()).value_or(random_faction_mode::type::independent);
654  find_widget<menu_button>("random_faction_mode").set_selected(static_cast<int>(mode));
655 
656  const int era_selection = create_engine_.find_extra_by_id(ng::create_engine::ERA, preset["era"].str());
657  if(era_selection >= 0) {
658  eras_menu_button_->set_selected(era_selection);
659  }
660  on_era_select();
661 
662  int i = 0;
663  const auto& activemods = utils::split(preset["modifications"]);
665  toggle_button& mod_button = mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state");
666  mod_button.set_value_bool(utils::contains(activemods, mod->id));
667  on_mod_toggle(mod->id, &mod_button);
668  i++;
669  }
670 
671  if(preset->has_child("options")) {
672  options_manager_->set_options(preset->mandatory_child("options"));
673  }
674  }
675  }
676 }
677 
679 {
680  const int i = find_widget<listbox>("tab_bar").get_selected_row();
681  find_widget<stacked_widget>("pager").select_layer(i);
682 }
683 
684 void mp_create_game::on_mod_toggle(const std::string& id, toggle_button* sender)
685 {
686  if(sender && (sender->get_value_bool() == create_engine_.dependency_manager().is_modification_active(id))) {
687  ERR_MP << "ignoring on_mod_toggle that is already set";
688  return;
689  }
690 
692 
694 
695  options_manager_->update_mod_options();
696 }
697 
699 {
701 
703 
705 
706  options_manager_->update_era_options();
707 }
708 
710 {
711  selected_rfm_index_ = find_widget<menu_button>("random_faction_mode").get_value();
712 }
713 
714 void mp_create_game::show_description(const std::string& new_description)
715 {
716  styled_widget& description = find_widget<styled_widget>("description");
717 
718  description.set_label(!new_description.empty() ? new_description : _("No description available."));
719  description.set_use_markup(true);
720 }
721 
723 {
724  const int index = find_widget<menu_button>("game_types").get_value();
725 
727  find_widget<button>("save_preset").set_active(level_types_[index].first == level_type::type::scenario);
728 
729  if(level_types_[index].first != level_type::type::preset) {
730  backup_settings();
731  }
732 }
733 
735 {
737 
738  listbox& list = find_widget<listbox>("games_list");
739 
740  list.clear();
741 
744  widget_item item;
745 
746  if(type == level_type::type::campaign || type == level_type::type::sp_campaign) {
747  item["label"] = game->icon();
748  data.emplace("game_icon", item);
749  }
750 
751  item["label"] = game->name();
752  data.emplace("game_name", item);
753 
754  grid& rg = list.add_row(data);
755 
756  auto& icon = rg.find_widget<image>("game_icon");
757  if(icon.get_label().empty()) {
758  icon.set_visible(gui2::widget::visibility::invisible);
759  }
760  }
761 
762  if(!level.empty() && !list.get_rows_shown().empty()) {
763  // Recalculate which rows should be visible
764  on_filter_change<slider>("num_players", false);
765  on_filter_change<text_box>("game_filter", false);
766 
767  int level_index = create_engine_.find_level_by_id(level).second;
768  if(level_index >= 0 && std::size_t(level_index) < list.get_item_count()) {
769  list.select_row(level_index);
770  }
771  }
772 
773  const bool is_random_map = type == level_type::type::random_map;
774 
775  find_widget<button>("random_map_regenerate").set_active(is_random_map);
776  find_widget<button>("random_map_settings").set_active(is_random_map);
777 
778  // Override the last selection so on_game_select selects the new level
780 
781  on_game_select();
782 }
783 
785 {
787 
789 }
790 
792 {
794 
795  update_details();
796 }
797 
798 int mp_create_game::convert_to_game_filtered_index(const unsigned int initial_index)
799 {
800  const std::vector<std::size_t>& filtered_indices = create_engine_.get_filtered_level_indices(create_engine_.current_level_type());
801  return std::distance(filtered_indices.begin(), std::find(filtered_indices.begin(), filtered_indices.end(), initial_index));
802 }
803 
805 {
806  styled_widget& players = find_widget<styled_widget>("map_num_players");
807  styled_widget& map_size = find_widget<styled_widget>("map_size");
808 
809  if(create_engine_.current_level_type() == level_type::type::random_map) {
810  // If the current random map doesn't have data, generate it
812  create_engine_.current_level().data()["map_data"].empty() &&
813  create_engine_.current_level().data()["map_file"].empty()) {
815  }
816 
817  find_widget<button>("random_map_settings").set_active(create_engine_.generator_has_settings());
818  }
819 
821 
822  // Reset the mp_parameters with the defaults
824 
825  // Set the title, with newlines replaced. Newlines are sometimes found in SP Campaign names
826  std::string title = create_engine_.current_level().name();
827  boost::replace_all(title, "\n", " " + font::unicode_em_dash + " ");
828  find_widget<styled_widget>("game_title").set_label(title);
829 
830 
832  case level_type::type::preset:
833  case level_type::type::scenario:
834  case level_type::type::user_map:
835  case level_type::type::user_scenario:
836  case level_type::type::random_map: {
837  ng::scenario* current_scenario = dynamic_cast<ng::scenario*>(&create_engine_.current_level());
838 
839  assert(current_scenario);
840 
842 
843  find_widget<stacked_widget>("minimap_stack").select_layer(0);
844 
845  if(current_scenario->data()["map_data"].empty()) {
846  saved_game::expand_map_file(current_scenario->data());
847  current_scenario->set_metadata();
848  }
849 
850  find_widget<minimap>("minimap").set_map_data(current_scenario->data()["map_data"]);
851 
852  players.set_label(std::to_string(current_scenario->num_players()));
853  map_size.set_label(current_scenario->map_size());
854 
855  break;
856  }
857  case level_type::type::campaign:
858  case level_type::type::sp_campaign: {
859  ng::campaign* current_campaign = dynamic_cast<ng::campaign*>(&create_engine_.current_level());
860 
861  assert(current_campaign);
862 
863  create_engine_.get_state().classification().campaign = current_campaign->data()["id"].str();
864 
865  const std::string img = formatter() << current_campaign->data()["image"] << "~SCALE_INTO(265,265)";
866 
867  find_widget<stacked_widget>("minimap_stack").select_layer(1);
868  find_widget<image>("campaign_image").set_image(img);
869 
870  const int p_min = current_campaign->min_players();
871  const int p_max = current_campaign->max_players();
872 
873  if(p_max > p_min) {
874  players.set_label(VGETTEXT("number of players^$min to $max", {{"min", std::to_string(p_min)}, {"max", std::to_string(p_max)}}));
875  } else {
876  players.set_label(std::to_string(p_min));
877  }
878 
880 
881  break;
882  }
883  }
884 
885  // This needs to be at the end, since errors found expanding the map data are put into the description
887 }
888 
890 {
892  if(level["force_lock_settings"].to_bool(!create_engine_.get_state().classification().is_normal_mp_game())) {
893  use_map_settings_->widget_set_enabled(false, false);
895  } else {
897  }
898 
900 
901  const bool use_map_settings = create_engine_.get_state().mp_settings().use_map_settings;
902 
903  fog_ ->widget_set_enabled(!use_map_settings, false);
904  shroud_ ->widget_set_enabled(!use_map_settings, false);
905  start_time_ ->widget_set_enabled(!use_map_settings, false);
906 
907  turns_ ->widget_set_enabled(!use_map_settings, false);
908  gold_ ->widget_set_enabled(!use_map_settings, false);
909  support_ ->widget_set_enabled(!use_map_settings, false);
910  experience_ ->widget_set_enabled(!use_map_settings, false);
911 
912  const bool time_limit = time_limit_->get_widget_value();
913 
914  init_turn_limit_->widget_set_enabled(time_limit, false);
915  turn_bonus_ ->widget_set_enabled(time_limit, false);
916  reservoir_ ->widget_set_enabled(time_limit, false);
917  action_bonus_ ->widget_set_enabled(time_limit, false);
918 
919  find_widget<button>("reset_timer_defaults").set_active(time_limit);
920 
924 
929 }
930 
932 {
934  if(!load_data) {
935  return;
936  }
937 
939 
940  if(load_data->cancel_orders) {
942  }
943 
945 }
946 
948 {
950 
951  settings["scenario"] = create_engine_.current_level().id();
953  settings["fog"] = fog_->get_widget_value();
954  settings["shroud"] = shroud_->get_widget_value();
955  settings["village_gold"] = gold_->get_widget_value();
956  settings["village_support"] = support_->get_widget_value();
957  settings["experience_modifier"] = experience_->get_widget_value();
958  settings["countdown"] = time_limit_->get_widget_value();
959  settings["countdown_turn_limit"] = init_turn_limit_->get_widget_value();
960  settings["countdown_action_bonus"] = action_bonus_->get_widget_value();
961  settings["countdown_turn_bonus"] = turn_bonus_->get_widget_value();
962  settings["countdown_reservoir"] = reservoir_->get_widget_value();
963  settings["random_start_time"] = start_time_->get_widget_value();
964  settings["shuffle_sides"] = shuffle_sides_->get_widget_value();
965  settings["turns"] = turns_->get_widget_value();
966  settings["observer"] = observers_->get_widget_value();
967  settings["use_map_settings"] = use_map_settings_->get_widget_value();
968  settings["modifications"] = utils::join(create_engine_.active_mods(), ",");
969  settings.add_child("options", options_manager_->get_options_config());
970  random_faction_mode::type type = random_faction_mode::get_enum(selected_rfm_index_).value_or(random_faction_mode::type::independent);
971  settings["random_faction_mode"] = random_faction_mode::get_string(type);
972 
973  return settings;
974 }
975 
977 {
979 }
980 
982 {
984 }
985 
986 std::vector<std::string> mp_create_game::get_active_mods()
987 {
988  int i = 0;
989  std::set<std::string> res;
991  if(mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state").get_value_bool()) {
992  res.insert(mod->id);
993  }
994  ++i;
995  }
996  return std::vector<std::string>(res.begin(), res.end());
997 }
998 
999 void mp_create_game::set_active_mods(const std::vector<std::string>& val)
1000 {
1001  std::set<std::string> val2(val.begin(), val.end());
1002  int i = 0;
1003  std::set<std::string> res;
1004  for(const auto& mod : create_engine_.get_extras_by_type(ng::create_engine::MOD)) {
1005  mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state").set_value_bool(val2.find(mod->id) != val2.end());
1006  ++i;
1007  }
1008 }
1009 
1011 {
1012  // This allows the defaults to be returned by the pref getters below
1017 
1018  init_turn_limit_->set_widget_value(prefs::get().countdown_init_time().count());
1019  turn_bonus_->set_widget_value(prefs::get().countdown_turn_bonus().count());
1020  reservoir_->set_widget_value(prefs::get().countdown_reservoir_time().count());
1021  action_bonus_->set_widget_value(prefs::get().countdown_action_bonus().count());
1022 }
1023 
1025 {
1027  gui2::show_transient_error_message(_("The selected game has no sides!"));
1028  return false;
1029  }
1030 
1032  std::stringstream msg;
1033  // 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
1034  msg << _("The selected game cannot be created.");
1035  msg << "\n\n";
1038  return false;
1039  }
1040 
1041  if(!create_engine_.is_campaign()) {
1042  return true;
1043  }
1044 
1045  return create_engine_.select_campaign_difficulty() != "CANCEL";
1046 }
1047 
1049 {
1050  plugins_context_.reset();
1051 
1052  // Show all tabs so that find_widget works correctly
1053  find_widget<stacked_widget>("pager").select_layer(-1);
1054 
1055  if(get_retval() == LOAD_GAME) {
1057 
1058  // 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.
1060  return;
1061  }
1062 
1063  if(get_retval() == retval::OK) {
1065  prefs::get().set_mp_level_type(static_cast<int>(create_engine_.current_level_type()));
1066  prefs::get().set_mp_level(create_engine_.current_level().id());
1067  prefs::get().set_mp_era(create_engine_.current_era().id);
1068 
1070 
1071  if(create_engine_.current_level_type() == level_type::type::campaign ||
1072  create_engine_.current_level_type() == level_type::type::sp_campaign) {
1074  } else if(create_engine_.current_level_type() == level_type::type::scenario) {
1076  } else {
1077  // This means define= doesn't work for randomly generated scenarios
1079  }
1080 
1082 
1084 
1085  std::vector<const config*> entry_points;
1086  std::vector<std::string> entry_point_titles;
1087 
1088  const auto& tagname = create_engine_.get_state().classification().get_tagname();
1089 
1090  if(tagname == "scenario") {
1091  const std::string first_scenario = create_engine_.current_level().data()["first_scenario"];
1092  for(const config& scenario : game_config_manager::get()->game_config().child_range(tagname)) {
1093  const bool is_first = scenario["id"] == first_scenario;
1094  if(scenario["allow_new_game"].to_bool(false) || is_first || game_config::debug ) {
1095  const std::string& title = !scenario["new_game_title"].empty()
1096  ? scenario["new_game_title"]
1097  : scenario["name"];
1098 
1099  entry_points.insert(is_first ? entry_points.begin() : entry_points.end(), &scenario);
1100  entry_point_titles.insert(is_first ? entry_point_titles.begin() : entry_point_titles.end(), title);
1101  }
1102  }
1103  }
1104 
1106  if(entry_points.size() > 1) {
1107  gui2::dialogs::simple_item_selector dlg(_("Choose Starting Scenario"), _("Select at which point to begin this campaign."), entry_point_titles);
1108 
1109  dlg.set_single_button(true);
1110  dlg.show();
1111 
1112  const config& scenario = *entry_points[dlg.selected_index()];
1113 
1114  params.hash = scenario.hash();
1116  }
1117 
1119 
1120  if(!create_engine_.current_level().data()["force_lock_settings"].to_bool(!create_engine_.get_state().classification().is_normal_mp_game())) {
1121  // Max slider value (in this case, 100) means 'unlimited turns', so pass the value -1
1122  const int num_turns = turns_->get_widget_value();
1123  params.num_turns = num_turns < ::settings::turns_max ? num_turns : - 1;
1124  params.village_gold = gold_->get_widget_value();
1128  params.fog_game = fog_->get_widget_value();
1129  params.shroud_game = shroud_->get_widget_value();
1130 
1131  // write to scenario
1133 
1134  if(params.random_start_time) {
1135  if(!tod_manager::is_start_ToD(scenario["random_start_time"])) {
1136  scenario["random_start_time"] = true;
1137  }
1138  } else {
1139  scenario["random_start_time"] = false;
1140  }
1141 
1142  scenario["experience_modifier"] = params.xp_modifier;
1143  scenario["turns"] = params.num_turns;
1144 
1145  for(config& side : scenario.child_range("side")) {
1146  if(!params.use_map_settings) {
1147  side["fog"] = params.fog_game;
1148  side["shroud"] = params.shroud_game;
1149  side["village_gold"] = params.village_gold;
1150  side["village_support"] = params.village_support;
1151  } else {
1152  if(side["fog"].empty()) {
1153  side["fog"] = params.fog_game;
1154  }
1155 
1156  if(side["shroud"].empty()) {
1157  side["shroud"] = params.shroud_game;
1158  }
1159 
1160  if(side["village_gold"].empty()) {
1161  side["village_gold"] = params.village_gold;
1162  }
1163 
1164  if(side["village_support"].empty()) {
1165  side["village_support"] = params.village_support;
1166  }
1167  }
1168  }
1169  }
1170 
1172  params.mp_countdown_init_time = std::chrono::seconds{init_turn_limit_->get_widget_value()};
1173  params.mp_countdown_turn_bonus = std::chrono::seconds{turn_bonus_->get_widget_value()};
1174  params.mp_countdown_reservoir_time = std::chrono::seconds{reservoir_->get_widget_value()};
1175  params.mp_countdown_action_bonus = std::chrono::seconds{action_bonus_->get_widget_value()};
1176 
1181 
1182  random_faction_mode::type type = random_faction_mode::get_enum(selected_rfm_index_).value_or(random_faction_mode::type::independent);
1183  params.mode = type;
1184 
1185  // Since we don't have a field handling this option, we need to save the value manually
1186  prefs::get().set_random_faction_mode(random_faction_mode::get_string(type));
1187 
1188  // Save custom option settings
1189  params.options = options_manager_->get_options_config();
1190  prefs::get().set_options(options_manager_->get_options_config());
1191 
1192  // Set game name
1193  const std::string name = find_widget<text_box>("game_name").get_value();
1194  if(!name.empty() && (name != settings::game_name_default())) {
1195  params.name = name;
1196  }
1197 
1198  // Set game password
1199  const std::string password = find_widget<text_box>("game_password").get_value();
1200  if(!password.empty()) {
1201  params.password = password;
1202  }
1203  }
1204 }
1205 
1206 } // 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
void set_current_era_id(const std::string &id, bool force=false)
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:441
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:97
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:67
const bool & debug
Definition: game_config.cpp:95
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:196
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:87
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
Definition: general.hpp:107
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
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:141
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
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