mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-25 19:12:02 +00:00
270 lines
7.2 KiB
C++
270 lines
7.2 KiB
C++
/*
|
|
Copyright (C) 2003 - 2025
|
|
by David White <dave@whitevine.net>
|
|
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#include "carryover.hpp"
|
|
|
|
#include "config.hpp"
|
|
#include "log.hpp"
|
|
#include "serialization/string_utils.hpp"
|
|
|
|
static lg::log_domain log_engine("engine");
|
|
#define LOG_NG LOG_STREAM(info, log_engine)
|
|
#define ERR_NG LOG_STREAM(err, log_engine)
|
|
|
|
carryover::carryover(const config& side)
|
|
: add_(!side["carryover_add"].empty() ? side["carryover_add"].to_bool() : side["add"].to_bool())
|
|
, current_player_(side["current_player"])
|
|
, gold_(!side["carryover_gold"].empty() ? side["carryover_gold"].to_int() : side["gold"].to_int())
|
|
// if we load it from a snapshot we need to read the recruits from "recruits" and not from "previous_recruits".
|
|
, previous_recruits_(side.has_attribute("recruit") ? utils::split_set(side["recruit"].str()) :utils::split_set(side["previous_recruits"].str()))
|
|
, recall_list_()
|
|
, save_id_(side["save_id"])
|
|
, variables_(side.child_or_empty("variables"))
|
|
{
|
|
for(const config& u : side.child_range("unit")) {
|
|
recall_list_.push_back(u);
|
|
config& u_back = recall_list_.back();
|
|
u_back.remove_attributes("side", "goto_x", "goto_y", "x", "y", "hidden");
|
|
}
|
|
}
|
|
|
|
static const int default_gold_qty = 100;
|
|
|
|
void carryover::transfer_all_gold_to(config& side_cfg){
|
|
|
|
int cfg_gold = side_cfg["gold"].to_int();
|
|
|
|
if(side_cfg["gold"].empty()) {
|
|
cfg_gold = default_gold_qty;
|
|
side_cfg["gold"] = cfg_gold;
|
|
}
|
|
|
|
if(add_ && gold_ > 0){
|
|
side_cfg["gold"] = cfg_gold + gold_;
|
|
}
|
|
else if(gold_ > cfg_gold){
|
|
side_cfg["gold"] = gold_;
|
|
}
|
|
side_cfg.child_or_add("variables").swap(variables_);
|
|
variables_.clear();
|
|
gold_ = 0;
|
|
}
|
|
|
|
void carryover::transfer_all_recruits_to(config& side_cfg){
|
|
std::string can_recruit_str = utils::join(previous_recruits_, ",");
|
|
previous_recruits_.clear();
|
|
side_cfg["previous_recruits"] = can_recruit_str;
|
|
}
|
|
|
|
void carryover::transfer_all_recalls_to(config& side_cfg){
|
|
for(const config & u_cfg : recall_list_) {
|
|
side_cfg.add_child("unit", u_cfg);
|
|
}
|
|
recall_list_.clear();
|
|
}
|
|
|
|
std::string carryover::get_recruits(bool erase){
|
|
// Join the previous recruits into a string.
|
|
std::string can_recruit_str = utils::join(previous_recruits_);
|
|
if ( erase )
|
|
// Clear the previous recruits.
|
|
previous_recruits_.clear();
|
|
|
|
return can_recruit_str;
|
|
}
|
|
|
|
const std::string carryover::to_string(){
|
|
std::string side = "";
|
|
side.append("Side " + save_id_ + ": gold " + std::to_string(gold_) + " recruits " + get_recruits(false) + " units ");
|
|
for(const config & u_cfg : recall_list_) {
|
|
side.append(u_cfg["name"].str() + ", ");
|
|
}
|
|
return side;
|
|
}
|
|
|
|
void carryover::to_config(config& cfg){
|
|
config& side = cfg.add_child("side");
|
|
side["save_id"] = save_id_;
|
|
side["gold"] = gold_;
|
|
side["add"] = add_;
|
|
side["current_player"] = current_player_;
|
|
side["previous_recruits"] = get_recruits(false);
|
|
side.add_child("variables", variables_);
|
|
for(const config & u_cfg : recall_list_) {
|
|
side.add_child("unit", u_cfg);
|
|
}
|
|
}
|
|
|
|
carryover_info::carryover_info(const config& cfg, bool from_snpashot)
|
|
: carryover_sides_()
|
|
, variables_(cfg.child_or_empty("variables"))
|
|
, rng_(cfg)
|
|
, wml_menu_items_()
|
|
, next_scenario_(cfg["next_scenario"])
|
|
, next_underlying_unit_id_(cfg["next_underlying_unit_id"].to_int(0))
|
|
{
|
|
for(const config& side : cfg.child_range("side"))
|
|
{
|
|
if(side["lost"].to_bool(false) || !side["persistent"].to_bool(true) || side["save_id"].empty())
|
|
{
|
|
//this shouldn't happen outside a snpshot.
|
|
if(!from_snpashot) {
|
|
ERR_NG << "found invalid carryover data in saved game, lost='" << side["lost"] << "' persistent='" << side["persistent"] << "' save_id='" << side["save_id"] << "'";
|
|
}
|
|
continue;
|
|
}
|
|
this->carryover_sides_.emplace_back(side);
|
|
}
|
|
for(const config& item : cfg.child_range("menu_item"))
|
|
{
|
|
if(item["persistent"].to_bool(true)) {
|
|
wml_menu_items_.push_back(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<carryover>& carryover_info::get_all_sides() {
|
|
return carryover_sides_;
|
|
}
|
|
|
|
void carryover_info::add_side(const config& cfg) {
|
|
carryover_sides_.emplace_back(cfg);
|
|
}
|
|
|
|
void carryover_info::remove_side(const std::string& id) {
|
|
for (std::vector<carryover>::iterator it = carryover_sides_.begin();
|
|
it != carryover_sides_.end(); ++it) {
|
|
|
|
if (it->get_save_id() == id) {
|
|
carryover_sides_.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct save_id_equals
|
|
{
|
|
save_id_equals(const std::string& val) : value (val) {}
|
|
bool operator () (carryover& v2) const
|
|
{
|
|
return value == v2.get_save_id();
|
|
}
|
|
|
|
std::string value;
|
|
};
|
|
|
|
void carryover_info::transfer_all_to(config& side_cfg){
|
|
if(side_cfg["save_id"].empty()){
|
|
side_cfg["save_id"] = side_cfg["id"];
|
|
}
|
|
std::vector<carryover>::iterator iside = std::find_if(
|
|
carryover_sides_.begin(),
|
|
carryover_sides_.end(),
|
|
save_id_equals(side_cfg["save_id"])
|
|
);
|
|
if(iside != carryover_sides_.end())
|
|
{
|
|
iside->transfer_all_gold_to(side_cfg);
|
|
iside->transfer_all_recalls_to(side_cfg);
|
|
iside->transfer_all_recruits_to(side_cfg);
|
|
carryover_sides_.erase(iside);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//if no carryover was found for this side, check if starting gold is defined
|
|
if(!side_cfg.has_attribute("gold") || side_cfg["gold"].empty()){
|
|
side_cfg["gold"] = default_gold_qty;
|
|
}
|
|
}
|
|
}
|
|
|
|
void carryover_info::transfer_to(config& level)
|
|
{
|
|
if(!level.has_attribute("next_underlying_unit_id"))
|
|
{
|
|
level["next_underlying_unit_id"] = next_underlying_unit_id_;
|
|
}
|
|
|
|
//if the game has been loaded from a snapshot, variables_ is empty since we cleared it below.
|
|
level.child_or_add("variables").append(std::move(variables_));
|
|
|
|
config::attribute_value & seed_value = level["random_seed"];
|
|
if ( seed_value.empty() ) {
|
|
seed_value = rng_.get_random_seed_str();
|
|
level["random_calls"] = rng_.get_random_calls();
|
|
}
|
|
|
|
if(!level.has_child("menu_item")){
|
|
for(config& item : wml_menu_items_)
|
|
{
|
|
level.add_child("menu_item").swap(item);
|
|
}
|
|
}
|
|
|
|
next_scenario_ = "";
|
|
variables_.clear();
|
|
wml_menu_items_.clear();
|
|
|
|
}
|
|
|
|
const config carryover_info::to_config()
|
|
{
|
|
config cfg;
|
|
cfg["next_underlying_unit_id"] = next_underlying_unit_id_;
|
|
cfg["next_scenario"] = next_scenario_;
|
|
|
|
for(carryover& c : carryover_sides_) {
|
|
c.to_config(cfg);
|
|
}
|
|
|
|
cfg["random_seed"] = rng_.get_random_seed_str();
|
|
cfg["random_calls"] = rng_.get_random_calls();
|
|
|
|
cfg.add_child("variables", variables_);
|
|
for(const config& item : wml_menu_items_)
|
|
{
|
|
cfg.add_child("menu_item", item);
|
|
}
|
|
return cfg;
|
|
}
|
|
|
|
carryover* carryover_info::get_side(const std::string& save_id){
|
|
for(carryover& side : carryover_sides_) {
|
|
if(side.get_save_id() == save_id){
|
|
return &side;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
void carryover_info::merge_old_carryover(const carryover_info& old_carryover)
|
|
{
|
|
for(const carryover & old_side : old_carryover.carryover_sides_)
|
|
{
|
|
std::vector<carryover>::iterator iside = std::find_if(
|
|
carryover_sides_.begin(),
|
|
carryover_sides_.end(),
|
|
save_id_equals(old_side.get_save_id())
|
|
);
|
|
//add the side if don't already have it.
|
|
if(iside == carryover_sides_.end())
|
|
{
|
|
this->carryover_sides_.push_back(old_side);
|
|
}
|
|
}
|
|
}
|