The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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
23static 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
44static 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_);
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 }
77}
78
79std::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
89const 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
111carryover_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
139std::vector<carryover>& carryover_info::get_all_sides() {
140 return carryover_sides_;
141}
142
144 carryover_sides_.emplace_back(cfg);
145}
146
147void carryover_info::remove_side(const std::string& id) {
148 for (std::vector<carryover>::iterator it = carryover_sides_.begin();
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"].str(), &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_ = "";
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
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
230carryover* 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 {
244 auto iside = utils::ranges::find(carryover_sides_, old_side.get_save_id(), &carryover::get_save_id);
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}
#define ERR_NG
static const int default_gold_qty
Definition carryover.cpp:44
static lg::log_domain log_engine("engine")
void add_side(const config &cfg)
const config to_config()
std::string next_scenario_
the scenario coming next (for campaigns)
std::vector< carryover > & get_all_sides()
void merge_old_carryover(const carryover_info &old_carryover)
std::vector< carryover > carryover_sides_
void transfer_to(config &level)
void transfer_all_to(config &side_cfg)
int next_underlying_unit_id_
randomness::mt_rng rng_
carryover * get_side(const std::string &save_id)
std::vector< config > wml_menu_items_
void remove_side(const std::string &id)
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
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:157
config & add_child(std::string_view key)
Definition config.cpp:436
void remove_attributes(T... keys)
Definition config.hpp:536
config & child_or_add(std::string_view key)
Returns a reference to the first child with the given key.
Definition config.cpp:401
child_itors child_range(std::string_view key)
Definition config.cpp:268
void swap(config &cfg) noexcept
Definition config.cpp:1280
bool has_attribute(std::string_view key) const
Definition config.cpp:157
bool empty() const
Definition config.cpp:823
void clear()
Definition config.cpp:802
std::string get_random_seed_str() const
Definition mt_rng.cpp:99
unsigned int get_random_calls() const
Definition mt_rng.hpp:56
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
Standard logging facilities (interface).
auto find(Container &container, const Value &value, const Projection &projection={})
Definition general.hpp:213
std::string join(const Range &v, const std::string &s=",")
Generates a new string joining container items in a list.
#define c