The Battle for Wesnoth  1.19.13+dev
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"
19 #include "game_config_manager.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 
31 #include "serialization/chrono.hpp"
33 #include "serialization/parser.hpp"
34 
35 #include <sstream>
36 
37 static lg::log_domain log_config("config");
38 #define ERR_CF LOG_STREAM(err, log_config)
39 
40 static 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 
44 namespace ng {
45 
47  : data_(data)
48 {
49 }
50 
52  : level(data)
53  , map_()
54  , map_hash_()
55  , num_players_(0)
56 {
57  set_metadata();
58 }
59 
61 {
62  return map_.get() != 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 
86 std::string scenario::map_size() const
87 {
88  std::stringstream map_size;
89 
90  if(map_.get() != nullptr) {
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_.get() != nullptr) {
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 
127 user_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 
143 std::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 {
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 
214  min_players_ = max_players_ = min;
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  , user_map_names_()
238  , user_scenario_names_()
239  , eras_()
240  , mods_()
241  , state_(state)
242  , dependency_manager_(nullptr)
243  , generator_(nullptr)
244  , selected_campaign_difficulty_()
245  , game_config_(game_config_manager::get()->game_config())
246 {
247  // Set up the type map. Do this first!
248  type_map_.emplace(level_type::type::scenario, type_list());
249  type_map_.emplace(level_type::type::user_map, type_list());
250  type_map_.emplace(level_type::type::user_scenario, type_list());
251  type_map_.emplace(level_type::type::campaign, type_list());
252  type_map_.emplace(level_type::type::sp_campaign, type_list());
253  type_map_.emplace(level_type::type::random_map, type_list());
254  type_map_.emplace(level_type::type::preset, type_list());
255 
256  DBG_MP << "restoring game config";
257 
258  // Restore game config for multiplayer.
260 
261  state_.clear();
263 
265 
266  // Initialize dependency_manager_ after refreshing game config.
268 
269  // TODO: the editor dir is already configurable, is the preferences value
272 
275 
276  DBG_MP << "initializing all levels, eras and mods";
277 
278  init_all_levels();
279  init_extras(ERA);
280  init_extras(MOD);
281 
282  state_.mp_settings().saved_game = saved_game_mode::type::no;
283 
284  for(const std::string& str : prefs::get().modifications(state_.classification().is_multiplayer())) {
285  if(game_config_.find_child("modification", "id", str)) {
286  state_.classification().active_mods.push_back(str);
287  }
288  }
289 
290  dependency_manager_->try_modifications(state_.classification().active_mods, true);
291 
293 }
294 
296 {
297  DBG_MP << "initializing generated level data";
298 
299  //DBG_MP << "current data:";
300  //DBG_MP << current_level().data().debug();
301 
302  random_map * cur_lev = dynamic_cast<random_map *> (&current_level());
303 
304  if(!cur_lev) {
305  WRN_MP << "Tried to initialized generated level data on a level that wasn't a random map";
306  return;
307  }
308 
309  try {
310  if(!cur_lev->generate_whole_scenario())
311  {
312  DBG_MP << "** replacing map **";
313 
314  config data = cur_lev->data();
315 
316  data["map_data"] = generator_->create_map();
317 
318  cur_lev->set_data(data);
319 
320  } else { //scenario generation
321 
322  DBG_MP << "** replacing scenario **";
323 
324  config data = generator_->create_scenario();
325 
326  // Set the scenario to have placing of sides
327  // based on the terrain they prefer
328  if(!data.has_attribute("modify_placing")) {
329  data["modify_placing"] = true;
330  }
331 
332  const std::string& description = cur_lev->data()["description"];
333  data["description"] = description;
335 
336  cur_lev->set_data(data);
337  }
338  } catch (const mapgen_exception & e) {
339  config data = cur_lev->data();
340 
341  data["error_message"] = e.what();
342 
343  cur_lev->set_data(data);
344  }
345 
346  //DBG_MP << "final data:";
347  //DBG_MP << current_level().data().debug();
348 }
349 
351 {
352  //
353  // We exclude campaigns from this check since they require preprocessing in order to check
354  // their side data. Since this function is used by the MP Create screen to verify side data
355  // before proceeding to Staging, this should cover most cases of false positives. It does,
356  // however, leave open the possibility of scenarios that require preprocessing before their
357  // side data is accessible, but that's an unlikely occurrence.
358  //
359  if(is_campaign()) {
360  return true;
361  }
362 
363  return current_level().data().has_child("side");
364 }
365 
367 {
368  DBG_MP << "preparing mp_game_settings for new level";
371 }
372 
374 {
375  get_parameters();
377  for(const std::string& mod_id : state_.classification().active_mods) {
378  state_.classification().mod_defines.push_back(game_config_.find_mandatory_child("modification", "id", mod_id)["define"].str());
379  }
380 }
381 
383 {
384  DBG_MP << "preparing data for scenario by reloading game config";
385 
386  state_.classification().scenario_define = current_level().data()["define"].str();
387 
389  config {"next_scenario", current_level().data()["id"]}
390  );
391 }
392 
393 void create_engine::prepare_for_campaign(const std::string& difficulty)
394 {
395  DBG_MP << "preparing data for campaign by reloading game config";
396 
397  if(!difficulty.empty()) {
398  state_.classification().difficulty = difficulty;
399  } else if(!selected_campaign_difficulty_.empty()) {
401  }
402 
403  config& current_level_data = current_level().data();
404 
405  state_.classification().campaign = current_level_data["id"].str();
406  state_.classification().campaign_name = current_level_data["name"].str();
407  state_.classification().abbrev = current_level_data["abbrev"].str();
408  if (current_level_data["type"] == "hybrid" && state_.classification().is_multiplayer()) {
409  // for hybrid campaigns in MP mode let's make a clarification in the abbrev
410  // so saves for sp and mp runs don't get confused
411  state_.classification().abbrev = state_.classification().abbrev + "-" + _("multiplayer^MP");
412  }
413 
414 
415  state_.classification().end_text = current_level_data["end_text"].str();
416  state_.classification().end_text_duration = chrono::parse_duration<std::chrono::milliseconds>(current_level_data["end_text_duration"]);
417  state_.classification().end_credits = current_level_data["end_credits"].to_bool(true);
418 
419  state_.classification().campaign_define = current_level_data["define"].str();
421  utils::split(current_level_data["extra_defines"]);
422 
424  config {"next_scenario", current_level_data["first_scenario"]}
425  );
426 }
427 
429 {
430  // Verify the existence of difficulties
431  std::vector<std::string> difficulties;
432 
433  for(const config& d : current_level().data().child_range("difficulty")) {
434  difficulties.push_back(d["define"]);
435  }
436 
437  // No difficulties found. Exit
438  if(difficulties.empty()) {
439  return "";
440  }
441 
442  // One difficulty found. Use it
443  if(difficulties.size() == 1) {
444  return difficulties[0];
445  }
446 
447  // A specific difficulty value was passed
448  // Use a minimalistic interface to get the specified define
449  if(set_value != -1) {
450  if(set_value > static_cast<int>(difficulties.size())) {
451  PLAIN_LOG << "incorrect difficulty number: [" <<
452  set_value << "]. maximum is [" << difficulties.size() << "].\n";
453  return "FAIL";
454  } else if(set_value < 1) {
455  PLAIN_LOG << "incorrect difficulty number: [" <<
456  set_value << "]. minimum is [1].\n";
457  return "FAIL";
458  } else {
459  return difficulties[set_value - 1];
460  }
461  }
462 
463  // If not, let the user pick one from the prompt
464  // We don't pass the difficulties vector here because additional data is required
465  // to constrict the dialog
467  dlg.show();
468 
470 
472 }
473 
475 {
476  DBG_MP << "preparing mp_game_settings for saved game";
477 
479 
480  // The save might be a start-of-scenario save so make sure we have the scenario data loaded.
482  state_.mp_settings().saved_game = state_.is_mid_game_save() ? saved_game_mode::type::midgame : saved_game_mode::type::scenaro_start;
483 }
484 
486 {
487  DBG_MP << "prepare_for_other";
491 }
492 
493 void create_engine::apply_level_filter(const std::string& name)
494 {
495  level_name_filter_ = name;
497 }
498 
500 {
501  player_count_filter_ = players;
503 }
504 
506 {
507  for(auto& type : type_map_) {
508  type.second.reset_filter();
509  }
510 
511  level_name_filter_ = "";
512 }
513 
515 {
517 }
518 
520 {
522 }
523 
525 {
526  try {
527  current_level_index_ = type_map_.at(current_level_type_).games_filtered.at(index);
528  } catch (const std::out_of_range&) {
530  }
531 
532  if(current_level_type_ == level_type::type::random_map) {
533  random_map* current_random_map = dynamic_cast<random_map*>(&current_level());
534 
535  // If dynamic cast has failed then we somehow have gotten all the pointers mixed together.
536  assert(current_random_map);
537 
538  generator_.reset(current_random_map->create_map_generator());
539  } else {
540  generator_.reset(nullptr);
541  }
542 
544  dependency_manager_->try_scenario(current_level().id());
545  }
546 }
547 
548 void create_engine::set_current_era_index(const std::size_t index, bool force)
549 {
551 
552  dependency_manager_->try_era_by_index(index, force);
553 }
554 
555 void create_engine::set_current_era_id(const std::string& id, bool force)
556 {
557  std::size_t index = dependency_manager_->get_era_index(id);
558 
560 
561  dependency_manager_->try_era_by_index(index, force);
562 }
563 
564 bool create_engine::toggle_mod(const std::string& id, bool force)
565 {
566  force |= state_.classification().type != campaign_type::type::multiplayer;
567 
568  bool is_active = dependency_manager_->is_modification_active(id);
569  dependency_manager_->try_modification_by_id(id, !is_active, force);
570 
571  state_.classification().active_mods = dependency_manager_->get_modifications();
572 
573  return !is_active;
574 }
575 
577 {
578  return generator_ != nullptr;
579 }
580 
582 {
583  return generator_->allow_user_config();
584 }
585 
587 {
588  generator_->user_config();
589 }
590 
591 std::pair<level_type::type, int> create_engine::find_level_by_id(const std::string& id) const
592 {
593  for(const auto& type : type_map_) {
594  int i = 0;
595 
596  for(const auto& game : type.second.games) {
597  if(game->id() == id) {
598  return {type.first, i};
599  }
600 
601  i++;
602  }
603  }
604 
605  return {level_type::type::sp_campaign, -1};
606 }
607 
608 int create_engine::find_extra_by_id(const MP_EXTRA extra_type, const std::string& id) const
609 {
610  int i = 0;
611  for(extras_metadata_ptr extra : get_const_extras_by_type(extra_type)) {
612  if(extra->id == id) {
613  return i;
614  }
615  i++;
616  }
617 
618  return -1;
619 }
620 
622 {
623  state_.classification().active_mods = dependency_manager_->get_modifications();
624 }
625 
626 std::vector<std::string>& create_engine::active_mods()
627 {
629 }
630 
631 std::vector<create_engine::extras_metadata_ptr> create_engine::active_mods_data()
632 {
633  const std::vector<extras_metadata_ptr>& mods = get_const_extras_by_type(MP_EXTRA::MOD);
634 
635  std::vector<extras_metadata_ptr> data_vec;
636  std::copy_if(mods.begin(), mods.end(), std::back_inserter(data_vec), [this](const extras_metadata_ptr& mod) {
637  return dependency_manager_->is_modification_active(mod->id);
638  });
639 
640  return data_vec;
641 }
642 
644 {
645  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
646  return *eras_[era_index]->cfg;
647 }
648 
650 {
651  DBG_MP << "getting parameter values";
652 
653  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
654  state_.classification().era_id = eras_[era_index]->id;
655  state_.mp_settings().mp_era_name = eras_[era_index]->name;
656 
657  return state_.mp_settings();
658 }
659 
661 {
662  if(auto generic_multiplayer = game_config_.optional_child("generic_multiplayer")) {
663  config gen_mp_data = *generic_multiplayer;
664 
665  // User maps.
666  int dep_index_offset = 0;
667  for(std::size_t i = 0; i < user_map_names_.size(); i++)
668  {
669  config user_map_data = gen_mp_data;
670  user_map_data["map_data"] = filesystem::read_map(user_map_names_[i]);
671 
672  // Check if a file is actually a map.
673  // Note that invalid maps should be displayed in order to
674  // show error messages in the GUI.
675  bool add_map = true;
676  std::unique_ptr<gamemap> map;
677  try {
678  map.reset(new gamemap(user_map_data["map_data"]));
679  } catch (const incorrect_map_format_error& e) {
680  // Set map content to nullptr, so that it fails can_launch_game()
681  map.reset(nullptr);
682  user_map_data["description"] = _("Map could not be loaded: ") + e.message;
683 
684  ERR_CF << "map could not be loaded: " << e.message;
685  } catch (const wml_exception&) {
686  add_map = false;
687  dep_index_offset++;
688  }
689 
690  if(add_map) {
691  type_map_[level_type::type::user_map].games.emplace_back(new user_map(user_map_data, user_map_names_[i], map.get()));
692 
693  // Since user maps are treated as scenarios, some dependency info is required
694  config depinfo;
695  depinfo["id"] = user_map_names_[i];
696  depinfo["name"] = user_map_names_[i];
697  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset);
698  }
699  }
700 
701  // User made scenarios.
702  dep_index_offset = 0;
703  for(std::size_t i = 0; i < user_scenario_names_.size(); i++)
704  {
705  config data;
706  try {
707  // Only attempt to load .cfg files (.cfg extension is enforced in Editor save)
709  continue;
710 
712  } catch(const config::error & e) {
713  ERR_CF << "Caught a config error while parsing user made (editor) scenarios:\n" << e.message;
714  ERR_CF << "Skipping file: " << (filesystem::get_legacy_editor_dir() + "/scenarios/" + user_scenario_names_[i]);
715  continue;
716  }
717 
718  scenario_ptr new_scenario(new scenario(data));
719  if(new_scenario->id().empty()) continue;
720 
721  type_map_[level_type::type::user_scenario].games.push_back(std::move(new_scenario));
722 
723  // Since user scenarios are treated as scenarios, some dependency info is required
724  config depinfo;
725  depinfo["id"] = data["id"];
726  depinfo["name"] = data["name"];
727  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset++);
728  }
729  }
730 
731  // Stand-alone scenarios.
732  for(const config& data : game_config_.child_range("multiplayer"))
733  {
734  if(!data["allow_new_game"].to_bool(true))
735  continue;
736 
737  if(!data["campaign_id"].empty())
738  continue;
739 
740  if(data.has_attribute("map_generation") || data.has_attribute("scenario_generation")) {
741  type_map_[level_type::type::random_map].games.emplace_back(new random_map(data));
742  } else {
743  type_map_[level_type::type::scenario].games.emplace_back(new scenario(data));
744  }
745  }
746 
747  // Presets.
748  for(const config& preset : prefs::get().get_game_presets()) {
749  optional_const_config data = game_config_.find_child("multiplayer", "id", preset["scenario"].str());
750 
751  if(data) {
752  type_map_[level_type::type::preset].games.emplace_back(new scenario(*data));
753  }
754  }
755 
756  // Campaigns.
757  for(const config& data : game_config_.child_range("campaign"))
758  {
759  if(data["id"].empty()) {
760  if(data["name"].empty()) {
761  ERR_CF << "Found a [campaign] with neither a name nor an id attribute, ignoring it";
762  } else {
763  ERR_CF << "Ignoring a [campaign] with no id attribute, but name '" << data["name"] << "'";
764  }
765  continue;
766  }
767 
768  const std::string& type = data["type"];
769  const bool mp = state_.classification().is_multiplayer();
770 
771  if(type == "mp" || (type == "hybrid" && mp)) {
772  type_map_[level_type::type::campaign].games.emplace_back(new campaign(data));
773  }
774 
775  if(type == "sp" || type.empty() || (type == "hybrid" && !mp)) {
776  campaign_ptr new_sp_campaign(new campaign(data));
777  new_sp_campaign->mark_if_completed();
778 
779  type_map_[level_type::type::sp_campaign].games.push_back(std::move(new_sp_campaign));
780  }
781  }
782 
783  auto& sp_campaigns = type_map_[level_type::type::sp_campaign].games;
784 
785  // Sort sp campaigns by rank.
786  std::stable_sort(sp_campaigns.begin(), sp_campaigns.end(),
788  return a->data()["rank"].to_int(1000) < b->data()["rank"].to_int(1000);
789  }
790  );
791 }
792 
793 void create_engine::init_extras(const MP_EXTRA extra_type)
794 {
795  std::vector<extras_metadata_ptr>& extras = get_extras_by_type(extra_type);
796  const std::string extra_name = (extra_type == ERA) ? "era" : "modification";
797 
798  component_availability::type default_availabilty = (extra_type == ERA)
799  ? component_availability::type::mp
800  : component_availability::type::hybrid;
801 
802  std::set<std::string> found_ids;
803  for(const config& extra : game_config_.child_range(extra_name))
804  {
805  component_availability::type type = component_availability::get_enum(extra["type"].str()).value_or(default_availabilty);
806  const bool mp = state_.classification().is_multiplayer();
807 
808  if((type != component_availability::type::mp || mp) && (type != component_availability::type::sp || !mp) )
809  {
810  if(found_ids.insert(extra["id"]).second) {
811  extras_metadata_ptr new_extras_metadata(new extras_metadata());
812  new_extras_metadata->id = extra["id"].str();
813  new_extras_metadata->name = extra["name"].str();
814  new_extras_metadata->description = extra["description"].str();
815  new_extras_metadata->cfg = &extra;
816 
817  extras.push_back(std::move(new_extras_metadata));
818  }
819  else {
820  ERR_CF << "found " << extra_name << " with id=" << extra["id"] << " twice";
821  }
822  }
823  }
824 }
825 
827 {
828  for(auto& type : type_map_) {
829  type.second.apply_filter(player_count_filter_, level_name_filter_);
830  }
831 }
832 
833 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type_unfiltered(level_type::type type) const
834 {
835  std::vector<level_ptr> levels;
836  for(const level_ptr& lvl : type_map_.at(type).games) {
837  levels.push_back(lvl);
838  }
839 
840  return levels;
841 }
842 
843 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type(level_type::type type) const
844 {
845  auto& g_list = type_map_.at(type);
846 
847  std::vector<level_ptr> levels;
848  for(std::size_t level : g_list.games_filtered) {
849  levels.push_back(g_list.games[level]);
850  }
851 
852  return levels;
853 }
854 
856 {
857  return type_map_.at(type).games_filtered;
858 }
859 
860 const std::vector<create_engine::extras_metadata_ptr>&
862 {
863  return (extra_type == ERA) ? eras_ : mods_;
864 }
865 
866 std::vector<create_engine::extras_metadata_ptr>&
868 {
869  return (extra_type == ERA) ? eras_ : mods_;
870 }
871 
873 {
874  return state_;
875 }
876 
877 } // end namespace ng
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
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
child_itors child_range(config_key_type key)
Definition: config.cpp:268
bool empty() const
Definition: config.cpp:845
void clear()
Definition: config.cpp:824
std::string hash() const
Definition: config.cpp:1279
config & add_child(config_key_type key)
Definition: config.cpp:436
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)
optional_const_config optional_child(config_key_type key) const
optional_const_config find_child(config_key_type key, const std::string &name, const std::string &value) const
config_array_view child_range(config_key_type key) const
const config & find_mandatory_child(config_key_type key, const std::string &name, const std::string &value) const
Encapsulates the map of the game.
Definition: map.hpp:172
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
void set_current_era_id(const std::string &id, bool force=false)
std::string level_name_filter_
int find_extra_by_id(const MP_EXTRA extra_type, const std::string &id) const
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)
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
saved_game & state_
void set_current_level(const std::size_t index)
void prepare_for_era_and_mods()
void init_generated_level_data()
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.
const config & data() const
void set_data(const config &data)
virtual bool allow_era_choice() const
level(const config &data)
bool generate_whole_scenario() const
const config & generator_data() const
random_map(const config &data)
std::string generator_name() const
map_generator * create_map_generator() const
std::unique_ptr< gamemap > map_
scenario(const config &data)
void set_metadata()
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()
bool is_campaign_completed(const std::string &campaign_id)
game_classification & classification()
Definition: saved_game.hpp:56
bool is_mid_game_save() const
Definition: saved_game.hpp:106
void expand_scenario()
copies the content of a [scenario] with the correct id attribute from the game config into this objec...
Definition: saved_game.cpp:306
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:165
std::string get_scenario_id() const
Definition: saved_game.cpp:698
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...
Definition: saved_game.cpp:558
void clear()
Definition: saved_game.cpp:833
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:60
void set_scenario(config scenario)
Definition: saved_game.cpp:615
void check_require_scenario()
Add addon_id information if needed.
Definition: saved_game.cpp:334
void expand_random_scenario()
takes care of generate_map=, generate_scenario=, map= attributes This should be called before expandi...
Definition: saved_game.cpp:524
static irdya_date read_date(const std::string &date)
static lg::log_domain log_mp_create_engine("mp/create/engine")
#define WRN_MP
#define DBG_MP
#define ERR_CF
static lg::log_domain log_config("config")
Declarations for File-IO.
std::size_t i
Definition: function.cpp:1032
static std::string _(const char *str)
Definition: gettext.hpp:97
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:296
map_generator * create_map_generator(const std::string &name, const config &cfg, const config *vars)
Definition: map_create.cpp:28
CURSOR_TYPE get()
Definition: cursor.cpp:218
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, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
Definition: filesystem.cpp:463
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.
Definition: build_info.cpp:61
static bool is_active(const widget *wgt)
Definition: window.cpp:1264
config read(std::istream &in, abstract_validator *validator)
Definition: parser.cpp:600
Main entry points of multiplayer mode.
Definition: lobby_data.cpp:49
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
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.
std::string mp_era_name
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 d
#define e
#define b