The Battle for Wesnoth  1.19.17+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";
493 }
494 
495 void create_engine::apply_level_filter(const std::string& name)
496 {
497  level_name_filter_ = name;
499 }
500 
502 {
503  player_count_filter_ = players;
505 }
506 
508 {
509  for(auto& type : type_map_) {
510  type.second.reset_filter();
511  }
512 
513  level_name_filter_ = "";
514 }
515 
517 {
519 }
520 
522 {
524 }
525 
527 {
528  try {
529  current_level_index_ = type_map_.at(current_level_type_).games_filtered.at(index);
530  } catch (const std::out_of_range&) {
532  }
533 
534  if(current_level_type_ == level_type::type::random_map) {
535  random_map* current_random_map = dynamic_cast<random_map*>(&current_level());
536 
537  // If dynamic cast has failed then we somehow have gotten all the pointers mixed together.
538  assert(current_random_map);
539 
540  generator_.reset(current_random_map->create_map_generator());
541  } else {
542  generator_.reset(nullptr);
543  }
544 
546  dependency_manager_->try_scenario(current_level().id());
547  }
548 }
549 
550 void create_engine::set_current_era_index(const std::size_t index, bool force)
551 {
553 
554  dependency_manager_->try_era_by_index(index, force);
555 }
556 
557 void create_engine::set_current_era_id(const std::string& id, bool force)
558 {
559  std::size_t index = dependency_manager_->get_era_index(id);
560 
562 
563  dependency_manager_->try_era_by_index(index, force);
564 }
565 
566 bool create_engine::toggle_mod(const std::string& id, bool force)
567 {
568  force |= state_.classification().type != campaign_type::type::multiplayer;
569 
570  bool is_active = dependency_manager_->is_modification_active(id);
571  dependency_manager_->try_modification_by_id(id, !is_active, force);
572 
573  state_.classification().active_mods = dependency_manager_->get_modifications();
574 
575  return !is_active;
576 }
577 
579 {
580  return generator_ != nullptr;
581 }
582 
584 {
585  return generator_->allow_user_config();
586 }
587 
589 {
590  generator_->user_config();
591 }
592 
593 std::pair<level_type::type, int> create_engine::find_level_by_id(const std::string& id) const
594 {
595  for(const auto& type : type_map_) {
596  int i = 0;
597 
598  for(const auto& game : type.second.games) {
599  if(game->id() == id) {
600  return {type.first, i};
601  }
602 
603  i++;
604  }
605  }
606 
607  return {level_type::type::sp_campaign, -1};
608 }
609 
610 int create_engine::find_extra_by_id(const MP_EXTRA extra_type, const std::string& id) const
611 {
612  int i = 0;
613  for(extras_metadata_ptr extra : get_const_extras_by_type(extra_type)) {
614  if(extra->id == id) {
615  return i;
616  }
617  i++;
618  }
619 
620  return -1;
621 }
622 
624 {
625  state_.classification().active_mods = dependency_manager_->get_modifications();
626 }
627 
628 std::vector<std::string>& create_engine::active_mods()
629 {
631 }
632 
633 std::vector<create_engine::extras_metadata_ptr> create_engine::active_mods_data()
634 {
635  const std::vector<extras_metadata_ptr>& mods = get_const_extras_by_type(MP_EXTRA::MOD);
636 
637  std::vector<extras_metadata_ptr> data_vec;
638  std::copy_if(mods.begin(), mods.end(), std::back_inserter(data_vec), [this](const extras_metadata_ptr& mod) {
639  return dependency_manager_->is_modification_active(mod->id);
640  });
641 
642  return data_vec;
643 }
644 
646 {
647  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
648  return *eras_[era_index]->cfg;
649 }
650 
652 {
653  DBG_MP << "getting parameter values";
654 
655  int era_index = current_level().allow_era_choice() ? current_era_index_ : 0;
656  state_.classification().era_id = eras_[era_index]->id;
657  state_.mp_settings().mp_era_name = eras_[era_index]->name;
658 
659  return state_.mp_settings();
660 }
661 
663 {
664  if(auto generic_multiplayer = game_config_.optional_child("generic_multiplayer")) {
665  config gen_mp_data = *generic_multiplayer;
666 
667  // User maps.
668  int dep_index_offset = 0;
669  for(std::size_t i = 0; i < user_map_names_.size(); i++)
670  {
671  config user_map_data = gen_mp_data;
672  user_map_data["map_data"] = filesystem::read_map(user_map_names_[i]);
673 
674  // Check if a file is actually a map.
675  // Note that invalid maps should be displayed in order to
676  // show error messages in the GUI.
677  bool add_map = true;
678  std::unique_ptr<gamemap> map;
679  try {
680  map.reset(new gamemap(user_map_data["map_data"]));
681  } catch (const incorrect_map_format_error& e) {
682  // Set map content to nullptr, so that it fails can_launch_game()
683  map.reset(nullptr);
684  user_map_data["description"] = _("Map could not be loaded: ") + e.message;
685 
686  ERR_CF << "map could not be loaded: " << e.message;
687  } catch (const wml_exception&) {
688  add_map = false;
689  dep_index_offset++;
690  }
691 
692  if(add_map) {
693  type_map_[level_type::type::user_map].games.emplace_back(new user_map(user_map_data, user_map_names_[i], map.get()));
694 
695  // Since user maps are treated as scenarios, some dependency info is required
696  config depinfo;
697  depinfo["id"] = user_map_names_[i];
698  depinfo["name"] = user_map_names_[i];
699  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset);
700  }
701  }
702 
703  // User made scenarios.
704  dep_index_offset = 0;
705  for(std::size_t i = 0; i < user_scenario_names_.size(); i++)
706  {
707  config data;
708  try {
709  // Only attempt to load .cfg files (.cfg extension is enforced in Editor save)
711  continue;
712 
714  } catch(const config::error & e) {
715  ERR_CF << "Caught a config error while parsing user made (editor) scenarios:\n" << e.message;
716  ERR_CF << "Skipping file: " << (filesystem::get_legacy_editor_dir() + "/scenarios/" + user_scenario_names_[i]);
717  continue;
718  }
719 
720  scenario_ptr new_scenario(new scenario(data));
721  if(new_scenario->id().empty()) continue;
722 
723  type_map_[level_type::type::user_scenario].games.push_back(std::move(new_scenario));
724 
725  // Since user scenarios are treated as scenarios, some dependency info is required
726  config depinfo;
727  depinfo["id"] = data["id"];
728  depinfo["name"] = data["name"];
729  dependency_manager_->insert_element(depcheck::SCENARIO, depinfo, i - dep_index_offset++);
730  }
731  }
732 
733  // Stand-alone scenarios.
734  for(const config& data : game_config_.child_range("multiplayer"))
735  {
736  if(!data["allow_new_game"].to_bool(true))
737  continue;
738 
739  if(!data["campaign_id"].empty())
740  continue;
741 
742  if(data.has_attribute("map_generation") || data.has_attribute("scenario_generation")) {
743  type_map_[level_type::type::random_map].games.emplace_back(new random_map(data));
744  } else {
745  type_map_[level_type::type::scenario].games.emplace_back(new scenario(data));
746  }
747  }
748 
749  // Presets.
750  for(const config& preset : prefs::get().get_game_presets()) {
751  optional_const_config data = game_config_.find_child("multiplayer", "id", preset["scenario"].str());
752 
753  if(data) {
754  type_map_[level_type::type::preset].games.emplace_back(new scenario(*data));
755  preset_ids_.emplace_back(preset["id"].to_int());
756  }
757  }
758 
759  // Campaigns.
760  for(const config& data : game_config_.child_range("campaign"))
761  {
762  if(data["id"].empty()) {
763  if(data["name"].empty()) {
764  ERR_CF << "Found a [campaign] with neither a name nor an id attribute, ignoring it";
765  } else {
766  ERR_CF << "Ignoring a [campaign] with no id attribute, but name '" << data["name"] << "'";
767  }
768  continue;
769  }
770 
771  const std::string& type = data["type"];
772  const bool mp = state_.classification().is_multiplayer();
773 
774  if(type == "mp" || (type == "hybrid" && mp)) {
775  type_map_[level_type::type::campaign].games.emplace_back(new campaign(data));
776  }
777 
778  if(type == "sp" || type.empty() || (type == "hybrid" && !mp)) {
779  campaign_ptr new_sp_campaign(new campaign(data));
780  new_sp_campaign->mark_if_completed();
781 
782  type_map_[level_type::type::sp_campaign].games.push_back(std::move(new_sp_campaign));
783  }
784  }
785 
786  auto& sp_campaigns = type_map_[level_type::type::sp_campaign].games;
787 
788  // Sort sp campaigns by rank.
789  std::stable_sort(sp_campaigns.begin(), sp_campaigns.end(),
791  return a->data()["rank"].to_int(1000) < b->data()["rank"].to_int(1000);
792  }
793  );
794 }
795 
796 void create_engine::init_extras(const MP_EXTRA extra_type)
797 {
798  std::vector<extras_metadata_ptr>& extras = get_extras_by_type(extra_type);
799  const std::string extra_name = (extra_type == ERA) ? "era" : "modification";
800 
801  component_availability::type default_availabilty = (extra_type == ERA)
802  ? component_availability::type::mp
803  : component_availability::type::hybrid;
804 
805  std::set<std::string> found_ids;
806  for(const config& extra : game_config_.child_range(extra_name))
807  {
808  component_availability::type type = component_availability::get_enum(extra["type"].str()).value_or(default_availabilty);
809  const bool mp = state_.classification().is_multiplayer();
810 
811  if((type != component_availability::type::mp || mp) && (type != component_availability::type::sp || !mp) )
812  {
813  if(found_ids.insert(extra["id"]).second) {
814  extras_metadata_ptr new_extras_metadata(new extras_metadata());
815  new_extras_metadata->id = extra["id"].str();
816  new_extras_metadata->name = extra["name"].str();
817  new_extras_metadata->description = extra["description"].str();
818  new_extras_metadata->cfg = &extra;
819 
820  extras.push_back(std::move(new_extras_metadata));
821  }
822  else {
823  ERR_CF << "found " << extra_name << " with id=" << extra["id"] << " twice";
824  }
825  }
826  }
827 }
828 
830 {
831  for(auto& type : type_map_) {
832  type.second.apply_filter(player_count_filter_, level_name_filter_);
833  }
834 }
835 
836 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type_unfiltered(level_type::type type) const
837 {
838  std::vector<level_ptr> levels;
839  for(const level_ptr& lvl : type_map_.at(type).games) {
840  levels.push_back(lvl);
841  }
842 
843  return levels;
844 }
845 
846 std::vector<create_engine::level_ptr> create_engine::get_levels_by_type(level_type::type type) const
847 {
848  auto& g_list = type_map_.at(type);
849 
850  std::vector<level_ptr> levels;
851  for(std::size_t level : g_list.games_filtered) {
852  levels.push_back(g_list.games[level]);
853  }
854 
855  return levels;
856 }
857 
859 {
860  return type_map_.at(type).games_filtered;
861 }
862 
863 const std::vector<create_engine::extras_metadata_ptr>&
865 {
866  return (extra_type == ERA) ? eras_ : mods_;
867 }
868 
869 std::vector<create_engine::extras_metadata_ptr>&
871 {
872  return (extra_type == ERA) ? eras_ : mods_;
873 }
874 
876 {
877  return state_;
878 }
879 
880 } // end namespace ng
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
config & add_child(std::string_view key)
Definition: config.cpp:435
child_itors child_range(std::string_view key)
Definition: config.cpp:267
bool has_attribute(std::string_view key) const
Definition: config.cpp:156
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
Definition: config.cpp:311
bool empty() const
Definition: config.cpp:822
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:361
void clear()
Definition: config.cpp:801
std::string hash() const
Definition: config.cpp:1256
std::vector< std::string > campaign_xtra_defines
more customization of data
std::vector< std::string > mod_defines
If there are defines the modifications use to customize data.
std::string scenario_define
If there is a define the scenario uses to customize data.
std::string difficulty
The difficulty level the game is being played on.
std::string era_define
If there is a define the era uses to customize data.
std::chrono::milliseconds end_text_duration
for how long the end-of-campaign text is shown
std::vector< std::string > active_mods
campaign_type::type type
bool end_credits
whether to show the standard credits at the end
std::string campaign_define
If there is a define the campaign uses to customize data.
std::string campaign
The id of the campaign being played.
std::string abbrev
the campaign abbreviation
std::string end_text
end-of-campaign text
std::string campaign_name
The name of the campaign being played.
static game_config_manager * get()
void load_game_config_for_game(const game_classification &classification, const std::string &scenario_id)
void load_game_config_for_create(bool is_mp, bool is_test=false)
config_array_view child_range(std::string_view key) const
const config & find_mandatory_child(std::string_view key, const std::string &name, const std::string &value) const
optional_const_config optional_child(std::string_view key) const
optional_const_config find_child(std::string_view key, const std::string &name, const std::string &value) const
Encapsulates the map of the game.
Definition: map.hpp: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