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