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