made multiplayer games replayable
This commit is contained in:
parent
59904c4e45
commit
08977ec4b1
11 changed files with 93 additions and 39 deletions
|
@ -8,7 +8,7 @@ hitpoints=48
|
|||
movement_type=elusivefoot
|
||||
movement=6
|
||||
experience=2
|
||||
level=3
|
||||
level=2
|
||||
alignment=neutral
|
||||
advanceto=Battle Princess
|
||||
cost=110
|
||||
|
|
|
@ -392,6 +392,10 @@ config::~config()
|
|||
|
||||
config& config::operator=(const config& cfg)
|
||||
{
|
||||
if(this == &cfg) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
clear();
|
||||
|
||||
values = cfg.values;
|
||||
|
|
41
src/game.cpp
41
src/game.cpp
|
@ -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
|
||||
|
|
|
@ -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,","));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue