The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
create_engine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2013 - 2025
3 by Andrius Silinskas <silinskas.andrius@gmail.com>
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
17
18#include "filesystem.hpp"
24#include "log.hpp"
25#include "map/exception.hpp"
26#include "map/map.hpp"
27#include "saved_game.hpp"
28#include "side_controller.hpp"
29#include "wml_exception.hpp"
30
34
35#include <sstream>
36
37static lg::log_domain log_config("config");
38#define ERR_CF LOG_STREAM(err, log_config)
39
40static lg::log_domain log_mp_create_engine("mp/create/engine");
41#define WRN_MP LOG_STREAM(warn, log_mp_create_engine)
42#define DBG_MP LOG_STREAM(debug, log_mp_create_engine)
43
44namespace ng {
45
47 : data_(data)
48{
49}
50
52 : level(data)
53 , map_()
54 , map_hash_()
55 , num_players_(0)
56{
58}
59
61{
62 return map_ != nullptr;
63}
64
66{
67 const std::string& map_data = data_["map_data"];
68
69 try {
70 map_.reset(new gamemap(map_data));
71 } catch(const incorrect_map_format_error& e) {
72 // Set map content to nullptr, so that it fails can_launch_game()
73 map_.reset(nullptr);
74 data_["description"] = _("Map could not be loaded: ") + e.message;
75
76 ERR_CF << "map could not be loaded: " << e.message;
77 } catch(const wml_exception& e) {
78 data_["description"] = _("Map could not be loaded.");
79
80 ERR_CF << "map could not be loaded: " << e.dev_message;
81 }
82
83 set_sides();
84}
85
86std::string scenario::map_size() const
87{
88 std::stringstream map_size;
89
90 if(map_) {
91 map_size << map_->w();
93 map_size << map_->h();
94 } else {
95 map_size << _("not available.");
96 }
97
98 return map_size.str();
99}
100
102{
103 if(map_) {
104 // If there are fewer sides in the configuration than there are
105 // starting positions, then generate the additional sides
106 const int map_positions = map_->num_valid_starting_positions();
107
108 if(!data_.has_child("side")) {
109 for(int pos = 0; pos < map_positions; ++pos) {
110 config& side = data_.add_child("side");
111 side["side"] = pos + 1;
112 side["team_name"] = "Team " + std::to_string(pos + 1);
113 side["canrecruit"] = true;
114 side["controller"] = side_controller::human;
115 }
116 }
117
118 num_players_ = 0;
119 for(const config& scenario : data_.child_range("side")) {
120 if(scenario["allow_player"].to_bool(true)) {
121 ++num_players_;
122 }
123 }
124 }
125}
126
127user_map::user_map(const config& data, const std::string& name, gamemap* map)
128 : scenario(data)
129 , name_(name)
130{
131 if(map != nullptr) {
132 map_.reset(new gamemap(*map));
133 }
134
135 set_metadata();
136}
137
139{
140 set_sides();
141}
142
143std::string user_map::description() const
144{
145 if(!data_["description"].empty()) {
146 return data_["description"];
147 }
148
149 // map error message
150 return _("Custom map.");
151}
152
154 : scenario(data)
155 , generator_data_()
156 , generate_whole_scenario_(data_.has_attribute("scenario_generation"))
157 , generator_name_(generate_whole_scenario_ ? data_["scenario_generation"] : data_["map_generation"])
158{
159 if(!data.has_child("generator")) {
160 data_.clear();
162 data_["description"] = "Error: Random map found with missing generator information. Scenario should have a [generator] child.";
163 data_["error_message"] = "missing [generator] tag";
164 } else {
165 generator_data_ = data.mandatory_child("generator");
166 }
167
168 if(!data.has_attribute("scenario_generation") && !data.has_attribute("map_generation")) {
169 data_.clear();
171 data_["description"] = "Error: Random map found with missing generator information. Scenario should have a [generator] child.";
172 data_["error_message"] = "couldn't find 'scenario_generation' or 'map_generation' attribute";
173 }
174}
175
177{
178 return ::create_map_generator(generator_name(), generator_data());
179}
180
182 : level(data)
183 , id_(data["id"])
184 , allow_era_choice_(level::allow_era_choice())
185 , image_label_()
186 , min_players_(1)
187 , max_players_(1)
188{
189 if(data.has_attribute("start_year")) {
190 dates_.first = utils::irdya_date::read_date(data["start_year"]);
191 if(data.has_attribute("end_year")) {
192 dates_.second = utils::irdya_date::read_date(data["end_year"]);
193 } else {
194 dates_.second = dates_.first;
195 }
196 } else if(data.has_attribute("year")) {
197 dates_.first = dates_.second = utils::irdya_date::read_date(data["year"]);
198 }
199 set_metadata();
200}
201
203{
204 return !data_.empty();
205}
206
208{
209 image_label_ = data_["image"].str();
210
211 int min = data_["min_players"].to_int(1);
212 int max = data_["max_players"].to_int(1);
213
215
216 if(max > min) {
217 max_players_ = max;
218 }
219}
220
222{
223 data_["completed"] = prefs::get().is_campaign_completed(data_["id"]);
224
225 for(auto& cfg : data_.child_range("difficulty")) {
226 cfg["completed_at"] = prefs::get().is_campaign_completed(data_["id"], cfg["define"]);
227 }
228}
229
231 : current_level_type_()
232 , current_level_index_(0)
233 , current_era_index_(0)
234 , level_name_filter_()
235 , player_count_filter_(1)
236 , type_map_()
237 , preset_ids_()
238 , user_map_names_()
239 , user_scenario_names_()
240 , eras_()
241 , mods_()
242 , state_(state)
243 , dependency_manager_(nullptr)
244 , generator_(nullptr)
245 , selected_campaign_difficulty_()
246 , game_config_(game_config_manager::get()->game_config())
247{
248 // Set up the type map. Do this first!
249 type_map_.emplace(level_type::type::scenario, type_list());
250 type_map_.emplace(level_type::type::user_map, type_list());
251 type_map_.emplace(level_type::type::user_scenario, type_list());
252 type_map_.emplace(level_type::type::campaign, type_list());
253 type_map_.emplace(level_type::type::sp_campaign, type_list());
254 type_map_.emplace(level_type::type::random_map, type_list());
255 type_map_.emplace(level_type::type::preset, type_list());
256
257 DBG_MP << "restoring game config";
258
259 // Restore game config for multiplayer.
260 campaign_type::type type = state_.classification().type;
261
262 state_.clear();
263 state_.classification().type = type;
264
266
267 // Initialize dependency_manager_ after refreshing game config.
269
270 // TODO: the editor dir is already configurable, is the preferences value
273
276
277 DBG_MP << "initializing all levels, eras and mods";
278
282
283 state_.mp_settings().saved_game = saved_game_mode::type::no;
284
285 for(const std::string& str : prefs::get().modifications(state_.classification().is_multiplayer())) {
286 if(game_config_.find_child("modification", "id", str)) {
287 state_.classification().active_mods.push_back(str);
288 }
289 }
290
291 dependency_manager_->try_modifications(state_.classification().active_mods, true);
292
294}
295
297{
298 DBG_MP << "initializing generated level data";
299
300 //DBG_MP << "current data:";
301 //DBG_MP << current_level().data().debug();
302
303 random_map * cur_lev = dynamic_cast<random_map *> (&current_level());
304
305 if(!cur_lev) {
306 WRN_MP << "Tried to initialized generated level data on a level that wasn't a random map";
307 return;
308 }
309
310 try {
311 if(!cur_lev->generate_whole_scenario())
312 {
313 DBG_MP << "** replacing map **";
314
315 config data = cur_lev->data();
316
317 data["map_data"] = generator_->create_map();
318
319 cur_lev->set_data(data);
320
321 } else { //scenario generation
322
323 DBG_MP << "** replacing scenario **";
324
325 config data = generator_->create_scenario();
326
327 // Set the scenario to have placing of sides
328 // based on the terrain they prefer
329 if(!data.has_attribute("modify_placing")) {
330 data["modify_placing"] = true;
331 }
332
333 const std::string& description = cur_lev->data()["description"];
334 data["description"] = description;
336
337 cur_lev->set_data(data);
338 }
339 } catch (const mapgen_exception & e) {
340 config data = cur_lev->data();
341
342 data["error_message"] = e.what();
343
344 cur_lev->set_data(data);
345 }
346
347 //DBG_MP << "final data:";
348 //DBG_MP << current_level().data().debug();
349}
350
352{
353 //
354 // We exclude campaigns from this check since they require preprocessing in order to check
355 // their side data. Since this function is used by the MP Create screen to verify side data
356 // before proceeding to Staging, this should cover most cases of false positives. It does,
357 // however, leave open the possibility of scenarios that require preprocessing before their
358 // side data is accessible, but that's an unlikely occurrence.
359 //
360 if(is_campaign()) {
361 return true;
362 }
363
364 return current_level().data().has_child("side");
365}
366
368{
369 DBG_MP << "preparing mp_game_settings for new level";
372}
373
375{
378 for(const std::string& mod_id : state_.classification().active_mods) {
379 state_.classification().mod_defines.push_back(game_config_.find_mandatory_child("modification", "id", mod_id)["define"].str());
380 }
381}
382
384{
385 DBG_MP << "preparing data for scenario by reloading game config";
386
388
390 config {"next_scenario", current_level().data()["id"]}
391 );
392}
393
394void create_engine::prepare_for_campaign(const std::string& difficulty)
395{
396 DBG_MP << "preparing data for campaign by reloading game config";
397
398 if(!difficulty.empty()) {
399 state_.classification().difficulty = difficulty;
400 } else if(!selected_campaign_difficulty_.empty()) {
402 }
403
404 config& current_level_data = current_level().data();
405
406 state_.classification().campaign = current_level_data["id"].str();
407 state_.classification().campaign_name = current_level_data["name"].str();
408 state_.classification().abbrev = current_level_data["abbrev"].str();
409 if (current_level_data["type"] == "hybrid" && state_.classification().is_multiplayer()) {
410 // for hybrid campaigns in MP mode let's make a clarification in the abbrev
411 // so saves for sp and mp runs don't get confused
412 state_.classification().abbrev = state_.classification().abbrev + "-" + _("multiplayer^MP");
413 }
414
415
416 state_.classification().end_text = current_level_data["end_text"].str();
417 state_.classification().end_text_duration = chrono::parse_duration<std::chrono::milliseconds>(current_level_data["end_text_duration"]);
418 state_.classification().end_credits = current_level_data["end_credits"].to_bool(true);
419
420 state_.classification().campaign_define = current_level_data["define"].str();
422 utils::split(current_level_data["extra_defines"]);
423
425 config {"next_scenario", current_level_data["first_scenario"]}
426 );
427}
428
430{
431 // Verify the existence of difficulties
432 std::vector<std::string> difficulties;
433
434 for(const config& d : current_level().data().child_range("difficulty")) {
435 difficulties.push_back(d["define"]);
436 }
437
438 // No difficulties found. Exit
439 if(difficulties.empty()) {
440 return "";
441 }
442
443 // One difficulty found. Use it
444 if(difficulties.size() == 1) {
445 return difficulties[0];
446 }
447
448 // A specific difficulty value was passed
449 // Use a minimalistic interface to get the specified define
450 if(set_value != -1) {
451 if(set_value > static_cast<int>(difficulties.size())) {
452 PLAIN_LOG << "incorrect difficulty number: [" <<
453 set_value << "]. maximum is [" << difficulties.size() << "].\n";
454 return "FAIL";
455 } else if(set_value < 1) {
456 PLAIN_LOG << "incorrect difficulty number: [" <<
457 set_value << "]. minimum is [1].\n";
458 return "FAIL";
459 } else {
460 return difficulties[set_value - 1];
461 }
462 }
463
464 // If not, let the user pick one from the prompt
465 // We don't pass the difficulties vector here because additional data is required
466 // to constrict the dialog
468 dlg.show();
469
471
473}
474
476{
477 DBG_MP << "preparing mp_game_settings for saved game";
478
480
481 // The save might be a start-of-scenario save so make sure we have the scenario data loaded.
483 state_.mp_settings().saved_game = state_.is_mid_game_save() ? saved_game_mode::type::midgame : saved_game_mode::type::scenaro_start;
484}
485
494
495void create_engine::apply_level_filter(const std::string& name)
496{
497 level_name_filter_ = name;
499}
500
502{
503 player_count_filter_ = players;
505}
506
508{
509 for(auto& type : type_map_) {
510 type.second.reset_filter();
511 }
512
514}
515
520
525
526void create_engine::set_current_level(const std::size_t index)
527{
528 try {
529 current_level_index_ = type_map_.at(current_level_type_).games_filtered.at(index);
530 } catch (const std::out_of_range&) {
532 }
533
534 if(current_level_type_ == level_type::type::random_map) {
535 random_map* current_random_map = dynamic_cast<random_map*>(&current_level());
536
537 // If dynamic cast has failed then we somehow have gotten all the pointers mixed together.
538 assert(current_random_map);
539
540 generator_.reset(current_random_map->create_map_generator());
541 } else {
542 generator_.reset(nullptr);
543 }
544
546 dependency_manager_->try_scenario(current_level().id());
547 }
548}
549
550void create_engine::set_current_era_index(const std::size_t index, bool force)
551{
552 current_era_index_ = index;
553
554 dependency_manager_->try_era_by_index(index, force);
556}
557
558void create_engine::set_current_era_index(const std::string& id, bool force)
559{
560 std::size_t index = dependency_manager_->get_era_index(id);
561
563 current_era_index_ = index;
564
565 dependency_manager_->try_era_by_index(index, force);
566}
567
568bool create_engine::toggle_mod(const std::string& id, bool force)
569{
570 force |= state_.classification().type != campaign_type::type::multiplayer;
571
572 bool is_active = dependency_manager_->is_modification_active(id);
573 dependency_manager_->try_modification_by_id(id, !is_active, force);
574
575 state_.classification().active_mods = dependency_manager_->get_modifications();
576
577 return !is_active;
578}
579
581{
582 return generator_ != nullptr;
583}
584
586{
587 return generator_->allow_user_config();
588}
589
591{
592 generator_->user_config();
593}
594
595std::pair<level_type::type, int> create_engine::find_level_by_id(const std::string& id) const
596{
597 for(const auto& type : type_map_) {
598 int i = 0;
599
600 for(const auto& game : type.second.games) {
601 if(game->id() == id) {
602 return {type.first, i};
603 }
604
605 i++;
606 }
607 }
608
609 return {level_type::type::sp_campaign, -1};
610}
611
612int create_engine::find_extra_by_id(const MP_EXTRA extra_type, const std::string& id) const
613{
614 int i = 0;
615 for(extras_metadata_ptr extra : get_const_extras_by_type(extra_type)) {
616 if(extra->id == id) {
617 return i;
618 }
619 i++;
620 }
621
622 return -1;
623}
624
629
630std::vector<std::string>& create_engine::active_mods()
631{
633}
634
635std::vector<create_engine::extras_metadata_ptr> create_engine::active_mods_data()
636{
637 const std::vector<extras_metadata_ptr>& mods = get_const_extras_by_type(MP_EXTRA::MOD);
638
639 std::vector<extras_metadata_ptr> data_vec;
640 std::copy_if(mods.begin(), mods.end(), std::back_inserter(data_vec), [this](const extras_metadata_ptr& mod) {
641 return dependency_manager_->is_modification_active(mod->id);
642 });
643
644 return data_vec;
645}
646
648{
649 int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
650 return *eras_[era_index]->cfg;
651}
652
654{
655 DBG_MP << "getting parameter values";
656
657 int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
658 // in case of joining a server queue with an unknown era
659 // the depcheck's get_era_index() returns -1 when not found
660 if(era_index == -1) {
661 throw config::error(_("Era not found: ")+current_era_id_);
662 }
663 state_.classification().era_id = eras_[era_index]->id;
664 state_.mp_settings().mp_era_name = eras_[era_index]->name;
665
666 return state_.mp_settings();
667}
668
670{
671 if(auto generic_multiplayer = game_config_.optional_child("generic_multiplayer")) {
672 config gen_mp_data = *generic_multiplayer;
673
674 // User maps.
675 int dep_index_offset = 0;
676 for(std::size_t i = 0; i < user_map_names_.size(); i++)
677 {
678 config user_map_data = gen_mp_data;
679 user_map_data["map_data"] = filesystem::read_map(user_map_names_[i]);
680
681 // Check if a file is actually a map.
682 // Note that invalid maps should be displayed in order to
683 // show error messages in the GUI.
684 bool add_map = true;
685 std::unique_ptr<gamemap> map;
686 try {
687 map.reset(new gamemap(user_map_data["map_data"].str()));
688 } catch (const incorrect_map_format_error& e) {
689 // Set map content to nullptr, so that it fails can_launch_game()
690 map.reset(nullptr);
691 user_map_data["description"] = _("Map could not be loaded: ") + e.message;
692
693 ERR_CF << "map could not be loaded: " << e.message;
694 } catch (const wml_exception&) {
695 add_map = false;
696 dep_index_offset++;
697 }
698
699 if(add_map) {
700 type_map_[level_type::type::user_map].games.emplace_back(new user_map(user_map_data, user_map_names_[i], map.get()));
701
702 // Since user maps are treated as scenarios, some dependency info is required
703 config depinfo;
704 depinfo["id"] = user_map_names_[i];
705 depinfo["name"] = user_map_names_[i];
706 dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset);
707 }
708 }
709
710 // User made scenarios.
711 dep_index_offset = 0;
712 for(std::size_t i = 0; i < user_scenario_names_.size(); i++)
713 {
714 config data;
715 try {
716 // Only attempt to load .cfg files (.cfg extension is enforced in Editor save)
718 continue;
719
721 } catch(const config::error & e) {
722 ERR_CF << "Caught a config error while parsing user made (editor) scenarios:\n" << e.message;
723 ERR_CF << "Skipping file: " << (filesystem::get_legacy_editor_dir() + "/scenarios/" + user_scenario_names_[i]);
724 continue;
725 }
726
727 scenario_ptr new_scenario(new scenario(data));
728 if(new_scenario->id().empty()) continue;
729
730 type_map_[level_type::type::user_scenario].games.push_back(std::move(new_scenario));
731
732 // Since user scenarios are treated as scenarios, some dependency info is required
733 config depinfo;
734 depinfo["id"] = data["id"];
735 depinfo["name"] = data["name"];
736 dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset++);
737 }
738 }
739
740 // Stand-alone scenarios.
741 for(const config& data : game_config_.child_range("multiplayer"))
742 {
743 if(!data["allow_new_game"].to_bool(true))
744 continue;
745
746 if(!data["campaign_id"].empty())
747 continue;
748
749 if(data.has_attribute("map_generation") || data.has_attribute("scenario_generation")) {
750 type_map_[level_type::type::random_map].games.emplace_back(new random_map(data));
751 } else {
752 type_map_[level_type::type::scenario].games.emplace_back(new scenario(data));
753 }
754 }
755
756 // Presets.
757 for(const config& preset : prefs::get().get_game_presets()) {
758 optional_const_config data = game_config_.find_child("multiplayer", "id", preset["scenario"].str());
759
760 if(data) {
761 type_map_[level_type::type::preset].games.emplace_back(new scenario(*data));
762 preset_ids_.emplace_back(preset["id"].to_int());
763 }
764 }
765
766 // Campaigns.
767 for(const config& data : game_config_.child_range("campaign"))
768 {
769 if(data["id"].empty()) {
770 if(data["name"].empty()) {
771 ERR_CF << "Found a [campaign] with neither a name nor an id attribute, ignoring it";
772 } else {
773 ERR_CF << "Ignoring a [campaign] with no id attribute, but name '" << data["name"] << "'";
774 }
775 continue;
776 }
777
778 const std::string& type = data["type"];
779 const bool mp = state_.classification().is_multiplayer();
780
781 if(type == "mp" || (type == "hybrid" && mp)) {
782 type_map_[level_type::type::campaign].games.emplace_back(new campaign(data));
783 }
784
785 if(type == "sp" || type.empty() || (type == "hybrid" && !mp)) {
786 campaign_ptr new_sp_campaign(new campaign(data));
787 new_sp_campaign->mark_if_completed();
788
789 type_map_[level_type::type::sp_campaign].games.push_back(std::move(new_sp_campaign));
790 }
791 }
792
793 auto& sp_campaigns = type_map_[level_type::type::sp_campaign].games;
794
795 // Sort sp campaigns by rank.
796 std::stable_sort(sp_campaigns.begin(), sp_campaigns.end(),
798 return a->data()["rank"].to_int(1000) < b->data()["rank"].to_int(1000);
799 }
800 );
801}
802
804{
805 std::vector<extras_metadata_ptr>& extras = get_extras_by_type(extra_type);
806 const std::string extra_name = (extra_type == ERA) ? "era" : "modification";
807
808 component_availability::type default_availabilty = (extra_type == ERA)
809 ? component_availability::type::mp
810 : component_availability::type::hybrid;
811
812 std::set<std::string> found_ids;
813 for(const config& extra : game_config_.child_range(extra_name))
814 {
815 component_availability::type type = component_availability::get_enum(extra["type"].str()).value_or(default_availabilty);
816 const bool mp = state_.classification().is_multiplayer();
817
818 if((type != component_availability::type::mp || mp) && (type != component_availability::type::sp || !mp) )
819 {
820 if(found_ids.insert(extra["id"]).second) {
821 extras_metadata_ptr new_extras_metadata(new extras_metadata());
822 new_extras_metadata->id = extra["id"].str();
823 new_extras_metadata->name = extra["name"].str();
824 new_extras_metadata->description = extra["description"].str();
825 new_extras_metadata->cfg = &extra;
826
827 extras.push_back(std::move(new_extras_metadata));
828 }
829 else {
830 ERR_CF << "found " << extra_name << " with id=" << extra["id"] << " twice";
831 }
832 }
833 }
834}
835
837{
838 for(auto& type : type_map_) {
839 type.second.apply_filter(player_count_filter_, level_name_filter_);
840 }
841}
842
843std::vector<create_engine::level_ptr> create_engine::get_levels_by_type_unfiltered(level_type::type type) const
844{
845 std::vector<level_ptr> levels;
846 for(const level_ptr& lvl : type_map_.at(type).games) {
847 levels.push_back(lvl);
848 }
849
850 return levels;
851}
852
853std::vector<create_engine::level_ptr> create_engine::get_levels_by_type(level_type::type type) const
854{
855 auto& g_list = type_map_.at(type);
856
857 std::vector<level_ptr> levels;
858 for(std::size_t level : g_list.games_filtered) {
859 levels.push_back(g_list.games[level]);
860 }
861
862 return levels;
863}
864
865std::vector<std::size_t> create_engine::get_filtered_level_indices(level_type::type type) const
866{
867 return type_map_.at(type).games_filtered;
868}
869
870const std::vector<create_engine::extras_metadata_ptr>&
872{
873 return (extra_type == ERA) ? eras_ : mods_;
874}
875
876std::vector<create_engine::extras_metadata_ptr>&
878{
879 return (extra_type == ERA) ? eras_ : mods_;
880}
881
886
887} // end namespace ng
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
child_itors child_range(std::string_view key)
Definition config.cpp:268
const attribute_value * get(std::string_view key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition config.cpp:665
bool has_attribute(std::string_view key) const
Definition config.cpp:157
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
void clear()
Definition config.cpp:802
std::string hash() const
Definition config.cpp:1227
std::vector< std::string > campaign_xtra_defines
more customization of data
std::vector< std::string > mod_defines
If there are defines the modifications use to customize data.
std::string scenario_define
If there is a define the scenario uses to customize data.
std::string difficulty
The difficulty level the game is being played on.
std::string era_define
If there is a define the era uses to customize data.
std::chrono::milliseconds end_text_duration
for how long the end-of-campaign text is shown
std::vector< std::string > active_mods
campaign_type::type type
bool end_credits
whether to show the standard credits at the end
std::string campaign_define
If there is a define the campaign uses to customize data.
std::string campaign
The id of the campaign being played.
std::string abbrev
the campaign abbreviation
std::string end_text
end-of-campaign text
std::string campaign_name
The name of the campaign being played.
static game_config_manager * get()
void load_game_config_for_game(const game_classification &classification, const std::string &scenario_id)
void load_game_config_for_create(bool is_mp, bool is_test=false)
config_array_view child_range(std::string_view key) const
const config & find_mandatory_child(std::string_view key, const std::string &name, const std::string &value) const
optional_const_config optional_child(std::string_view key) const
optional_const_config find_child(std::string_view key, const std::string &name, const std::string &value) const
Encapsulates the map of the game.
Definition map.hpp:176
std::string selected_difficulty() const
Returns the selected difficulty define after displaying.
bool show(const unsigned auto_close_time=0)
Shows the window.
std::pair< utils::irdya_date, utils::irdya_date > dates_
campaign(const config &data)
void mark_if_completed()
std::string image_label_
bool can_launch_game() const
std::string level_name_filter_
int find_extra_by_id(const MP_EXTRA extra_type, const std::string &id) const
std::string current_era_id_
create_engine(saved_game &state)
std::shared_ptr< campaign > campaign_ptr
std::vector< std::size_t > get_filtered_level_indices(level_type::type type) const
bool toggle_mod(const std::string &id, bool force=false)
std::vector< int > preset_ids_
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
std::string selected_campaign_difficulty_
bool generator_has_settings() const
std::vector< std::string > & active_mods()
std::vector< std::string > user_map_names_
bool current_level_has_side_data()
Returns true if the current level has one or more [side] tags.
bool generator_assigned() const
std::map< level_type::type, type_list > type_map_
void init_extras(const MP_EXTRA extra_type)
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
std::size_t current_era_index_
const config & curent_era_cfg() const
std::vector< extras_metadata_ptr > mods_
std::unique_ptr< depcheck::manager > dependency_manager_
std::vector< std::string > user_scenario_names_
std::vector< extras_metadata_ptr > active_mods_data()
std::shared_ptr< level > level_ptr
const game_config_view & game_config_
Reference to the main game config.
std::unique_ptr< map_generator > generator_
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.
std::vector< extras_metadata_ptr > eras_
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()
std::shared_ptr< extras_metadata > extras_metadata_ptr
void set_current_level(const std::size_t index)
level_type::type current_level_type_
std::size_t current_level_index_
level & current_level() const
std::shared_ptr< scenario > scenario_ptr
Note to all triers: It's not guaranteed that the specified component will be selected (if the user de...
Definition depcheck.hpp:50
Base class for all level type classes.
void set_data(const config &data)
const config & data() const
virtual bool allow_era_choice() const
level(const config &data)
bool generate_whole_scenario() const
random_map(const config &data)
const config & generator_data() const
std::string generator_name() const
map_generator * create_map_generator() const
std::unique_ptr< gamemap > map_
scenario(const config &data)
std::string map_size() const
bool can_launch_game() const
user_map(const config &data, const std::string &name, gamemap *map)
std::string description() const
static prefs & get()
config::child_itors get_game_presets()
bool is_campaign_completed(const std::string &campaign_id)
const std::vector< std::string > & modifications(bool mp=true)
bool is_mid_game_save() const
void expand_scenario()
copies the content of a [scenario] with the correct id attribute from the game config into this objec...
void set_carryover_sides_start(config carryover_sides_start)
std::string get_scenario_id() const
static void post_scenario_generation(const config &old_scenario, config &generated_scenario)
copies attributes & tags from the 'outer' [scenario] to the scenario that is generated by scenario_ge...
game_classification & classification()
void set_scenario(config scenario)
void check_require_scenario()
Add addon_id information if needed.
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
void expand_random_scenario()
takes care of generate_map=, generate_scenario=, map= attributes This should be called before expandi...
static irdya_date read_date(const std::string &date)
#define ERR_CF
Definition config.cpp:37
#define WRN_MP
#define DBG_MP
static lg::log_domain log_mp_create_engine("mp/create/engine")
static lg::log_domain log_config("config")
const config * cfg
Declarations for File-IO.
static std::string _(const char *str)
Definition gettext.hpp:100
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
Standard logging facilities (interface).
#define PLAIN_LOG
Definition log.hpp:296
std::string get_legacy_editor_dir()
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, name_mode mode=name_mode::FILE_NAME_ONLY, filter_mode filter=filter_mode::NO_FILTER, reorder_mode reorder=reorder_mode::DONT_REORDER, file_tree_checksum *checksum=nullptr)
Get a list of all files and/or directories in a given directory.
bool is_cfg(const std::string &filename)
Returns true if the file ends with the wmlfile extension.
std::string read_map(const std::string &name)
const std::string unicode_multiplication_sign
Definition constants.cpp:46
Game configuration data as global variables.
config read(std::istream &in, abstract_validator *validator)
Definition parser.cpp:610
Main entry points of multiplayer mode.
std::vector< std::string > split(const config_attribute_value &val)
std::string_view data
Definition picture.cpp:188
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map &defines)
Function to use the WML preprocessor on a file.
saved_game_mode::type saved_game
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
Helper class, don't construct this directly.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define i
#define d
#define e
#define b