made multiplayer games replayable

This commit is contained in:
Dave White 2004-03-30 18:21:04 +00:00
parent 59904c4e45
commit 08977ec4b1
11 changed files with 93 additions and 39 deletions

View file

@ -8,7 +8,7 @@ hitpoints=48
movement_type=elusivefoot
movement=6
experience=2
level=3
level=2
alignment=neutral
advanceto=Battle Princess
cost=110

View file

@ -392,6 +392,10 @@ config::~config()
config& config::operator=(const config& cfg)
{
if(this == &cfg) {
return *this;
}
clear();
values = cfg.values;

View file

@ -66,13 +66,22 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
//see if we load the scenario from the scenario data -- if there is
//no snapshot data available from a save, or if the user has selected
//to view the replay from scratch
if(state.starting_pos.child("side") == NULL || !recorder.at_end()) {
scenario = game_config.find_child(type,"id",state.scenario);
if(state.snapshot.child("side") == NULL || !recorder.at_end()) {
//if the starting state is specified, then use that,
//otherwise get the scenario data and start from there.
if(state.starting_pos.empty() == false) {
std::cerr << "loading starting position: '" << state.starting_pos.write() << "'\n";
starting_pos = state.starting_pos;
scenario = &starting_pos;
} else {
scenario = game_config.find_child(type,"id",state.scenario);
}
} else {
std::cerr << "loading snapshot...\n";
//load from a save-snapshot.
starting_pos = state.starting_pos;
starting_pos = state.snapshot;
scenario = &starting_pos;
state = read_game(units_data,&state.starting_pos);
state = read_game(units_data,&state.snapshot);
}
while(scenario != NULL) {
@ -94,7 +103,7 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
video,state,story);
}
state.starting_pos = config();
state.snapshot = config();
//ask to save a replay of the game
if(res == VICTORY || res == DEFEAT) {
@ -114,9 +123,9 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
&label);
if(should_save == 0) {
try {
config starting_pos;
config snapshot, starting_pos;
recorder.save_game(units_data,label,starting_pos);
recorder.save_game(units_data,label,snapshot,starting_pos);
} catch(gamestatus::save_game_failed& e) {
gui::show_dialog(disp,NULL,"",string_table["save_game_failed"],gui::MESSAGE);
retry=true;
@ -476,7 +485,7 @@ int play_game(int argc, char** argv)
state.campaign_type = "multiplayer";
state.scenario = "";
state.starting_pos = config();
state.snapshot = config();
config level = *lvl;
std::vector<config*> story;
@ -585,11 +594,11 @@ int play_game(int argc, char** argv)
recorder = replay(state.replay_data);
std::cerr << "has starting position: " << (state.starting_pos.child("side") ? "yes" : "no") << "\n";
std::cerr << "has snapshot: " << (state.snapshot.child("side") ? "yes" : "no") << "\n";
//only play replay data if the user has selected to view the replay,
//or if there is no starting position data to use.
if(!show_replay && state.starting_pos.child("side") != NULL) {
if(!show_replay && state.snapshot.child("side") != NULL) {
std::cerr << "setting replay to end...\n";
recorder.set_to_end();
if(!recorder.at_end())
@ -609,7 +618,7 @@ int play_game(int argc, char** argv)
if(state.campaign_type == "multiplayer") {
//make all network players local
for(config::child_itors sides = state.starting_pos.child_range("side");
for(config::child_itors sides = state.snapshot.child_range("side");
sides.first != sides.second; ++sides.first) {
if((**sides.first)["controller"] == "network")
(**sides.first)["controller"] = "human";
@ -618,9 +627,15 @@ int play_game(int argc, char** argv)
recorder.set_save_info(state);
std::vector<config*> story;
config starting_pos;
if(recorder.at_end()) {
starting_pos = state.snapshot;
} else {
starting_pos = state.starting_pos;
}
try {
play_level(units_data,game_config,&state.starting_pos,
video,state,story);
play_level(units_data,game_config,&starting_pos,video,state,story);
recorder.clear();
} catch(gamestatus::load_game_failed& e) {
std::cerr << "error loading the game: " << e.message

View file

@ -156,9 +156,20 @@ game_state read_game(game_data& data, const config* cfg)
res.replay_data = *replay;
}
const config* starting_pos = cfg->child("start");
if(starting_pos != NULL) {
res.starting_pos = *starting_pos;
const config* snapshot = cfg->child("snapshot");
//older save files used to use 'start', so still support that for now
if(snapshot == NULL) {
snapshot = cfg->child("start");
}
if(snapshot != NULL) {
res.snapshot = *snapshot;
}
const config* replay_start = cfg->child("replay_start");
if(replay_start != NULL) {
res.starting_pos = *replay_start;
}
res.can_recruit.clear();
@ -206,7 +217,8 @@ void write_game(const game_state& game, config& cfg)
cfg.add_child("replay",game.replay_data);
}
cfg.add_child("start",game.starting_pos);
cfg.add_child("snapshot",game.snapshot);
cfg.add_child("replay_start",game.starting_pos);
std::stringstream can_recruit;
std::copy(game.can_recruit.begin(),game.can_recruit.end(),std::ostream_iterator<std::string>(can_recruit,","));

View file

@ -108,10 +108,17 @@ struct game_state
//take the game up to the position it was saved at.
config replay_data;
//for multiplayer games, the position the game started in may be different to
//the scenario, so we save the starting state of the game here.
config starting_pos;
//information about the starting conditions of the scenario. Used in
//multiplayer games, when the starting position isn't just literally
//read from a file, since there is game setup information.
config starting_pos;
//the snapshot of the game's current contents. i.e. unless the player selects
//to view a replay, the game's settings are read in from this object
config snapshot;
};
struct save_info {

View file

@ -413,6 +413,7 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
//if yes, then show the replay, otherwise
//skip showing the replay
if(res == 0) {
sides = state.starting_pos;
recorder.set_skip(0);
} else {
std::cerr << "skipping...\n";
@ -424,8 +425,9 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
}
std::cerr << "starting game\n";
state.starting_pos = sides;
state.snapshot = sides;
state.can_recruit.clear();
recorder.set_save_info(state);

View file

@ -113,7 +113,7 @@ int mp_connect::load_map(int map, int num_turns, int village_gold,
}
}
loaded_level_ = state_->starting_pos;
loaded_level_ = state_->snapshot;
level_ptr = &loaded_level_;
//make all sides untaken
@ -610,8 +610,6 @@ int mp_connect::gui_do()
config cfg;
cfg.add_child("start_game");
network::send_data(cfg);
state_->starting_pos = *level_;
recorder.set_save_info(*state_);
@ -625,6 +623,12 @@ int mp_connect::gui_do()
}
}
state_->snapshot = *level_;
if(save_ == false) {
state_->starting_pos = *level_;
}
//any replay data isn't meant to hang around under the level,
//it was just there to tell clients about the replay data
level_->clear_children("replay");

View file

@ -99,11 +99,6 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
game_state& state_of_game,
const std::vector<config*>& story)
{
const statistics::scenario_context statistics_context(translate_string_default((*level)["id"],(*level)["name"]));
const int num_turns = atoi(level->values["turns"].c_str());
gamestatus status(*level,num_turns);
std::string map_data = (*level)["map_data"];
if(map_data == "" && (*level)["map"] != "") {
map_data = read_file("data/maps/" + (*level)["map"]);
@ -112,8 +107,22 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
//if the map should be randomly generated
if(map_data == "" && (*level)["map_generation"] != "") {
map_data = random_generate_map((*level)["map_generation"],level->child("generator"));
//since we've had to generate the map, make sure that when we save the game,
//it will not ask for the map to be generated again on reload
static config new_level;
new_level = *level;
new_level.values["map_data"] = map_data;
level = &new_level;
state_of_game.starting_pos = new_level;
}
const statistics::scenario_context statistics_context(translate_string_default((*level)["id"],(*level)["name"]));
const int num_turns = atoi(level->values["turns"].c_str());
gamestatus status(*level,num_turns);
gamemap map(game_config,map_data);
CKey key;

View file

@ -968,10 +968,10 @@ void turn_info::end_turn()
end_turn_ = true;
//auto-save
config start_pos;
write_game_snapshot(start_pos);
config snapshot;
write_game_snapshot(snapshot);
try {
recorder.save_game(gameinfo_,string_table["auto_save"],start_pos);
recorder.save_game(gameinfo_,string_table["auto_save"],snapshot,state_of_game_.starting_pos);
} catch(gamestatus::save_game_failed& e) {
gui::show_dialog(gui_,NULL,"",string_table["auto_save_game_failed"],gui::MESSAGE);
//do not bother retrying, since the user can just save the game
@ -1256,10 +1256,10 @@ void turn_info::save_game(const std::string& message)
const int res = dialogs::get_save_name(gui_,message,string_table["save_game_label"],&label,gui::OK_CANCEL);
if(res == 0) {
config start;
write_game_snapshot(start);
config snapshot;
write_game_snapshot(snapshot);
try {
recorder.save_game(gameinfo_,label,start);
recorder.save_game(gameinfo_,label,snapshot,state_of_game_.starting_pos);
gui::show_dialog(gui_,NULL,"",string_table["save_confirm_message"], gui::OK_ONLY);
} catch(gamestatus::save_game_failed& e) {
gui::show_dialog(gui_,NULL,"",string_table["save_game_failed"],gui::MESSAGE);

View file

@ -106,11 +106,12 @@ bool replay::skipping() const
return at_end() == false && skip_ != 0;
}
void replay::save_game(game_data& data, const std::string& label, const config& start_pos,
bool include_replay)
void replay::save_game(game_data& data, const std::string& label, const config& snapshot,
const config& starting_pos, bool include_replay)
{
log_scope("replay::save_game");
saveInfo_.starting_pos = start_pos;
saveInfo_.snapshot = snapshot;
saveInfo_.starting_pos = starting_pos;
if(include_replay)
saveInfo_.replay_data = cfg_;
@ -122,7 +123,7 @@ void replay::save_game(game_data& data, const std::string& label, const config&
::save_game(saveInfo_);
saveInfo_.replay_data = config();
saveInfo_.starting_pos = config();
saveInfo_.snapshot = config();
}
void replay::add_recruit(int value, const gamemap::location& loc)

View file

@ -37,7 +37,7 @@ public:
void next_skip();
bool skipping() const;
void save_game(game_data& data, const std::string& label, const config& start_pos, bool include_replay=true);
void save_game(game_data& data, const std::string& label, const config& snapshot, const config& starting_pos, bool include_replay=true);
void add_recruit(int unit_index, const gamemap::location& loc);
void add_recall(int unit_index, const gamemap::location& loc);