The Battle for Wesnoth  1.19.0-dev
saved_game.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * Some information about savefiles:
17  *
18  * A savefile can contain:
19  *
20  * - General information (toplevel attributes, [multiplayer])
21  * This is present in all savefiles
22  *
23  * - [statistics]
24  * This is present in all savefiles.
25  *
26  * - [snapshot]
27  * If a savegame was saved during a scenario this contains a snapshot of the game at the point when
28  * it was saved.
29  *
30  * - [carryover_sides_start]
31  * At start-of-scenario saves this contains data from the previous scenario that was preserved.
32  *
33  * - [carryover_sides]
34  * In savefile made during the game, this tag contains data from [carryover_sides_start] that was not
35  * used in the current scenario but should be saved for a next scenario
36  *
37  * - [replay_start]
38  * A snapshot made very early to replay the game from.
39  *
40  * - [replay]
41  * A record of game actions that was made between the creation of [replay_start] and [snapshot].
42  *
43  *
44  * The following types of savegames are known:
45  *
46  * - Start of scenario savefiles
47  * These files only contain general information, statistics, and [carryover_sides_start]. When these
48  * saves are loaded, the scenario data is loaded form the game config using the next_scenario attribute
49  * from [carryover_sides_start].
50  *
51  * - Expanded Start of scenario savefiles
52  * Similar to normal Start-of-scenario savefiles, but the also contain a [scenario] that contains the
53  * scenario data. This type is only used internally and usually doesn't get written to the disk.
54  *
55  * - In-game savefile
56  * These files contain general information, statistics, [snapshot], [replay], [replay_start], [snapshot],
57  * and [carryover_sides]. These files don't contain a [carryover_sides_start] because both starting points
58  * ([replay_start] and [snapshot]) were made after [carryover_sides_start] was merged into the scenario.
59  *
60  * - Replay savefiles
61  * Like a in-game save made during linger mode, but without the [snapshot].
62  */
63 
64 #include "saved_game.hpp"
65 
66 #include "carryover.hpp"
67 #include "config.hpp"
68 #include "cursor.hpp"
69 #include "formula/string_utils.hpp"
70 #include "game_config_manager.hpp"
72 #include "log.hpp"
73 #include "random.hpp"
75 #include "side_controller.hpp"
76 #include "statistics.hpp"
77 #include "variable.hpp" // for config_variable_set
78 #include "variable_info.hpp"
79 
80 #include <cassert>
81 #include <iomanip>
82 
83 static lg::log_domain log_engine("engine");
84 #define ERR_NG LOG_STREAM(err, log_engine)
85 #define WRN_NG LOG_STREAM(warn, log_engine)
86 #define LOG_NG LOG_STREAM(info, log_engine)
87 #define DBG_NG LOG_STREAM(debug, log_engine)
88 
89 namespace
90 {
91 bool variable_to_bool(const config& vars, const std::string& expression)
92 {
93  std::string res = utils::interpolate_variables_into_string(expression, config_variable_set(vars));
94  return res == "true" || res == "yes" || res == "1";
95 }
96 
97 // helper objects for saved_game::expand_mp_events()
98 struct modevents_entry
99 {
100  modevents_entry(const std::string& _type, const std::string& _id)
101  : type(_type)
102  , id(_id)
103  {
104  }
105 
106  std::string type;
107  std::string id;
108 };
109 
110 bool is_illegal_file_char(char c)
111 {
112  return c == '/' || c == '\\' || c == ':' || (c >= 0x00 && c < 0x20)
113 #ifdef _WIN32
114  || c == '?' || c == '|' || c == '<' || c == '>' || c == '*' || c == '"'
115 #endif
116  ;
117 }
118 
119 } // end anon namespace
120 
122  : has_carryover_expanded_(false)
123  , carryover_(carryover_info().to_config())
124  , replay_start_()
125  , classification_()
126  , mp_settings_()
127  , starting_point_type_(starting_point::NONE)
128  , starting_point_()
129  , replay_data_()
130  , statistics_()
131  , skip_story_(false)
132 {
133 }
134 
136  : has_carryover_expanded_(false)
137  , carryover_()
138  , replay_start_()
139  , classification_(cfg)
140  , mp_settings_()
141  , starting_point_type_(starting_point::NONE)
142  , starting_point_()
143  , replay_data_()
144  , statistics_()
145  , skip_story_(false)
146 {
147  set_data(cfg);
148 }
149 
151  : has_carryover_expanded_(state.has_carryover_expanded_)
152  , carryover_(state.carryover_)
153  , replay_start_(state.replay_start_)
154  , classification_(state.classification_)
155  , mp_settings_(state.mp_settings_)
156  , starting_point_type_(state.starting_point_type_)
157  , starting_point_(state.starting_point_)
158  , replay_data_(state.replay_data_)
159  , statistics_(state.statistics_)
160  , skip_story_(state.skip_story_)
161 {
162 }
163 
165 {
166  carryover_.swap(carryover_sides_start);
167  has_carryover_expanded_ = false;
168 }
169 
171 {
172  if(has_carryover_expanded_ || !carryover_["random_seed"].empty()) {
173  return;
174  }
175 
176  std::stringstream stream;
177  stream << std::setfill('0') << std::setw(8) << std::hex << randomness::generator->get_random_int(0, INT_MAX);
178  carryover_["random_seed"] = stream.str();
179  carryover_["random_calls"] = 0;
180 }
181 
183 {
184  write_general_info(out);
186 
187  if(!replay_start_.empty()) {
188  out.write_child("replay_start", replay_start_);
189  }
190 
191  out.open_child("replay");
192  replay_data_.write(out);
193  out.close_child("replay");
194  write_carryover(out);
195 }
196 
198 {
200  out.write_child("snapshot", starting_point_);
202  out.write_child("scenario", starting_point_);
203  }
204 }
205 
207 {
208  assert(not_corrupt());
209  out.write_child(has_carryover_expanded_ ? "carryover_sides" : "carryover_sides_start", carryover_);
210 }
211 
213 {
215  out.write_child("multiplayer", mp_settings_.to_config());
216  out.open_child("statistics");
217  statistics().write(out);
218  out.close_child("statistics");
219 }
220 
222 {
223  const bool is_loaded_game = starting_point_type_ != starting_point::SCENARIO;
224  const bool is_multiplayer_tag = classification().get_tagname() == "multiplayer";
226 
227  static const std::vector<std::string> team_defaults {
228  "carryover_percentage",
229  "carryover_add",
230  };
231 
232  if(auto campaign = game_config.find_child("campaign", "id", classification_.campaign)) {
233  // FIXME: The mp code could use `require_scenario` to check whether we have the addon in question installed.
234  // But since [scenario]s are usually hidden behind `#ifdef CAMPAIGN_DEFINE` it would not be able to find them.
235  // Investigate how this should actually work.
236  bool require_campaign = campaign["require_campaign"].to_bool(true);
237  starting_point_["require_scenario"] = require_campaign;
238  }
239 
240  for(config& side : starting_point_.child_range("side")) {
241  // Set save_id default value directly after loading to its default to prevent different default behaviour in
242  // mp_connect code and sp code.
243 
244  if(side["no_leader"].to_bool()) {
245  side["leader_lock"] = true;
246  side.remove_attribute("type");
247  }
248 
249  if(side["save_id"].empty()) {
250  side["save_id"] = side["id"];
251  }
252  if(side["save_id"].empty()) {
253  side["save_id"] = side.child_or_empty("leader")["id"];
254  }
255 
256  if(!is_multiplayer_tag) {
257  if(side["name"].blank()) {
258  side["name"] = side.child_or_empty("leader")["name"];
259  }
260  if(side["side_name"].blank()) {
261  side["side_name"] = side["name"];
262  }
263  }
264 
265  if(!is_loaded_game && !side["current_player"].empty()) {
266  ERR_NG << "Removed invalid 'current_player' attribute from [side] while loading a scenario. Consider using "
267  "'side_name' instead";
268 
269  side["current_player"] = config::attribute_value();
270  }
271 
272  // Set some team specific values to their defaults specified in scenario
273  for(const std::string& att_name : team_defaults) {
274  const config::attribute_value* scenario_value = starting_point_.get(att_name);
275  config::attribute_value& team_value = side[att_name];
276 
277  if(scenario_value && team_value.empty()) {
278  team_value = *scenario_value;
279  }
280  }
281  }
282 }
283 
285 {
288 
290  auto scenario =
291  game_config.find_child(classification().get_tagname(), "id", carryover_["next_scenario"]);
292 
293  if(scenario) {
295  starting_point_ = *scenario;
296 
297  // A hash has to be generated using an unmodified scenario data.
298  mp_settings_.hash = scenario->hash();
299 
301 
302  update_label();
303  set_defaults();
304  } else {
305  ERR_NG << "Couldn't find [" << classification().get_tagname() << "] with id=" << carryover_["next_scenario"];
308  }
309  }
310 }
311 
313 {
314  const std::string version_default = starting_point_["addon_id"].empty() ? game_config::wesnoth_version.str() : "";
315  config scenario;
316  scenario["id"] = starting_point_["addon_id"].str("mainline");
317  scenario["name"] = starting_point_["addon_title"].str("mainline");
318  scenario["version"] = starting_point_["addon_version"].str(version_default);
319  scenario["min_version"] = starting_point_["addon_min_version"];
320  scenario["required"] = starting_point_["require_scenario"].to_bool(false);
321  config& content = scenario.add_child("content");
322  content["id"] = starting_point_["id"];
323  content["name"] = starting_point_["name"];
324  // TODO: would it be better if this used the actual tagname ([multiplayer]/[scenario]) instead of always using [scenario]?
325  content["type"] = "scenario";
326 
328 }
329 
330 // "non scenario" at the time of writing this meaning any era, campaign, mods, or resources (see expand_mp_events() below).
331 void saved_game::load_non_scenario(const std::string& type, const std::string& id, size_t pos)
332 {
333  if(auto cfg = game_config_manager::get()->game_config().find_child(type, "id", id)) {
334  // Note the addon_id if this mod is required to play the game in mp.
335  std::string require_attr = "require_" + type;
336 
337  // anything with no addon_id is from mainline, and therefore isn't required in the sense that all players already have it
338  const std::string version_default = cfg["addon_id"].empty() ? game_config::wesnoth_version.str() : "";
339  config non_scenario;
340  // if there's no addon_id, then this isn't an add-on
341  non_scenario["id"] = cfg["addon_id"].str("mainline");
342  non_scenario["name"] = cfg["addon_title"].str("mainline");
343  non_scenario["version"] = cfg["addon_version"].str(version_default);
344  non_scenario["min_version"] = cfg["addon_min_version"];
345  non_scenario["required"] = cfg[require_attr].to_bool(!cfg["addon_id"].empty());
346  config& content = non_scenario.add_child("content");
347  content["id"] = id;
348  content["name"] = cfg["addon_title"].str(cfg["name"].str(""));
349  content["type"] = type;
350 
352 
353  // Copy events
354  for(const config& modevent : cfg->child_range("event")) {
355  if(modevent["enable_if"].empty()
356  || variable_to_bool(carryover_.child_or_empty("variables"), modevent["enable_if"])
357  ) {
358  starting_point_.add_child_at_total("event", modevent, pos++);
359  }
360  }
361 
362  // Copy lua
363  for(const config& modlua : cfg->child_range("lua")) {
364  starting_point_.add_child_at_total("lua", modlua, pos++);
365  }
366 
367  // Copy modify_unit_type
368  for(const config& modlua : cfg->child_range("modify_unit_type")) {
369  starting_point_.add_child_at_total("modify_unit_type", modlua, pos++);
370  }
371 
372  // Copy load_resource
373  for(const config& load_resource : cfg->child_range("load_resource")) {
374  starting_point_.add_child_at_total("load_resource", load_resource, pos++);
375  }
376  } else {
377  // TODO: A user message instead?
378  ERR_NG << "Couldn't find [" << type << "] with id=" << id;
379  }
380 }
381 
382 // Gets the ids of the mp_era and modifications which were set to be active, then fetches these configs from the
383 // game_config and copies their [event] and [lua] to the starting_point_.
384 // At this time, also collect the addon_id attributes which appeared in them and put this list in the addon_ids
385 // attribute of the mp_settings.
387 {
388  expand_scenario();
389 
390  if(starting_point_type_ == starting_point::SCENARIO && !starting_point_["has_mod_events"].to_bool(false)) {
391  std::vector<modevents_entry> mods;
392  std::set<std::string> loaded_resources;
393 
394  std::transform(classification_.active_mods.begin(), classification_.active_mods.end(), std::back_inserter(mods),
395  [](const std::string& id) { return modevents_entry("modification", id); }
396  );
397 
398  // We don't want the error message below if there is no era (= if this is a sp game).
399  if(!classification_.era_id.empty()) {
400  mods.emplace_back("era", classification_.era_id);
401  }
402 
403  if(!classification_.campaign.empty()) {
404  mods.emplace_back("campaign", classification_.campaign);
405  }
406 
407  for(modevents_entry& mod : mods) {
409  }
410  mods.clear();
411 
412  while(starting_point_.has_child("load_resource")) {
413  assert(starting_point_.child_count("load_resource") > 0);
414  std::string id = starting_point_.mandatory_child("load_resource")["id"];
415  size_t pos = starting_point_.find_total_first_of("load_resource");
416  starting_point_.remove_child("load_resource", 0);
417  if(loaded_resources.find(id) == loaded_resources.end()) {
418  loaded_resources.insert(id);
419  load_non_scenario("resource", id, pos);
420  }
421  }
422  starting_point_["has_mod_events"] = true;
423  starting_point_["loaded_resources"] = utils::join(loaded_resources);
424  }
425 }
426 
428 {
430  std::vector<modevents_entry> mods;
431 
432  std::transform(classification_.active_mods.begin(), classification_.active_mods.end(), std::back_inserter(mods),
433  [](const std::string& id) { return modevents_entry("modification", id); }
434  );
435 
436  mods.emplace_back("era", classification_.era_id);
437  mods.emplace_back("multiplayer", get_scenario_id());
438  mods.emplace_back("campaign", classification().campaign);
439 
440  config& variables = carryover_.child_or_add("variables");
441 
442  for(modevents_entry& mod : mods) {
443  if(auto cfg = mp_settings().options.find_child(mod.type, "id", mod.id)) {
444  for(const config& option : cfg->child_range("option")) {
445  try {
446  variable_access_create(option["id"], variables).as_scalar() = option["value"];
447  } catch(const invalid_variablename_exception&) {
448  ERR_NG << "variable " << option["id"] << "cannot be set to " << option["value"];
449  }
450  }
451  } else {
452  LOG_NG << "Couldn't find [" << mod.type << "] with id=" << mod.id << " for [option]s";
453  }
454  }
455  }
456 }
457 
458 static void inherit_scenario(config& scenario, config& map_scen)
459 {
460  config& map_scenario = map_scen.has_child("multiplayer") ? map_scen.mandatory_child("multiplayer") : (map_scen.has_child("scenario") ? map_scen.mandatory_child("scenario") : map_scen);
461  config sides;
462  sides.splice_children(map_scenario, "side");
463  scenario.append_children(map_scenario);
464  scenario.inherit_attributes(map_scenario);
465  for(config& side_from : sides.child_range("side")) {
466  auto side_to = scenario.find_child("side", "side", side_from["side"]);
467  if(side_to) {
468  side_to->inherit_attributes(side_from);
469  side_to->append_children(side_from);
470  } else {
471  scenario.add_child("side", side_from);
472  }
473  }
474 }
475 
477 {
478  if(!scenario["include_file"].empty()) {
479  std::string include_data = filesystem::read_scenario(scenario["include_file"]);
480  if(!include_data.empty()) {
481  config include_data_cfg;
482  read(include_data_cfg, include_data);
483  inherit_scenario(scenario, include_data_cfg);
484  }
485  // this method gets called two additional times, so without this you end up calling inherit_scenario() three times total
486  // this is equivalent to the below check for map_data being empty
487  scenario["include_file"] = "";
488  }
489 
490  if(scenario["map_data"].empty() && !scenario["map_file"].empty()) {
491  std::string map_data = filesystem::read_map(scenario["map_file"]);
492  if(map_data.find("map_data") != std::string::npos) {
493  // we have a scenario, generated by the editor
494  deprecated_message("map_file cfg", DEP_LEVEL::FOR_REMOVAL, "1.19", "Providing a .cfg file to the map_file attribute is deprecated. Use map_file for .map files and include_file for .cfg files.");
495  config map_data_cfg;
496  read(map_data_cfg, map_data);
497  inherit_scenario(scenario, map_data_cfg);
498  } else {
499  // we have an plain map_data file
500  scenario["map_data"] = map_data;
501  }
502  }
503 }
504 
506 {
507  expand_scenario();
508 
510  // If the entire scenario should be randomly generated
511  if(!starting_point_["scenario_generation"].empty()) {
512  LOG_NG << "randomly generating scenario...";
513  const cursor::setter cursor_setter(cursor::WAIT);
514 
515  config scenario_new =
516  random_generate_scenario(starting_point_["scenario_generation"], starting_point_.mandatory_child("generator"), &carryover_.child_or_empty("variables"));
517 
519  starting_point_ = std::move(scenario_new);
520 
521  update_label();
522  set_defaults();
523  }
524 
525  // If no map_data is provided, try to load the specified file directly
527  // If the map should be randomly generated
528  // We don’t want that we accidentally to this twice so we check for starting_point_["map_data"].empty()
529  if(starting_point_["map_data"].empty() && !starting_point_["map_generation"].empty()) {
530  LOG_NG << "randomly generating map...";
531  const cursor::setter cursor_setter(cursor::WAIT);
532 
533  starting_point_["map_data"] =
534  random_generate_map(starting_point_["map_generation"], starting_point_.mandatory_child("generator"), &carryover_.child_or_empty("variables"));
535  }
536  }
537 }
538 
539 void saved_game::post_scenario_generation(const config& old_scenario, config& generated_scenario)
540 {
541  static const std::vector<std::string> attributes_to_copy {
542  "id",
543  "addon_id",
544  "addon_title",
545  "addon_version",
546  "addon_min_version",
547  "require_scenario",
548  };
549 
550  // TODO: should we add "description" to this list?
551  // TODO: in theory it is possible that whether the scenario is required depends on the generated scenario, so maybe remove require_scenario from this list.
552 
553  for(const auto& str : attributes_to_copy) {
554  generated_scenario[str] = old_scenario[str];
555  }
556 
557  // Preserve "story" from the scenario toplevel.
558  // Note that it does not delete [story] tags in generated_scenario, so you can still have your story
559  // dependent on the generated scenario.
560  for(const config& story : old_scenario.child_range("story")) {
561  generated_scenario.add_child("story", story);
562  }
563 }
564 
565 
567 {
568  expand_scenario();
570  carryover_info sides(carryover_);
571 
573  for(config& side_cfg : get_starting_point().child_range("side")) {
574  sides.transfer_all_to(side_cfg);
575  }
576 
577  carryover_ = sides.to_config();
580  }
581 }
582 
583 bool saved_game::valid() const
584 {
586 }
587 
589 {
591  starting_point_.swap(snapshot);
592 
593  return starting_point_;
594 }
595 
597 {
599  starting_point_.swap(scenario);
600 
601  has_carryover_expanded_ = false;
602 
603  update_label();
604 }
605 
607 {
610 }
611 
613 {
614  return starting_point_;
615 }
616 
618 {
619  if(!replay_start_.empty()) {
620  return replay_start_;
621  }
622 
624  // Try to load the scenario form game config or from [scenario] if there is no [replay_start]
625  expand_scenario();
627  }
628 
630  return starting_point_;
631  }
632 
633  throw config::error("No replay_start found");
634 }
635 
637 {
639 
640  carryover_info sides(starting_point_, true);
641 
643  sides.rng().rotate_random();
644 
645  carryover_ = sides.to_config();
646 
647  has_carryover_expanded_ = false;
648 
651 
652  remove_snapshot();
653 }
654 
656 {
657  // TODO: remove this code duplication with write_... functions.
659 
660  if(!replay_start_.empty()) {
661  r.add_child("replay_start", replay_start_);
662  }
663 
664  replay_data_.write(r.add_child("replay"));
665 
667  r.add_child("snapshot", starting_point_);
669  r.add_child("scenario", starting_point_);
670  }
671 
672  r.add_child(has_carryover_expanded_ ? "carryover_sides" : "carryover_sides_start", carryover_);
673  r.add_child("multiplayer", mp_settings_.to_config());
674  r.add_child("statistics", statistics_.to_config());
675 
676  return r;
677 }
678 
679 std::string saved_game::get_scenario_id() const
680 {
681  std::string scenario_id;
682 
684  scenario_id = starting_point_["id"].str();
685  } else if(!has_carryover_expanded_) {
686  scenario_id = carryover_["next_scenario"].str();
687  } else if(!replay_start_.empty()) {
688  scenario_id = replay_start_["id"].str();
689  } else {
690  assert(!"cannot figure out scenario_id");
691  throw "assertion ignored";
692  }
693 
694  return scenario_id == "null" ? "" : scenario_id;
695 }
696 
698 {
699  return true;
700 }
701 
703 {
704  std::string& label = classification().label;
705 
706  if(classification().abbrev.empty()) {
707  label = starting_point_["name"].str();
708  } else {
709  label = classification().abbrev + "-" + starting_point_["name"];
710  }
711 
712  label.erase(std::remove_if(label.begin(), label.end(), is_illegal_file_char), label.end());
713  std::replace(label.begin(), label.end(), '_', ' ');
714 }
715 
717 {
718  for(config& side : starting_point_.child_range("side")) {
719  // for humans "goto_x/y" is used for multi-turn-moves
720  // for the ai "goto_x/y" is a way for wml to order the ai to move a unit to a certain place.
721  // we want to cancel human order but not to break wml.
722  if(side["controller"] != side_controller::human) {
723  continue;
724  }
725 
726  for(config& unit : side.child_range("unit")) {
727  unit["goto_x"] = -999;
728  unit["goto_y"] = -999;
729  }
730  }
731 }
732 
734 {
735  for(config& side : starting_point_.child_range("side")) {
736  side.remove_attribute("is_local");
737  }
738 }
739 
741 {
742  swap(other);
743  return *this;
744 }
745 
747 {
748  carryover_.swap(other.carryover_);
749 
753 
757 
759 }
760 
762 {
763  log_scope("read_game");
764 
765  if(auto caryover_sides = cfg.optional_child("carryover_sides")) {
766  carryover_.swap(*caryover_sides);
768  } else if(auto caryover_sides_start = cfg.optional_child("carryover_sides_start")) {
769  carryover_.swap(*caryover_sides_start);
770  has_carryover_expanded_ = false;
771  } else {
772  carryover_.clear();
773  has_carryover_expanded_ = false;
774  }
775 
776  if(auto replay_start = cfg.optional_child("replay_start")) {
778  } else {
780  }
781 
783 
784  // Serversided replays can contain multiple [replay]
785  for(config& replay : cfg.child_range("replay")) {
787  }
788 
790 
791  if(auto snapshot = cfg.optional_child("snapshot")) {
793  starting_point_.swap(*snapshot);
794  } else if(auto scenario = cfg.optional_child("scenario")) {
796  starting_point_.swap(*scenario);
797  } else {
800  }
801 
802  LOG_NG << "scenario: '" << carryover_["next_scenario"].str() << "'";
803 
804  if(auto stats = cfg.optional_child("statistics")) {
805  statistics_.read(*stats);
806  }
807 
809  mp_settings_ = { cfg.child_or_empty("multiplayer") };
810 
811  cfg.clear();
812 }
813 
815 {
816  carryover_.clear();
817  classification_ = {};
818  has_carryover_expanded_ = false;
819  mp_settings_ = {};
820  replay_data_.swap({});
825 }
826 
827 void swap(saved_game& lhs, saved_game& rhs)
828 {
829  lhs.swap(rhs);
830 }
const config to_config()
Definition: carryover.cpp:224
void merge_old_carryover(const carryover_info &old_carryover)
Definition: carryover.cpp:255
void transfer_to(config &level)
Definition: carryover.cpp:195
void transfer_all_to(config &side_cfg)
Definition: carryover.cpp:169
const randomness::mt_rng & rng() const
Definition: carryover.hpp:93
Variant for storing WML attributes.
bool empty() const
Tests for an attribute that either was never set or was set to "".
Class for writing a config out to a file in pieces.
void close_child(const std::string &key)
void write(const config &cfg)
void write_child(const std::string &key, const config &cfg)
void open_child(const std::string &key)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:396
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:368
void remove_child(config_key_type key, std::size_t index)
Definition: config.cpp:646
config_attribute_value attribute_value
Variant for storing WML attributes.
Definition: config.hpp:292
std::size_t child_count(config_key_type key) const
Definition: config.cpp:298
optional_config_impl< config > find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:788
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:318
child_itors child_range(config_key_type key)
Definition: config.cpp:274
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:407
std::size_t all_children_count() const
Definition: config.cpp:308
config & add_child_at_total(config_key_type key, const config &val, std::size_t pos)
Definition: config.cpp:514
void remove_attribute(config_key_type key)
Definition: config.cpp:161
void inherit_attributes(const config &c)
Merge the attributes of config 'c' into this config, preserving this config's values.
Definition: config.cpp:1190
std::size_t find_total_first_of(config_key_type key, std::size_t start=0)
Definition: config.cpp:500
void swap(config &cfg)
Definition: config.cpp:1341
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:166
void splice_children(config &src, const std::string &key)
Moves all the children with tag key from src to this.
Definition: config.cpp:582
bool empty() const
Definition: config.cpp:853
void clear()
Definition: config.cpp:832
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:688
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:386
config & add_child(config_key_type key)
Definition: config.cpp:442
std::vector< std::string > active_mods
std::string label
Name of the game (e.g.
std::string get_tagname() const
std::string campaign
The id of the campaign being played.
std::string abbrev
the campaign abbreviation
static game_config_manager * get()
void load_game_config_for_game(const game_classification &classification, const std::string &scenario_id)
const game_config_view & game_config() const
A class grating read only view to a vector of config objects, viewed as one config with all children ...
void rotate_random()
Resets the random to the 0 calls and the seed to the random this way we stay in the same sequence but...
Definition: mt_rng.cpp:74
int get_random_int(int min, int max)
Definition: random.hpp:51
void write(config_writer &out) const
void append_config(const config &data)
void swap(replay_recorder_base &other)
game_classification & classification()
Definition: saved_game.hpp:56
bool not_corrupt() const
Definition: saved_game.cpp:697
saved_game & operator=(const saved_game &other)=delete
void set_random_seed()
sets the random seed if that didn't already happen.
Definition: saved_game.cpp:170
config replay_start_
snapshot made before the start event.
Definition: saved_game.hpp:158
void swap(saved_game &other)
Definition: saved_game.cpp:746
void write_config(config_writer &out) const
writes the config information into a stream (file)
Definition: saved_game.cpp:182
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:284
void expand_mp_options()
adds values of [option]s into [carryover_sides_start][variables] so that they are applied in the next...
Definition: saved_game.cpp:427
void unify_controllers()
Definition: saved_game.cpp:733
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:164
config to_config() const
Definition: saved_game.cpp:655
starting_point starting_point_type_
Definition: saved_game.hpp:163
std::string get_scenario_id() const
Definition: saved_game.cpp:679
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:539
replay_recorder_base replay_data_
Definition: saved_game.hpp:170
void clear()
Definition: saved_game.cpp:814
void load_non_scenario(const std::string &type, const std::string &id, size_t pos)
helper for expand_mp_events();
Definition: saved_game.cpp:331
void update_label()
sets classification().label to the correct value.
Definition: saved_game.cpp:702
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:60
statistics_record::campaign_stats_t statistics_
Definition: saved_game.hpp:172
void set_scenario(config scenario)
Definition: saved_game.cpp:596
bool has_carryover_expanded_
Definition: saved_game.hpp:150
config & replay_start()
Definition: saved_game.hpp:128
static void expand_map_file(config &scenario)
reads scenario["map_file"]
Definition: saved_game.cpp:476
void remove_snapshot()
Definition: saved_game.cpp:606
config & get_starting_point()
Definition: saved_game.cpp:612
config starting_point_
The starting pos where the (non replay) game will be started from.
Definition: saved_game.hpp:168
const config & get_replay_starting_point()
Definition: saved_game.cpp:617
void check_require_scenario()
Add addon_id information if needed.
Definition: saved_game.cpp:312
void write_starting_point(config_writer &out) const
Definition: saved_game.cpp:197
void expand_mp_events()
adds [event]s from [era] and [modification] into this scenario does NOT expand [option]s because vari...
Definition: saved_game.cpp:386
config carryover_
depends on has_carryover_expanded_: if true: The carryover information for all sides from the previou...
Definition: saved_game.hpp:156
void expand_random_scenario()
takes care of generate_map=, generate_scenario=, map= attributes This should be called before expandi...
Definition: saved_game.cpp:505
mp_game_settings mp_settings_
Definition: saved_game.hpp:161
void expand_carryover()
merges [carryover_sides_start] into [scenario] and saves the rest into [carryover_sides] Removes [car...
Definition: saved_game.cpp:566
@ SNAPSHOT
We have a [snapshot] (mid-game-savefile).
@ SCENARIO
We have a [scenario] (start-of-scenario) savefile.
@ NONE
There is no scenario stating pos data (start-of-scenario).
@ INVALID
We failed to get a starting pos in expand_scenario.
statistics_record::campaign_stats_t & statistics()
Definition: saved_game.hpp:143
void cancel_orders()
Definition: saved_game.cpp:716
void convert_to_start_save()
converts a normal savegame form the end of a scenaio to a start-of-scenario savefile for the next sce...
Definition: saved_game.cpp:636
bool valid() const
Definition: saved_game.cpp:583
void set_defaults()
does some post loading stuff must be used before passing the data to connect_engine
Definition: saved_game.cpp:221
void write_general_info(config_writer &out) const
Definition: saved_game.cpp:212
game_classification classification_
some general information of the game that doesn't change during the game
Definition: saved_game.hpp:160
void set_data(config &cfg)
destroys the passed config.
Definition: saved_game.cpp:761
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:588
void write_carryover(config_writer &out) const
Definition: saved_game.cpp:206
This class represents a single unit of a specific type.
Definition: unit.hpp:135
std::string str() const
Serializes the version number into string form.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:30
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:217
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
Standard logging facilities (interface).
#define log_scope(description)
Definition: log.hpp:274
std::string random_generate_map(const std::string &name, const config &cfg, const config *vars)
Definition: map_create.cpp:45
config random_generate_scenario(const std::string &name, const config &cfg, const config *vars)
Definition: map_create.cpp:57
@ WAIT
Definition: cursor.hpp:29
std::string read_scenario(const std::string &name)
std::string read_map(const std::string &name)
Game configuration data as global variables.
Definition: build_info.cpp:63
const version_info wesnoth_version(VERSION)
@ NONE
Default, unset return value.
Definition: retval.hpp:32
const config & options()
Definition: game.cpp:555
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
void swap(saved_game &lhs, saved_game &rhs)
Implement non-member swap function for std::swap (calls saved_game::swap).
Definition: saved_game.cpp:827
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: saved_game.cpp:84
static void inherit_scenario(config &scenario, config &map_scen)
Definition: saved_game.cpp:458
#define LOG_NG
Definition: saved_game.cpp:86
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627
void update_addon_requirements(const config &addon_cfg)
Takes a config with addon metadata (id, name, version, min_version) and adds it as a requirement for ...
config to_config() const
void new_scenario(const std::string &scenario_name)
Adds an entry for anew scenario to wrte to.
void read(const config &cfg, bool append=false)
void write(config_writer &out) const
mock_char c
variable_info_mutable< variable_info_implementation::vi_policy_create > variable_access_create
'Create if nonexistent' access.