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