The Battle for Wesnoth  1.19.15+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  // Note this cannot be in the keyboard chain or it will capture focus from other text boxes
275 
276  //
277  // Set up game types menu_button
278  //
279  std::vector<config> game_types;
280  for(level_type_info& type_info : level_types_) {
281  game_types.emplace_back("label", type_info.second);
282  }
283 
284  if(game_types.empty()) {
285  gui2::show_transient_message("", _("No games found."));
286  throw game::error(_("No games found."));
287  }
288 
289  menu_button& game_menu_button = find_widget<menu_button>("game_types");
290 
291  // Helper to make sure the initially selected level type is valid
292  auto get_initial_type_index = [this]()->int {
294  level_type::get_enum(prefs::get().mp_level_type()).value(), &level_type_info::first);
295 
296  if(index != level_types_.end()) {
297  return std::distance(level_types_.begin(), index);
298  }
299 
300  return 0;
301  };
302 
303  int initial_type = get_initial_type_index();
304  game_menu_button.set_values(game_types, initial_type);
305  find_widget<button>("save_preset").set_active(level_type::get_enum(initial_type) == level_type::type::scenario);
306 
307  connect_signal_notify_modified(game_menu_button,
308  std::bind(&mp_create_game::update_games_list, this));
309 
310  //
311  // Set up mods list
312  //
313  mod_list_ = &find_widget<listbox>("mod_list");
314 
315  const auto& activemods = prefs::get().modifications();
317  grid* row_grid = &mod_list_->add_row(widget_data{{ "mod_name", {{ "label", mod->name }}}});
318 
319  row_grid->find_widget<toggle_panel>("panel").set_tooltip(mod->description);
320 
321  toggle_button& mog_toggle = row_grid->find_widget<toggle_button>("mod_active_state");
322 
323  if(utils::contains(activemods, mod->id)) {
324  create_engine_.active_mods().push_back(mod->id);
325  mog_toggle.set_value_bool(true);
326  }
327 
328  connect_signal_notify_modified(mog_toggle, std::bind(&mp_create_game::on_mod_toggle, this, mod->id, &mog_toggle));
329  }
330 
331  // No mods, hide the header
332  if(mod_list_->get_item_count() <= 0) {
333  find_widget<styled_widget>("mods_header").set_visible(widget::visibility::invisible);
334  }
335 
336  //
337  // Set up eras menu_button
338  //
339  eras_menu_button_ = &find_widget<menu_button>("eras");
340 
341  std::vector<config> era_names;
343  era_names.emplace_back("label", era->name, "tooltip", era->description);
344  }
345 
346  if(era_names.empty()) {
347  gui2::show_transient_message("", _("No eras found."));
348  throw config::error(_("No eras found"));
349  }
350 
351  eras_menu_button_->set_values(era_names);
352 
354  std::bind(&mp_create_game::on_era_select, this));
355 
356  const int era_selection = create_engine_.find_extra_by_id(ng::create_engine::ERA, prefs::get().mp_era());
357  if(era_selection >= 0) {
358  eras_menu_button_->set_selected(era_selection);
359  }
360 
361  on_era_select();
362 
363  //
364  // Set up random faction mode menu_button
365  //
366  const int initial_index = static_cast<int>(random_faction_mode::get_enum(prefs::get().random_faction_mode()).value_or(random_faction_mode::type::independent));
367 
368  menu_button& rfm_menu_button = find_widget<menu_button>("random_faction_mode");
369  rfm_menu_button.set_selected(initial_index);
370 
371  connect_signal_notify_modified(rfm_menu_button,
373 
375 
376  //
377  // Set up the setting status labels
378  //
380  bind_default_status_label(static_cast<slider&>(*gold_->get_widget()));
387 
388  //
389  // Timer reset button
390  //
392  find_widget<button>("reset_timer_defaults"),
393  std::bind(&mp_create_game::reset_timer_settings, this));
394 
395  //
396  // Disable certain settings if we're playing a local game.
397  //
398  if(local_mode_) {
399  find_widget<text_box>("game_name").set_active(false);
400  find_widget<text_box>("game_password").set_active(false);
401 
402  observers_->widget_set_enabled(false, false);
403  strict_sync_->widget_set_enabled(false, false);
404  private_replay_->widget_set_enabled(false, false);
405  }
406 
407  //
408  // Set up tab control
409  //
410  listbox& tab_bar = find_widget<listbox>("tab_bar");
411 
413  std::bind(&mp_create_game::on_tab_select, this));
414 
415  // Allow the settings stack to find widgets in all pages, regardless of which is selected.
416  // This ensures settings (especially game settings) widgets are appropriately updated when
417  // a new game is selected, regardless of which settings tab is active at the time.
418  find_widget<stacked_widget>("pager").set_find_in_all_layers(true);
419 
420  // We call on_tab_select farther down.
421 
422  //
423  // Main games list
424  //
425  listbox& list = find_widget<listbox>("games_list");
426 
428  std::bind(&mp_create_game::on_game_select, this));
429 
430  add_to_keyboard_chain(&list);
431 
432  // This handles the initial game selection as well
433  display_games_of_type(level_types_[get_initial_type_index()].first, prefs::get().mp_level());
434 
435  // Initial tab selection must be done after game selection so the field widgets are set to their correct active state.
436  on_tab_select();
437 
438  //
439  // Set up the Lua plugin context
440  //
441  plugins_context_.reset(new plugins_context("Multiplayer Create"));
442 
443  plugins_context_->set_callback("create", [this](const config&) { set_retval(retval::OK); }, false);
444  plugins_context_->set_callback("quit", [this](const config&) { set_retval(retval::CANCEL); }, false);
445  plugins_context_->set_callback("load", [this](const config&) { load_game_callback(); }, false);
446 
447 #define UPDATE_ATTRIBUTE(field, convert) \
448  do { if(cfg.has_attribute(#field)) { field##_->set_widget_value(cfg[#field].convert()); } } while(false) \
449 
450  plugins_context_->set_callback("update_settings", [this](const config& cfg) {
451  UPDATE_ATTRIBUTE(turns, to_int);
452  UPDATE_ATTRIBUTE(gold, to_int);
453  UPDATE_ATTRIBUTE(support, to_int);
454  UPDATE_ATTRIBUTE(experience, to_int);
455  UPDATE_ATTRIBUTE(start_time, to_bool);
456  UPDATE_ATTRIBUTE(fog, to_bool);
457  UPDATE_ATTRIBUTE(shroud, to_bool);
458  UPDATE_ATTRIBUTE(time_limit, to_bool);
459  UPDATE_ATTRIBUTE(init_turn_limit, to_int);
460  UPDATE_ATTRIBUTE(turn_bonus, to_int);
461  UPDATE_ATTRIBUTE(reservoir, to_int);
462  UPDATE_ATTRIBUTE(action_bonus, to_int);
463  UPDATE_ATTRIBUTE(observers, to_bool);
464  UPDATE_ATTRIBUTE(strict_sync, to_bool);
465  UPDATE_ATTRIBUTE(private_replay, to_bool);
466  UPDATE_ATTRIBUTE(shuffle_sides, to_bool);
467  }, true);
468 
469 #undef UPDATE_ATTRIBUTE
470 
471  plugins_context_->set_callback("set_name", [this](const config& cfg) {
473  }, true);
474 
475  plugins_context_->set_callback("set_password", [this](const config& cfg) {
476  create_engine_.get_state().mp_settings().password = cfg["password"];
477  }, true);
478 
479  plugins_context_->set_callback("select_level", [this](const config& cfg) {
482  }, true);
483 
484  plugins_context_->set_callback("select_type", [this](const config& cfg) {
485  create_engine_.set_current_level_type(level_type::get_enum(cfg["type"].str()).value_or(level_type::type::scenario)); }, true);
486 
487  plugins_context_->set_callback("select_era", [this](const config& cfg) {
488  create_engine_.set_current_era_index(cfg["index"].to_int()); }, true);
489 
490  plugins_context_->set_callback("select_mod", [this](const config& cfg) {
491  on_mod_toggle(cfg["id"].str(), nullptr);
492  }, true);
493 
494  plugins_context_->set_accessor("get_selected", [this](const config&) {
495  const ng::level& current_level = create_engine_.current_level();
496  return config {
497  "id", current_level.id(),
498  "name", current_level.name(),
499  "icon", current_level.icon(),
500  "description", current_level.description(),
501  "allow_era_choice", current_level.allow_era_choice(),
503  };
504  });
505 
506  plugins_context_->set_accessor("find_level", [this](const config& cfg) {
507  const std::string id = cfg["id"].str();
508  auto result = create_engine_.find_level_by_id(id);
509  return config {
510  "index", result.second,
511  "type", level_type::get_string(result.first),
512  };
513  });
514 
515  plugins_context_->set_accessor_int("find_era", [this](const config& cfg) {
517  });
518 
519  plugins_context_->set_accessor_int("find_mod", [this](const config& cfg) {
521  });
522 }
523 
525 {
526  DBG_MP << "sync_with_depcheck: start";
527 
529  DBG_MP << "sync_with_depcheck: correcting era";
530  const int new_era_index = create_engine_.dependency_manager().get_era_index();
531 
532  create_engine_.set_current_era_index(new_era_index, true);
533  eras_menu_button_->set_value(new_era_index);
534  }
535 
537  DBG_MP << "sync_with_depcheck: correcting scenario";
538 
539  // Match scenario and scenario type
540  const auto [new_level_type, new_level_index] = create_engine_.find_level_by_id(create_engine_.dependency_manager().get_scenario());
541  const bool different_type = new_level_type != create_engine_.current_level_type();
542 
543  if(new_level_index != -1) {
544  create_engine_.set_current_level_type(new_level_type);
545  create_engine_.set_current_level(new_level_index);
546  selected_game_index_ = new_level_index;
547 
548  auto iter = utils::ranges::find(level_types_, new_level_type, &level_type_info::first);
549  find_widget<menu_button>("game_types").set_value(std::distance(level_types_.begin(), iter));
550 
551  if(different_type) {
553  } else {
554  // This function (or rather on_game_select) might be triggered by a listbox callback, in
555  // which case we cannot use display_games_of_type since it destroys the list (and its
556  // elements) which might result in segfaults. Instead, we assume that a listbox-triggered
557  // sync_with_depcheck call never changes the game type and goes to this branch instead.
558  find_widget<listbox>("games_list").select_row(new_level_index);
559 
560  // Override the last selection so on_game_select selects the new level
562 
563  on_game_select();
564  }
565  }
566  }
567 
569  DBG_MP << "sync_with_depcheck: correcting modifications";
571  }
572 
574  DBG_MP << "sync_with_depcheck: end";
575 }
576 
577 template<typename T>
578 void mp_create_game::on_filter_change(const std::string& id, bool do_select)
579 {
580  create_engine_.apply_level_filter(find_widget<T>(id).get_value());
581 
582  listbox& game_list = find_widget<listbox>("games_list");
583 
584  boost::dynamic_bitset<> filtered(game_list.get_item_count());
586  filtered[i] = true;
587  }
588 
589  game_list.set_row_shown(filtered);
590 
591  if(do_select) {
592  on_game_select();
593  }
594 }
595 
597 {
598  const int selected_game = find_widget<listbox>("games_list").get_selected_row();
599 
600  if(selected_game == selected_game_index_) {
601  return;
602  }
603 
604  // Convert the absolute-index get_selected_row to a relative index for the create_engine to handle
606 
608 
610 
611  update_details();
612 
613  // General settings
614  const bool can_select_era = create_engine_.current_level().allow_era_choice();
615 
616  if(!can_select_era) {
617  eras_menu_button_->set_label(_("No eras available for this game."));
618  } else {
620  }
621 
622  eras_menu_button_->set_active(can_select_era);
623 
624  // Custom options
625  options_manager_->update_game_options();
626 
627  // Game settings
628  if(level_types_[find_widget<menu_button>("game_types").get_value()].first != level_type::type::preset) {
630  } else {
631  if(auto preset = prefs::get().get_game_preset(create_engine_.preset_id_by_index(selected_game))) {
632  use_map_settings_->set_widget_value(preset["use_map_settings"].to_bool());
633  fog_->set_widget_value(preset["fog"].to_bool());
634  shroud_->set_widget_value(preset["shroud"].to_bool());
635  start_time_->set_widget_value(preset["random_start_time"].to_bool());
636  shuffle_sides_->set_widget_value(preset["shuffle_sides"].to_bool());
637  observers_->set_widget_value(preset["observer"].to_bool());
638  turns_->set_widget_value(preset["turns"].to_int());
639  gold_->set_widget_value(preset["village_gold"].to_int());
640  support_->set_widget_value(preset["village_support"].to_int());
641  experience_->set_widget_value(preset["experience_modifier"].to_int());
642  time_limit_->set_widget_value(preset["countdown"].to_bool());
643  init_turn_limit_->set_widget_value(preset["countdown_turn_limit"].to_int());
644  turn_bonus_->set_widget_value(preset["countdown_turn_bonus"].to_int());
645  reservoir_->set_widget_value(preset["countdown_reservoir"].to_int());
646  action_bonus_->set_widget_value(preset["countdown_action_bonus"].to_int());
647 
648  random_faction_mode::type mode = random_faction_mode::get_enum(preset["random_faction_mode"].str()).value_or(random_faction_mode::type::independent);
649  find_widget<menu_button>("random_faction_mode").set_selected(static_cast<int>(mode));
650 
651  const int era_selection = create_engine_.find_extra_by_id(ng::create_engine::ERA, preset["era"].str());
652  if(era_selection >= 0) {
653  eras_menu_button_->set_selected(era_selection);
654  }
655  on_era_select();
656 
657  int i = 0;
658  const auto& activemods = utils::split(preset["modifications"]);
660  toggle_button& mod_button = mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state");
661  mod_button.set_value_bool(utils::contains(activemods, mod->id));
662  on_mod_toggle(mod->id, &mod_button);
663  i++;
664  }
665 
666  if(preset->has_child("options")) {
667  options_manager_->set_options(preset->mandatory_child("options"));
668  }
669  }
670  }
671 }
672 
674 {
675  const int i = find_widget<listbox>("tab_bar").get_selected_row();
676  find_widget<stacked_widget>("pager").select_layer(i);
677 }
678 
679 void mp_create_game::on_mod_toggle(const std::string& id, toggle_button* sender)
680 {
681  if(sender && (sender->get_value_bool() == create_engine_.dependency_manager().is_modification_active(id))) {
682  ERR_MP << "ignoring on_mod_toggle that is already set";
683  return;
684  }
685 
687 
689 
690  options_manager_->update_mod_options();
691 }
692 
694 {
696 
698 
700 
701  options_manager_->update_era_options();
702 }
703 
705 {
706  selected_rfm_index_ = find_widget<menu_button>("random_faction_mode").get_value();
707 }
708 
709 void mp_create_game::show_description(const std::string& new_description)
710 {
711  styled_widget& description = find_widget<styled_widget>("description");
712 
713  description.set_label(!new_description.empty() ? new_description : _("No description available."));
714  description.set_use_markup(true);
715 }
716 
718 {
719  const int index = find_widget<menu_button>("game_types").get_value();
720 
722  find_widget<button>("save_preset").set_active(level_types_[index].first == level_type::type::scenario);
723 
724  if(level_types_[index].first != level_type::type::preset) {
725  backup_settings();
726  }
727 }
728 
730 {
732 
733  listbox& list = find_widget<listbox>("games_list");
734 
735  list.clear();
736 
739  widget_item item;
740 
741  if(type == level_type::type::campaign || type == level_type::type::sp_campaign) {
742  item["label"] = game->icon();
743  data.emplace("game_icon", item);
744  }
745 
746  item["label"] = game->name();
747  data.emplace("game_name", item);
748 
749  grid& rg = list.add_row(data);
750 
751  auto& icon = rg.find_widget<image>("game_icon");
752  if(icon.get_label().empty()) {
753  icon.set_visible(gui2::widget::visibility::invisible);
754  }
755  }
756 
757  if(!level.empty() && !list.get_rows_shown().empty()) {
758  // Recalculate which rows should be visible
759  on_filter_change<slider>("num_players", false);
760  on_filter_change<text_box>("game_filter", false);
761 
762  int level_index = create_engine_.find_level_by_id(level).second;
763  if(level_index >= 0 && std::size_t(level_index) < list.get_item_count()) {
764  list.select_row(level_index);
765  }
766  }
767 
768  const bool is_random_map = type == level_type::type::random_map;
769 
770  find_widget<button>("random_map_regenerate").set_active(is_random_map);
771  find_widget<button>("random_map_settings").set_active(is_random_map);
772 
773  // Override the last selection so on_game_select selects the new level
775 
776  on_game_select();
777 }
778 
780 {
782 
784 }
785 
787 {
789 
790  update_details();
791 }
792 
793 int mp_create_game::convert_to_game_filtered_index(const unsigned int initial_index)
794 {
795  const std::vector<std::size_t>& filtered_indices = create_engine_.get_filtered_level_indices(create_engine_.current_level_type());
796  return std::distance(filtered_indices.begin(), std::find(filtered_indices.begin(), filtered_indices.end(), initial_index));
797 }
798 
800 {
801  styled_widget& players = find_widget<styled_widget>("map_num_players");
802  styled_widget& map_size = find_widget<styled_widget>("map_size");
803 
804  if(create_engine_.current_level_type() == level_type::type::random_map) {
805  // If the current random map doesn't have data, generate it
807  create_engine_.current_level().data()["map_data"].empty() &&
808  create_engine_.current_level().data()["map_file"].empty()) {
810  }
811 
812  find_widget<button>("random_map_settings").set_active(create_engine_.generator_has_settings());
813  }
814 
816 
817  // Reset the mp_parameters with the defaults
819 
820  // Set the title, with newlines replaced. Newlines are sometimes found in SP Campaign names
821  std::string title = create_engine_.current_level().name();
822  boost::replace_all(title, "\n", " " + font::unicode_em_dash + " ");
823  find_widget<styled_widget>("game_title").set_label(title);
824 
825 
827  case level_type::type::preset:
828  case level_type::type::scenario:
829  case level_type::type::user_map:
830  case level_type::type::user_scenario:
831  case level_type::type::random_map: {
832  ng::scenario* current_scenario = dynamic_cast<ng::scenario*>(&create_engine_.current_level());
833 
834  assert(current_scenario);
835 
837 
838  find_widget<stacked_widget>("minimap_stack").select_layer(0);
839 
840  if(current_scenario->data()["map_data"].empty()) {
841  saved_game::expand_map_file(current_scenario->data());
842  current_scenario->set_metadata();
843  }
844 
845  find_widget<minimap>("minimap").set_map_data(current_scenario->data()["map_data"]);
846 
847  players.set_label(std::to_string(current_scenario->num_players()));
848  map_size.set_label(current_scenario->map_size());
849 
850  break;
851  }
852  case level_type::type::campaign:
853  case level_type::type::sp_campaign: {
854  ng::campaign* current_campaign = dynamic_cast<ng::campaign*>(&create_engine_.current_level());
855 
856  assert(current_campaign);
857 
858  create_engine_.get_state().classification().campaign = current_campaign->data()["id"].str();
859 
860  const std::string img = formatter() << current_campaign->data()["image"] << "~SCALE_INTO(265,265)";
861 
862  find_widget<stacked_widget>("minimap_stack").select_layer(1);
863  find_widget<image>("campaign_image").set_image(img);
864 
865  const int p_min = current_campaign->min_players();
866  const int p_max = current_campaign->max_players();
867 
868  if(p_max > p_min) {
869  players.set_label(VGETTEXT("number of players^$min to $max", {{"min", std::to_string(p_min)}, {"max", std::to_string(p_max)}}));
870  } else {
871  players.set_label(std::to_string(p_min));
872  }
873 
875 
876  break;
877  }
878  }
879 
880  // This needs to be at the end, since errors found expanding the map data are put into the description
882 }
883 
885 {
887  if(level["force_lock_settings"].to_bool(!create_engine_.get_state().classification().is_normal_mp_game())) {
888  use_map_settings_->widget_set_enabled(false, false);
890  } else {
892  }
893 
895 
896  const bool use_map_settings = create_engine_.get_state().mp_settings().use_map_settings;
897 
898  fog_ ->widget_set_enabled(!use_map_settings, false);
899  shroud_ ->widget_set_enabled(!use_map_settings, false);
900  start_time_ ->widget_set_enabled(!use_map_settings, false);
901 
902  turns_ ->widget_set_enabled(!use_map_settings, false);
903  gold_ ->widget_set_enabled(!use_map_settings, false);
904  support_ ->widget_set_enabled(!use_map_settings, false);
905  experience_ ->widget_set_enabled(!use_map_settings, false);
906 
907  const bool time_limit = time_limit_->get_widget_value();
908 
909  init_turn_limit_->widget_set_enabled(time_limit, false);
910  turn_bonus_ ->widget_set_enabled(time_limit, false);
911  reservoir_ ->widget_set_enabled(time_limit, false);
912  action_bonus_ ->widget_set_enabled(time_limit, false);
913 
914  find_widget<button>("reset_timer_defaults").set_active(time_limit);
915 
919 
924 }
925 
927 {
929  if(!load_data) {
930  return;
931  }
932 
934 
935  if(load_data->cancel_orders) {
937  }
938 
940 }
941 
943 {
945 
946  settings["scenario"] = create_engine_.current_level().id();
948  settings["fog"] = fog_->get_widget_value();
949  settings["shroud"] = shroud_->get_widget_value();
950  settings["village_gold"] = gold_->get_widget_value();
951  settings["village_support"] = support_->get_widget_value();
952  settings["experience_modifier"] = experience_->get_widget_value();
953  settings["countdown"] = time_limit_->get_widget_value();
954  settings["countdown_turn_limit"] = init_turn_limit_->get_widget_value();
955  settings["countdown_action_bonus"] = action_bonus_->get_widget_value();
956  settings["countdown_turn_bonus"] = turn_bonus_->get_widget_value();
957  settings["countdown_reservoir"] = reservoir_->get_widget_value();
958  settings["random_start_time"] = start_time_->get_widget_value();
959  settings["shuffle_sides"] = shuffle_sides_->get_widget_value();
960  settings["turns"] = turns_->get_widget_value();
961  settings["observer"] = observers_->get_widget_value();
962  settings["use_map_settings"] = use_map_settings_->get_widget_value();
963  settings["modifications"] = utils::join(create_engine_.active_mods(), ",");
964  settings.add_child("options", options_manager_->get_options_config());
965  random_faction_mode::type type = random_faction_mode::get_enum(selected_rfm_index_).value_or(random_faction_mode::type::independent);
966  settings["random_faction_mode"] = random_faction_mode::get_string(type);
967 
968  return settings;
969 }
970 
972 {
974 }
975 
977 {
979 }
980 
981 std::vector<std::string> mp_create_game::get_active_mods()
982 {
983  int i = 0;
984  std::set<std::string> res;
986  if(mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state").get_value_bool()) {
987  res.insert(mod->id);
988  }
989  ++i;
990  }
991  return std::vector<std::string>(res.begin(), res.end());
992 }
993 
994 void mp_create_game::set_active_mods(const std::vector<std::string>& val)
995 {
996  std::set<std::string> val2(val.begin(), val.end());
997  int i = 0;
998  std::set<std::string> res;
1000  mod_list_->get_row_grid(i)->find_widget<toggle_button>("mod_active_state").set_value_bool(val2.find(mod->id) != val2.end());
1001  ++i;
1002  }
1003 }
1004 
1006 {
1007  // This allows the defaults to be returned by the pref getters below
1012 
1013  init_turn_limit_->set_widget_value(prefs::get().countdown_init_time().count());
1014  turn_bonus_->set_widget_value(prefs::get().countdown_turn_bonus().count());
1015  reservoir_->set_widget_value(prefs::get().countdown_reservoir_time().count());
1016  action_bonus_->set_widget_value(prefs::get().countdown_action_bonus().count());
1017 }
1018 
1020 {
1022  gui2::show_transient_error_message(_("The selected game has no sides!"));
1023  return false;
1024  }
1025 
1027  std::stringstream msg;
1028  // 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
1029  msg << _("The selected game cannot be created.");
1030  msg << "\n\n";
1033  return false;
1034  }
1035 
1036  if(!create_engine_.is_campaign()) {
1037  return true;
1038  }
1039 
1040  return create_engine_.select_campaign_difficulty() != "CANCEL";
1041 }
1042 
1044 {
1045  plugins_context_.reset();
1046 
1047  // Show all tabs so that find_widget works correctly
1048  find_widget<stacked_widget>("pager").select_layer(-1);
1049 
1050  if(get_retval() == LOAD_GAME) {
1052 
1053  // 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.
1055  return;
1056  }
1057 
1058  if(get_retval() == retval::OK) {
1060  prefs::get().set_mp_level_type(static_cast<int>(create_engine_.current_level_type()));
1061  prefs::get().set_mp_level(create_engine_.current_level().id());
1062  prefs::get().set_mp_era(create_engine_.current_era().id);
1063 
1065 
1066  if(create_engine_.current_level_type() == level_type::type::campaign ||
1067  create_engine_.current_level_type() == level_type::type::sp_campaign) {
1069  } else if(create_engine_.current_level_type() == level_type::type::scenario) {
1071  } else {
1072  // This means define= doesn't work for randomly generated scenarios
1074  }
1075 
1077 
1079 
1080  std::vector<const config*> entry_points;
1081  std::vector<std::string> entry_point_titles;
1082 
1083  const auto& tagname = create_engine_.get_state().classification().get_tagname();
1084 
1085  if(tagname == "scenario") {
1086  const std::string first_scenario = create_engine_.current_level().data()["first_scenario"];
1087  for(const config& scenario : game_config_manager::get()->game_config().child_range(tagname)) {
1088  const bool is_first = scenario["id"] == first_scenario;
1089  if(scenario["allow_new_game"].to_bool(false) || is_first || game_config::debug ) {
1090  const std::string& title = !scenario["new_game_title"].empty()
1091  ? scenario["new_game_title"]
1092  : scenario["name"];
1093 
1094  entry_points.insert(is_first ? entry_points.begin() : entry_points.end(), &scenario);
1095  entry_point_titles.insert(is_first ? entry_point_titles.begin() : entry_point_titles.end(), title);
1096  }
1097  }
1098  }
1099 
1101  if(entry_points.size() > 1) {
1102  gui2::dialogs::simple_item_selector dlg(_("Choose Starting Scenario"), _("Select at which point to begin this campaign."), entry_point_titles);
1103 
1104  dlg.set_single_button(true);
1105  dlg.show();
1106 
1107  const config& scenario = *entry_points[dlg.selected_index()];
1108 
1109  params.hash = scenario.hash();
1111  }
1112 
1114 
1115  if(!create_engine_.current_level().data()["force_lock_settings"].to_bool(!create_engine_.get_state().classification().is_normal_mp_game())) {
1116  // Max slider value (in this case, 100) means 'unlimited turns', so pass the value -1
1117  const int num_turns = turns_->get_widget_value();
1118  params.num_turns = num_turns < ::settings::turns_max ? num_turns : - 1;
1119  params.village_gold = gold_->get_widget_value();
1123  params.fog_game = fog_->get_widget_value();
1124  params.shroud_game = shroud_->get_widget_value();
1125 
1126  // write to scenario
1128 
1129  if(params.random_start_time) {
1130  if(!tod_manager::is_start_ToD(scenario["random_start_time"])) {
1131  scenario["random_start_time"] = true;
1132  }
1133  } else {
1134  scenario["random_start_time"] = false;
1135  }
1136 
1137  scenario["experience_modifier"] = params.xp_modifier;
1138  scenario["turns"] = params.num_turns;
1139 
1140  for(config& side : scenario.child_range("side")) {
1141  if(!params.use_map_settings) {
1142  side["fog"] = params.fog_game;
1143  side["shroud"] = params.shroud_game;
1144  side["village_gold"] = params.village_gold;
1145  side["village_support"] = params.village_support;
1146  } else {
1147  if(side["fog"].empty()) {
1148  side["fog"] = params.fog_game;
1149  }
1150 
1151  if(side["shroud"].empty()) {
1152  side["shroud"] = params.shroud_game;
1153  }
1154 
1155  if(side["village_gold"].empty()) {
1156  side["village_gold"] = params.village_gold;
1157  }
1158 
1159  if(side["village_support"].empty()) {
1160  side["village_support"] = params.village_support;
1161  }
1162  }
1163  }
1164  }
1165 
1167  params.mp_countdown_init_time = std::chrono::seconds{init_turn_limit_->get_widget_value()};
1168  params.mp_countdown_turn_bonus = std::chrono::seconds{turn_bonus_->get_widget_value()};
1169  params.mp_countdown_reservoir_time = std::chrono::seconds{reservoir_->get_widget_value()};
1170  params.mp_countdown_action_bonus = std::chrono::seconds{action_bonus_->get_widget_value()};
1171 
1176 
1177  random_faction_mode::type type = random_faction_mode::get_enum(selected_rfm_index_).value_or(random_faction_mode::type::independent);
1178  params.mode = type;
1179 
1180  // Since we don't have a field handling this option, we need to save the value manually
1181  prefs::get().set_random_faction_mode(random_faction_mode::get_string(type));
1182 
1183  // Save custom option settings
1184  params.options = options_manager_->get_options_config();
1185  prefs::get().set_options(options_manager_->get_options_config());
1186 
1187  // Set game name
1188  const std::string name = find_widget<text_box>("game_name").get_value();
1189  if(!name.empty() && (name != settings::game_name_default())) {
1190  params.name = name;
1191  }
1192 
1193  // Set game password
1194  const std::string password = find_widget<text_box>("game_password").get_value();
1195  if(!password.empty()) {
1196  params.password = password;
1197  }
1198  }
1199 }
1200 
1201 } // namespace dialogs
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:362
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
child_itors child_range(config_key_type key)
Definition: config.cpp:268
bool empty() const
Definition: config.cpp:839
std::string hash() const
Definition: config.cpp:1273
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:1032
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:61
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:31
utils::optional< load_game_metadata > load_interactive_for_multiplayer()
Definition: savegame.cpp:236
void set_gamestate(saved_game &gamestate, load_game_metadata &load_data)
Definition: savegame.cpp:278
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:179
constexpr auto filter
Definition: ranges.hpp:38
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