The Battle for Wesnoth  1.17.0-dev
carryover.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
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 
16 #include "carryover.hpp"
17 
18 #include "config.hpp"
19 #include "log.hpp"
20 #include "team.hpp"
21 #include "units/unit.hpp"
22 #include <cassert>
23 
24 static lg::log_domain log_engine("engine");
25 #define LOG_NG LOG_STREAM(info, log_engine)
26 #define ERR_NG LOG_STREAM(err, log_engine)
27 
29  : add_(!side["carryover_add"].empty() ? side["carryover_add"].to_bool() : side["add"].to_bool())
30  , current_player_(side["current_player"])
31  , gold_(!side["carryover_gold"].empty() ? side["carryover_gold"].to_int() : side["gold"].to_int())
32  // if we load it from a snapshot we need to read the recruits from "recruits" and not from "previous_recruits".
33  , previous_recruits_(side.has_attribute("recruit") ? utils::set_split(side["recruit"]) :utils::set_split(side["previous_recruits"]))
34  , recall_list_()
35  , save_id_(side["save_id"])
36  , variables_(side.child_or_empty("variables"))
37 {
38  for(const config& u : side.child_range("unit")) {
39  recall_list_.push_back(u);
40  config& u_back = recall_list_.back();
41  u_back.remove_attributes("side", "goto_x", "goto_y", "x", "y", "hidden");
42  }
43 }
44 
45 carryover::carryover(const team& t, const int gold, const bool add)
46  : add_ (add)
47  , current_player_(t.current_player())
48  , gold_(gold)
49  , previous_recruits_(t.recruits())
50  , recall_list_()
51  , save_id_(t.save_id())
52  , variables_(t.variables())
53 {
54  for(const unit_const_ptr u : t.recall_list()) {
55  recall_list_.emplace_back();
56  u->write(recall_list_.back());
57  }
58 }
59 
60 static const int default_gold_qty = 100;
61 
63 
64  int cfg_gold = side_cfg["gold"].to_int();
65 
66  if(side_cfg["gold"].empty()) {
67  cfg_gold = default_gold_qty;
68  side_cfg["gold"] = cfg_gold;
69  }
70 
71  if(add_ && gold_ > 0){
72  side_cfg["gold"] = cfg_gold + gold_;
73  }
74  else if(gold_ > cfg_gold){
75  side_cfg["gold"] = gold_;
76  }
77  side_cfg.child_or_add("variables").swap(variables_);
78  variables_.clear();
79  gold_ = 0;
80 }
81 
83  std::string can_recruit_str = utils::join(previous_recruits_, ",");
84  previous_recruits_.clear();
85  side_cfg["previous_recruits"] = can_recruit_str;
86 }
87 
89  for(const config & u_cfg : recall_list_) {
90  side_cfg.add_child("unit", u_cfg);
91  }
92  recall_list_.clear();
93 }
94 
95 std::string carryover::get_recruits(bool erase){
96  // Join the previous recruits into a string.
97  std::string can_recruit_str = utils::join(previous_recruits_);
98  if ( erase )
99  // Clear the previous recruits.
100  previous_recruits_.clear();
101 
102  return can_recruit_str;
103 }
104 
105 const std::string carryover::to_string(){
106  std::string side = "";
107  side.append("Side " + save_id_ + ": gold " + std::to_string(gold_) + " recruits " + get_recruits(false) + " units ");
108  for(const config & u_cfg : recall_list_) {
109  side.append(u_cfg["name"].str() + ", ");
110  }
111  return side;
112 }
113 
115  config& side = cfg.add_child("side");
116  side["save_id"] = save_id_;
117  side["gold"] = gold_;
118  side["add"] = add_;
119  side["current_player"] = current_player_;
120  side["previous_recruits"] = get_recruits(false);
121  side.add_child("variables", variables_);
122  for(const config & u_cfg : recall_list_) {
123  side.add_child("unit", u_cfg);
124  }
125 }
126 
127 carryover_info::carryover_info(const config& cfg, bool from_snpashot)
128  : carryover_sides_()
129  , variables_(cfg.child_or_empty("variables"))
130  , rng_(cfg)
131  , wml_menu_items_()
132  , next_scenario_(cfg["next_scenario"])
133  , next_underlying_unit_id_(cfg["next_underlying_unit_id"].to_int(0))
134 {
135  for(const config& side : cfg.child_range("side"))
136  {
137  if(side["lost"].to_bool(false) || !side["persistent"].to_bool(true) || side["save_id"].empty())
138  {
139  //this shouldn't happen outside a snpshot.
140  if(!from_snpashot) {
141  ERR_NG << "found invalid carryover data in saved game, lost='" << side["lost"] << "' persistent='" << side["persistent"] << "' save_id='" << side["save_id"] << "'\n";
142  }
143  continue;
144  }
145  this->carryover_sides_.emplace_back(side);
146  }
147  for(const config& item : cfg.child_range("menu_item"))
148  {
149  if(item["persistent"].to_bool(true)) {
150  wml_menu_items_.push_back(item);
151  }
152  }
153 }
154 
155 std::vector<carryover>& carryover_info::get_all_sides() {
156  return carryover_sides_;
157 }
158 
160  carryover_sides_.emplace_back(cfg);
161 }
162 
163 void carryover_info::remove_side(const std::string& id) {
165  it != carryover_sides_.end(); ++it) {
166 
167  if (it->get_save_id() == id) {
168  carryover_sides_.erase(it);
169  break;
170  }
171  }
172 }
173 
175 {
176  save_id_equals(const std::string& val) : value (val) {}
177  bool operator () (carryover& v2) const
178  {
179  return value == v2.get_save_id();
180  }
181 
182  std::string value;
183 };
184 
186  if(side_cfg["save_id"].empty()){
187  side_cfg["save_id"] = side_cfg["id"];
188  }
189  std::vector<carryover>::iterator iside = std::find_if(
190  carryover_sides_.begin(),
191  carryover_sides_.end(),
192  save_id_equals(side_cfg["save_id"])
193  );
194  if(iside != carryover_sides_.end())
195  {
196  iside->transfer_all_gold_to(side_cfg);
197  iside->transfer_all_recalls_to(side_cfg);
198  iside->transfer_all_recruits_to(side_cfg);
199  carryover_sides_.erase(iside);
200  return;
201  }
202  else
203  {
204  //if no carryover was found for this side, check if starting gold is defined
205  if(!side_cfg.has_attribute("gold") || side_cfg["gold"].empty()){
206  side_cfg["gold"] = default_gold_qty;
207  }
208  }
209 }
210 
212 {
213  if(!level.has_attribute("next_underlying_unit_id"))
214  {
215  level["next_underlying_unit_id"] = next_underlying_unit_id_;
216  }
217 
218  //if the game has been loaded from a snapshot, variables_ is empty since we cleared it below.
219  level.child_or_add("variables").append(std::move(variables_));
220 
221  config::attribute_value & seed_value = level["random_seed"];
222  if ( seed_value.empty() ) {
223  seed_value = rng_.get_random_seed_str();
224  level["random_calls"] = rng_.get_random_calls();
225  }
226 
227  if(!level.has_child("menu_item")){
228  for(config& item : wml_menu_items_)
229  {
230  level.add_child("menu_item").swap(item);
231  }
232  }
233 
234  next_scenario_ = "";
235  variables_.clear();
236  wml_menu_items_.clear();
237 
238 }
239 
241 {
242  config cfg;
243  cfg["next_underlying_unit_id"] = next_underlying_unit_id_;
244  cfg["next_scenario"] = next_scenario_;
245 
246  for(carryover& c : carryover_sides_) {
247  c.to_config(cfg);
248  }
249 
250  cfg["random_seed"] = rng_.get_random_seed_str();
251  cfg["random_calls"] = rng_.get_random_calls();
252 
253  cfg.add_child("variables", variables_);
254  for(const config& item : wml_menu_items_)
255  {
256  cfg.add_child("menu_item", item);
257  }
258  return cfg;
259 }
260 
261 carryover* carryover_info::get_side(const std::string& save_id){
262  for(carryover& side : carryover_sides_) {
263  if(side.get_save_id() == save_id){
264  return &side;
265  }
266  }
267  return nullptr;
268 }
269 
270 
272 {
273  for(const carryover & old_side : old_carryover.carryover_sides_)
274  {
275  std::vector<carryover>::iterator iside = std::find_if(
276  carryover_sides_.begin(),
277  carryover_sides_.end(),
278  save_id_equals(old_side.get_save_id())
279  );
280  //add the side if don't already have it.
281  if(iside == carryover_sides_.end())
282  {
283  this->carryover_sides_.push_back(old_side);
284  }
285  }
286 }
bool empty() const
Tests for an attribute that either was never set or was set to "".
void add_side(const config &cfg)
Definition: carryover.cpp:159
const std::string & get_save_id() const
Definition: carryover.hpp:42
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:269
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< carryover > & get_all_sides()
Definition: carryover.cpp:155
Variant for storing WML attributes.
const config to_config()
Definition: carryover.cpp:240
bool has_attribute(config_key_type key) const
Definition: config.cpp:211
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:394
void transfer_all_recalls_to(config &side_cfg)
Definition: carryover.cpp:88
child_itors child_range(config_key_type key)
Definition: config.cpp:344
void remove_attributes(T... keys)
Definition: config.hpp:522
std::string save_id_
Definition: carryover.hpp:57
void merge_old_carryover(const carryover_info &old_carryover)
Definition: carryover.cpp:271
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:478
config variables_
Definition: carryover.hpp:104
void clear()
Definition: config.cpp:920
void transfer_to(config &level)
Definition: carryover.cpp:211
Definitions for the interface to Wesnoth Markup Language (WML).
std::string get_random_seed_str() const
Definition: mt_rng.cpp:100
std::set< std::string > previous_recruits_
Definition: carryover.hpp:52
int next_underlying_unit_id_
Definition: carryover.hpp:108
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
void transfer_all_to(config &side_cfg)
Definition: carryover.cpp:185
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
void swap(config &cfg)
Definition: config.cpp:1447
std::vector< config > recall_list_
Definition: carryover.hpp:56
save_id_equals(const std::string &val)
Definition: carryover.cpp:176
std::vector< config > wml_menu_items_
Definition: carryover.hpp:106
config variables_
Definition: carryover.hpp:58
void erase(const std::string &key)
Definition: general.cpp:201
void transfer_all_gold_to(config &side_cfg)
Definition: carryover.cpp:62
std::vector< carryover > carryover_sides_
Definition: carryover.hpp:103
carryover * get_side(const std::string &save_id)
Definition: carryover.cpp:261
#define ERR_NG
Definition: carryover.cpp:26
std::string current_player_
Definition: carryover.hpp:50
std::string next_scenario_
the scenario coming next (for campaigns)
Definition: carryover.hpp:107
unsigned int get_random_calls() const
Definition: mt_rng.hpp:56
const std::string to_string()
Definition: carryover.cpp:105
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
bool add_
Definition: carryover.hpp:49
std::string get_recruits(bool erase=false)
Definition: carryover.cpp:95
void transfer_all_recruits_to(config &side_cfg)
Definition: carryover.cpp:82
randomness::mt_rng rng_
Definition: carryover.hpp:105
static lg::log_domain log_engine("engine")
config & add_child(config_key_type key)
Definition: config.cpp:514
std::string value
Definition: carryover.cpp:182
double t
Definition: astarsearch.cpp:65
Standard logging facilities (interface).
void remove_side(const std::string &id)
Definition: carryover.cpp:163
recall_list_manager & recall_list()
Definition: team.hpp:227
std::set< std::string > set_split(const std::string &val, const char c=',', const int flags=REMOVE_EMPTY|STRIP_SPACES)
Splits a (comma-)separated string into a set of pieces.
void to_config(config &cfg)
Definition: carryover.cpp:114
static const int default_gold_qty
Definition: carryover.cpp:60
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
mock_char c
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
bool empty() const
Definition: config.cpp:941
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410