The Battle for Wesnoth  1.19.10+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 
255  DBG_MP << "restoring game config";
256 
257  // Restore game config for multiplayer.
259 
260  state_.clear();
262 
264 
265  // Initialize dependency_manager_ after refreshing game config.
267 
268  // TODO: the editor dir is already configurable, is the preferences value
271 
274 
275  DBG_MP << "initializing all levels, eras and mods";
276 
277  init_all_levels();
278  init_extras(ERA);
279  init_extras(MOD);
280 
281  state_.mp_settings().saved_game = saved_game_mode::type::no;
282 
283  for(const std::string& str : prefs::get().modifications(state_.classification().is_multiplayer())) {
284  if(game_config_.find_child("modification", "id", str)) {
285  state_.classification().active_mods.push_back(str);
286  }
287  }
288 
289  dependency_manager_->try_modifications(state_.classification().active_mods, true);
290 
292 }
293 
295 {
296  DBG_MP << "initializing generated level data";
297 
298  //DBG_MP << "current data:";
299  //DBG_MP << current_level().data().debug();
300 
301  random_map * cur_lev = dynamic_cast<random_map *> (&current_level());
302 
303  if(!cur_lev) {
304  WRN_MP << "Tried to initialized generated level data on a level that wasn't a random map";
305  return;
306  }
307 
308  try {
309  if(!cur_lev->generate_whole_scenario())
310  {
311  DBG_MP << "** replacing map **";
312 
313  config data = cur_lev->data();
314 
315  data["map_data"] = generator_->create_map();
316 
317  cur_lev->set_data(data);
318 
319  } else { //scenario generation
320 
321  DBG_MP << "** replacing scenario **";
322 
323  config data = generator_->create_scenario();
324 
325  // Set the scenario to have placing of sides
326  // based on the terrain they prefer
327  if(!data.has_attribute("modify_placing")) {
328  data["modify_placing"] = true;
329  }
330 
331  const std::string& description = cur_lev->data()["description"];
332  data["description"] = description;
334 
335  cur_lev->set_data(data);
336  }
337  } catch (const mapgen_exception & e) {
338  config data = cur_lev->data();
339 
340  data["error_message"] = e.what();
341 
342  cur_lev->set_data(data);
343  }
344 
345  //DBG_MP << "final data:";
346  //DBG_MP << current_level().data().debug();
347 }
348 
350 {
351  //
352  // We exclude campaigns from this check since they require preprocessing in order to check
353  // their side data. Since this function is used by the MP Create screen to verify side data
354  // before proceeding to Staging, this should cover most cases of false positives. It does,
355  // however, leave open the possibility of scenarios that require preprocessing before their
356  // side data is accessible, but that's an unlikely occurrence.
357  //
358  if(is_campaign()) {
359  return true;
360  }
361 
362  return current_level().data().has_child("side");
363 }
364 
366 {
367  DBG_MP << "preparing mp_game_settings for new level";
370 }
371 
373 {
374  get_parameters();
376  for(const std::string& mod_id : state_.classification().active_mods) {
377  state_.classification().mod_defines.push_back(game_config_.find_mandatory_child("modification", "id", mod_id)["define"].str());
378  }
379 }
380 
382 {
383  DBG_MP << "preparing data for scenario by reloading game config";
384 
385  state_.classification().scenario_define = current_level().data()["define"].str();
386 
388  config {"next_scenario", current_level().data()["id"]}
389  );
390 }
391 
392 void create_engine::prepare_for_campaign(const std::string& difficulty)
393 {
394  DBG_MP << "preparing data for campaign by reloading game config";
395 
396  if(!difficulty.empty()) {
397  state_.classification().difficulty = difficulty;
398  } else if(!selected_campaign_difficulty_.empty()) {
400  }
401 
402  config& current_level_data = current_level().data();
403 
404  state_.classification().campaign = current_level_data["id"].str();
405  state_.classification().campaign_name = current_level_data["name"].str();
406  state_.classification().abbrev = current_level_data["abbrev"].str();
407 
408  state_.classification().end_text = current_level_data["end_text"].str();
409  state_.classification().end_text_duration = chrono::parse_duration<std::chrono::milliseconds>(current_level_data["end_text_duration"]);
410  state_.classification().end_credits = current_level_data["end_credits"].to_bool(true);
411 
412  state_.classification().campaign_define = current_level_data["define"].str();
414  utils::split(current_level_data["extra_defines"]);
415 
417  config {"next_scenario", current_level_data["first_scenario"]}
418  );
419 }
420 
422 {
423  // Verify the existence of difficulties
424  std::vector<std::string> difficulties;
425 
426  for(const config& d : current_level().data().child_range("difficulty")) {
427  difficulties.push_back(d["define"]);
428  }
429 
430  // No difficulties found. Exit
431  if(difficulties.empty()) {
432  return "";
433  }
434 
435  // One difficulty found. Use it
436  if(difficulties.size() == 1) {
437  return difficulties[0];
438  }
439 
440  // A specific difficulty value was passed
441  // Use a minimalistic interface to get the specified define
442  if(set_value != -1) {
443  if(set_value > static_cast<int>(difficulties.size())) {
444  PLAIN_LOG << "incorrect difficulty number: [" <<
445  set_value << "]. maximum is [" << difficulties.size() << "].\n";
446  return "FAIL";
447  } else if(set_value < 1) {
448  PLAIN_LOG << "incorrect difficulty number: [" <<
449  set_value << "]. minimum is [1].\n";
450  return "FAIL";
451  } else {
452  return difficulties[set_value - 1];
453  }
454  }
455 
456  // If not, let the user pick one from the prompt
457  // We don't pass the difficulties vector here because additional data is required
458  // to constrict the dialog
460  dlg.show();
461 
463 
465 }
466 
468 {
469  DBG_MP << "preparing mp_game_settings for saved game";
470 
472 
473  // The save might be a start-of-scenario save so make sure we have the scenario data loaded.
475  state_.mp_settings().saved_game = state_.is_mid_game_save() ? saved_game_mode::type::midgame : saved_game_mode::type::scenaro_start;
476 }
477 
479 {
480  DBG_MP << "prepare_for_other";
484 }
485 
486 void create_engine::apply_level_filter(const std::string& name)
487 {
488  level_name_filter_ = name;
490 }
491 
493 {
494  player_count_filter_ = players;
496 }
497 
499 {
500  for(auto& type : type_map_) {
501  type.second.reset_filter();
502  }
503 
504  level_name_filter_ = "";
505 }
506 
508 {
510 }
511 
513 {
515 }
516 
518 {
519  try {
520  current_level_index_ = type_map_.at(current_level_type_).games_filtered.at(index);
521  } catch (const std::out_of_range&) {
523  }
524 
525  if(current_level_type_ == level_type::type::random_map) {
526  random_map* current_random_map = dynamic_cast<random_map*>(&current_level());
527 
528  // If dynamic cast has failed then we somehow have gotten all the pointers mixed together.
529  assert(current_random_map);
530 
531  generator_.reset(current_random_map->create_map_generator());
532  } else {
533  generator_.reset(nullptr);
534  }
535 
537  dependency_manager_->try_scenario(current_level().id());
538  }
539 }
540 
541 void create_engine::set_current_era_index(const std::size_t index, bool force)
542 {
544 
545  dependency_manager_->try_era_by_index(index, force);
546 }
547 
548 void create_engine::set_current_era_id(const std::string& id, bool force)
549 {
550  std::size_t index = dependency_manager_->get_era_index(id);
551 
553 
554  dependency_manager_->try_era_by_index(index, force);
555 }
556 
557 bool create_engine::toggle_mod(const std::string& id, bool force)
558 {
559  force |= state_.classification().type != campaign_type::type::multiplayer;
560 
561  bool is_active = dependency_manager_->is_modification_active(id);
562  dependency_manager_->try_modification_by_id(id, !is_active, force);
563 
564  state_.classification().active_mods = dependency_manager_->get_modifications();
565 
566  return !is_active;
567 }
568 
570 {
571  return generator_ != nullptr;
572 }
573 
575 {
576  return generator_->allow_user_config();
577 }
578 
580 {
581  generator_->user_config();
582 }
583 
584 std::pair<level_type::type, int> create_engine::find_level_by_id(const std::string& id) const
585 {
586  for(const auto& type : type_map_) {
587  int i = 0;
588 
589  for(const auto& game : type.second.games) {
590  if(game->id() == id) {
591  return {type.first, i};
592  }
593 
594  i++;
595  }
596  }
597 
598  return {level_type::type::sp_campaign, -1};
599 }
600 
601 int create_engine::find_extra_by_id(const MP_EXTRA extra_type, const std::string& id) const
602 {
603  int i = 0;
604  for(extras_metadata_ptr extra : get_const_extras_by_type(extra_type)) {
605  if(extra->id == id) {
606  return i;
607  }
608  i++;
609  }
610 
611  return -1;
612 }
613 
615 {
616  state_.classification().active_mods = dependency_manager_->get_modifications();
617 }
618 
619 std::vector<std::string>& create_engine::active_mods()
620 {
622 }
623 
624 std::vector<create_engine::extras_metadata_ptr> create_engine::active_mods_data()
625 {
626  const std::vector<extras_metadata_ptr>& mods = get_const_extras_by_type(MP_EXTRA::MOD);
627 
628  std::vector<extras_metadata_ptr> data_vec;
629  std::copy_if(mods.begin(), mods.end(), std::back_inserter(data_vec), [this](const extras_metadata_ptr& mod) {
630  return dependency_manager_->is_modification_active(mod->id);
631  });
632 
633  return data_vec;
634 }
635 
637 {
638  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
639  return *eras_[era_index]->cfg;
640 }
641 
643 {
644  DBG_MP << "getting parameter values";
645 
646  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
647  state_.classification().era_id = eras_[era_index]->id;
648  state_.mp_settings().mp_era_name = eras_[era_index]->name;
649 
650  return state_.mp_settings();
651 }
652 
654 {
655  if(auto generic_multiplayer = game_config_.optional_child("generic_multiplayer")) {
656  config gen_mp_data = *generic_multiplayer;
657 
658  // User maps.
659  int dep_index_offset = 0;
660  for(std::size_t i = 0; i < user_map_names_.size(); i++)
661  {
662  config user_map_data = gen_mp_data;
663  user_map_data["map_data"] = filesystem::read_map(user_map_names_[i]);
664 
665  // Check if a file is actually a map.
666  // Note that invalid maps should be displayed in order to
667  // show error messages in the GUI.
668  bool add_map = true;
669  std::unique_ptr<gamemap> map;
670  try {
671  map.reset(new gamemap(user_map_data["map_data"]));
672  } catch (const incorrect_map_format_error& e) {
673  // Set map content to nullptr, so that it fails can_launch_game()
674  map.reset(nullptr);
675  user_map_data["description"] = _("Map could not be loaded: ") + e.message;
676 
677  ERR_CF << "map could not be loaded: " << e.message;
678  } catch (const wml_exception&) {
679  add_map = false;
680  dep_index_offset++;
681  }
682 
683  if(add_map) {
684  type_map_[level_type::type::user_map].games.emplace_back(new user_map(user_map_data, user_map_names_[i], map.get()));
685 
686  // Since user maps are treated as scenarios, some dependency info is required
687  config depinfo;
688  depinfo["id"] = user_map_names_[i];
689  depinfo["name"] = user_map_names_[i];
690  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset);
691  }
692  }
693 
694  // User made scenarios.
695  dep_index_offset = 0;
696  for(std::size_t i = 0; i < user_scenario_names_.size(); i++)
697  {
698  config data;
699  try {
700  // Only attempt to load .cfg files (.cfg extension is enforced in Editor save)
702  continue;
703 
705  } catch(const config::error & e) {
706  ERR_CF << "Caught a config error while parsing user made (editor) scenarios:\n" << e.message;
707  ERR_CF << "Skipping file: " << (filesystem::get_legacy_editor_dir() + "/scenarios/" + user_scenario_names_[i]);
708  continue;
709  }
710 
711  scenario_ptr new_scenario(new scenario(data));
712  if(new_scenario->id().empty()) continue;
713 
714  type_map_[level_type::type::user_scenario].games.push_back(std::move(new_scenario));
715 
716  // Since user scenarios are treated as scenarios, some dependency info is required
717  config depinfo;
718  depinfo["id"] = data["id"];
719  depinfo["name"] = data["name"];
720  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset++);
721  }
722  }
723 
724  // Stand-alone scenarios.
725  for(const config& data : game_config_.child_range("multiplayer"))
726  {
727  if(!data["allow_new_game"].to_bool(true))
728  continue;
729 
730  if(!data["campaign_id"].empty())
731  continue;
732 
733  if(data.has_attribute("map_generation") || data.has_attribute("scenario_generation")) {
734  type_map_[level_type::type::random_map].games.emplace_back(new random_map(data));
735  } else {
736  type_map_[level_type::type::scenario].games.emplace_back(new scenario(data));
737  }
738  }
739 
740  // Campaigns.
741  for(const config& data : game_config_.child_range("campaign"))
742  {
743  if(data["id"].empty()) {
744  if(data["name"].empty()) {
745  ERR_CF << "Found a [campaign] with neither a name nor an id attribute, ignoring it";
746  } else {
747  ERR_CF << "Ignoring a [campaign] with no id attribute, but name '" << data["name"] << "'";
748  }
749  continue;
750  }
751 
752  const std::string& type = data["type"];
753  const bool mp = state_.classification().is_multiplayer();
754 
755  if(type == "mp" || (type == "hybrid" && mp)) {
756  type_map_[level_type::type::campaign].games.emplace_back(new campaign(data));
757  }
758 
759  if(type == "sp" || type.empty() || (type == "hybrid" && !mp)) {
760  campaign_ptr new_sp_campaign(new campaign(data));
761  new_sp_campaign->mark_if_completed();
762 
763  type_map_[level_type::type::sp_campaign].games.push_back(std::move(new_sp_campaign));
764  }
765  }
766 
767  auto& sp_campaigns = type_map_[level_type::type::sp_campaign].games;
768 
769  // Sort sp campaigns by rank.
770  std::stable_sort(sp_campaigns.begin(), sp_campaigns.end(),
772  return a->data()["rank"].to_int(1000) < b->data()["rank"].to_int(1000);
773  }
774  );
775 }
776 
777 void create_engine::init_extras(const MP_EXTRA extra_type)
778 {
779  std::vector<extras_metadata_ptr>& extras = get_extras_by_type(extra_type);
780  const std::string extra_name = (extra_type == ERA) ? "era" : "modification";
781 
782  component_availability::type default_availabilty = (extra_type == ERA)
783  ? component_availability::type::mp
784  : component_availability::type::hybrid;
785 
786  std::set<std::string> found_ids;
787  for(const config& extra : game_config_.child_range(extra_name))
788  {
789  component_availability::type type = component_availability::get_enum(extra["type"].str()).value_or(default_availabilty);
790  const bool mp = state_.classification().is_multiplayer();
791 
792  if((type != component_availability::type::mp || mp) && (type != component_availability::type::sp || !mp) )
793  {
794  if(found_ids.insert(extra["id"]).second) {
795  extras_metadata_ptr new_extras_metadata(new extras_metadata());
796  new_extras_metadata->id = extra["id"].str();
797  new_extras_metadata->name = extra["name"].str();
798  new_extras_metadata->description = extra["description"].str();
799  new_extras_metadata->cfg = &extra;
800 
801  extras.push_back(std::move(new_extras_metadata));
802  }
803  else {
804  ERR_CF << "found " << extra_name << " with id=" << extra["id"] << " twice";
805  }
806  }
807  }
808 }
809 
811 {
812  for(auto& type : type_map_) {
813  type.second.apply_filter(player_count_filter_, level_name_filter_);
814  }
815 }
816 
817 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type_unfiltered(level_type::type type) const
818 {
819  std::vector<level_ptr> levels;
820  for(const level_ptr& lvl : type_map_.at(type).games) {
821  levels.push_back(lvl);
822  }
823 
824  return levels;
825 }
826 
827 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type(level_type::type type) const
828 {
829  auto& g_list = type_map_.at(type);
830 
831  std::vector<level_ptr> levels;
832  for(std::size_t level : g_list.games_filtered) {
833  levels.push_back(g_list.games[level]);
834  }
835 
836  return levels;
837 }
838 
840 {
841  return type_map_.at(type).games_filtered;
842 }
843 
844 const std::vector<create_engine::extras_metadata_ptr>&
846 {
847  return (extra_type == ERA) ? eras_ : mods_;
848 }
849 
850 std::vector<create_engine::extras_metadata_ptr>&
852 {
853  return (extra_type == ERA) ? eras_ : mods_;
854 }
855 
857 {
858  return state_;
859 }
860 
861 } // 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:1030
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:450
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:1261
config read(std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627
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:178
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