[[No change.]]

This commit is contained in:
Jörg Hinrichs 2006-03-09 17:08:11 +00:00
parent c8d7bfb95d
commit d0e95571f0
9 changed files with 1464 additions and 25 deletions

20
src/play_controller.cpp Normal file
View file

@ -0,0 +1,20 @@
#include "play_controller.hpp"
#include "replay.hpp"
play_controller::play_controller(const config& level, game_state& state_of_game, int ticks, int num_turns) :
level_(level), ticks_(ticks),
gamestate_(state_of_game), status_(level, num_turns)
{
init();
}
void play_controller::init(){
//if the recorder has no event, adds an "game start" event to the
//recorder, whose only goal is to initialize the RNG
if(recorder.empty()) {
recorder.add_start();
} else {
recorder.pre_replay();
}
recorder.set_skip(false);
}

40
src/play_controller.hpp Normal file
View file

@ -0,0 +1,40 @@
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef PLAY_CONTROLLER_H_INCLUDED
#define PLAY_CONTROLLER_H_INCLUDED
#include "global.hpp"
#include "gamestatus.hpp"
#include "hotkeys.hpp"
#include "playlevel.hpp"
#include <vector>
class play_controller
{
public:
play_controller(const config& level, game_state& state_of_game, int ticks, int num_turns);
protected:
virtual void init();
const config& level_;
game_state& gamestate_;
gamestatus status_;
const int ticks_;
private:
};
#endif

View file

@ -19,10 +19,12 @@
#include "playcampaign.hpp"
#include "playlevel.hpp"
#include "config.hpp"
#include "filesystem.hpp"
#include "gamestatus.hpp"
#include "map_create.hpp"
//30.12.2005 YogiHH
//please keep in for the moment, supports me in merging gameplay and replay functionality
//#include "play_controller.hpp"
#include "playsingle_controller.hpp"
#include "replay.hpp"
#include "replay_controller.hpp"
#include "log.hpp"
@ -203,8 +205,41 @@ LEVEL_RESULT play_game(display& disp, game_state& state, const config& game_conf
if (state.label.empty())
state.label = (*scenario)["name"];
LEVEL_RESULT res = play_level(units_data,game_config,scenario,video,state,story,log, skip_replay);
//LEVEL_RESULT res = play_scenario(units_data,game_config,scenario,video,state,story);
//if the entire scenario should be randomly generated
if((*scenario)["scenario_generation"] != "") {
LOG_G << "randomly generating scenario...\n";
const cursor::setter cursor_setter(cursor::WAIT);
static config scenario2;
scenario2 = random_generate_scenario((*scenario)["scenario_generation"], scenario->child("generator"));
//level_ = scenario;
state.starting_pos = scenario2;
}
std::string map_data = (*scenario)["map_data"];
if(map_data == "" && (*scenario)["map"] != "") {
map_data = read_map((*scenario)["map"]);
}
//if the map should be randomly generated
if(map_data == "" && (*scenario)["map_generation"] != "") {
const cursor::setter cursor_setter(cursor::WAIT);
map_data = random_generate_map((*scenario)["map_generation"],scenario->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 = *scenario;
new_level.values["map_data"] = map_data;
scenario = &new_level;
state.starting_pos = new_level;
LOG_G << "generated map\n";
}
//LEVEL_RESULT res = play_level(units_data,game_config,scenario,video,state,story,log, skip_replay);
LEVEL_RESULT res = playsingle_scenario(units_data,game_config,&starting_pos,video,state,story,log, skip_replay);
state.snapshot = config();
if (res == DEFEAT) {

654
src/playmp_controller.cpp Normal file
View file

@ -0,0 +1,654 @@
#include "global.hpp"
#include "ai_interface.hpp"
#include "config_adapter.hpp"
#include "cursor.hpp"
#include "display.hpp"
#include "filesystem.hpp"
#include "font.hpp"
#include "game_events.hpp"
#include "gettext.hpp"
#include "halo.hpp"
#include "help.hpp"
#include "intro.hpp"
#include "key.hpp"
#include "log.hpp"
#include "map_create.hpp"
#include "marked-up_text.hpp"
#include "playmp_controller.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "preferences_display.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "tooltips.hpp"
#define LOG_NG LOG_STREAM(info, engine)
LEVEL_RESULT playmp_scenario(const game_data& gameinfo, const config& game_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& log, bool skip_replay)
{
// try{
const int ticks = SDL_GetTicks();
const int num_turns = atoi((*level)["turns"].c_str());
playmp_controller playcontroller(*level, state_of_game, ticks, num_turns);
return playcontroller.play_scenario(gameinfo, game_config, level, video, state_of_game, story, log, skip_replay);
//return LEVEL_CONTINUE;
//play event-loop
// for (;;){
// replaycontroller.replay_slice();
// }
// }
// catch(end_level_exception&){
// }
}
playmp_controller::playmp_controller(const config& level, game_state& state_of_game, const int ticks, const int num_turns)
: play_controller(level, state_of_game, ticks, num_turns),
generator_setter(&recorder), cursor_setter(cursor::NORMAL)
{
}
LEVEL_RESULT playmp_controller::play_scenario(const game_data& gameinfo, const config& game_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& log, bool skip_replay)
{
const int ticks = SDL_GetTicks();
LOG_NG << "in playmp_controller::play_scenario()...\n";
//if the entire scenario should be randomly generated
if((*level)["scenario_generation"] != "") {
LOG_NG << "randomly generating scenario...\n";
const cursor::setter cursor_setter(cursor::WAIT);
static config scenario;
scenario = random_generate_scenario((*level)["scenario_generation"],level->child("generator"));
level = &scenario;
state_of_game.starting_pos = scenario;
}
std::string map_data = (*level)["map_data"];
if(map_data == "" && (*level)["map"] != "") {
map_data = read_map((*level)["map"]);
}
//if the map should be randomly generated
if(map_data == "" && (*level)["map_generation"] != "") {
const cursor::setter cursor_setter(cursor::WAIT);
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 config& lvl = *level;
LOG_NG << "generated map " << (SDL_GetTicks() - ticks) << "\n";
const statistics::scenario_context statistics_context(lvl["name"]);
const int num_turns = atoi(lvl["turns"].c_str());
gamestatus status(*level,num_turns);
gamemap map(game_config,map_data);
LOG_NG << "created objects... " << (SDL_GetTicks() - ticks) << "\n";
CKey key;
unit_map units;
const verification_manager verify_manager(units);
const int xp_modifier = atoi(lvl["experience_modifier"].c_str());
const unit_type::experience_accelerator xp_mod(xp_modifier > 0 ? xp_modifier : 100);
std::vector<team> teams;
teams_manager team_manager(teams);
int first_human_team = -1;
const config::child_list& unit_cfg = level->get_children("side");
if(lvl["modify_placing"] == "true") {
LOG_NG << "modifying placing...\n";
play::place_sides_in_preferred_locations(map,unit_cfg);
}
LOG_NG << "initializing teams..." << unit_cfg.size() << "\n";;
LOG_NG << (SDL_GetTicks() - ticks) << "\n";
std::set<std::string> seen_save_ids;
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
std::string save_id = get_unique_saveid(**ui, seen_save_ids);
seen_save_ids.insert(save_id);
if (first_human_team == -1){
first_human_team = get_first_human_team(ui, unit_cfg);
}
get_player_info(**ui, state_of_game, save_id, teams, lvl, gameinfo, map, units);
}
preferences::encounter_recruitable_units(teams);
preferences::encounter_start_units(units);
preferences::encounter_recallable_units(state_of_game);
preferences::encounter_map_terrain(map);
LOG_NG << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
const config* theme_cfg = NULL;
if(lvl["theme"] != "") {
theme_cfg = game_config.find_child("theme","name",lvl["theme"]);
}
if(theme_cfg == NULL) {
theme_cfg = game_config.find_child("theme","name",preferences::theme());
}
LOG_NG << "initializing display... " << (SDL_GetTicks() - ticks) << "\n";
const config dummy_cfg;
display gui(units,video,map,status,teams,theme_cfg != NULL ? *theme_cfg : dummy_cfg, game_config, *level);
theme::set_known_themes(&game_config);
LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks) << "\n";
LOG_NG << "a... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
gui.set_team(first_human_team);
}
const preferences::display_manager prefs_disp_manager(&gui);
const tooltips::manager tooltips_manager(gui.video());
LOG_NG << "b... " << (SDL_GetTicks() - ticks) << "\n";
//this *needs* to be created before the show_intro and show_map_scene
//as that functions use the manager state_of_game
game_events::manager events_manager(*level,gui,map,units,teams,
state_of_game,status,gameinfo);
if(!recorder.is_skipping()) {
for(std::vector<config*>::const_iterator story_i = story.begin(); story_i != story.end(); ++story_i) {
show_intro(gui,**story_i, *level);
}
}
//object that will make sure that labels are removed at the end of the scenario
const font::floating_label_context labels_manager;
const halo::manager halo_manager(gui);
gui.labels().read(*level);
LOG_NG << "c... " << (SDL_GetTicks() - ticks) << "\n";
const std::string& music = lvl["music"];
if(music != "") {
sound::play_music_repeatedly(music);
}
LOG_NG << "d... " << (SDL_GetTicks() - ticks) << "\n";
victory_conditions::set_victory_when_enemies_defeated(
lvl["victory_when_enemies_defeated"] != "no");
LOG_NG << "initializing events manager... " << (SDL_GetTicks() - ticks) << "\n";
help::help_manager help_manager(&game_config, &gameinfo, &map);
//find a list of 'items' (i.e. overlays) on the level, and add them
const config::child_list& overlays = level->get_children("item");
for(config::child_list::const_iterator overlay = overlays.begin(); overlay != overlays.end(); ++overlay) {
gui.add_overlay(gamemap::location(**overlay),(**overlay)["image"], (**overlay)["halo"]);
}
int turn = 1, player_number = 0;
turn_info::floating_textbox textbox_info;
LOG_NG << "entering try... " << (SDL_GetTicks() - ticks) << "\n";
replay_network_sender replay_sender(recorder);
try {
//if a team is specified whose turn it is, it means we're loading a game
//instead of starting a fresh one
const bool loading_game = lvl["playing_team"].empty() == false;
//pre-start events must be executed before any GUI operation,
//as those may cause the display to be refreshed.
if(!loading_game) {
update_locker lock_display(gui.video());
game_events::fire("prestart");
}
gui.begin_game();
gui.adjust_colours(0,0,0);
LOG_NG << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
LOG_NG << "b " << (SDL_GetTicks() - ticks) << "\n";
gui.scroll_to_tile(map.starting_position(first_human_team + 1).x,
map.starting_position(first_human_team + 1).y, display::WARP);
LOG_NG << "c " << (SDL_GetTicks() - ticks) << "\n";
}
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,display::WARP);
LOG_NG << "done scrolling... " << (SDL_GetTicks() - ticks) << "\n";
bool replaying = (recorder.at_end() == false);
int first_player = atoi(lvl["playing_team"].c_str());
if(first_player < 0 || first_player >= int(teams.size())) {
first_player = 0;
}
for(std::vector<team>::iterator t = teams.begin(); t != teams.end(); ++t) {
clear_shroud(gui,status,map,gameinfo,units,teams,(t-teams.begin()));
}
std::deque<config> data_backlog;
LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks) << "\n";
for(bool first_time = true; true; first_time = false, first_player = 0) {
if(first_time) {
const hotkey::basic_handler key_events_handler(&gui);
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui.video(),recorder.is_skipping());
events::raise_draw_event();
gui.draw();
for(std::vector<team>::iterator t = teams.begin(); t != teams.end(); ++t) {
clear_shroud(gui,status,map,gameinfo,units,teams,(t-teams.begin()));
}
if(!loading_game) {
game_events::fire("start");
state_of_game.set_variable("turn_number", "1");
}
gui.recalculate_minimap();
}
player_number = 0;
gui.new_turn();
gui.invalidate_game_status();
events::raise_draw_event();
LOG_NG << "turn: " << turn++ << "\n";
for(std::vector<team>::iterator team_it = teams.begin()+first_player; team_it != teams.end(); ++team_it) {
log_scope("player turn");
player_number = (team_it - teams.begin()) + 1;
//if a side is dead, don't do their turn
if(team_it->is_empty() || team_units(units,player_number) == 0) {
continue;
}
if(team_manager.is_observer()) {
gui.set_team(size_t(player_number-1));
}
std::stringstream player_number_str;
player_number_str << player_number;
state_of_game.set_variable("side_number",player_number_str.str());
//fire side turn event only if real side change occurs not counting changes from void to a side
if (team_it != teams.begin()+first_player || !first_time) {
game_events::fire("side turn");
}
//we want to work out if units for this player should get healed, and the
//player should get income now. healing/income happen if it's not the first
//turn of processing, or if we are loading a game, and this is not the
//player it started with.
const bool turn_refresh = !first_time || loading_game && team_it != teams.begin()+first_player;
if(turn_refresh) {
for(unit_map::iterator i = units.begin(); i != units.end(); ++i) {
if(i->second.side() == player_number) {
i->second.new_turn();
}
}
team_it->new_turn();
//if the expense is less than the number of villages owned,
//then we don't have to pay anything at all
const int expense = team_upkeep(units,player_number) -
team_it->villages().size();
if(expense > 0) {
team_it->spend_gold(expense);
}
calculate_healing(gui,status,map,units,player_number,teams, !skip_replay);
}
team_it->set_time_of_day(int(status.turn()),status.get_time_of_day());
gui.set_playing_team(size_t(player_number-1));
clear_shroud(gui,status,map,gameinfo,units,teams,player_number-1);
if (!skip_replay){
gui.scroll_to_leader(units, player_number);
}
if(replaying) {
const hotkey::basic_handler key_events_handler(&gui);
LOG_NG << "doing replay " << player_number << "\n";
try {
replaying = do_replay(gui,map,gameinfo,units,teams,
player_number,status,state_of_game);
} catch(replay::error&) {
gui::show_dialog(gui,NULL,"",_("The file you have tried to load is corrupt"),gui::OK_ONLY);
replaying = false;
}
LOG_NG << "result of replay: " << (replaying?"true":"false") << "\n";
}
if(!replaying && team_it->music().empty() == false &&
(teams[gui.viewing_team()].knows_about_team(player_number-1) || teams[gui.viewing_team()].has_seen(player_number-1))) {
LOG_NG << "playing music: '" << team_it->music() << "'\n";
sound::play_music_repeatedly(team_it->music());
} else if(!replaying && team_it->music().empty() == false){
LOG_NG << "playing music: '" << game_config::anonymous_music<< "'\n";
sound::play_music_repeatedly(game_config::anonymous_music);
}
// else leave old music playing, it's a scenario specific music
//goto this label if the type of a team (human/ai/networked) has changed mid-turn
redo_turn:
if(!replaying && team_it->is_human()) {
LOG_NG << "is human...\n";
try {
play_turn(gameinfo,state_of_game,status,game_config,
*level, key, gui, map, teams, player_number,
units, textbox_info, replay_sender, skip_replay);
} catch(end_turn_exception& end_turn) {
if (end_turn.redo == player_number)
goto redo_turn;
}
if(game_config::debug)
display::clear_debug_highlights();
LOG_NG << "human finished turn...\n";
} else if(!replaying && team_it->is_ai()) {
LOG_NG << "is ai...\n";
gui.recalculate_minimap();
const cursor::setter cursor_setter(cursor::WAIT);
turn_info turn_data(gameinfo,state_of_game,status,
game_config,*level,key,gui,
map,teams,player_number,units,
turn_info::BROWSE_AI,textbox_info,replay_sender);
ai_interface::info ai_info(gui,map,gameinfo,units,teams,player_number,status,turn_data);
util::scoped_ptr<ai_interface> ai_obj(create_ai(team_it->ai_algorithm(),ai_info));
ai_obj->play_turn();
recorder.end_turn();
ai_obj->sync_network();
gui.recalculate_minimap();
clear_shroud(gui,status,map,gameinfo,units,teams,player_number-1);
gui.invalidate_unit();
gui.invalidate_game_status();
gui.invalidate_all();
gui.draw();
SDL_Delay(500);
} else if(!replaying && team_it->is_network()) {
LOG_NG << "is networked...\n";
turn_info turn_data(gameinfo,state_of_game,status,
game_config,*level,key,gui,
map,teams,player_number,units,
turn_info::BROWSE_NETWORKED,
textbox_info,replay_sender);
for(;;) {
bool have_data = false;
config cfg;
network::connection from = network::null_connection;
if(data_backlog.empty() == false) {
have_data = true;
cfg = data_backlog.front();
data_backlog.pop_front();
} else {
from = network::receive_data(cfg);
have_data = from != network::null_connection;
}
if(have_data) {
const turn_info::PROCESS_DATA_RESULT result = turn_data.process_network_data(cfg,from,data_backlog,skip_replay);
if(result == turn_info::PROCESS_RESTART_TURN) {
goto redo_turn;
} else if(result == turn_info::PROCESS_END_TURN) {
break;
}
}
turn_data.turn_slice();
turn_data.send_data();
gui.draw();
}
LOG_NG << "finished networked...\n";
}
for(unit_map::iterator uit = units.begin(); uit != units.end(); ++uit) {
if(uit->second.side() == player_number)
uit->second.end_turn();
}
//This implements "delayed map sharing." It's meant as an alternative to shared vision.
if(team_it->copy_ally_shroud()) {
gui.recalculate_minimap();
gui.invalidate_all();
}
game_events::pump();
check_victory(units,teams);
}
//time has run out
if(!status.next_turn()) {
if(non_interactive()) {
std::cout << "time over (draw)\n";
}
LOG_NG << "firing time over event...\n";
game_events::fire("time over");
LOG_NG << "done firing time over event...\n";
throw end_level_exception(DEFEAT);
}
std::stringstream event_stream;
event_stream << status.turn();
{
LOG_NG << "turn event..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui.video(),recorder.is_skipping());
const std::string turn_num = event_stream.str();
state_of_game.set_variable("turn_number",turn_num);
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
}
} //end for loop
} catch(end_level_exception& end_level) {
bool obs = team_manager.is_observer();
if (end_level.result == DEFEAT || end_level.result == VICTORY) {
// if we're a player, and the result is victory/defeat, then send a message to notify
// the server of the reason for the game ending
if (!obs) {
config cfg;
config& info = cfg.add_child("info");
info["type"] = "termination";
info["condition"] = "game over";
network::send_data(cfg);
} else {
gui::show_dialog(gui, NULL, _("Game Over"),
_("The game is over."), gui::OK_ONLY);
return QUIT;
}
}
if(end_level.result == QUIT) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
}
if (!obs)
return DEFEAT;
else
return QUIT;
} else if (end_level.result == VICTORY || end_level.result == LEVEL_CONTINUE ||
end_level.result == LEVEL_CONTINUE_NO_SAVE) {
try {
game_events::fire("victory");
} catch(end_level_exception&) {
}
if(state_of_game.scenario == (*level)["id"]) {
state_of_game.scenario = (*level)["next_scenario"];
}
const bool has_next_scenario = !state_of_game.scenario.empty() &&
state_of_game.scenario != "null";
//add all the units that survived the scenario
for(std::map<gamemap::location,unit>::iterator un = units.begin(); un != units.end(); ++un) {
player_info *player=state_of_game.get_player(teams[un->second.side()-1].save_id());
if(player) {
un->second.new_turn();
un->second.new_level();
player->available_units.push_back(un->second);
}
}
//'continue' is like a victory, except it doesn't announce victory,
//and the player retains 100% of gold.
if(end_level.result == LEVEL_CONTINUE || end_level.result == LEVEL_CONTINUE_NO_SAVE) {
for(std::vector<team>::iterator i=teams.begin(); i!=teams.end(); ++i) {
player_info *player=state_of_game.get_player(i->save_id());
if(player) {
player->gold = i->gold();
}
}
return end_level.result == LEVEL_CONTINUE_NO_SAVE ? LEVEL_CONTINUE_NO_SAVE : VICTORY;
}
std::stringstream report;
for(std::vector<team>::iterator i=teams.begin(); i!=teams.end(); ++i) {
if (!i->is_persistent())
continue;
player_info *player=state_of_game.get_player(i->save_id());
const int remaining_gold = i->gold();
const int finishing_bonus_per_turn =
map.villages().size() * game_config::village_income +
game_config::base_income;
const int turns_left = maximum<int>(0,status.number_of_turns() - status.turn());
const int finishing_bonus = end_level.gold_bonus ?
(finishing_bonus_per_turn * turns_left) : 0;
if(player) {
player->gold = ((remaining_gold + finishing_bonus) * 80) / 100;
if(state_of_game.players.size()>1) {
if(i!=teams.begin()) {
report << "\n";
}
report << font::BOLD_TEXT << i->save_id() << "\n";
}
report << _("Remaining gold: ")
<< remaining_gold << "\n";
if(end_level.gold_bonus) {
report << _("Early finish bonus: ")
<< finishing_bonus_per_turn
<< " " << _("per turn") << "\n"
<< _("Turns finished early: ")
<< turns_left << "\n"
<< _("Bonus: ")
<< finishing_bonus << "\n"
<< _("Gold: ")
<< (remaining_gold+finishing_bonus);
}
// xgettext:no-c-format
report << '\n' << _("80% of gold is retained for the next scenario") << '\n'
<< _("Retained Gold: ") << player->gold;
}
}
if (!obs)
gui::show_dialog(gui, NULL, _("Victory"),
_("You have emerged victorious!"), gui::OK_ONLY);
if (state_of_game.players.size() > 0 && has_next_scenario ||
state_of_game.campaign_type == "test")
gui::show_dialog(gui, NULL, _("Scenario Report"), report.str(), gui::OK_ONLY);
return VICTORY;
}
} //end catch
catch(replay::error&) {
gui::show_dialog(gui,NULL,"",_("The file you have tried to load is corrupt"),
gui::OK_ONLY);
return QUIT;
}
catch(network::error& e) {
bool disconnect = false;
if(e.socket) {
e.disconnect();
disconnect = true;
}
turn_info turn_data(gameinfo,state_of_game,status,
game_config,*level,key,gui,
map,teams,player_number,units,turn_info::BROWSE_NETWORKED,textbox_info,replay_sender);
turn_data.save_game(_("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();
} else {
return QUIT;
}
}
return QUIT;
}

44
src/playmp_controller.hpp Normal file
View file

@ -0,0 +1,44 @@
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef PLAYMP_CONTROLLER_H_INCLUDED
#define PLAYMP_CONTROLLER_H_INCLUDED
#include "hotkeys.hpp"
#include "playlevel.hpp"
#include "play_controller.hpp"
#include "random.hpp"
#include <vector>
class playmp_controller : public play_controller
{
public:
playmp_controller(const config& level, game_state& state_of_game, const int ticks, const int num_turns);
LEVEL_RESULT play_scenario(const game_data& gameinfo, const config& terrain_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& log, bool skip_replay);
protected:
const set_random_generator generator_setter;
const cursor::setter cursor_setter;
private:
};
LEVEL_RESULT playmp_scenario(const game_data& gameinfo, const config& terrain_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& log, bool skip_replay);
#endif

View file

@ -0,0 +1,612 @@
#include "global.hpp"
#include "ai_interface.hpp"
#include "config_adapter.hpp"
#include "cursor.hpp"
#include "display.hpp"
#include "filesystem.hpp"
#include "font.hpp"
#include "game_events.hpp"
#include "gettext.hpp"
#include "halo.hpp"
#include "help.hpp"
#include "intro.hpp"
#include "key.hpp"
#include "log.hpp"
#include "map_create.hpp"
#include "marked-up_text.hpp"
#include "playsingle_controller.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "preferences_display.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "tooltips.hpp"
#define LOG_NG LOG_STREAM(info, engine)
LEVEL_RESULT playsingle_scenario(const game_data& gameinfo, const config& game_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& log, bool skip_replay)
{
// try{
const int ticks = SDL_GetTicks();
const int num_turns = atoi((*level)["turns"].c_str());
playsingle_controller playcontroller(*level, state_of_game, ticks, num_turns);
return playcontroller.play_scenario(gameinfo, game_config, video, story, log, skip_replay);
//return LEVEL_CONTINUE;
//play event-loop
// for (;;){
// replaycontroller.replay_slice();
// }
// }
// catch(end_level_exception&){}
}
playsingle_controller::playsingle_controller(const config& level, game_state& state_of_game, const int ticks, const int num_turns)
: play_controller(level, state_of_game, ticks, num_turns),
generator_setter(&recorder), cursor_setter(cursor::NORMAL)
{}
LEVEL_RESULT playsingle_controller::play_scenario(const game_data& gameinfo, const config& game_config,
CVideo& video,
const std::vector<config*>& story, upload_log& log, bool skip_replay)
{
const int ticks = SDL_GetTicks();
LOG_NG << "in playsingle_controller::play_scenario()...\n";
const statistics::scenario_context statistics_context(level_["name"]);
gamemap map(game_config,level_["map_data"]);
LOG_NG << "created objects... " << (SDL_GetTicks() - ticks) << "\n";
CKey key;
unit_map units;
const verification_manager verify_manager(units);
const int xp_modifier = atoi(level_["experience_modifier"].c_str());
const unit_type::experience_accelerator xp_mod(xp_modifier > 0 ? xp_modifier : 100);
std::vector<team> teams;
teams_manager team_manager(teams);
int first_human_team = -1;
const config::child_list& unit_cfg = level_.get_children("side");
if(level_["modify_placing"] == "true") {
LOG_NG << "modifying placing...\n";
play::place_sides_in_preferred_locations(map,unit_cfg);
}
LOG_NG << "initializing teams..." << unit_cfg.size() << "\n";;
LOG_NG << (SDL_GetTicks() - ticks) << "\n";
std::set<std::string> seen_save_ids;
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
std::string save_id = get_unique_saveid(**ui, seen_save_ids);
seen_save_ids.insert(save_id);
if (first_human_team == -1){
first_human_team = get_first_human_team(ui, unit_cfg);
}
get_player_info(**ui, gamestate_, save_id, teams, level_, gameinfo, map, units);
}
preferences::encounter_recruitable_units(teams);
preferences::encounter_start_units(units);
preferences::encounter_recallable_units(gamestate_);
preferences::encounter_map_terrain(map);
LOG_NG << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
const config* theme_cfg = NULL;
if(level_["theme"] != "") {
theme_cfg = game_config.find_child("theme","name",level_["theme"]);
}
if(theme_cfg == NULL) {
theme_cfg = game_config.find_child("theme","name",preferences::theme());
}
LOG_NG << "initializing display... " << (SDL_GetTicks() - ticks) << "\n";
const config dummy_cfg;
display gui(units,video,map,status_,teams,theme_cfg != NULL ? *theme_cfg : dummy_cfg, game_config, level_);
theme::set_known_themes(&game_config);
LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks) << "\n";
LOG_NG << "a... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
gui.set_team(first_human_team);
}
const preferences::display_manager prefs_disp_manager(&gui);
const tooltips::manager tooltips_manager(gui.video());
LOG_NG << "b... " << (SDL_GetTicks() - ticks) << "\n";
//this *needs* to be created before the show_intro and show_map_scene
//as that functions use the manager state_of_game
game_events::manager events_manager(level_,gui,map,units,teams,
gamestate_,status_,gameinfo);
if(!recorder.is_skipping()) {
for(std::vector<config*>::const_iterator story_i = story.begin(); story_i != story.end(); ++story_i) {
show_intro(gui,**story_i, level_);
}
}
//object that will make sure that labels are removed at the end of the scenario
const font::floating_label_context labels_manager;
const halo::manager halo_manager(gui);
gui.labels().read(level_);
LOG_NG << "c... " << (SDL_GetTicks() - ticks) << "\n";
const std::string& music = level_["music"];
if(music != "") {
sound::play_music_repeatedly(music);
}
LOG_NG << "d... " << (SDL_GetTicks() - ticks) << "\n";
victory_conditions::set_victory_when_enemies_defeated(
level_["victory_when_enemies_defeated"] != "no");
LOG_NG << "initializing events manager... " << (SDL_GetTicks() - ticks) << "\n";
help::help_manager help_manager(&game_config, &gameinfo, &map);
//find a list of 'items' (i.e. overlays) on the level, and add them
const config::child_list& overlays = level_.get_children("item");
for(config::child_list::const_iterator overlay = overlays.begin(); overlay != overlays.end(); ++overlay) {
gui.add_overlay(gamemap::location(**overlay),(**overlay)["image"], (**overlay)["halo"]);
}
int turn = 1, player_number = 0;
turn_info::floating_textbox textbox_info;
LOG_NG << "entering try... " << (SDL_GetTicks() - ticks) << "\n";
replay_network_sender replay_sender(recorder);
try {
//if a team is specified whose turn it is, it means we're loading a game
//instead of starting a fresh one
const bool loading_game = level_["playing_team"].empty() == false;
//pre-start events must be executed before any GUI operation,
//as those may cause the display to be refreshed.
if(!loading_game) {
update_locker lock_display(gui.video());
game_events::fire("prestart");
}
gui.begin_game();
gui.adjust_colours(0,0,0);
LOG_NG << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
LOG_NG << "b " << (SDL_GetTicks() - ticks) << "\n";
gui.scroll_to_tile(map.starting_position(first_human_team + 1).x,
map.starting_position(first_human_team + 1).y, display::WARP);
LOG_NG << "c " << (SDL_GetTicks() - ticks) << "\n";
}
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,display::WARP);
LOG_NG << "done scrolling... " << (SDL_GetTicks() - ticks) << "\n";
bool replaying = (recorder.at_end() == false);
int first_player = atoi(level_["playing_team"].c_str());
if(first_player < 0 || first_player >= int(teams.size())) {
first_player = 0;
}
for(std::vector<team>::iterator t = teams.begin(); t != teams.end(); ++t) {
clear_shroud(gui,status_,map,gameinfo,units,teams,(t-teams.begin()));
}
std::deque<config> data_backlog;
LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks) << "\n";
for(bool first_time = true; true; first_time = false, first_player = 0) {
if(first_time) {
const hotkey::basic_handler key_events_handler(&gui);
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui.video(),recorder.is_skipping());
events::raise_draw_event();
gui.draw();
for(std::vector<team>::iterator t = teams.begin(); t != teams.end(); ++t) {
clear_shroud(gui,status_,map,gameinfo,units,teams,(t-teams.begin()));
}
if(!loading_game) {
game_events::fire("start");
gamestate_.set_variable("turn_number", "1");
}
gui.recalculate_minimap();
}
player_number = 0;
gui.new_turn();
gui.invalidate_game_status();
events::raise_draw_event();
LOG_NG << "turn: " << turn++ << "\n";
for(std::vector<team>::iterator team_it = teams.begin()+first_player; team_it != teams.end(); ++team_it) {
log_scope("player turn");
player_number = (team_it - teams.begin()) + 1;
//if a side is dead, don't do their turn
if(team_it->is_empty() || team_units(units,player_number) == 0) {
continue;
}
if(team_manager.is_observer()) {
gui.set_team(size_t(player_number-1));
}
std::stringstream player_number_str;
player_number_str << player_number;
gamestate_.set_variable("side_number",player_number_str.str());
//fire side turn event only if real side change occurs not counting changes from void to a side
if (team_it != teams.begin()+first_player || !first_time) {
game_events::fire("side turn");
}
//we want to work out if units for this player should get healed, and the
//player should get income now. healing/income happen if it's not the first
//turn of processing, or if we are loading a game, and this is not the
//player it started with.
const bool turn_refresh = !first_time || loading_game && team_it != teams.begin()+first_player;
if(turn_refresh) {
for(unit_map::iterator i = units.begin(); i != units.end(); ++i) {
if(i->second.side() == player_number) {
i->second.new_turn();
}
}
team_it->new_turn();
//if the expense is less than the number of villages owned,
//then we don't have to pay anything at all
const int expense = team_upkeep(units,player_number) -
team_it->villages().size();
if(expense > 0) {
team_it->spend_gold(expense);
}
calculate_healing(gui,status_,map,units,player_number,teams, !skip_replay);
}
team_it->set_time_of_day(int(status_.turn()),status_.get_time_of_day());
gui.set_playing_team(size_t(player_number-1));
clear_shroud(gui,status_,map,gameinfo,units,teams,player_number-1);
if (!skip_replay){
gui.scroll_to_leader(units, player_number);
}
if(replaying) {
const hotkey::basic_handler key_events_handler(&gui);
LOG_NG << "doing replay " << player_number << "\n";
try {
replaying = do_replay(gui,map,gameinfo,units,teams,
player_number,status_,gamestate_);
} catch(replay::error&) {
gui::show_dialog(gui,NULL,"",_("The file you have tried to load is corrupt"),gui::OK_ONLY);
replaying = false;
}
LOG_NG << "result of replay: " << (replaying?"true":"false") << "\n";
}
if(!replaying && team_it->music().empty() == false &&
(teams[gui.viewing_team()].knows_about_team(player_number-1) || teams[gui.viewing_team()].has_seen(player_number-1))) {
LOG_NG << "playing music: '" << team_it->music() << "'\n";
sound::play_music_repeatedly(team_it->music());
} else if(!replaying && team_it->music().empty() == false){
LOG_NG << "playing music: '" << game_config::anonymous_music<< "'\n";
sound::play_music_repeatedly(game_config::anonymous_music);
}
// else leave old music playing, it's a scenario specific music
//goto this label if the type of a team (human/ai/networked) has changed mid-turn
redo_turn:
if(!replaying && team_it->is_human()) {
LOG_NG << "is human...\n";
try {
play_turn(gameinfo,gamestate_,status_,game_config,
level_, key, gui, map, teams, player_number,
units, textbox_info, replay_sender, skip_replay);
} catch(end_turn_exception& end_turn) {
if (end_turn.redo == player_number)
goto redo_turn;
}
if(game_config::debug)
display::clear_debug_highlights();
LOG_NG << "human finished turn...\n";
} else if(!replaying && team_it->is_ai()) {
LOG_NG << "is ai...\n";
gui.recalculate_minimap();
const cursor::setter cursor_setter(cursor::WAIT);
turn_info turn_data(gameinfo,gamestate_,status_,
game_config,level_,key,gui,
map,teams,player_number,units,
turn_info::BROWSE_AI,textbox_info,replay_sender);
ai_interface::info ai_info(gui,map,gameinfo,units,teams,player_number,status_,turn_data);
util::scoped_ptr<ai_interface> ai_obj(create_ai(team_it->ai_algorithm(),ai_info));
ai_obj->play_turn();
recorder.end_turn();
ai_obj->sync_network();
gui.recalculate_minimap();
clear_shroud(gui,status_,map,gameinfo,units,teams,player_number-1);
gui.invalidate_unit();
gui.invalidate_game_status();
gui.invalidate_all();
gui.draw();
SDL_Delay(500);
} else if(!replaying && team_it->is_network()) {
LOG_NG << "is networked...\n";
turn_info turn_data(gameinfo,gamestate_,status_,
game_config,level_,key,gui,
map,teams,player_number,units,
turn_info::BROWSE_NETWORKED,
textbox_info,replay_sender);
for(;;) {
bool have_data = false;
config cfg;
network::connection from = network::null_connection;
if(data_backlog.empty() == false) {
have_data = true;
cfg = data_backlog.front();
data_backlog.pop_front();
} else {
from = network::receive_data(cfg);
have_data = from != network::null_connection;
}
if(have_data) {
const turn_info::PROCESS_DATA_RESULT result = turn_data.process_network_data(cfg,from,data_backlog,skip_replay);
if(result == turn_info::PROCESS_RESTART_TURN) {
goto redo_turn;
} else if(result == turn_info::PROCESS_END_TURN) {
break;
}
}
turn_data.turn_slice();
turn_data.send_data();
gui.draw();
}
LOG_NG << "finished networked...\n";
}
for(unit_map::iterator uit = units.begin(); uit != units.end(); ++uit) {
if(uit->second.side() == player_number)
uit->second.end_turn();
}
//This implements "delayed map sharing." It's meant as an alternative to shared vision.
if(team_it->copy_ally_shroud()) {
gui.recalculate_minimap();
gui.invalidate_all();
}
game_events::pump();
check_victory(units,teams);
}
//time has run out
if(!status_.next_turn()) {
if(non_interactive()) {
std::cout << "time over (draw)\n";
}
LOG_NG << "firing time over event...\n";
game_events::fire("time over");
LOG_NG << "done firing time over event...\n";
throw end_level_exception(DEFEAT);
}
std::stringstream event_stream;
event_stream << status_.turn();
{
LOG_NG << "turn event..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui.video(),recorder.is_skipping());
const std::string turn_num = event_stream.str();
gamestate_.set_variable("turn_number",turn_num);
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
}
} //end for loop
} catch(end_level_exception& end_level) {
bool obs = team_manager.is_observer();
if (end_level.result == DEFEAT || end_level.result == VICTORY) {
// if we're a player, and the result is victory/defeat, then send a message to notify
// the server of the reason for the game ending
if (!obs) {
config cfg;
config& info = cfg.add_child("info");
info["type"] = "termination";
info["condition"] = "game over";
network::send_data(cfg);
} else {
gui::show_dialog(gui, NULL, _("Game Over"),
_("The game is over."), gui::OK_ONLY);
return QUIT;
}
}
if(end_level.result == QUIT) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
}
if (!obs)
return DEFEAT;
else
return QUIT;
} else if (end_level.result == VICTORY || end_level.result == LEVEL_CONTINUE ||
end_level.result == LEVEL_CONTINUE_NO_SAVE) {
try {
game_events::fire("victory");
} catch(end_level_exception&) {
}
if(gamestate_.scenario == (level_)["id"]) {
gamestate_.scenario = (level_)["next_scenario"];
}
const bool has_next_scenario = !gamestate_.scenario.empty() &&
gamestate_.scenario != "null";
//add all the units that survived the scenario
for(std::map<gamemap::location,unit>::iterator un = units.begin(); un != units.end(); ++un) {
player_info *player=gamestate_.get_player(teams[un->second.side()-1].save_id());
if(player) {
un->second.new_turn();
un->second.new_level();
player->available_units.push_back(un->second);
}
}
//'continue' is like a victory, except it doesn't announce victory,
//and the player retains 100% of gold.
if(end_level.result == LEVEL_CONTINUE || end_level.result == LEVEL_CONTINUE_NO_SAVE) {
for(std::vector<team>::iterator i=teams.begin(); i!=teams.end(); ++i) {
player_info *player=gamestate_.get_player(i->save_id());
if(player) {
player->gold = i->gold();
}
}
return end_level.result == LEVEL_CONTINUE_NO_SAVE ? LEVEL_CONTINUE_NO_SAVE : VICTORY;
}
std::stringstream report;
for(std::vector<team>::iterator i=teams.begin(); i!=teams.end(); ++i) {
if (!i->is_persistent())
continue;
player_info *player=gamestate_.get_player(i->save_id());
const int remaining_gold = i->gold();
const int finishing_bonus_per_turn =
map.villages().size() * game_config::village_income +
game_config::base_income;
const int turns_left = maximum<int>(0,status_.number_of_turns() - status_.turn());
const int finishing_bonus = end_level.gold_bonus ?
(finishing_bonus_per_turn * turns_left) : 0;
if(player) {
player->gold = ((remaining_gold + finishing_bonus) * 80) / 100;
if(gamestate_.players.size()>1) {
if(i!=teams.begin()) {
report << "\n";
}
report << font::BOLD_TEXT << i->save_id() << "\n";
}
report << _("Remaining gold: ")
<< remaining_gold << "\n";
if(end_level.gold_bonus) {
report << _("Early finish bonus: ")
<< finishing_bonus_per_turn
<< " " << _("per turn") << "\n"
<< _("Turns finished early: ")
<< turns_left << "\n"
<< _("Bonus: ")
<< finishing_bonus << "\n"
<< _("Gold: ")
<< (remaining_gold+finishing_bonus);
}
// xgettext:no-c-format
report << '\n' << _("80% of gold is retained for the next scenario") << '\n'
<< _("Retained Gold: ") << player->gold;
}
}
if (!obs)
gui::show_dialog(gui, NULL, _("Victory"),
_("You have emerged victorious!"), gui::OK_ONLY);
if (gamestate_.players.size() > 0 && has_next_scenario ||
gamestate_.campaign_type == "test")
gui::show_dialog(gui, NULL, _("Scenario Report"), report.str(), gui::OK_ONLY);
return VICTORY;
}
} //end catch
catch(replay::error&) {
gui::show_dialog(gui,NULL,"",_("The file you have tried to load is corrupt"),
gui::OK_ONLY);
return QUIT;
}
catch(network::error& e) {
bool disconnect = false;
if(e.socket) {
e.disconnect();
disconnect = true;
}
turn_info turn_data(gameinfo,gamestate_,status_,
game_config,level_,key,gui,
map,teams,player_number,units,turn_info::BROWSE_NETWORKED,textbox_info,replay_sender);
turn_data.save_game(_("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();
} else {
return QUIT;
}
}
return QUIT;
}

View file

@ -0,0 +1,45 @@
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef PLAYSINGLE_CONTROLLER_H_INCLUDED
#define PLAYSINGLE_CONTROLLER_H_INCLUDED
#include "cursor.hpp"
#include "hotkeys.hpp"
#include "play_controller.hpp"
#include "playlevel.hpp"
#include "random.hpp"
#include <vector>
class playsingle_controller : play_controller
{
public:
playsingle_controller(const config& level, game_state& state_of_game, const int ticks, const int num_turns);
LEVEL_RESULT play_scenario(const game_data& gameinfo, const config& terrain_config,
CVideo& video,
const std::vector<config*>& story, upload_log& log, bool skip_replay);
protected:
const set_random_generator generator_setter;
const cursor::setter cursor_setter;
private:
};
LEVEL_RESULT playsingle_scenario(const game_data& gameinfo, const config& terrain_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& loo, bool skip_replay);
#endif

View file

@ -49,12 +49,13 @@ LEVEL_RESULT play_replay_level(const game_data& gameinfo, const config& game_con
replay_controller::replay_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
const int ticks, const int num_turns, const config& game_config,
CVideo& video, const std::vector<config*>& story) :
CVideo& video, const std::vector<config*>& story)
: play_controller(level, state_of_game, ticks, num_turns),
verify_manager_(units_), team_manager_(teams_), labels_manager_(), help_manager_(&game_config, &gameinfo, &map_),
level_(level), game_config_(game_config), gameinfo_(gameinfo), gamestate_(state_of_game),
gamestate_start_(state_of_game), status_(level, num_turns), status_start_(level, num_turns),
game_config_(game_config), gameinfo_(gameinfo),
gamestate_start_(state_of_game), status_start_(level, num_turns),
map_(game_config, level["map_data"]), mouse_handler_(gui_, teams_, units_, map_, status_, gameinfo),
ticks_(ticks), xp_modifier_(atoi(level["experience_modifier"].c_str()))
xp_modifier_(atoi(level["experience_modifier"].c_str()))
{
player_number_ = 1;
delay_ = 0;
@ -77,18 +78,6 @@ replay_controller::~replay_controller(){
}
void replay_controller::init(CVideo& video, const std::vector<config*>& /*story*/){
//if the recorder has no event, adds an "game start" event to the
//recorder, whose only goal is to initialize the RNG
if(recorder.empty()) {
recorder.add_start();
} else {
recorder.pre_replay();
}
recorder.set_skip(false);
replay_network_sender replay_sender(recorder);
const set_random_generator generator_setter(&recorder);
//guarantee the cursor goes back to 'normal' at the end of the level
const cursor::setter cursor_setter(cursor::NORMAL);

View file

@ -21,7 +21,8 @@
#include "help.hpp"
#include "hotkeys.hpp"
#include "menu_events.hpp"
#include "mouse_events.hpp"
#include "mouse_events.hpp"
#include "play_controller.hpp"
#include "playlevel.hpp"
#include "preferences_display.hpp"
#include "tooltips.hpp"
@ -29,7 +30,7 @@
#include <vector>
class replay_controller : public hotkey::command_executor, public events::handler
class replay_controller : public play_controller, public hotkey::command_executor, public events::handler
{
public:
replay_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
@ -90,18 +91,17 @@ private:
game_events::manager* events_manager_;
help::help_manager help_manager_;
const config& level_;
//const config& level_;
const config& game_config_;
const game_data& gameinfo_;
game_state& gamestate_, gamestate_start_;
game_state& /*gamestate_,*/ gamestate_start_;
display* gui_;
gamestatus status_, status_start_;
gamestatus /*status_,*/ status_start_;
gamemap map_;
unit_map units_, units_start_;
events::mouse_handler mouse_handler_;
events::menu_handler menu_handler_;
const int ticks_;
int player_number_;
int first_player_;
bool loading_game_;