mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-03 12:42:19 +00:00

Move gamestatus.cpp::load_game_summary to savegame.cpp and get rid of gamestatus.cpp::read_save_file.
681 lines
20 KiB
C++
681 lines
20 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003 - 2009 by Jörg Hinrichs, refactored from various
|
|
places formerly created by David White <dave@whitevine.net>
|
|
Part of the Battle for Wesnoth Project http://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 version 2
|
|
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 "savegame.hpp"
|
|
|
|
#include "dialogs.hpp" //FIXME: move illegal file character function here and get rid of this include
|
|
#include "foreach.hpp"
|
|
#include "game_end_exceptions.hpp"
|
|
#include "game_events.hpp"
|
|
#include "gettext.hpp"
|
|
#include "log.hpp"
|
|
#include "map.hpp"
|
|
#include "map_label.hpp"
|
|
#include "preferences_display.hpp"
|
|
#include "replay.hpp"
|
|
#include "serialization/binary_or_text.hpp"
|
|
#include "serialization/parser.hpp"
|
|
#include "sound.hpp"
|
|
#include "statistics.hpp"
|
|
#include "unit_id.hpp"
|
|
#include "version.hpp"
|
|
|
|
#define LOG_SAVE LOG_STREAM(info, engine)
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
|
|
/**
|
|
* conv_ansi_utf8()
|
|
* - Convert a string between ANSI encoding (for Windows filename) and UTF-8
|
|
* string &name
|
|
* - filename to be converted
|
|
* bool a2u
|
|
* - if true, convert the string from ANSI to UTF-8.
|
|
* - if false, reverse. (convert it from UTF-8 to ANSI)
|
|
*/
|
|
void conv_ansi_utf8(std::string &name, bool a2u) {
|
|
int wlen = MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0,
|
|
name.c_str(), -1, NULL, 0);
|
|
if (wlen == 0) return;
|
|
WCHAR *wc = new WCHAR[wlen];
|
|
if (wc == NULL) return;
|
|
if (MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0, name.c_str(), -1,
|
|
wc, wlen) == 0) {
|
|
delete wc;
|
|
return;
|
|
}
|
|
int alen = WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
|
|
NULL, 0, NULL, NULL);
|
|
if (alen == 0) {
|
|
delete wc;
|
|
return;
|
|
}
|
|
CHAR *ac = new CHAR[alen];
|
|
if (ac == NULL) {
|
|
delete wc;
|
|
return;
|
|
}
|
|
WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
|
|
ac, alen, NULL, NULL);
|
|
delete wc;
|
|
if (ac == NULL) {
|
|
return;
|
|
}
|
|
name = ac;
|
|
delete ac;
|
|
|
|
return;
|
|
}
|
|
|
|
void replace_underbar2space(std::string &name) {
|
|
LOG_SAVE << "conv(A2U)-from:[" << name << "]" << std::endl;
|
|
conv_ansi_utf8(name, true);
|
|
LOG_SAVE << "conv(A2U)-to:[" << name << "]" << std::endl;
|
|
LOG_SAVE << "replace_underbar2space-from:[" << name << "]" << std::endl;
|
|
std::replace(name.begin(), name.end(), '_', ' ');
|
|
LOG_SAVE << "replace_underbar2space-to:[" << name << "]" << std::endl;
|
|
}
|
|
|
|
void replace_space2underbar(std::string &name) {
|
|
LOG_SAVE << "conv(U2A)-from:[" << name << "]" << std::endl;
|
|
conv_ansi_utf8(name, false);
|
|
LOG_SAVE << "conv(U2A)-to:[" << name << "]" << std::endl;
|
|
LOG_SAVE << "replace_underbar2space-from:[" << name << "]" << std::endl;
|
|
std::replace(name.begin(), name.end(), ' ', '_');
|
|
LOG_SAVE << "replace_underbar2space-to:[" << name << "]" << std::endl;
|
|
}
|
|
#else /* ! _WIN32 */
|
|
void replace_underbar2space(std::string &name) {
|
|
std::replace(name.begin(),name.end(),'_',' ');
|
|
}
|
|
void replace_space2underbar(std::string &name) {
|
|
std::replace(name.begin(),name.end(),' ','_');
|
|
}
|
|
#endif /* _WIN32 */
|
|
|
|
static void read_save_file(const std::string& name, config& cfg, std::string* error_log)
|
|
{
|
|
std::string modified_name = name;
|
|
replace_space2underbar(modified_name);
|
|
|
|
// Try reading the file both with and without underscores
|
|
scoped_istream file_stream = istream_file(get_saves_dir() + "/" + modified_name);
|
|
if (file_stream->fail())
|
|
file_stream = istream_file(get_saves_dir() + "/" + name);
|
|
|
|
cfg.clear();
|
|
try{
|
|
if(is_gzip_file(name)) {
|
|
read_gz(cfg, *file_stream, error_log);
|
|
} else {
|
|
detect_format_and_read(cfg, *file_stream, error_log);
|
|
}
|
|
} catch (config::error &err)
|
|
{
|
|
LOG_SAVE << err.message;
|
|
throw game::load_game_failed();
|
|
}
|
|
|
|
if(cfg.empty()) {
|
|
LOG_SAVE << "Could not parse file data into config\n";
|
|
throw game::load_game_failed();
|
|
}
|
|
}
|
|
|
|
void save_summary::load_summary(const std::string& name, config& cfg_summary, std::string* error_log){
|
|
log_scope("load_game_summary");
|
|
|
|
config cfg;
|
|
read_save_file(name,cfg,error_log);
|
|
|
|
::extract_summary_from_config(cfg, cfg_summary);
|
|
}
|
|
|
|
loadgame::loadgame(display& gui, const config& game_config, game_state& gamestate)
|
|
: game_config_(game_config)
|
|
, gui_(gui)
|
|
, gamestate_(gamestate)
|
|
{
|
|
gamestate_ = game_state();
|
|
}
|
|
|
|
void loadgame::show_dialog(bool show_replay, bool cancel_orders)
|
|
{
|
|
bool show_replay_dialog = show_replay;
|
|
bool cancel_orders_dialog = cancel_orders;
|
|
|
|
//FIXME: Integrate the load_game dialog into this class
|
|
filename_ = dialogs::load_game_dialog(gui_, game_config_, &show_replay_dialog, &cancel_orders_dialog);
|
|
|
|
show_replay_ = show_replay_dialog;
|
|
cancel_orders_ = cancel_orders_dialog;
|
|
}
|
|
|
|
void loadgame::load_game()
|
|
{
|
|
show_dialog(false, false);
|
|
|
|
if(filename_ != "")
|
|
throw game::load_game_exception(filename_, show_replay_, cancel_orders_);
|
|
}
|
|
|
|
void loadgame::load_game(std::string& filename, bool show_replay, bool cancel_orders)
|
|
{
|
|
filename_ = filename;
|
|
|
|
if (filename_.empty()){
|
|
show_dialog(show_replay, cancel_orders);
|
|
}
|
|
else{
|
|
show_replay_ = show_replay;
|
|
cancel_orders_ = cancel_orders;
|
|
}
|
|
|
|
if (filename_.empty())
|
|
throw load_game_cancelled_exception();
|
|
|
|
std::string error_log;
|
|
read_save_file(filename_, load_config_, &error_log);
|
|
|
|
if(!error_log.empty()) {
|
|
try {
|
|
gui::show_error_message(gui_,
|
|
_("Warning: The file you have tried to load is corrupt. Loading anyway.\n") +
|
|
error_log);
|
|
} catch (utils::invalid_utf8_exception&) {
|
|
gui::show_error_message(gui_,
|
|
_("Warning: The file you have tried to load is corrupt. Loading anyway.\n") +
|
|
std::string("(UTF-8 ERROR)"));
|
|
}
|
|
}
|
|
|
|
gamestate_.difficulty = load_config_["difficulty"];
|
|
gamestate_.campaign_define = load_config_["campaign_define"];
|
|
gamestate_.campaign_type = load_config_["campaign_type"];
|
|
gamestate_.campaign_xtra_defines = utils::split(load_config_["campaign_extra_defines"]);
|
|
gamestate_.version = load_config_["version"];
|
|
|
|
if(gamestate_.version != game_config::version) {
|
|
const version_info parsed_savegame_version(gamestate_.version);
|
|
if(game_config::wesnoth_version.minor_version() % 2 != 0 ||
|
|
game_config::wesnoth_version.major_version() != parsed_savegame_version.major_version() ||
|
|
game_config::wesnoth_version.minor_version() != parsed_savegame_version.minor_version()) {
|
|
check_version_compatibility();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void loadgame::check_version_compatibility()
|
|
{
|
|
// do not load if too old, if either the savegame or the current game
|
|
// has the version 'test' allow loading
|
|
if(!game_config::is_compatible_savegame_version(gamestate_.version)) {
|
|
/* GCC-3.3 needs a temp var otherwise compilation fails */
|
|
gui::message_dialog dlg(gui_, "", _("This save is from a version too old to be loaded."));
|
|
dlg.show();
|
|
throw load_game_cancelled_exception();
|
|
}
|
|
|
|
const int res = gui::dialog(gui_,"",
|
|
_("This save is from a different version of the game. Do you want to try to load it?"),
|
|
gui::YES_NO).show();
|
|
if(res == 1) {
|
|
throw load_game_cancelled_exception();
|
|
}
|
|
}
|
|
|
|
void loadgame::set_gamestate()
|
|
{
|
|
gamestate_ = game_state(load_config_, show_replay_);
|
|
|
|
// Get the status of the random in the snapshot.
|
|
// For a replay we need to restore the start only, the replaying gets at
|
|
// proper location.
|
|
// For normal loading also restore the call count.
|
|
const int seed = lexical_cast_default<int>
|
|
(load_config_["random_seed"], 42);
|
|
const unsigned calls = show_replay_ ? 0 :
|
|
lexical_cast_default<unsigned> (gamestate_.snapshot["random_calls"]);
|
|
gamestate_.rng().seed_random(seed, calls);
|
|
}
|
|
|
|
void loadgame::load_multiplayer_game()
|
|
{
|
|
show_dialog(false, false);
|
|
|
|
if (filename_.empty())
|
|
throw load_game_cancelled_exception();
|
|
|
|
std::string error_log;
|
|
{
|
|
cursor::setter cur(cursor::WAIT);
|
|
log_scope("load_game");
|
|
|
|
read_save_file(filename_, load_config_, &error_log);
|
|
copy_era(load_config_);
|
|
|
|
gamestate_ = game_state(load_config_);
|
|
}
|
|
|
|
if(!error_log.empty()) {
|
|
gui::show_error_message(gui_,
|
|
_("The file you have tried to load is corrupt: '") +
|
|
error_log);
|
|
throw load_game_cancelled_exception();
|
|
}
|
|
|
|
if(gamestate_.campaign_type != "multiplayer") {
|
|
/* GCC-3.3 needs a temp var otherwise compilation fails */
|
|
gui::message_dialog dlg(gui_, "", _("This is not a multiplayer save"));
|
|
dlg.show();
|
|
throw load_game_cancelled_exception();
|
|
}
|
|
|
|
check_version_compatibility();
|
|
}
|
|
|
|
void loadgame::copy_era(config &cfg)
|
|
{
|
|
const config &replay_start = cfg.child("replay_start");
|
|
if (!replay_start) return;
|
|
|
|
const config &era = replay_start.child("era");
|
|
if (!era) return;
|
|
|
|
config &snapshot = cfg.child("snapshot");
|
|
if (!snapshot) return;
|
|
|
|
snapshot.add_child("era", era);
|
|
}
|
|
|
|
savegame::savegame(game_state& gamestate, const std::string title)
|
|
: gamestate_(gamestate)
|
|
, snapshot_()
|
|
, filename_()
|
|
, title_(title)
|
|
, error_message_(_("The game could not be saved"))
|
|
, interactive_(false)
|
|
{}
|
|
|
|
void savegame::save_game_interactive(display& gui, const std::string& message,
|
|
gui::DIALOG_TYPE dialog_type, const bool has_exit_button,
|
|
const bool ask_for_filename)
|
|
{
|
|
interactive_ = ask_for_filename;
|
|
create_filename();
|
|
const int res = dialogs::get_save_name(gui, message, _("Name: "), &filename_, dialog_type, title_, has_exit_button, ask_for_filename);
|
|
|
|
if (res == 2)
|
|
throw end_level_exception(QUIT);
|
|
|
|
if (res != 0)
|
|
return;
|
|
|
|
save_game(&gui);
|
|
}
|
|
|
|
void savegame::before_save()
|
|
{
|
|
gamestate_.replay_data = recorder.get_replay_data();
|
|
}
|
|
|
|
void savegame::save_game(const std::string& filename)
|
|
{
|
|
filename_ = filename;
|
|
if (!interactive_)
|
|
create_filename();
|
|
|
|
save_game();
|
|
}
|
|
|
|
void savegame::save_game(display* gui)
|
|
{
|
|
try {
|
|
before_save();
|
|
save_game_internal(filename_);
|
|
|
|
if (gui != NULL && interactive_)
|
|
gui::message_dialog(*gui,_("Saved"),_("The game has been saved")).show();
|
|
} catch(game::save_game_failed&) {
|
|
if (gui != NULL){
|
|
gui::message_dialog to_show(*gui,_("Error"), error_message_);
|
|
to_show.show();
|
|
//do not bother retrying, since the user can just try to save the game again
|
|
//maybe show a yes-no dialog for "disable autosaves now"?
|
|
}
|
|
};
|
|
}
|
|
|
|
void savegame::save_game_internal(const std::string& filename)
|
|
{
|
|
LOG_SAVE << "savegame::save_game";
|
|
|
|
filename_ = filename;
|
|
|
|
if(preferences::compress_saves()) {
|
|
filename_ += ".gz";
|
|
}
|
|
|
|
std::stringstream ss;
|
|
{
|
|
config_writer out(ss, preferences::compress_saves());
|
|
write_game(out);
|
|
finish_save_game(out);
|
|
}
|
|
scoped_ostream os(open_save_game(filename_));
|
|
(*os) << ss.str();
|
|
|
|
if (!os->good()) {
|
|
throw game::save_game_failed(_("Could not write to file"));
|
|
}
|
|
}
|
|
|
|
void savegame::write_game(config_writer &out) const
|
|
{
|
|
log_scope("write_game");
|
|
|
|
out.write_key_val("label", gamestate_.label);
|
|
out.write_key_val("history", gamestate_.history);
|
|
out.write_key_val("abbrev", gamestate_.abbrev);
|
|
out.write_key_val("version", game_config::version);
|
|
out.write_key_val("scenario", gamestate_.scenario);
|
|
out.write_key_val("next_scenario", gamestate_.next_scenario);
|
|
out.write_key_val("completion", gamestate_.completion);
|
|
out.write_key_val("campaign", gamestate_.campaign);
|
|
out.write_key_val("campaign_type", gamestate_.campaign_type);
|
|
out.write_key_val("difficulty", gamestate_.difficulty);
|
|
out.write_key_val("campaign_define", gamestate_.campaign_define);
|
|
out.write_key_val("campaign_extra_defines", utils::join(gamestate_.campaign_xtra_defines));
|
|
out.write_key_val("random_seed", lexical_cast<std::string>(gamestate_.rng().get_random_seed()));
|
|
out.write_key_val("random_calls", lexical_cast<std::string>(gamestate_.rng().get_random_calls()));
|
|
out.write_key_val("next_underlying_unit_id", lexical_cast<std::string>(n_unit::id_manager::instance().get_save_id()));
|
|
out.write_key_val("end_text", gamestate_.end_text);
|
|
out.write_key_val("end_text_duration", str_cast<unsigned int>(gamestate_.end_text_duration));
|
|
out.write_child("variables", gamestate_.get_variables());
|
|
|
|
for(std::map<std::string, wml_menu_item *>::const_iterator j=gamestate_.wml_menu_items.begin();
|
|
j!=gamestate_.wml_menu_items.end(); ++j) {
|
|
out.open_child("menu_item");
|
|
out.write_key_val("id", j->first);
|
|
out.write_key_val("image", j->second->image);
|
|
out.write_key_val("description", j->second->description);
|
|
out.write_key_val("needs_select", (j->second->needs_select) ? "yes" : "no");
|
|
if(!j->second->show_if.empty())
|
|
out.write_child("show_if", j->second->show_if);
|
|
if(!j->second->filter_location.empty())
|
|
out.write_child("filter_location", j->second->filter_location);
|
|
if(!j->second->command.empty())
|
|
out.write_child("command", j->second->command);
|
|
out.close_child("menu_item");
|
|
}
|
|
|
|
if (!gamestate_.replay_data.child("replay")) {
|
|
out.write_child("replay", gamestate_.replay_data);
|
|
}
|
|
|
|
out.write_child("snapshot",snapshot_);
|
|
out.write_child("replay_start",gamestate_.starting_pos);
|
|
out.open_child("statistics");
|
|
statistics::write_stats(out);
|
|
out.close_child("statistics");
|
|
}
|
|
|
|
void savegame::finish_save_game(const config_writer &out)
|
|
{
|
|
std::string name = gamestate_.label;
|
|
std::replace(name.begin(),name.end(),' ','_');
|
|
std::string fname(get_saves_dir() + "/" + name);
|
|
|
|
try {
|
|
if(!out.good()) {
|
|
throw game::save_game_failed(_("Could not write to file"));
|
|
}
|
|
|
|
config& summary = save_summary(gamestate_.label);
|
|
extract_summary_data_from_save(summary);
|
|
const int mod_time = static_cast<int>(file_create_time(fname));
|
|
summary["mod_time"] = str_cast(mod_time);
|
|
write_save_index();
|
|
} catch(io_exception& e) {
|
|
throw game::save_game_failed(e.what());
|
|
}
|
|
}
|
|
|
|
void savegame::extract_summary_data_from_save(config& out)
|
|
{
|
|
const bool has_replay = gamestate_.replay_data.empty() == false;
|
|
const bool has_snapshot = gamestate_.snapshot.child("side");
|
|
|
|
out["replay"] = has_replay ? "yes" : "no";
|
|
out["snapshot"] = has_snapshot ? "yes" : "no";
|
|
|
|
out["label"] = gamestate_.label;
|
|
out["campaign"] = gamestate_.campaign;
|
|
out["campaign_type"] = gamestate_.campaign_type;
|
|
out["scenario"] = gamestate_.scenario;
|
|
out["difficulty"] = gamestate_.difficulty;
|
|
out["version"] = gamestate_.version;
|
|
out["corrupt"] = "";
|
|
|
|
if(has_snapshot) {
|
|
out["turn"] = gamestate_.snapshot["turn_at"];
|
|
if(gamestate_.snapshot["turns"] != "-1") {
|
|
out["turn"] = out["turn"].str() + "/" + gamestate_.snapshot["turns"].str();
|
|
}
|
|
}
|
|
|
|
// Find the first human leader so we can display their icon in the load menu.
|
|
|
|
/** @todo Ideally we should grab all leaders if there's more than 1 human player? */
|
|
std::string leader;
|
|
|
|
for(std::map<std::string, player_info>::const_iterator p = gamestate_.players.begin();
|
|
p!=gamestate_.players.end(); ++p) {
|
|
for(std::vector<unit>::const_iterator u = p->second.available_units.begin(); u != p->second.available_units.end(); ++u) {
|
|
if(u->can_recruit()) {
|
|
leader = u->type_id();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool shrouded = false;
|
|
|
|
if(leader == "") {
|
|
const config& snapshot = has_snapshot ? gamestate_.snapshot : gamestate_.starting_pos;
|
|
foreach (const config &side, snapshot.child_range("side"))
|
|
{
|
|
if (side["controller"] != "human") {
|
|
continue;
|
|
}
|
|
|
|
if (utils::string_bool(side["shroud"])) {
|
|
shrouded = true;
|
|
}
|
|
|
|
foreach (const config &u, side.child_range("unit"))
|
|
{
|
|
if (utils::string_bool(u["canrecruit"], false)) {
|
|
leader = u["id"];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
out["leader"] = leader;
|
|
out["map_data"] = "";
|
|
|
|
if(!shrouded) {
|
|
if(has_snapshot) {
|
|
if (!gamestate_.snapshot.find_child("side", "shroud", "yes")) {
|
|
out["map_data"] = gamestate_.snapshot["map_data"];
|
|
}
|
|
} else if(has_replay) {
|
|
if (!gamestate_.starting_pos.find_child("side", "shroud", "yes")) {
|
|
out["map_data"] = gamestate_.starting_pos["map_data"];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void savegame::set_filename(std::string filename)
|
|
{
|
|
filename.erase(std::remove_if(filename.begin(), filename.end(),
|
|
dialogs::is_illegal_file_char), filename.end());
|
|
filename_ = filename;
|
|
}
|
|
|
|
scenariostart_savegame::scenariostart_savegame(game_state &gamestate)
|
|
: savegame(gamestate)
|
|
{
|
|
set_filename(gamestate.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
|
|
write_players(gamestate(), gamestate().starting_pos);
|
|
}
|
|
|
|
replay_savegame::replay_savegame(game_state &gamestate)
|
|
: savegame(gamestate, _("Save Replay"))
|
|
{}
|
|
|
|
void replay_savegame::create_filename()
|
|
{
|
|
std::stringstream stream;
|
|
|
|
const std::string ellipsed_name = font::make_text_ellipsis(gamestate().label,
|
|
font::SIZE_NORMAL, 200);
|
|
stream << ellipsed_name << " " << _("replay");
|
|
|
|
set_filename(stream.str());
|
|
}
|
|
|
|
autosave_savegame::autosave_savegame(game_state &gamestate, const config& level_cfg,
|
|
const game_display& gui, const std::vector<team>& teams,
|
|
const unit_map& units, const gamestatus& gamestatus,
|
|
const gamemap& map)
|
|
: game_savegame(gamestate, level_cfg, gui, teams, units, gamestatus, map)
|
|
{
|
|
set_error_message(_("Could not auto save the game. Please save the game manually."));
|
|
create_filename();
|
|
}
|
|
|
|
void autosave_savegame::create_filename()
|
|
{
|
|
std::string filename;
|
|
if (gamestate().label.empty())
|
|
filename = _("Auto-Save");
|
|
else
|
|
filename = gamestate().label + "-" + _("Auto-Save") + lexical_cast<std::string>(gamestatus_.turn());
|
|
|
|
set_filename(filename);
|
|
}
|
|
|
|
game_savegame::game_savegame(game_state &gamestate, const config& level_cfg,
|
|
const game_display& gui, const std::vector<team>& teams,
|
|
const unit_map& units, const gamestatus& gamestatus,
|
|
const gamemap& map)
|
|
: savegame(gamestate, _("Save Game")),
|
|
level_cfg_(level_cfg), gui_(gui),
|
|
teams_(teams), units_(units),
|
|
gamestatus_(gamestatus), map_(map)
|
|
{}
|
|
|
|
void game_savegame::create_filename()
|
|
{
|
|
std::stringstream stream;
|
|
|
|
const std::string ellipsed_name = font::make_text_ellipsis(gamestate().label,
|
|
font::SIZE_NORMAL, 200);
|
|
stream << ellipsed_name << " " << _("Turn") << " " << gamestatus_.turn();
|
|
set_filename(stream.str());
|
|
}
|
|
|
|
void game_savegame::before_save()
|
|
{
|
|
savegame::before_save();
|
|
write_game_snapshot();
|
|
}
|
|
|
|
void game_savegame::write_game_snapshot()
|
|
{
|
|
snapshot().merge_attributes(level_cfg_);
|
|
|
|
snapshot()["snapshot"] = "yes";
|
|
|
|
std::stringstream buf;
|
|
buf << gui_.playing_team();
|
|
snapshot()["playing_team"] = buf.str();
|
|
|
|
for(std::vector<team>::const_iterator t = teams_.begin(); t != teams_.end(); ++t) {
|
|
const unsigned int side_num = t - teams_.begin() + 1;
|
|
|
|
config& side = snapshot().add_child("side");
|
|
t->write(side);
|
|
side["no_leader"] = "yes";
|
|
buf.str(std::string());
|
|
buf << side_num;
|
|
side["side"] = buf.str();
|
|
|
|
//current visible units
|
|
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
|
|
if(i->second.side() == side_num) {
|
|
config& u = side.add_child("unit");
|
|
i->first.write(u);
|
|
i->second.write(u);
|
|
}
|
|
}
|
|
//recall list
|
|
{
|
|
for(std::map<std::string, player_info>::const_iterator i=gamestate().players.begin();
|
|
i!=gamestate().players.end(); ++i) {
|
|
for(std::vector<unit>::const_iterator j = i->second.available_units.begin();
|
|
j != i->second.available_units.end(); ++j) {
|
|
if (j->side() == side_num){
|
|
config& u = side.add_child("unit");
|
|
j->write(u);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gamestatus_.write(snapshot());
|
|
game_events::write_events(snapshot());
|
|
|
|
// Write terrain_graphics data in snapshot, too
|
|
const config::child_list& terrains = level_cfg_.get_children("terrain_graphics");
|
|
for(config::child_list::const_iterator tg = terrains.begin();
|
|
tg != terrains.end(); ++tg) {
|
|
|
|
snapshot().add_child("terrain_graphics", **tg);
|
|
}
|
|
|
|
sound::write_music_play_list(snapshot());
|
|
|
|
gamestate().write_snapshot(snapshot());
|
|
|
|
//write out the current state of the map
|
|
snapshot()["map_data"] = map_.write();
|
|
|
|
gui_.labels().write(snapshot());
|
|
}
|
|
|