The Battle for Wesnoth  1.17.17+dev
create_engine.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2023
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"
21 #include "preferences/game.hpp"
25 #include "log.hpp"
26 #include "map/exception.hpp"
27 #include "map/map.hpp"
28 #include "minimap.hpp"
29 #include "saved_game.hpp"
30 #include "side_controller.hpp"
31 #include "wml_exception.hpp"
32 
34 #include "serialization/parser.hpp"
36 
37 #include <sstream>
38 #include <cctype>
39 
40 static lg::log_domain log_config("config");
41 #define ERR_CF LOG_STREAM(err, log_config)
42 
43 static lg::log_domain log_mp_create_engine("mp/create/engine");
44 #define WRN_MP LOG_STREAM(warn, log_mp_create_engine)
45 #define DBG_MP LOG_STREAM(debug, log_mp_create_engine)
46 
47 namespace ng {
48 
50  : data_(data)
51 {
52 }
53 
55  : level(data)
56  , map_()
57  , map_hash_()
58  , num_players_(0)
59 {
60  set_metadata();
61 }
62 
64 {
65  return map_.get() != nullptr;
66 }
67 
69 {
70  const std::string& map_data = data_["map_data"];
71 
72  try {
73  map_.reset(new gamemap(map_data));
74  } catch(const incorrect_map_format_error& e) {
75  // Set map content to nullptr, so that it fails can_launch_game()
76  map_.reset(nullptr);
77  data_["description"] = _("Map could not be loaded: ") + e.message;
78 
79  ERR_CF << "map could not be loaded: " << e.message;
80  } catch(const wml_exception& e) {
81  data_["description"] = _("Map could not be loaded.");
82 
83  ERR_CF << "map could not be loaded: " << e.dev_message;
84  }
85 
86  set_sides();
87 }
88 
89 std::string scenario::map_size() const
90 {
91  std::stringstream map_size;
92 
93  if(map_.get() != nullptr) {
94  map_size << map_->w();
96  map_size << map_->h();
97  } else {
98  map_size << _("not available.");
99  }
100 
101  return map_size.str();
102 }
103 
105 {
106  if(map_.get() != nullptr) {
107  // If there are fewer sides in the configuration than there are
108  // starting positions, then generate the additional sides
109  const int map_positions = map_->num_valid_starting_positions();
110 
111  if(!data_.has_child("side")) {
112  for(int pos = 0; pos < map_positions; ++pos) {
113  config& side = data_.add_child("side");
114  side["side"] = pos + 1;
115  side["team_name"] = "Team " + std::to_string(pos + 1);
116  side["canrecruit"] = true;
117  side["controller"] = side_controller::human;
118  }
119  }
120 
121  num_players_ = 0;
122  for(const config& scenario : data_.child_range("side")) {
123  if(scenario["allow_player"].to_bool(true)) {
124  ++num_players_;
125  }
126  }
127  }
128 }
129 
130 user_map::user_map(const config& data, const std::string& name, gamemap* map)
131  : scenario(data)
132  , name_(name)
133 {
134  if(map != nullptr) {
135  map_.reset(new gamemap(*map));
136  }
137 
138  set_metadata();
139 }
140 
142 {
143  set_sides();
144 }
145 
146 std::string user_map::description() const
147 {
148  if(!data_["description"].empty()) {
149  return data_["description"];
150  }
151 
152  // map error message
153  return _("Custom map.");
154 }
155 
157  : scenario(data)
158  , generator_data_()
159  , generate_whole_scenario_(data_.has_attribute("scenario_generation"))
160  , generator_name_(generate_whole_scenario_ ? data_["scenario_generation"] : data_["map_generation"])
161 {
162  if(!data.has_child("generator")) {
163  data_.clear();
165  data_["description"] = "Error: Random map found with missing generator information. Scenario should have a [generator] child.";
166  data_["error_message"] = "missing [generator] tag";
167  } else {
168  generator_data_ = data.mandatory_child("generator");
169  }
170 
171  if(!data.has_attribute("scenario_generation") && !data.has_attribute("map_generation")) {
172  data_.clear();
174  data_["description"] = "Error: Random map found with missing generator information. Scenario should have a [generator] child.";
175  data_["error_message"] = "couldn't find 'scenario_generation' or 'map_generation' attribute";
176  }
177 }
178 
180 {
182 }
183 
185  : level(data)
186  , id_(data["id"])
187  , allow_era_choice_(level::allow_era_choice())
188  , image_label_()
189  , min_players_(1)
190  , max_players_(1)
191 {
192  if(data.has_attribute("start_year")) {
193  dates_.first = utils::irdya_date::read_date(data["start_year"]);
194  if(data.has_attribute("end_year")) {
195  dates_.second = utils::irdya_date::read_date(data["end_year"]);
196  } else {
197  dates_.second = dates_.first;
198  }
199  } else if(data.has_attribute("year")) {
200  dates_.first = dates_.second = utils::irdya_date::read_date(data["year"]);
201  }
202  set_metadata();
203 }
204 
206 {
207  return !data_.empty();
208 }
209 
211 {
212  image_label_ = data_["image"].str();
213 
214  int min = data_["min_players"].to_int(1);
215  int max = data_["max_players"].to_int(1);
216 
217  min_players_ = max_players_ = min;
218 
219  if(max > min) {
220  max_players_ = max;
221  }
222 }
223 
225 {
226  data_["completed"] = preferences::is_campaign_completed(data_["id"]);
227 
228  for(auto& cfg : data_.child_range("difficulty")) {
229  cfg["completed_at"] = preferences::is_campaign_completed(data_["id"], cfg["define"]);
230  }
231 }
232 
234  : current_level_type_()
235  , current_level_index_(0)
236  , current_era_index_(0)
237  , level_name_filter_()
238  , player_count_filter_(1)
239  , type_map_()
240  , user_map_names_()
241  , user_scenario_names_()
242  , eras_()
243  , mods_()
244  , state_(state)
245  , dependency_manager_(nullptr)
246  , generator_(nullptr)
247  , selected_campaign_difficulty_()
248  , game_config_(game_config_manager::get()->game_config())
249 {
250  // Set up the type map. Do this first!
251  type_map_.emplace(level_type::type::scenario, type_list());
252  type_map_.emplace(level_type::type::user_map, type_list());
253  type_map_.emplace(level_type::type::user_scenario, type_list());
254  type_map_.emplace(level_type::type::campaign, type_list());
255  type_map_.emplace(level_type::type::sp_campaign, type_list());
256  type_map_.emplace(level_type::type::random_map, type_list());
257 
258  DBG_MP << "restoring game config";
259 
260  // Restore game config for multiplayer.
262 
263  state_.clear();
265 
267 
268  // Initialize dependency_manager_ after refreshing game config.
270 
271  // TODO: the editor dir is already configurable, is the preferences value
274 
277 
278  DBG_MP << "initializing all levels, eras and mods";
279 
280  init_all_levels();
281  init_extras(ERA);
282  init_extras(MOD);
283 
284  state_.mp_settings().saved_game = saved_game_mode::type::no;
285 
286  for(const std::string& str : preferences::modifications(state_.classification().is_multiplayer())) {
287  if(game_config_.find_child("modification", "id", str)) {
288  state_.classification().active_mods.push_back(str);
289  }
290  }
291 
292  dependency_manager_->try_modifications(state_.classification().active_mods, true);
293 
295 }
296 
298 {
299  DBG_MP << "initializing generated level data";
300 
301  //DBG_MP << "current data:";
302  //DBG_MP << current_level().data().debug();
303 
304  random_map * cur_lev = dynamic_cast<random_map *> (&current_level());
305 
306  if(!cur_lev) {
307  WRN_MP << "Tried to initialized generated level data on a level that wasn't a random map";
308  return;
309  }
310 
311  try {
312  if(!cur_lev->generate_whole_scenario())
313  {
314  DBG_MP << "** replacing map **";
315 
316  config data = cur_lev->data();
317 
318  data["map_data"] = generator_->create_map();
319 
320  cur_lev->set_data(data);
321 
322  } else { //scenario generation
323 
324  DBG_MP << "** replacing scenario **";
325 
326  config data = generator_->create_scenario();
327 
328  // Set the scenario to have placing of sides
329  // based on the terrain they prefer
330  if(!data.has_attribute("modify_placing")) {
331  data["modify_placing"] = true;
332  }
333 
334  const std::string& description = cur_lev->data()["description"];
335  data["description"] = description;
337 
338  cur_lev->set_data(data);
339  }
340  } catch (const mapgen_exception & e) {
341  config data = cur_lev->data();
342 
343  data["error_message"] = e.what();
344 
345  cur_lev->set_data(data);
346  }
347 
348  //DBG_MP << "final data:";
349  //DBG_MP << current_level().data().debug();
350 }
351 
353 {
354  //
355  // We exclude campaigns from this check since they require preprocessing in order to check
356  // their side data. Since this function is used by the MP Create screen to verify side data
357  // before proceeding to Staging, this should cover most cases of false positives. It does,
358  // however, leave open the possibility of scenarios that require preprocessing before their
359  // side data is accessible, but that's an unlikely occurrence.
360  //
361  if(is_campaign()) {
362  return true;
363  }
364 
365  return current_level().data().has_child("side");
366 }
367 
369 {
370  DBG_MP << "preparing mp_game_settings for new level";
373 }
374 
376 {
377  get_parameters();
379  for(const std::string& mod_id : state_.classification().active_mods) {
380  state_.classification().mod_defines.push_back(game_config_.find_mandatory_child("modification", "id", mod_id)["define"].str());
381  }
382 }
383 
385 {
386  DBG_MP << "preparing data for scenario by reloading game config";
387 
388  state_.classification().scenario_define = current_level().data()["define"].str();
389 
391  config {"next_scenario", current_level().data()["id"]}
392  );
393 }
394 
395 void create_engine::prepare_for_campaign(const std::string& difficulty)
396 {
397  DBG_MP << "preparing data for campaign by reloading game config";
398 
399  if(!difficulty.empty()) {
400  state_.classification().difficulty = difficulty;
401  } else if(!selected_campaign_difficulty_.empty()) {
403  }
404 
405  config& current_level_data = current_level().data();
406 
407  state_.classification().campaign = current_level_data["id"].str();
408  state_.classification().campaign_name = current_level_data["name"].str();
409  state_.classification().abbrev = current_level_data["abbrev"].str();
410 
411  state_.classification().end_text = current_level_data["end_text"].str();
412  state_.classification().end_text_duration = current_level_data["end_text_duration"];
413  state_.classification().end_credits = current_level_data["end_credits"].to_bool(true);
414 
415  state_.classification().campaign_define = current_level_data["define"].str();
417  utils::split(current_level_data["extra_defines"]);
418 
420  config {"next_scenario", current_level_data["first_scenario"]}
421  );
422 }
423 
425 {
426  // Verify the existence of difficulties
427  std::vector<std::string> difficulties;
428 
429  for(const config& d : current_level().data().child_range("difficulty")) {
430  difficulties.push_back(d["define"]);
431  }
432 
433  // No difficulties found. Exit
434  if(difficulties.empty()) {
435  return "";
436  }
437 
438  // One difficulty found. Use it
439  if(difficulties.size() == 1) {
440  return difficulties[0];
441  }
442 
443  // A specific difficulty value was passed
444  // Use a minimalistic interface to get the specified define
445  if(set_value != -1) {
446  if(set_value > static_cast<int>(difficulties.size())) {
447  PLAIN_LOG << "incorrect difficulty number: [" <<
448  set_value << "]. maximum is [" << difficulties.size() << "].\n";
449  return "FAIL";
450  } else if(set_value < 1) {
451  PLAIN_LOG << "incorrect difficulty number: [" <<
452  set_value << "]. minimum is [1].\n";
453  return "FAIL";
454  } else {
455  return difficulties[set_value - 1];
456  }
457  }
458 
459  // If not, let the user pick one from the prompt
460  // We don't pass the difficulties vector here because additional data is required
461  // to constrict the dialog
463  dlg.show();
464 
466 
468 }
469 
471 {
472  DBG_MP << "preparing mp_game_settings for saved game";
473 
475 
476  // The save might be a start-of-scenario save so make sure we have the scenario data loaded.
478  state_.mp_settings().saved_game = state_.is_mid_game_save() ? saved_game_mode::type::midgame : saved_game_mode::type::scenaro_start;
479 }
480 
482 {
483  DBG_MP << "prepare_for_other";
487 }
488 
489 void create_engine::apply_level_filter(const std::string& name)
490 {
491  level_name_filter_ = name;
493 }
494 
496 {
497  player_count_filter_ = players;
499 }
500 
502 {
503  for(auto& type : type_map_) {
504  type.second.reset_filter();
505  }
506 
507  level_name_filter_ = "";
508 }
509 
511 {
513 }
514 
516 {
518 }
519 
521 {
522  try {
523  current_level_index_ = type_map_.at(current_level_type_).games_filtered.at(index);
524  } catch (const std::out_of_range&) {
526  }
527 
528  if(current_level_type_ == level_type::type::random_map) {
529  random_map* current_random_map = dynamic_cast<random_map*>(&current_level());
530 
531  // If dynamic cast has failed then we somehow have gotten all the pointers mixed together.
532  assert(current_random_map);
533 
534  generator_.reset(current_random_map->create_map_generator());
535  } else {
536  generator_.reset(nullptr);
537  }
538 
540  dependency_manager_->try_scenario(current_level().id());
541  }
542 }
543 
544 void create_engine::set_current_era_index(const std::size_t index, bool force)
545 {
547 
548  dependency_manager_->try_era_by_index(index, force);
549 }
550 
551 bool create_engine::toggle_mod(int index, bool force)
552 {
553  force |= state_.classification().type != campaign_type::type::multiplayer;
554 
555  bool is_active = dependency_manager_->is_modification_active(index);
556  dependency_manager_->try_modification_by_index(index, !is_active, force);
557 
558  state_.classification().active_mods = dependency_manager_->get_modifications();
559 
560  return !is_active;
561 }
562 
564 {
565  return generator_ != nullptr;
566 }
567 
569 {
570  return generator_->allow_user_config();
571 }
572 
574 {
575  generator_->user_config();
576 }
577 
578 std::pair<level_type::type, int> create_engine::find_level_by_id(const std::string& id) const
579 {
580  for(const auto& type : type_map_) {
581  int i = 0;
582 
583  for(const auto& game : type.second.games) {
584  if(game->id() == id) {
585  return {type.first, i};
586  }
587 
588  i++;
589  }
590  }
591 
592  return {level_type::type::sp_campaign, -1};
593 }
594 
595 int create_engine::find_extra_by_id(const MP_EXTRA extra_type, const std::string& id) const
596 {
597  int i = 0;
598  for(extras_metadata_ptr extra : get_const_extras_by_type(extra_type)) {
599  if(extra->id == id) {
600  return i;
601  }
602  i++;
603  }
604 
605  return -1;
606 }
607 
609 {
610  state_.classification().active_mods = dependency_manager_->get_modifications();
611 }
612 
613 std::vector<std::string>& create_engine::active_mods()
614 {
616 }
617 
618 std::vector<create_engine::extras_metadata_ptr> create_engine::active_mods_data()
619 {
620  const std::vector<extras_metadata_ptr>& mods = get_const_extras_by_type(MP_EXTRA::MOD);
621 
622  std::vector<extras_metadata_ptr> data_vec;
623  std::copy_if(mods.begin(), mods.end(), std::back_inserter(data_vec), [this](extras_metadata_ptr mod) {
624  return dependency_manager_->is_modification_active(mod->id);
625  });
626 
627  return data_vec;
628 }
629 
631 {
632  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
633  return *eras_[era_index]->cfg;
634 }
635 
637 {
638  DBG_MP << "getting parameter values";
639 
640  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
641  state_.classification().era_id = eras_[era_index]->id;
642  state_.mp_settings().mp_era_name = eras_[era_index]->name;
643 
644  return state_.mp_settings();
645 }
646 
648 {
649  if(auto generic_multiplayer = game_config_.optional_child("generic_multiplayer")) {
650  config gen_mp_data = *generic_multiplayer;
651 
652  // User maps.
653  int dep_index_offset = 0;
654  for(std::size_t i = 0; i < user_map_names_.size(); i++)
655  {
656  config user_map_data = gen_mp_data;
657  user_map_data["map_data"] = filesystem::read_map(user_map_names_[i]);
658 
659  // Check if a file is actually a map.
660  // Note that invalid maps should be displayed in order to
661  // show error messages in the GUI.
662  bool add_map = true;
663  std::unique_ptr<gamemap> map;
664  try {
665  map.reset(new gamemap(user_map_data["map_data"]));
666  } catch (const incorrect_map_format_error& e) {
667  // Set map content to nullptr, so that it fails can_launch_game()
668  map.reset(nullptr);
669  user_map_data["description"] = _("Map could not be loaded: ") + e.message;
670 
671  ERR_CF << "map could not be loaded: " << e.message;
672  } catch (const wml_exception&) {
673  add_map = false;
674  dep_index_offset++;
675  }
676 
677  if(add_map) {
678  type_map_[level_type::type::user_map].games.emplace_back(new user_map(user_map_data, user_map_names_[i], map.get()));
679 
680  // Since user maps are treated as scenarios, some dependency info is required
681  config depinfo;
682  depinfo["id"] = user_map_names_[i];
683  depinfo["name"] = user_map_names_[i];
684  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset);
685  }
686  }
687 
688  // User made scenarios.
689  dep_index_offset = 0;
690  for(std::size_t i = 0; i < user_scenario_names_.size(); i++)
691  {
692  config data;
693  try {
695  } catch(const config::error & e) {
696  ERR_CF << "Caught a config error while parsing user made (editor) scenarios:\n" << e.message;
697  ERR_CF << "Skipping file: " << (filesystem::get_user_data_dir() + "/editor/scenarios/" + user_scenario_names_[i]);
698  continue;
699  }
700 
701  scenario_ptr new_scenario(new scenario(data));
702  if(new_scenario->id().empty()) continue;
703 
704  type_map_[level_type::type::user_scenario].games.push_back(std::move(new_scenario));
705 
706  // Since user scenarios are treated as scenarios, some dependency info is required
707  config depinfo;
708  depinfo["id"] = data["id"];
709  depinfo["name"] = data["name"];
710  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset++);
711  }
712  }
713 
714  // Stand-alone scenarios.
715  for(const config& data : game_config_.child_range("multiplayer"))
716  {
717  if(!data["allow_new_game"].to_bool(true))
718  continue;
719 
720  if(!data["campaign_id"].empty())
721  continue;
722 
723  if(data.has_attribute("map_generation") || data.has_attribute("scenario_generation")) {
724  type_map_[level_type::type::random_map].games.emplace_back(new random_map(data));
725  } else {
726  type_map_[level_type::type::scenario].games.emplace_back(new scenario(data));
727  }
728  }
729 
730  // Campaigns.
731  for(const config& data : game_config_.child_range("campaign"))
732  {
733  if(data["id"].empty()) {
734  if(data["name"].empty()) {
735  ERR_CF << "Found a [campaign] with neither a name nor an id attribute, ignoring it";
736  } else {
737  ERR_CF << "Ignoring a [campaign] with no id attribute, but name '" << data["name"] << "'";
738  }
739  continue;
740  }
741 
742  const std::string& type = data["type"];
743  const bool mp = state_.classification().is_multiplayer();
744 
745  if(type == "mp" || (type == "hybrid" && mp)) {
746  type_map_[level_type::type::campaign].games.emplace_back(new campaign(data));
747  }
748 
749  if(type == "sp" || type.empty() || (type == "hybrid" && !mp)) {
750  campaign_ptr new_sp_campaign(new campaign(data));
751  new_sp_campaign->mark_if_completed();
752 
753  type_map_[level_type::type::sp_campaign].games.push_back(std::move(new_sp_campaign));
754  }
755  }
756 
757  auto& sp_campaigns = type_map_[level_type::type::sp_campaign].games;
758 
759  // Sort sp campaigns by rank.
760  std::stable_sort(sp_campaigns.begin(), sp_campaigns.end(),
762  return a->data()["rank"].to_int(1000) < b->data()["rank"].to_int(1000);
763  }
764  );
765 }
766 
767 void create_engine::init_extras(const MP_EXTRA extra_type)
768 {
769  std::vector<extras_metadata_ptr>& extras = get_extras_by_type(extra_type);
770  const std::string extra_name = (extra_type == ERA) ? "era" : "modification";
771 
772  component_availability::type default_availabilty = (extra_type == ERA)
773  ? component_availability::type::mp
774  : component_availability::type::hybrid;
775 
776  std::set<std::string> found_ids;
777  for(const config& extra : game_config_.child_range(extra_name))
778  {
779  component_availability::type type = component_availability::get_enum(extra["type"].str()).value_or(default_availabilty);
780  const bool mp = state_.classification().is_multiplayer();
781 
782  if((type != component_availability::type::mp || mp) && (type != component_availability::type::sp || !mp) )
783  {
784  if(found_ids.insert(extra["id"]).second) {
785  extras_metadata_ptr new_extras_metadata(new extras_metadata());
786  new_extras_metadata->id = extra["id"].str();
787  new_extras_metadata->name = extra["name"].str();
788  new_extras_metadata->description = extra["description"].str();
789  new_extras_metadata->cfg = &extra;
790 
791  extras.push_back(std::move(new_extras_metadata));
792  }
793  else {
794  //TODO: use a more visible error message.
795  ERR_CF << "found " << extra_name << " with id=" << extra["id"] << " twice";
796  }
797  }
798  }
799 }
800 
802 {
803  for(auto& type : type_map_) {
804  type.second.apply_filter(player_count_filter_, level_name_filter_);
805  }
806 }
807 
808 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type_unfiltered(level_type::type type) const
809 {
810  std::vector<level_ptr> levels;
811  for(const level_ptr& lvl : type_map_.at(type).games) {
812  levels.push_back(lvl);
813  }
814 
815  return levels;
816 }
817 
818 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type(level_type::type type) const
819 {
820  auto& g_list = type_map_.at(type);
821 
822  std::vector<level_ptr> levels;
823  for(std::size_t level : g_list.games_filtered) {
824  levels.push_back(g_list.games[level]);
825  }
826 
827  return levels;
828 }
829 
831 {
832  return type_map_.at(type).games_filtered;
833 }
834 
835 const std::vector<create_engine::extras_metadata_ptr>&
837 {
838  return (extra_type == ERA) ? eras_ : mods_;
839 }
840 
841 std::vector<create_engine::extras_metadata_ptr>&
843 {
844  return (extra_type == ERA) ? eras_ : mods_;
845 }
846 
848 {
849  return state_;
850 }
851 
852 } // end namespace ng
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
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:371
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:321
bool has_attribute(config_key_type key) const
Definition: config.cpp:159
child_itors child_range(config_key_type key)
Definition: config.cpp:277
bool empty() const
Definition: config.cpp:856
void clear()
Definition: config.cpp:835
std::string hash() const
Definition: config.cpp:1291
config & add_child(config_key_type key)
Definition: config.cpp:445
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::vector< std::string > active_mods
campaign_type::type type
bool end_credits
whether to show the standard credits at the end
unsigned int end_text_duration
for how long the end-of-campaign text is shown
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
The campaign mode difficulty menu.
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
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
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()
bool toggle_mod(int index, bool force=false)
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
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:282
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:165
std::string get_scenario_id() const
Definition: saved_game.cpp:664
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:524
void clear()
Definition: saved_game.cpp:799
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:60
void set_scenario(config scenario)
Definition: saved_game.cpp:581
void check_require_scenario()
Add addon_id information if needed.
Definition: saved_game.cpp:310
void expand_random_scenario()
takes care of generate_map=, generate_scenario=, map= attributes This should be called before expandi...
Definition: saved_game.cpp:490
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:968
static std::string _(const char *str)
Definition: gettext.hpp:93
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:261
map_generator * create_map_generator(const std::string &name, const config &cfg, const config *vars)
Definition: map_create.cpp:29
CURSOR_TYPE get()
Definition: cursor.cpp:216
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:406
std::string get_user_data_dir()
Definition: filesystem.cpp:857
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:63
static bool is_active(const widget *wgt)
Definition: window.cpp:1190
Main entry points of multiplayer mode.
Definition: lobby_data.cpp:52
bool is_campaign_completed(const std::string &campaign_id)
Definition: game.cpp:293
const std::vector< std::string > & modifications(bool mp)
Definition: game.cpp:711
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
std::vector< std::string > split(const config_attribute_value &val)
std::string_view data
Definition: picture.cpp:199
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627
std::string mp_era_name
saved_game_mode::type saved_game
static constexpr std::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 a
#define b