The Battle for Wesnoth  1.19.0-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 #include <cassert>
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 {
160  save_id_equals(const std::string& val) : value (val) {}
161  bool operator () (carryover& v2) const
162  {
163  return value == v2.get_save_id();
164  }
165 
166  std::string value;
167 };
168 
170  if(side_cfg["save_id"].empty()){
171  side_cfg["save_id"] = side_cfg["id"];
172  }
173  std::vector<carryover>::iterator iside = std::find_if(
174  carryover_sides_.begin(),
175  carryover_sides_.end(),
176  save_id_equals(side_cfg["save_id"])
177  );
178  if(iside != carryover_sides_.end())
179  {
180  iside->transfer_all_gold_to(side_cfg);
181  iside->transfer_all_recalls_to(side_cfg);
182  iside->transfer_all_recruits_to(side_cfg);
183  carryover_sides_.erase(iside);
184  return;
185  }
186  else
187  {
188  //if no carryover was found for this side, check if starting gold is defined
189  if(!side_cfg.has_attribute("gold") || side_cfg["gold"].empty()){
190  side_cfg["gold"] = default_gold_qty;
191  }
192  }
193 }
194 
196 {
197  if(!level.has_attribute("next_underlying_unit_id"))
198  {
199  level["next_underlying_unit_id"] = next_underlying_unit_id_;
200  }
201 
202  //if the game has been loaded from a snapshot, variables_ is empty since we cleared it below.
203  level.child_or_add("variables").append(std::move(variables_));
204 
205  config::attribute_value & seed_value = level["random_seed"];
206  if ( seed_value.empty() ) {
207  seed_value = rng_.get_random_seed_str();
208  level["random_calls"] = rng_.get_random_calls();
209  }
210 
211  if(!level.has_child("menu_item")){
212  for(config& item : wml_menu_items_)
213  {
214  level.add_child("menu_item").swap(item);
215  }
216  }
217 
218  next_scenario_ = "";
219  variables_.clear();
220  wml_menu_items_.clear();
221 
222 }
223 
225 {
226  config cfg;
227  cfg["next_underlying_unit_id"] = next_underlying_unit_id_;
228  cfg["next_scenario"] = next_scenario_;
229 
230  for(carryover& c : carryover_sides_) {
231  c.to_config(cfg);
232  }
233 
234  cfg["random_seed"] = rng_.get_random_seed_str();
235  cfg["random_calls"] = rng_.get_random_calls();
236 
237  cfg.add_child("variables", variables_);
238  for(const config& item : wml_menu_items_)
239  {
240  cfg.add_child("menu_item", item);
241  }
242  return cfg;
243 }
244 
245 carryover* carryover_info::get_side(const std::string& save_id){
246  for(carryover& side : carryover_sides_) {
247  if(side.get_save_id() == save_id){
248  return &side;
249  }
250  }
251  return nullptr;
252 }
253 
254 
256 {
257  for(const carryover & old_side : old_carryover.carryover_sides_)
258  {
259  std::vector<carryover>::iterator iside = std::find_if(
260  carryover_sides_.begin(),
261  carryover_sides_.end(),
262  save_id_equals(old_side.get_save_id())
263  );
264  //add the side if don't already have it.
265  if(iside == carryover_sides_.end())
266  {
267  this->carryover_sides_.push_back(old_side);
268  }
269  }
270 }
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:224
std::string next_scenario_
the scenario coming next (for campaigns)
Definition: carryover.hpp:106
std::vector< carryover > & get_all_sides()
Definition: carryover.cpp:139
config variables_
Definition: carryover.hpp:103
void merge_old_carryover(const carryover_info &old_carryover)
Definition: carryover.cpp:255
std::vector< carryover > carryover_sides_
Definition: carryover.hpp:102
void transfer_to(config &level)
Definition: carryover.cpp:195
void transfer_all_to(config &side_cfg)
Definition: carryover.cpp:169
int next_underlying_unit_id_
Definition: carryover.hpp:107
randomness::mt_rng rng_
Definition: carryover.hpp:104
carryover * get_side(const std::string &save_id)
Definition: carryover.cpp:245
std::vector< config > wml_menu_items_
Definition: carryover.hpp:105
void remove_side(const std::string &id)
Definition: carryover.cpp:147
std::string save_id_
Definition: carryover.hpp:56
void transfer_all_gold_to(config &side_cfg)
Definition: carryover.cpp:46
config variables_
Definition: carryover.hpp:57
std::vector< config > recall_list_
Definition: carryover.hpp:55
std::string current_player_
Definition: carryover.hpp:49
const std::string & get_save_id() const
Definition: carryover.hpp:41
bool add_
Definition: carryover.hpp:48
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:51
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:159
void remove_attributes(T... keys)
Definition: config.hpp:573
bool has_attribute(config_key_type key) const
Definition: config.cpp:156
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
void swap(config &cfg)
Definition: config.cpp:1341
bool empty() const
Definition: config.cpp:853
void clear()
Definition: config.cpp:832
config & add_child(config_key_type key)
Definition: config.cpp:442
std::string get_random_seed_str() const
Definition: mt_rng.cpp:100
unsigned int get_random_calls() const
Definition: mt_rng.hpp:56
Standard logging facilities (interface).
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:414
void erase(const std::string &key)
Definition: general.cpp:205
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:160
bool operator()(carryover &v2) const
Definition: carryover.cpp:161
std::string value
Definition: carryover.cpp:166
mock_char c