Changed the structure of savegames to only contain the data necessary.

Old savegames are loaded via a conversion method.
This commit is contained in:
Anja Keicher 2012-08-15 12:42:56 +00:00
parent 86de023177
commit 8f6a3a5490
9 changed files with 303 additions and 301 deletions

View file

@ -40,6 +40,10 @@
#include "map.hpp"
#include "pathfind/pathfind.hpp"
#include "whiteboard/side_actions.hpp"
#include "sound.hpp"
#include "soundsource.hpp"
#include "game_events.hpp"
#include "map_label.hpp"
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
@ -819,12 +823,10 @@ void game_data::write_snapshot(config& cfg){
wml_menu_items_.to_config(cfg);
}
void game_data::write_config(config_writer& out, bool write_variables){
void game_data::write_config(config_writer& out){
out.write_key_val("random_seed", lexical_cast<std::string>(rng_.get_random_seed()));
out.write_key_val("random_calls", lexical_cast<std::string>(rng_.get_random_calls()));
if (write_variables) {
out.write_child("variables", variables_);
}
out.write_child("variables", variables_);
config cfg;
wml_menu_items_.to_config(cfg);
@ -956,9 +958,9 @@ config game_classification::to_config() const
game_state::game_state() :
replay_data(),
starting_pos(),
snapshot(),
carryover_sides(carryover_info().to_config()),
carryover_sides(),
carryover_sides_start(carryover_info().to_config()),
classification_(),
mp_settings_()
{}
@ -973,9 +975,9 @@ void write_players(game_state& gamestate, config& cfg, const bool use_snapshot,
config *source = NULL;
if (use_snapshot) {
source = &gamestate.snapshot;
source = &gamestate.carryover_sides_start;
} else {
source = &gamestate.starting_pos;
source = &gamestate.replay_start();
}
if (merge_side) {
@ -1047,66 +1049,43 @@ void write_players(game_state& gamestate, config& cfg, const bool use_snapshot,
game_state::game_state(const config& cfg, bool show_replay) :
replay_data(),
starting_pos(),
snapshot(),
// carryover_sides(cfg.child_or_empty("carryover_sides")),
carryover_sides(),
carryover_sides_start(),
replay_start_(),
classification_(cfg),
mp_settings_(cfg)
{
n_unit::id_manager::instance().set_save_id(cfg["next_underlying_unit_id"]);
log_scope("read_game");
carryover_info sides(cfg.child_or_empty("carryover_sides"));
if(cfg.has_child("carryover_sides")){
carryover_sides = cfg.child("carryover_sides");
}
if(cfg.has_child("carryover_sides_start")){
carryover_sides_start = cfg.child("carryover_sides_start");
}
const config &snapshot = cfg.child("snapshot");
const config &replay_start = cfg.child("replay_start");
// We're loading a snapshot if we have it and the user didn't request a replay.
bool load_snapshot = !show_replay && snapshot && !snapshot.empty();
if (load_snapshot) {
this->snapshot = snapshot;
//for backwards compatibility
if(snapshot.has_child("variables")){
sides.set_variables(snapshot.child("variables"));
if(show_replay){
//If replay_start and replay_data couldn't be loaded
if(!load_replay(cfg)){
//TODO: notify user of failure
ERR_NG<<"Could not load as replay \n";
}
} else {
assert(replay_start);
//for backwards compatibility
if(replay_start.has_child("variables")){
sides.set_variables(replay_start.child("variables"));
if(const config& snapshot = cfg.child("snapshot")){
this->snapshot = snapshot;
load_replay(cfg);
} else if(carryover_sides_start.empty() && !carryover_sides.empty()){
//if we are loading a start of scenario save and don't have carryover_sides_start, use carryover_sides
carryover_sides_start = carryover_sides;
}
//TODO: check if loading fails completely
}
//for backwards compatibility
if(cfg.has_child("variables")){
sides.set_variables(cfg.child("variables"));
}
carryover_sides = sides.to_config();
LOG_NG << "scenario: '" << classification_.scenario << "'\n";
LOG_NG << "next_scenario: '" << classification_.next_scenario << "'\n";
if (const config &replay = cfg.child("replay")) {
replay_data = replay;
}
if (replay_start) {
starting_pos = replay_start;
//This is a quick hack to make replays for campaigns work again:
//The [player] information needs to be stored somewhere within the gamestate,
//because we need it later on when creating the replay savegame.
//We therefore put it inside the starting_pos, so it doesn't get lost.
//See also playcampaign::play_game, where after finishing the scenario the replay
//will be saved.
if(!starting_pos.empty()) {
BOOST_FOREACH(const config &p, cfg.child_range("player")) {
config& cfg_player = starting_pos.add_child("player");
cfg_player.merge_with(p);
}
}
}
if (const config &stats = cfg.child("statistics")) {
statistics::fresh_stats();
statistics::read_stats(stats);
@ -1114,9 +1093,124 @@ game_state::game_state(const config& cfg, bool show_replay) :
}
void game_state::write_snapshot(config& cfg) const
bool game_state::load_replay(const config& cfg){
bool replay_loaded = false;
if(const config& replay_start = cfg.child("replay_start")){
replay_start_ = replay_start;
if(const config& replay = cfg.child("replay")){
this->replay_data = replay;
replay_loaded = true;
}
}
return replay_loaded;
}
void convert_old_saves(config& cfg){
if(!cfg.has_child("snapshot")){
return;
}
const config& snapshot = cfg.child("snapshot");
const config& replay_start = cfg.child("replay_start");
const config& replay = cfg.child("replay");
if(!cfg.has_child("carryover_sides") && !cfg.has_child("carryover_sides_start")){
config carryover;
//copy rng and menu items from toplevel to new carryover_sides
carryover["random_seed"] = cfg["random_seed"];
carryover["random_calls"] = cfg["random_calls"];
BOOST_FOREACH(const config& menu_item, cfg.child_range("menu_item")){
carryover.add_child("menu_item", menu_item);
}
config carryover_start = carryover;
//copy sides from either snapshot or replay_start to new carryover_sides
if(!snapshot.empty()){
BOOST_FOREACH(const config& side, snapshot.child_range("side")){
carryover.add_child("side", side);
}
//for compatibility with old savegames that use player instead of side
BOOST_FOREACH(const config& side, snapshot.child_range("player")){
carryover.add_child("side", side);
}
//save the sides from replay_start in carryover_sides_start
BOOST_FOREACH(const config& side, replay_start.child_range("side")){
carryover_start.add_child("side", side);
}
//for compatibility with old savegames that use player instead of side
BOOST_FOREACH(const config& side, replay_start.child_range("player")){
carryover_start.add_child("side", side);
}
} else if (!replay_start.empty()){
BOOST_FOREACH(const config& side, replay_start.child_range("side")){
carryover.add_child("side", side);
carryover_start.add_child("side", side);
}
//for compatibility with old savegames that use player instead of side
BOOST_FOREACH(const config& side, replay_start.child_range("player")){
carryover.add_child("side", side);
carryover_start.add_child("side", side);
}
}
//get variables according to old hierarchy and copy them to new carryover_sides
if(!snapshot.empty()){
if(const config& variables = snapshot.child("variables")){
carryover.add_child("variables", variables);
carryover_start.add_child("variables", replay_start.child_or_empty("variables"));
} else if (const config& variables = cfg.child("variables")){
carryover.add_child("variables", variables);
carryover_start.add_child("variables", variables);
}
} else if (!replay_start.empty()){
if(const config& variables = replay_start.child("variables")){
carryover.add_child("variables", variables);
carryover_start.add_child("variables", variables);
}
} else {
carryover.add_child("variables", cfg.child("variables"));
carryover_start.add_child("variables", cfg.child("variables"));
}
cfg.add_child("carryover_sides", carryover);
cfg.add_child("carryover_sides_start", carryover_start);
}
//if replay and snapshot are empty we've got a start of scenario save and don't want replay_start either
if(replay.empty() && snapshot.empty()){
LOG_RG<<"removing replay_start \n";
cfg.remove_child("replay_start", 0);
}
//remove empty replay or snapshot so type of save can be detected more easily
if(replay.empty()){
LOG_RG<<"removing replay \n";
cfg.remove_child("replay", 0);
}
if(snapshot.empty()){
LOG_RG<<"removing snapshot \n";
cfg.remove_child("snapshot", 0);
}
LOG_RG<<"cfg after conversion "<<cfg<<"\n";
}
void game_state::write_snapshot(config& cfg, game_display* gui) const
{
log_scope("write_game");
if(gui != NULL){
cfg["snapshot"] = true;
cfg["playing_team"] = str_cast(gui->playing_team());
game_events::write_events(cfg);
sound::write_music_play_list(cfg);
}
cfg["label"] = classification_.label;
cfg["history"] = classification_.history;
cfg["abbrev"] = classification_.abbrev;
@ -1142,6 +1236,10 @@ void game_state::write_snapshot(config& cfg) const
if(resources::gamedata != NULL){
resources::gamedata->write_snapshot(cfg);
}
if(gui != NULL){
gui->labels().write(cfg);
}
}
void extract_summary_from_config(config& cfg_save, config& cfg_summary)
@ -1241,9 +1339,10 @@ void extract_summary_from_config(config& cfg_save, config& cfg_summary)
game_state::game_state(const game_state& state) :
replay_data(state.replay_data),
starting_pos(state.starting_pos),
snapshot(state.snapshot),
carryover_sides(state.carryover_sides),
carryover_sides_start(state.carryover_sides_start),
replay_start_(state.replay_start_),
classification_(state.classification_),
mp_settings_(state.mp_settings_)
{}
@ -1259,20 +1358,10 @@ game_state& game_state::operator=(const game_state& state)
}
void game_state::write_config(config_writer& out, bool write_variables) const
void game_state::write_config(config_writer& out) const
{
out.write(classification_.to_config());
if (classification_.campaign_type == "multiplayer")
out.write_child("multiplayer", mp_settings_.to_config());
if(resources::gamedata != NULL){
resources::gamedata->write_config(out, write_variables);
}
if (!replay_data.child("replay")) {
out.write_child("replay", replay_data);
}
out.write_child("replay_start",starting_pos);
}

View file

@ -32,6 +32,8 @@ class scoped_wml_variable;
class team;
class gamemap;
void convert_old_saves(config& cfg);
namespace t_translation {
struct t_match;
}
@ -208,7 +210,7 @@ public:
map_location last_selected;
void write_snapshot(config& cfg);
void write_config(config_writer& out, bool write_variables);
void write_config(config_writer& out);
game_data& operator=(const game_data& info);
game_data* operator=(const game_data* info);
@ -264,9 +266,9 @@ public:
game_state& operator=(const game_state& state);
//write the gamestate into a config object
void write_snapshot(config& cfg) const;
void write_snapshot(config& cfg, game_display* gui = NULL) const;
//write the config information into a stream (file)
void write_config(config_writer& out, bool write_variables=true) const;
void write_config(config_writer& out) const;
game_classification& classification() { return classification_; }
const game_classification& classification() const { return classification_; } //FIXME: const getter to allow use from const gamestatus::sog() (see ai.cpp:344) - remove after merge?
@ -275,20 +277,16 @@ public:
mp_game_settings& mp_settings() { return mp_settings_; }
const mp_game_settings& mp_settings() const { return mp_settings_; }
bool load_replay(const config& cfg);
config& replay_start() { return replay_start_; }
/**
* If the game is saved mid-level, we have a series of replay steps
* to take the game up to the position it was saved at.
*/
config replay_data;
/**
* Saved starting state of the game.
*
* For multiplayer games, the position the game started in may be different
* to the scenario.
*/
config starting_pos;
/**
* Snapshot of the game's current contents.
*
@ -300,7 +298,13 @@ public:
/** The carryover information for all sides*/
config carryover_sides;
/** The carryover information for all sides as it was before the scenario started*/
config carryover_sides_start;
private:
/** First turn snapshot for replays, contains starting position */
config replay_start_;
game_classification classification_;
mp_game_settings mp_settings_;
};

View file

@ -1659,7 +1659,7 @@ void connect::load_game()
}
level_["experience_modifier"] = params_.xp_modifier;
level_["random_seed"] = state_.carryover_sides["random_seed"];
level_["random_seed"] = state_.carryover_sides_start["random_seed"];
}
// Add the map name to the title.
@ -1720,7 +1720,7 @@ config* connect::current_config(){
cfg_level = &snapshot;
} else if (!level_.child("side")) {
// Start-of-scenario save, the info has to be taken from the starting_pos
cfg_level = &state_.starting_pos;
cfg_level = &state_.replay_start();
} else {
// Fresh game, no snapshot available
cfg_level = &level_;

View file

@ -109,7 +109,7 @@ void level_to_gamestate(config& level, game_state& state)
}
}
carryover_info sides = carryover_info(state.carryover_sides);
carryover_info sides = carryover_info(state.carryover_sides_start);
//set random
const std::string seed = level["random_seed"];
@ -129,7 +129,7 @@ void level_to_gamestate(config& level, game_state& state)
//this is important, if it does not happen, the starting position is missing and
//will be drawn from the snapshot instead (which is not what we want since we have
//all needed information here already)
state.starting_pos = level.child("replay_start");
state.replay_start() = level.child("replay_start");
level["campaign_type"] = "multiplayer";
state.classification().campaign_type = "multiplayer";
@ -149,7 +149,7 @@ void level_to_gamestate(config& level, game_state& state)
//It might be a MP campaign start-of-scenario save
//In this case, it's not entirely a new game, but not a save, either
//Check whether it is no savegame and the starting_pos contains [player] information
bool start_of_scenario = !saved_game && state.starting_pos.child("player");
bool start_of_scenario = !saved_game && state.replay_start().child("player");
//If we start a fresh game, there won't be any snapshot information. If however this
//is a savegame, we got a valid snapshot here.
@ -166,7 +166,7 @@ void level_to_gamestate(config& level, game_state& state)
if(saved_game || start_of_scenario){
config::child_itors saved_sides = saved_game ?
state.snapshot.child_range("side") :
state.starting_pos.child_range("side");
state.replay_start().child_range("side");
config::const_child_itors level_sides = level.child_range("side");
BOOST_FOREACH(config &side, saved_sides)
@ -193,7 +193,7 @@ void level_to_gamestate(config& level, game_state& state)
LOG_NG << sides.get_variables();
}
state.carryover_sides = sides.to_config();
state.carryover_sides_start = sides.to_config();
}
std::string get_color_string(int id)

View file

@ -215,7 +215,7 @@ void play_controller::init(CVideo& video){
}
}
team_builder_ptr tb_ptr = gamedata_.create_team_builder(side,
save_id, teams_, level_, map_, units_, snapshot, gamestate_.starting_pos);
save_id, teams_, level_, map_, units_, snapshot, gamestate_.replay_start());
++team_num;
gamedata_.build_team_stage_one(tb_ptr);
team_builders.push_back(tb_ptr);
@ -393,7 +393,7 @@ void play_controller::status_table(){
void play_controller::save_game(){
if(save_blocker::try_block()) {
save_blocker::save_unblocker unblocker;
savegame::game_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
savegame::ingame_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
save.save_game_interactive(gui_->video(), "", gui::OK_CANCEL);
} else {
save_blocker::on_unblock(this,&play_controller::save_game);

View file

@ -74,6 +74,11 @@ typedef std::map<std::string, player_controller> controller_map;
static void team_init(config& level, game_state& gamestate){
//if we are at the start of a new scenario, initialize carryover_sides
if(gamestate.snapshot.child_or_empty("variables")["turn_number"].to_int(-1)<1){
gamestate.carryover_sides = gamestate.carryover_sides_start;
}
carryover_info sides(gamestate.carryover_sides);
sides.transfer_to(level);
@ -151,7 +156,7 @@ static void store_carryover(game_state& gamestate, playsingle_controller& playco
gui2::show_transient_message(disp.video(), title, report.str(), "", true);
}
gamestate.carryover_sides = sides.to_config();
gamestate.carryover_sides_start = sides.to_config();
}
@ -165,17 +170,17 @@ void play_replay(display& disp, game_state& gamestate, const config& game_config
// 'starting_pos' will contain the position we start the game from.
config starting_pos;
if (gamestate.starting_pos.empty()){
if (gamestate.replay_start().empty()){
// Backwards compatibility code for 1.2 and 1.2.1
const config &scenario = game_config.find_child(type,"id",gamestate.classification().scenario);
assert(scenario);
gamestate.starting_pos = scenario;
gamestate.replay_start() = scenario;
}
starting_pos = gamestate.starting_pos;
starting_pos = gamestate.replay_start();
//for replays, use the variables specified in starting_pos
if (const config &vars = starting_pos.child("variables")) {
gamestate.carryover_sides["variables"] = vars;
gamestate.carryover_sides_start["variables"] = vars;
}
try {
@ -318,7 +323,7 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
// 'starting_pos' will contain the position we start the game from.
config starting_pos;
carryover_info sides = carryover_info(gamestate.carryover_sides);
carryover_info sides = carryover_info(gamestate.carryover_sides_start);
// Do we have any snapshot data?
// yes => this must be a savegame
@ -330,29 +335,36 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
// If the gamestate already contains a starting_pos,
// then we are starting a fresh multiplayer game.
// Otherwise this is the start of a campaign scenario.
if(gamestate.starting_pos["id"].empty() == false) {
LOG_G << "loading starting position...\n";
starting_pos = gamestate.starting_pos;
if(gamestate.replay_start()["id"].empty() == false) {
starting_pos = gamestate.replay_start();
scenario = &starting_pos;
} else {
//reload of the scenario, as starting_pos contains carryover information only
LOG_G << "loading scenario: '" << gamestate.classification().scenario << "'\n";
scenario = &game_config.find_child(type, "id", gamestate.classification().scenario);
if (*scenario) {
starting_pos = *scenario;
config temp(starting_pos);
write_players(gamestate, temp, false, true);
gamestate.starting_pos = temp;
starting_pos = temp;
scenario = &starting_pos;
} else
if(!*scenario){
scenario = NULL;
}
LOG_G << "scenario found: " << (scenario != NULL ? "yes" : "no") << "\n";
//TODO: remove when replay_start is confirmed
// if (*scenario) {
// starting_pos = *scenario;
// config temp(starting_pos);
// write_players(gamestate, temp, false, true);
// gamestate.replay_start() = temp;
// starting_pos = temp;
// scenario = &starting_pos;
// } else {
// scenario = NULL;
// }
// LOG_G << "scenario found: " << (scenario != NULL ? "yes" : "no") << "\n";
}
} else {
// This game was started from a savegame
LOG_G << "loading snapshot...\n";
starting_pos = gamestate.starting_pos;
starting_pos = gamestate.replay_start();
scenario = &gamestate.snapshot;
// When starting wesnoth --multiplayer there might be
// no variables which leads to a segfault
@ -366,7 +378,7 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
}
}
gamestate.carryover_sides = sides.to_config();
gamestate.carryover_sides_start = sides.to_config();
controller_map controllers;
@ -433,9 +445,11 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
//TODO comment or remove
//level_ = scenario;
//merge carryover information into the newly generated scenario
config temp(scenario2);
write_players(gamestate, temp, false, true);
gamestate.starting_pos = temp;
//TODO: remove when replay_start is confirmed
// config temp(scenario2);
// write_players(gamestate, temp, false, true);
// gamestate.starting_pos = temp;
scenario = &scenario2;
}
@ -458,19 +472,20 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
scenario = &new_level;
//merge carryover information into the scenario
config temp(new_level);
write_players(gamestate, temp, false, true);
gamestate.starting_pos = temp;
//TODO: remove when replay_start is confirmed
// config temp(new_level);
// write_players(gamestate, temp, false, true);
// gamestate.starting_pos = temp;
LOG_G << "generated map\n";
}
sound::empty_playlist();
//add the variables to the starting_pos unless they are already there
const config &wmlvars = gamestate.starting_pos.child("variables");
const config &wmlvars = gamestate.replay_start().child("variables");
if (!wmlvars || wmlvars.empty()){
gamestate.starting_pos.clear_children("variables");
gamestate.starting_pos.add_child("variables", gamestate.carryover_sides.child_or_empty("variables"));
gamestate.replay_start().clear_children("variables");
gamestate.replay_start().add_child("variables", gamestate.carryover_sides_start.child_or_empty("variables"));
}
switch (io_type){
@ -519,6 +534,7 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
recorder.clear();
gamestate.replay_data.clear();
gamestate.replay_start().clear();
// On DEFEAT, QUIT, or OBSERVER_END, we're done now
if (res != VICTORY)
@ -545,7 +561,10 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
// Switch to the next scenario.
gamestate.classification().scenario = gamestate.classification().next_scenario;
sides = carryover_info(gamestate.carryover_sides_start);
sides.rng().rotate_random();
gamestate.carryover_sides_start = sides.to_config();
if(io_type == IO_CLIENT) {
if (gamestate.classification().next_scenario.empty()) {
@ -619,7 +638,8 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
scenario2 = random_generate_scenario((*scenario)["scenario_generation"], scenario->child("generator"));
//TODO comment or remove
//level_ = scenario;
gamestate.starting_pos = scenario2;
//TODO: remove once replay_start is confirmed
//gamestate.starting_pos = scenario2;
scenario = &scenario2;
}
std::string map_data = (*scenario)["map_data"];
@ -639,8 +659,8 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
map["usage"] = "map";
map["border_size"] = 1;
scenario = &new_level;
gamestate.starting_pos = new_level;
//TODO: remove once replay_start is confirmed
//gamestate.starting_pos = new_level;
LOG_G << "generated map\n";
}
@ -655,14 +675,14 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
next_cfg.add_child("snapshot");
//move the player information into the hosts gamestate
write_players(gamestate, starting_pos, true, true);
gamestate.starting_pos = *scenario;
next_cfg.add_child("multiplayer", gamestate.mp_settings().to_config());
next_cfg.add_child("replay_start", gamestate.starting_pos);
next_cfg.add_child("replay_start", gamestate.replay_start());
//move side information from gamestate into the config that is sent to the other clients
next_cfg.clear_children("side");
BOOST_FOREACH(config& side, gamestate.starting_pos.child_range("side"))
BOOST_FOREACH(config& side, starting_pos.child_range("side")){
next_cfg.add_child("side", side);
}
network::send_data(cfg, 0);
}
@ -687,30 +707,30 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
// start-of-scenario save and the
// starting position needs to be empty,
// to force a reload of the scenario config.
if (gamestate.classification().campaign_type != "multiplayer"){
gamestate.starting_pos = config();
}
//TODO: remove once replay_start is confirmed
//if (gamestate.classification().campaign_type != "multiplayer"){
// gamestate.starting_pos = config();
//}
//add the variables to the starting position
gamestate.starting_pos.add_child("variables", sides.get_variables());
//gamestate.starting_pos.add_child("variables", sides.get_variables());
savegame::scenariostart_savegame save(gamestate, preferences::compress_saves());
save.save_game_automatic(disp.video());
}
if (gamestate.classification().campaign_type != "multiplayer"){
gamestate.starting_pos = *scenario;
//add the variables to the starting position
gamestate.starting_pos.add_child("variables", sides.get_variables());
write_players(gamestate, gamestate.starting_pos, true, true);
}
//TODO: remove once replay_start is confirmed
// if (gamestate.classification().campaign_type != "multiplayer"){
// gamestate.starting_pos = *scenario;
// //add the variables to the starting position
// gamestate.starting_pos.add_child("variables", sides.get_variables());
// write_players(gamestate, gamestate.starting_pos, true, true);
// }
}
gamestate.snapshot = config();
}
gamestate.carryover_sides = sides.to_config();
if (!gamestate.classification().scenario.empty() && gamestate.classification().scenario != "null") {
std::string message = _("Unknown scenario: '$scenario|'");
utils::string_map symbols;

View file

@ -53,6 +53,9 @@ static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
static lg::log_domain log_enginerefac("enginerefac");
#define LOG_RG LOG_STREAM(info, log_enginerefac)
playsingle_controller::playsingle_controller(const config& level,
game_state& state_of_game, const int ticks, const int num_turns,
const config& game_config, CVideo& video, bool skip_replay) :
@ -381,6 +384,12 @@ LEVEL_RESULT playsingle_controller::play_scenario(
throw end_level_exception(SKIP_TO_LINGER);
}
//before first turn, save a snapshot as replay_start
if(gamestate_.snapshot.empty()){
gamestate_.write_snapshot(gamestate_.replay_start(), gui_.get());
gamestate_.replay_start().merge_with(to_config());
}
// Avoid autosaving after loading, but still
// allow the first turn to have an autosave.
bool save = !loading_game_;
@ -526,7 +535,7 @@ LEVEL_RESULT playsingle_controller::play_scenario(
disconnect = true;
}
savegame::game_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
savegame::ingame_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO);
if(disconnect) {
throw network::error();
@ -879,121 +888,6 @@ void playsingle_controller::check_time_over(){
}
}
//TODO: remove store_recalls() and store_gold() once everything is moved and works correctly
//void playsingle_controller::store_recalls() {
// std::set<std::string> side_ids;
// std::vector<team>::iterator i;
// for(i=teams_.begin(); i!=teams_.end(); ++i) {
// side_ids.insert(i->save_id());
// if (i->persistent()) {
// config& new_side = gamestate_.snapshot.add_child("side");
// new_side["save_id"] = i->save_id();
// new_side["name"] = i->current_player();
// std::stringstream can_recruit;
// std::copy(i->recruits().begin(),i->recruits().end(),std::ostream_iterator<std::string>(can_recruit,","));
// std::string can_recruit_str = can_recruit.str();
// // Remove the trailing comma
// if(can_recruit_str.empty() == false) {
// can_recruit_str.resize(can_recruit_str.size()-1);
// }
// new_side["previous_recruits"] = can_recruit_str;
// LOG_NG << "stored side in snapshot:\n" << new_side["save_id"] << std::endl;
// //add the units of the recall list
// BOOST_FOREACH(const unit& u, i->recall_list()) {
// config& new_unit = new_side.add_child("unit");
// u.write(new_unit);
// }
// }
// }
// //add any players from starting_pos that do not have a team in the current scenario
// BOOST_FOREACH(const config &player_cfg, gamestate_.starting_pos.child_range("player")) {
// if (side_ids.count(player_cfg["save_id"]) == 0) {
// LOG_NG << "stored inactive side in snapshot:\n" << player_cfg["save_id"] << std::endl;
// gamestate_.snapshot.add_child("side", player_cfg);
// }
// }
//}
//
//void playsingle_controller::store_gold(bool obs)
//{
// bool has_next_scenario = !gamestate_.classification().next_scenario.empty() &&
// gamestate_.classification().next_scenario != "null";
//
// std::ostringstream report;
// std::string title;
//
// if (obs) {
// title = _("Scenario Report");
// } else {
// persist_.end_transaction();
// title = _("Victory");
// report << "<b>" << _("You have emerged victorious!") << "</b>\n\n";
// }
//
// int persistent_teams = 0;
// BOOST_FOREACH(const team &t, teams_) {
// if (t.persistent()) ++persistent_teams;
// }
//
// const end_level_data &end_level = get_end_level_data_const();
//
// if (persistent_teams > 0 && (has_next_scenario ||
// gamestate_.classification().campaign_type == "test"))
// {
// int finishing_bonus_per_turn =
// map_.villages().size() * game_config::village_income +
// game_config::base_income;
// int turns_left = std::max<int>(0, tod_manager_.number_of_turns() - turn());
// int finishing_bonus = (end_level.gold_bonus && turns_left > -1) ?
// finishing_bonus_per_turn * turns_left : 0;
// BOOST_FOREACH(const team &t, teams_)
// {
// if (!t.persistent()) continue;
// int carryover_gold = div100rounded((t.gold() + finishing_bonus) * end_level.carryover_percentage);
// config::child_itors side_range = gamestate_.snapshot.child_range("side");
// config::child_iterator side_it = side_range.first;
//
// // Check if this side already exists in the snapshot.
// while (side_it != side_range.second) {
// if ((*side_it)["save_id"] == t.save_id()) {
// (*side_it)["gold"] = str_cast<int>(carryover_gold);
// (*side_it)["gold_add"] = end_level.carryover_add;
// (*side_it)["color"] = t.color();
// (*side_it)["current_player"] = t.current_player();
// (*side_it)["name"] = t.name();
// break;
// }
// ++side_it;
// }
//
// // If it doesn't, add a new child.
// if (side_it == side_range.second) {
// config &new_side = gamestate_.snapshot.add_child("side");
// new_side["save_id"] = t.save_id();
// new_side["gold"] = str_cast<int>(carryover_gold);
// new_side["gold_add"] = end_level.carryover_add;
// new_side["color"] = t.color();
// new_side["current_player"] = t.current_player();
// new_side["name"] = t.name();
// }
//
// // Only show the report for ourselves.
// if (!t.is_human()) continue;
//
// if (persistent_teams > 1) {
// report << "\n<b>" << t.current_player() << "</b>\n";
// }
//
// report_victory(report, carryover_gold, t.gold(), finishing_bonus_per_turn, turns_left, finishing_bonus);
// }
// }
//
// if (end_level.carryover_report) {
// gui2::show_transient_message(gui_->video(), title, report.str(), "", true);
// }
//}
bool playsingle_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
{
bool res = true;

View file

@ -684,6 +684,7 @@ void loadgame::check_version_compatibility()
void loadgame::set_gamestate()
{
convert_old_saves(load_config_);
gamestate_ = game_state(load_config_, show_replay_);
// Get the status of the random in the snapshot.
@ -692,13 +693,13 @@ void loadgame::set_gamestate()
// For normal loading also restore the call count.
int seed = load_config_["random_seed"].to_int(42);
if(seed == 42){
config cfg = load_config_.child_or_empty("carryover_sides");
config cfg = load_config_.child_or_empty("carryover_sides_start");
seed = cfg["random_seed"].to_int(42);
}
unsigned calls = show_replay_ ? 0 : gamestate_.snapshot["random_calls"].to_int();
carryover_info sides(gamestate_.carryover_sides);
carryover_info sides(gamestate_.carryover_sides_start);
sides.rng().seed_random(seed, calls);
gamestate_.carryover_sides = sides.to_config();
gamestate_.carryover_sides_start = sides.to_config();
}
void loadgame::load_multiplayer_game()
@ -739,7 +740,7 @@ void loadgame::fill_mplevel_config(config& level){
// If we have a start of scenario MP campaign scenario the snapshot
// is empty the starting position contains the wanted info.
const config& start_data = !gamestate_.snapshot.empty() ? gamestate_.snapshot : gamestate_.starting_pos;
const config& start_data = !gamestate_.snapshot.empty() ? gamestate_.snapshot : gamestate_.replay_start();
level.add_child("map", start_data.child_or_empty("map"));
level["id"] = start_data["id"];
@ -764,7 +765,7 @@ void loadgame::fill_mplevel_config(config& level){
level.add_child("snapshot") = config();
} else {
level.add_child("snapshot") = start_data;
level.add_child("replay_start") = gamestate_.starting_pos;
level.add_child("replay_start") = gamestate_.replay_start();
}
level["random_seed"] = start_data["random_seed"];
level["random_calls"] = start_data["random_calls"];
@ -792,7 +793,6 @@ void loadgame::copy_era(config &cfg)
savegame::savegame(game_state& gamestate, const bool compress_saves, const std::string& title)
: gamestate_(gamestate)
, snapshot_()
, carryover_sides_(gamestate.carryover_sides)
, filename_()
, title_(title)
, error_message_(_("The game could not be saved: "))
@ -995,15 +995,14 @@ void savegame::write_game_to_disk(const std::string& filename)
}
}
void savegame::write_game(config_writer &out) const
void savegame::write_game(config_writer &out)
{
log_scope("write_game");
out.write_key_val("version", game_config::version);
out.write_key_val("next_underlying_unit_id", lexical_cast<std::string>(n_unit::id_manager::instance().get_save_id()));
gamestate_.write_config(out, false);
out.write_child("snapshot",snapshot_);
out.write_child("carryover_sides",carryover_sides_);
gamestate_.write_config(out);
out.open_child("statistics");
statistics::write_stats(out);
out.close_child("statistics");
@ -1040,18 +1039,10 @@ scenariostart_savegame::scenariostart_savegame(game_state &gamestate, const bool
set_filename(gamestate.classification().label);
}
void scenariostart_savegame::before_save()
{
//Add the player section to the starting position so we can get the correct recall list
//when loading the replay later on
// if there is no scenario information in the starting pos, add the (persistent) sides from the snapshot
// else do nothing, as persistence information was already added at the end of the previous scenario
if (gamestate().starting_pos["id"].empty()) {
BOOST_FOREACH(const config &snapshot_side, gamestate().snapshot.child_range("side")) {
//add all side tags (assuming they only contain carryover information)
gamestate().starting_pos.add_child("side", snapshot_side);
}
}
void scenariostart_savegame::write_game(config_writer &out){
savegame::write_game(out);
out.write_child("carryover_sides_start", gamestate().carryover_sides_start);
}
replay_savegame::replay_savegame(game_state &gamestate, const bool compress_saves)
@ -1069,9 +1060,18 @@ void replay_savegame::create_filename()
set_filename(stream.str());
}
void replay_savegame::write_game(config_writer &out) {
savegame::write_game(out);
out.write_child("carryover_sides_start", gamestate().carryover_sides_start);
out.write_child("carryover_sides", gamestate().carryover_sides);
out.write_child("replay_start", gamestate().replay_start());
out.write_child("replay", gamestate().replay_data);
}
autosave_savegame::autosave_savegame(game_state &gamestate,
game_display& gui, const config& snapshot_cfg, const bool compress_saves)
: game_savegame(gamestate, gui, snapshot_cfg, compress_saves)
: ingame_savegame(gamestate, gui, snapshot_cfg, compress_saves)
{
set_error_message(_("Could not auto save the game. Please save the game manually."));
}
@ -1098,7 +1098,7 @@ void autosave_savegame::create_filename()
}
oos_savegame::oos_savegame(const config& snapshot_cfg)
: game_savegame(*resources::state_of_game, *resources::screen, snapshot_cfg, preferences::compress_saves())
: ingame_savegame(*resources::state_of_game, *resources::screen, snapshot_cfg, preferences::compress_saves())
{}
int oos_savegame::show_save_dialog(CVideo& video, const std::string& message, const gui::DIALOG_TYPE /*dialog_type*/)
@ -1120,7 +1120,7 @@ int oos_savegame::show_save_dialog(CVideo& video, const std::string& message, co
return res;
}
game_savegame::game_savegame(game_state &gamestate,
ingame_savegame::ingame_savegame(game_state &gamestate,
game_display& gui, const config& snapshot_cfg, const bool compress_saves)
: savegame(gamestate, compress_saves, _("Save Game")),
gui_(gui)
@ -1128,7 +1128,7 @@ game_savegame::game_savegame(game_state &gamestate,
snapshot().merge_with(snapshot_cfg);
}
void game_savegame::create_filename()
void ingame_savegame::create_filename()
{
std::stringstream stream;
@ -1138,24 +1138,21 @@ void game_savegame::create_filename()
set_filename(stream.str());
}
void game_savegame::before_save()
void ingame_savegame::before_save()
{
savegame::before_save();
write_game_snapshot();
gamestate().write_snapshot(snapshot(), &gui_);
}
void game_savegame::write_game_snapshot()
{
snapshot()["snapshot"] = true;
snapshot()["playing_team"] = str_cast(gui_.playing_team());
void ingame_savegame::write_game(config_writer &out) {
log_scope("write_game");
write_events(snapshot());
write_music_play_list(snapshot());
gamestate().write_snapshot(snapshot());
gui_.labels().write(snapshot());
savegame::write_game(out);
out.write_child("snapshot",snapshot());
out.write_child("replay_start", gamestate().replay_start());
out.write_child("carryover_sides", gamestate().carryover_sides);
out.write_child("carryover_sides_start", gamestate().carryover_sides_start);
out.write_child("replay", gamestate().replay_data);
}
}

View file

@ -167,14 +167,16 @@ protected:
/** Customize the standard error message */
void set_error_message(const std::string& error_message) { error_message_ = error_message; }
const std::string& title() const { return title_; }
game_state& gamestate() const { return gamestate_; }
const std::string& title() { return title_; }
game_state& gamestate() { return gamestate_; }
config& snapshot() { return snapshot_; }
config& carryover_sides() { return carryover_sides_; }
/** If there needs to be some data fiddling before saving the game, this is the place to go. */
virtual void before_save();
/** Writing the savegame config to a file. */
virtual void write_game(config_writer &out);
private:
/** Checks if a certain character is allowed in a savefile name. */
static bool is_illegal_file_char(char c);
@ -191,8 +193,6 @@ private:
data manipulation has to happen before calling this method. */
void write_game_to_disk(const std::string& filename);
/** Writing the savegame config to a file. */
void write_game(config_writer &out) const;
/** Update the save_index */
void finish_save_game(const config_writer &out);
/** Throws game::save_game_failed. */
@ -205,8 +205,6 @@ private:
even if it is empty the code relies on it to be there. */
config snapshot_;
config carryover_sides_;
std::string filename_; /** Filename of the savegame file on disk */
const std::string title_; /** Title of the savegame dialog */
@ -220,10 +218,10 @@ private:
/** Class for "normal" midgame saves. The additional members are needed for creating the snapshot
information. */
class game_savegame : public savegame
class ingame_savegame : public savegame
{
public:
game_savegame(game_state& gamestate,
ingame_savegame(game_state& gamestate,
game_display& gui, const config& snapshot_cfg, const bool compress_saves);
private:
@ -233,8 +231,7 @@ private:
/** Builds the snapshot config. */
virtual void before_save();
/** For normal game saves. Builds a snapshot config object out of the relevant information. */
void write_game_snapshot();
void write_game(config_writer &out);
protected:
game_display& gui_;
@ -249,10 +246,12 @@ public:
private:
/** Create a filename for automatic saves */
virtual void create_filename();
void write_game(config_writer &out);
};
/** Class for autosaves. */
class autosave_savegame : public game_savegame
class autosave_savegame : public ingame_savegame
{
public:
autosave_savegame(game_state &gamestate,
@ -264,7 +263,7 @@ private:
virtual void create_filename();
};
class oos_savegame : public game_savegame
class oos_savegame : public ingame_savegame
{
public:
oos_savegame(const config& snapshot_cfg);
@ -281,8 +280,7 @@ public:
scenariostart_savegame(game_state& gamestate, const bool compress_saves);
private:
/** Adds the player information to the starting position (= [replay_start]). */
virtual void before_save();
void write_game(config_writer &out);
};
} //end of namespace savegame