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